Post
PT EN

Valores Efêmeros: Como o OpenTofu Trata Segredos que Não Vão para o State

Valores Efêmeros: Como o OpenTofu Trata Segredos que Não Vão para o State

Introdução

Marcar uma variável como sensitive resolve bem menos do que parece. O valor para de aparecer na saída do terminal, mas segue gravado no arquivo de estado, em texto claro, disponível pra quem conseguir abrir o arquivo.

A confusão é comum e faz sentido. A palavra sugere proteção, quando na real ela só oculta da exibição.

O OpenTofu 1.11 trouxe resposta de outra natureza pra esse problema. Em vez de esconder o valor depois de gravado, ele deixa certo dado nunca ser gravado. São os valores efêmeros, que existem só em memória durante uma única execução.

Sensitive e Ephemeral: a Diferença

Vale fixar a distinção antes de qualquer código, porque ela organiza o resto todo.

Um valor marcado como sensitive é persistido normalmente no estado e nos arquivos de plano. A marcação muda só a forma como ele aparece, trocando o conteúdo por um marcador na saída dos comandos.

Um valor efêmero não é persistido. Vive na memória durante a execução e some no final. Não vai pro snapshot do estado nem pro arquivo de plano.

São mecanismos complementares, não concorrentes. Um protege contra exposição visual, o outro contra persistência.

Variáveis Efêmeras

A declaração é direta, com atributo próprio:

1
2
3
4
5
6
7
8
9
10
11
variable "chave_de_acesso" {
  type      = string
  ephemeral = true
  sensitive = true
}

variable "chave_secreta" {
  type      = string
  ephemeral = true
  sensitive = true
}

O uso típico é alimentar a configuração de um provider com credencial temporária, obtida na hora da execução:

1
2
3
4
provider "aws" {
  access_key = var.chave_de_acesso
  secret_key = var.chave_secreta
}

A credencial passa pela ferramenta, autentica e não deixa rastro no estado.

Recursos Efêmeros

Além de receber valor por variável, a ferramenta consegue buscar ativamente. O bloco ephemeral funciona parecido com fonte de dado: lê o recurso, disponibiliza o resultado e encerra quando não é mais necessário.

1
2
3
ephemeral "aws_secretsmanager_secret_version" "senha_banco" {
  secret_id = "producao/banco/senha"
}

Tudo que vem na resposta de um recurso efêmero fica automaticamente marcado como efêmero, o que impede o uso acidental num contexto que persistiria o valor.

Vale notar uma dependência prática: recurso efêmero exige provider atualizado pra suportar. Nem todo provider oferece esse tipo, então convém checar a documentação do seu antes de planejar a adoção.

Atributos Write-Only

Esse é o complemento que fecha o ciclo. Valores efêmeros resolvem a entrada de segredo, mas e quando você precisa gravar um segredo num recurso, tipo a senha de um banco?

Os atributos write-only existem pra isso. Aceitam um valor na configuração e transmitem ao provider, mas o comportamento deles em relação ao estado é peculiar:

  • O atributo existe só na seção de configuração do recurso.
  • No estado e no plano, ele fica sempre registrado como nulo.
  • O provider sempre devolve nulo, mesmo tendo recebido valor real.
  • Pode receber tanto valor comum quanto efêmero, diferente do atributo normal, que não aceita valor efêmero.

Como o valor não vai pro plano, a ferramenta não tem como perceber que mudou. A solução adotada pelos providers é um argumento de versão junto:

1
2
3
4
5
6
7
resource "aws_ssm_parameter" "senha" {
  name = "/producao/banco/senha"
  type = "SecureString"

  value_wo         = ephemeral.aws_secretsmanager_secret_version.senha_banco.secret_string
  value_wo_version = 1
}

Pra atualizar o segredo, você incrementa o número da versão. Sem essa mudança, a ferramenta não gera diferença nenhuma, porque não enxerga o conteúdo.

Onde Valores Efêmeros Podem Ser Usados

Essa é a parte que mais gera erro na adoção, e a lista é fechada. Um valor efêmero pode aparecer em:

  • Outros recursos efêmeros
  • Variáveis efêmeras
  • Saídas efêmeras
  • Locais
  • Configuração de providers
  • Provisionadores
  • Blocos de conexão de recursos
  • Atributos write-only

Qualquer uso fora desses contextos dá erro. Na prática, você não consegue jogar um valor efêmero num argumento comum de recurso, porque esse argumento seria persistido. A restrição é o próprio mecanismo de garantia.

Saídas Efêmeras

Saída também pode ser efêmera, com uma limitação relevante: só vale em módulo filho.

1
2
3
4
5
# modules/leitor-de-segredos/outputs.tf
output "credenciais" {
  value     = ephemeral.aws_secretsmanager_secret_version.senha_banco.secret_string
  ephemeral = true
}

A restrição faz sentido. Saída de módulo raiz é registrada no estado por definição, então deixar ela efêmera criaria contradição.

O padrão útil aqui é encapsular a obtenção de segredo num módulo dedicado, que expõe por saída efêmera pro módulo chamador consumir em provider ou atributo write-only.

Um Fluxo Completo

Juntando as peças, o caminho de um segredo fica assim: um recurso efêmero busca a credencial no cofre, ela alimenta a configuração de um provider ou é gravada por um atributo write-only, e nada disso aparece no estado no final.

O contraste com o fluxo antigo é o ponto central. Antes, o mesmo segredo passaria por variável comum, seria usado num argumento normal e ficaria registrado em texto claro no arquivo de estado, esperando alguém encontrar.

Cuidados

Três pontos antes de adotar.

Depende do provider. Recurso efêmero e atributo write-only precisam de suporte explícito no provider. O recurso da linguagem existe, mas sem o provider correspondente ele não se materializa.

A versão precisa ser gerenciada. O argumento de versão que acompanha atributo write-only vira responsabilidade sua. Esquecer de incrementar significa que o segredo novo simplesmente não é aplicado, sem erro aparente.

Não substitui a criptografia do estado. As duas coisas resolvem problema diferente. Valor efêmero evita que certo dado seja gravado; criptografia protege tudo que precisa ser gravado. O estado segue com identificador, configuração e atributo que descreve sua infraestrutura em detalhe.

Conclusão

Por anos, a orientação sobre segredo em Infraestrutura como Código foi basicamente defensiva: restrinja acesso ao estado, criptografe o bucket, evite gravar o que der. Todas partiam da premissa de que o segredo ia acabar no arquivo de qualquer jeito.

Os valores efêmeros mudam essa premissa. A pergunta deixa de ser como proteger o que foi gravado e passa a ser o que realmente precisa ser gravado.

Essa é a diferença entre mitigar risco e eliminar. Um segredo que nunca chegou ao arquivo não vaza por ele, não precisa ser rotacionado por precaução quando alguém ganha acesso ao bucket e não aparece em backup antigo que ninguém lembra que existe. É proteção que não depende de ninguém acertar a configuração depois.

This post is licensed under CC BY 4.0 by the author.