Armazenando dados sensíveis em código Terraform utilizando KMS
Tags: terraform, aws, sre, kms
Published: 2022-04-27 08:27:11

Tratando-se de IaC, chegará cedo ou tarde o fatídico dia em que você terá que colocar uma senha, chave, ou qualquer outro dado sigiloso no seu código. Algumas pessoas não se preocupam muito quando estão versionando seus projetos, levando em consideração coisas como o fato de seus repositórios serem privados, por exemplo. Entretanto, sejamos sinceros: não há lá muita garantia que o código fique só no seu repositório, não é?

A grande maioria das ferramentas hoje conta com pelo menos um mecanismo para resolver esse pequeno probleminha. O Ansible, por exemplo, conta com o **Ansible Vault**. Já o Terraform conta com vários, como por exemplo a integração com o Vault (também da Hashicorp).

Neste breve artigo demonstrarei como manter seus dados seguros utilizando o **AWS KMS** (Key Management Service), serviço da AWS utilizado para criação e armazenamento de chaves e criptografia que conta com 20.000 requisições gratuitas no *free tier* e que pode ser utilizado entre múltiplos serviços. Bora lá?

O primeiro passo é a criação de uma chave dentro do KMS, que pode ser feita por meio do próprio Terraform ou através da Console. Caso opte pela primeira opção, serão necessários três tipos diferentes de objetos Terraform: um *data source* (**aws_iam_policy_document**) e dois *resources* (**aws_kms_key** e **aws_kms_alias**). Abaixo uma breve descrição do papel que cada um desempenhará:

- **aws_iam_policy_document**: este é o *data source* onde definiremos o documento da política informando usuários e suas respectivas permissões referentes às chaves e recursos dentro do KMS.

> Detalhe: aqui nós estamos utilizando uma *policy* do tipo *resource-based*.

- **aws_kms_key**: a chave propriamente dita. Aqui informaremos o tipo da chave, se ela é simétrica ou assimétrica, se ela pode ser utilizada para criptografar ou descriptografar, assim como associar o documento criado no *data source* para definir uma política de acesso à ela';
  
- **aws_kms_alias**: um *alias* nada mais é do que um apelido utilizado para referenciar a chave.
  

Abaixo o código Terraform:

```hcl
data "aws_iam_policy_document" "how_to_terraform_kms_pd" {
  statement {
    sid = "Enable IAM User Permissions"
    effect = "Allow"
    principals {
      type = "AWS"
      identifiers = [
        "arn:aws:iam::111111111111:root"
      ]
    }
    actions = [
      "kms:*"
    ]
    resources = [
      "*"
    ]
  }

  statement {
    sid = "Allow use of the key"
    effect = "Allow"
    principals {
      type = "AWS"
      identifiers = [
        "arn:aws:iam::111111111111:user/meu-usuario"
      ]
    }
    actions = [
      "kms:Encrypt",
      "kms:Decrypt",
      "kms:ReEncrypt*",
      "kms:GenerateDataKey*",
      "kms:DescribeKey"
    ]
    resources = [
      "*"
    ]
  }

  statement {
    sid = "Allow attachment of persistent resources"
    principals {
      type = "AWS"
      identifiers = [
        "arn:aws:iam::111111111111:user/meu-usuario"
      ]
    }
    actions = [
      "kms:CreateGrant",
      "kms:ListGrants",
      "kms:RevokeGrant"
    ]
    resources = [
      "*"
    ]
    condition {
      test = "Bool"
      variable = "kms:GrantIsForAWSResource"
      values = [
        "true"
      ]
    }
  }
}

resource "aws_kms_key" "how_to_terraform_kms" {
  description = "Teste de criptografia com KMS"
  key_usage = "ENCRYPT_DECRYPT"
  customer_master_key_spec = "SYMMETRIC_DEFAULT"
  policy = data.aws_iam_policy_document.how_to_terraform_kms_pd.json
}

resource "aws_kms_alias" "how_to_terraform_kms_alias" {
  name = "alias/how_to_terraform_kms"
  target_key_id = aws_kms_key.how_to_terraform_kms.key_id
}

output "how_to_terraform_kms_id" {
  value = aws_kms_key.how_to_terraform_kms.key_id
}
```

> Nota: no *data source* `how_to_terraform_kms_pd`, nós definimos o *account ID* como **111111111111** e o usuário como **meu-usuario**. Ajuste esses valores para o seu caso.

Após um `terraform plan` e um `terraform apply` os recursos já devem estar presentes na sua conta AWS. Note que há também um `output` que imprime o ID da chave recém-criada e caso você opte por consultá-lo diretamente na Console, o mesmo pode ser removido.

Com a chave pronta para uso, agora é hora de criptografar algo. E é aí que o nosso velho amigo **AWS CLI** entra em cena. Utilize para isso o seguinte comando:

```bash
aws kms encrypt --key-id ${key_id} \ 
  --plaintext ${texto_a_ser_criptografado} \
  --output text \
  --query CiphertextBlob
```

> Nota: não se esqueça que você deve ter exportado as variáveis como `AWS_SECRET_ACCESS_KEY` e `AWS_ACCESS_KEY_ID` corretamente no seu terminal para acessar sua conta.

Se tudo der certo, o valor criptografado deve aparecer na tela.

Neste momento você deve estar se perguntando "OK, mas o propósito não era esconder as coisas no código do Terraform?". Estamos chegando lá. Este valor criptografado que nós geramos utilizando o AWS CLI pode ser resgatado dentro do próprio Terraform, que consulta o KMS durante sua execução utilizando o *data source* **aws_kms_secrets** da seguinte forma:

```hcl
data "aws_kms_secrets" "teste_kms" {
  secret {
    name = "valor_resgatado"
    payload = "AQICAHhAVXI4s1jnWVzWUxKnuk9yI2/aDoEQu8vyYkhh0tUC2QEMKXf54sQFje6ImNjDyraEAAAAcjBwBgkqhkiG9w0BBwagYzBhAgEAMFwGCSqGSIb3DQEHATAeBglghkgBZQMEAS4wEQQMoKPEjuATAOXqrzraAgEQgC9Kwf2DiUTxrDy0g8kggiLc+bhWhuYLDQaB/DhsMxyV9pD6ROsT/boZztyJFD30oQ=="
  }
}

output "valor_resgatado" {
  value = data.aws_kms_secrets.teste_kms.plaintext["valor_resgatado"]
}
```

Dentro do bloco *secret* do *data source* você deve dar um nome único para referenciá-lo e o seu valor criptográfico. Não é necessário informar a chave, uma vez que ela já está contida no *payload*. Você pode então referenciar o *secret* em qualquer parte do seu código utilizando a notação `data.aws_kms_secrets.teste_kms.plaintext["valor_resgatado"]`. Você pode incluir quantos blocos `secret` quiser dentro do *data source*.

### Referências

- [Storing secrets in terraform codebase with KMS](https://eddwardo.github.io/posts/terraform-secrets-and-passwords/)