Criando um bucket S3 customizado com Terraform

Rodolfo Costa
7 min readJan 5, 2022

--

Quando pensamos sobre infraestrutura para sistemas web nos dias de hoje, a primeira opção que vem à nossa cabeça é computação em nuvem. Criar recursos em nuvem pode ser fácil de início, porém quando ela escala, a manutenção pode se tornar complicada.

Além disso, compartilhar o conhecimento com membros da equipe pode, às vezes, acarretar em perda de informação. Diversas razões podem surgir que demandem a criação ou atualização de uma infraestrutura como novos requisitos de uma aplicação, conformidade com segurança da informação, entre outros.

É nesse contexto que adotar a Infraestrutura como Código (IaC) traz um grande valor agregado. Ao usar scripts e/ou arquivos de definição, garante-se que o processo de provisionamento se mantenha sempre o mesmo de uma forma confiável e também facilmente reutilisável. Existem muitas ferramentas que ajudam no provisionamento de recursos computacionais para diferentes plataformas em nuvem. Algumas são exclusivas de suas provedoras como o AWS CloudFormation, enquanto outras possuem integração com várias provedoras.

No caso que demonstraremos aqui, criaremos um simples bucket S3 com algumas configurações de segurança utilizando o Terraform. Essas configurações vieram do nosso time de segurança para nos ajudar a termos os ambientes de nossas aplicações mais seguros e confiáveis.

A tecnologia

Antes de mostrar os scripts propriamente, uma rápida explanação sobre o Terraform. Como descrito no site terraform.io:

Terraform é um software de código aberto de infraestrutura como código que provém um workflow CLI consistente para gerenciar múltiplos serviços em nuvem. Terraform codifica APIs de nuvem em arquivos de configuração declarativos.

Terraform foi escolhido por ser largamente usado, multiplataforma e possuir uma documentação bem amigável e intuitiva. Ele pode ser utilizado para provisionar de simples a complexas infraestruturas, ou seja, componentes independentes ou dependentes nas principais provedoras em nuvem.

Ele também possui uma funcionalidade muito boa, em que ele salva o estado atual dos seus recursos computacionais provisionados. Caso você atualize o código, qualquer modificação detectada será mostrada para ser revisada antes de provisionar. Adicionalmente, quando precisar deletar esses componentes criados, é possível deletá-los com um único comando! Não se preocupe, pois qualquer ação, seja criação, atualização ou destruição, irá exigir uma verificação e confirmação das mudanças a serem aplicadas antes de executá-las de fato.

Eu quero esses bytes!

Aqui estão os dois arquivos responsáveis pela criação do nosso bucket:

  1. main.tf

2. variables.tf

Ambos nomes dos arquivos são usados nos tutoriais do Terraform para nos ajudar a compreender a finalidade de cada arquivo, então mantive para facilitar as coisas. No Terraform, essa coleção de arquivos “.tf” em um mesmo diretório representa um módulo. Uma infraestrutura, que significa os recursos que criaremos, pode ser feita a partir de diversos módulos conectados por meio de um módulo raíz, que será o diretório de trabalho no qual os comandos terraform serão executados.

Nesse caso, temos apenas um módulo que criará um bucket S3 com algumas configurações de segurança. Vamos entender um pouco mais desses arquivos.

variables.tf

É o arquivo onde armazenamos nossas variáveis. Podemos definir uma descrição, tipo e valor padrão, bem como uma validação e outros atributos para uma variável.

Definimos apenas a região da conta AWS e o nome do nosso bucket S3 com uma validação básica do tamanho e caracteres permitidos. Existem outras limitações para nomenclatura de um bucket S3 que não estão contempladas nesse exemplo por se aplicarem a casos específicos de criação de bucket. O bucket para registro em log de acesso ao servidor foi criado previamente (é apenas um bucket padrão vazio sem nenhuma configuração extra).

Para o caso em que o projeto tenha muitas definições de variáveis, utilizar arquivos “.tfvars” pode ser uma alternativa mais prática.

main.tf

É o arquivo em que definimos componentes de infraestrutura que serão provisionados. Em um projeto multi-modular, esse arquivo terá os componentes raíz e chamadas para submódulos.

Antes de verificar cada trecho do código, é importante discriminar as tão faladas configurações de segurança exigidas pelo time de segurança. São elas:

  • Habilitar registro em log de acesso ao servidor, para ajudar no rastreamento das requisiões de acesso a um bucket quando auditoria é necessária.
  • Habilitar versionamento, para ajudar com o gerenciamento e proteção do conteúdo do bucket.
  • Habilitar criptografia no lado do servidor, para proteger o conteúdo com criptografia customizada. O recomendado é habilitar essa configuração com uma chave KMS personalizada (Customer Managed KMS key).
  • Bloquear acesso público, para que o conteúdo do bucket seja apenas acessível pelos usuários da conta AWS e seus serviços.
  • Habilitar uma política de ciclo de vida, usada para otimizar custos do bucket S3. Uma configuração de ciclo de vida pode variar de acordo com o propósito do bucket (para esse caso o exemplo de política base fornecido pelo Terraform satisfaz nossa necessidade).

Com isso, ao código:

required_providers: define qual provedor será instalado para que o Terraform possa utilizá-lo. O provedor pode ser visto como uma API ou um provedor em nuvem que gerenciará nossa infraestrutura. Considerando que queremos AWS, usaremos o provedor AWS padrão da HashiCorp. Todo módulo deve declarar essa propriedade.

provider: define a configuração do provedor especificado. Isso pode ser traduzido como um usuário IAM da conta AWS e a região. O valor da região vem da variável definida em variables.tf e “profile” como “default” indica que usaremos os valores do arquivo “credentials” (a ser explicado na próxima seção).

aws_kms_key: configura a chave KMS a ser usada para criptografia no lado do servidor.

aws_kms_alias: é o “nome” da chave a ser usada por outros serviços AWS. Seu nome deve ser precedido por “alias/”.

aws_s3_bucket: representa o nosso bucket. A maior parte das configurações do bucket propriamente é definida nesse bloco.

aws_s3_bucket_acl: define se o acesso ao bucket será privado ou não.

aws_s3_bucket_versioning: define a configuração para o versionamento do bucket.

aws_s3_bucket_logging: define a configuração para registro em log de acesso ao bucket.

aws_s3_bucket_server_side_encryption_configuration: define a configuração para criptografia no lado do servidor.

aws_s3_bucket_lifecycle_configuration: define a configuração de uma regra de ciclo de vida para os objetos desse bucket.

aws_s3_bucket_public_access_block: define diferentes níveis de permissão de acesso ao conteúdo do bucket. Para bloquear totalmente o acesso público, deve-se setar todas as opções como true.

Agora que temos esse entendimento, porque não vermos isso em ação?

Execução

Para executar esse projeto simples, primeiro precisaremos definir o usuário IAM que será usado dentro do arquivo “credentials”, que pode ser encontrado:

  • Linux/MacOS: em “~/.aws/credentials”
  • Windows: em “%USERPROFILE%\.aws\credentials”

Definir o usuário IAM significa setar os valores de aws_access_key_id e aws_secret_access_key referentes a esse usuário. Ele também precisa ter permissões de Leitura/Escrita para ambos os serviços S3 e KMS. Dando AdministratorAccess” ou “AmazonS3FullAccess” com a política customizada de KMS abaixo, teremos todas as permissões requeridas.

CustomKmsPolicy.json

{
"Version": "2012-10-17",
"Id": "Kms_key_policy",
"Statement": [
{
"Sid": "AllowKeyUse",
"Effect": "Allow",
"Action": [
"kms:Encrypt",
"kms:Decrypt",
"kms:ReEncrypt*",
"kms:GenerateDataKey*",
"kms:GetKeyPolicy",
"kms:GetKeyRotationStatus",
"kms:DescribeKey",
"kms:CreateKey",
"kms:CreateAlias",
"kms:ListResourceTags",
"kms:ScheduleKeyDeletion",
"kms:DeleteAlias"
],
"Resource": "*"
}
]
}
Criando usuário com acesso programático e as políticas necessárias
Definindo os valores para “aws_access_key_id” e “aws_secret_access_key”

Diante disso, tendo o Terraform instalado, podemos executar o “terraform init” e “terraform apply”, confirmar com “yes” e finalizar os trabalhos!

Uma rápida explicação do próprio Terraform: terraform init “inicializa um diretório de trabalho contendo arquivos de configuração Terraform” e terraform apply “executa as ações propostas em um planejamento Terraform”. Toda vez que terraform apply é disparado, será mostrado o planejamento a ser executado antes de perguntar a confirmação do usuário e, por isso, pulei o comando terraform plan nesse caso simples.

Execução com sucesso do comando terraform init
Execução com sucesso do comando terraform apply

Agora podemos verificar se o bucket e a chave KMS foram criados corretamente.

Bucket criado
Chave KMS criada
Chave KMS aplicada ao bucket

Yay! Tudo funcionou como o esperado!

Conclusão

A possibilidade de provisionamento de infraestrutura por meio de arquivos de configuração e scripts facilita bastante o gerenciamento de infraestrutura em nuvem. Ele nos dá o poder de centralizar toda a informação necessária para construir uma pilha de tecnologia que dará vida às nossas aplicações, quase eliminando a dor de cabeça da busca pelo famoso “o que pode estar faltando aqui” (mesmo com todo o cuidado, desenvolvimento de software é sempre uma surpresa).

Ademais, isso também ajuda a criar uma forma “padrão” de criação/implantação que auxilia no momento em que se precisa rastrear um problema/bug e corrigí-lo o mais cedo possível em um grupo de aplicações/ambientes. Sabendo que eles foram construídos e configurados de uma mesma forma minimiza a chance de ter que realizar diferentes soluções para cada um.

Referências

--

--