Mais segurança no Atlantis
Entenda Conftest como requisito mínimo
Photo by Jefferson Santos on Unsplash
TL;DR
O foco desse pequeno artigo é enfatizar os pontos de execução de comandos arbitrários dos quais o Atlantis está sujeito pelo Terraform. Ou seja, RCE. Não temos intenção de abordar as mais variadas ferramentas de análise SAST ou algo do tipo. Mas, aproveitar o recurso presente nas imagens Atlantis: o Conftest.
Intro
O Atlantis possui suporte a Conftest por padrão.
O Conftest suporta o arquivo de mudanças (plan) gerado pelo Terraform. Assim como também suporta os códigos HCL do Terraform, mas o padrão tem sido o arquivo de mudanças.
Esse arquivo de mudanças pode ser gerado como demonstrado abaixo:
$PLANFILE
e $SHOWFILE
apenas definem os caminhos de arquivo. Você deve definir antes.terraform init -upgrade
terraform plan -input=false -refresh -out $PLANFILE
terraform show -json $PLANFILE > $SHOWFILE
Feito! O primeiro subsídio está disponível. O arquivo $SHOWFILE
será bastante útil.
Conftest
O comando Conftest segue no formato abaixo:
conftest test $SHOWFILE --policy policy/ --all-namespaces
Assim, você pode fazer testes localmente se precisar. Se houver dúvidas, você pode consultar por conftest test --help
ou a própria documentação oficial.
No Atlantis, segue no modelo similar:
policy_check:
steps:
# ...
- policy_check:
extra_args: ["--policy /home/atlantis/policy", "--all-namespaces"]
Confira mais sobre na documentação oficial do Atlantis.
Policies
Mas, qual policy usar?
O Conftest utiliza OPA com policies escritas em REGO (pronuncia-se ray-go).
Atualmente, existem bibliotecas que empoderam a confecção de policies. A síntaxe pode ser bastante distinta. Então, dependendo do grau de profundidade, alguns recursos auxiliares e/ou complementares podem contribuir no seu desenvolvimento.
Um outro ponto muito importante sobre segurança é: o terraform plan
não está imune a algumas ações mesmo com a presença das policies. No Atlantis, as policies acontecem posteriormente ao plano de mudanças. Então, pode ser necessário controlar o plano de mudança ou personalizar a step do plano de mudança.
Blocklist de providers
Útil para especificar providers Terraform não permitidos.
package main
blocked_providers = {"registry.terraform.io/hashicorp/external"}
caught_providers[provider] = all {
some provider
blocked_providers[provider]
all := [module |
module := input.configuration.provider_config[_]
module.full_name == provider
]
}
deny[msg] {
num_resources := caught_providers[provider]
num_resources > 0
msg := sprintf("Provider %s detected in Terraform plan file.", [provider])
}
Allowlist de providers
Pode ser uma alternativa melhor. Assim, apenas providers Terraform autorizados passam. Evitando o risco de providers desconhecidos.
package main
allowed_providers = {"registry.terraform.io/hashicorp/aws"}
deny[msg] {
filtered := [f | f = input.configuration.provider_config[_].full_name; not allowed_providers[f]]
count(filtered) > 0
msg := sprintf("Got %d unauthorized provider(s) like %v.", [count(filtered), filtered[0]])
}
Entre outras infinidades de policies
Acima, são exemplos reais. Você também pode incluir policies para null_resources
.
É importante entender a diferença entre policies que conduzem adoção de boas práticas e aquelas que tratam questões pontuais.
Você pode utilizar algumas bibliotecas/frameworks de policies prontas. A Regula é um exemplo. Consulte a documentação sobre Regula e Conftest para saber mais. Também há opção para integrar com GitHub Actions diretamente.
Policy para HCL
Como visto anteriormente, pode ser muito útil verificar o manifesto HCL do Terraform. Aqui vai um exemplo simples:
package main
deny[msg] {
proto := input.data.external[name]
count(proto) > 0
msg = sprintf("External '%v' detected in HCL.", [name])
}
Detecção de HCL para fontes de módulos antes do plano? É possível:
package main
allowed_sources = {
"git@github.com:business/business.git//modules/"
}
deny[msg] {
module := input.module[name].source
finding := [trusted | trusted := allowed_sources[_]; not startswith(module, trusted)]
count(finding) > 0
msg = sprintf("Unauthorized module.%s source.", [name])
}
O conceito de allowlist aplica-se perfeitamente aqui.
O plano de mudança está sujeito a execução de comandos arbitrários. Portanto, é interessante a ideia de executar a verificação antes do plano de mudança.
E como funcionaria o comando para Conftest nessa estratégia?
conftest test *.tf --policy policy/ --all-namespaces
$SHOWFILE
.Portanto, antes de efetivar o plano de mudança no Terraform, execute o teste.
$ conftest test $SHOWFILE *.tf --policy /home/atlantis/policy -n main --output table
+---------+--------------+-----------+--------------------------------+
| RESULT | FILE | NAMESPACE | MESSAGE |
+---------+--------------+-----------+--------------------------------+
| success | outputs.tf | main | SUCCESS |
| success | outputs.tf | main | SUCCESS |
| success | tests.tf | main | SUCCESS |
| failure | tests.tf | main | External 'example' detected in |
| | | | HCL. |
| success | variables.tf | main | SUCCESS |
| success | variables.tf | main | SUCCESS |
| success | showfile | main | SUCCESS |
| failure | showfile | main | Resource 'null_resource' |
| | | | detected in Terraform plan |
| | | | file. Denied. |
| success | main.tf | main | SUCCESS |
| success | main.tf | main | SUCCESS |
+---------+--------------+-----------+--------------------------------+
Também é possível utilizar o arquivo .terraform.lock.hcl
gerado durante terrraform init
para verificar providers antes do terraform plan
.
conftest test .terraform.lock.hcl -p /home/atlantis/policy
package main
allowed_providers = {
"registry.terraform.io/hashicorp/aws",
"registry.terraform.io/hashicorp/random",
"registry.terraform.io/hashicorp/null"
}
deny[msg] {
input.provider[name]
not allowed_providers[name]
msg = sprintf("Provider '%s' caught.", [name])
}
Veja que é possível combinar os arquivos com as policies de maneiras variadas.
Dessa forma, o plano de mudança não vai acontecer. Pois, com os exemplos acima, retornará um código de saída 1
quando qualquer provider não autorizado for detectado.
If a workflow step returns a non-zero exit code, the workflow will stop.
Esse tipo de medida estabelece uma melhor prevenção.
Ou seja, se o teste ocorrer bem, o plano será executado como esperado. Caso contrário, o risco será mitigado.
Assim, não tem problema ter testes antes do plan e a policy check que acontecerá depois do plan normalmente. As custom workflows de Atlantis são personalizáveis.
Recurso terraform_data
Em poucas palavras, a HashiCorp inseriu o sucessor de null_resource
. Esse recurso tem a missão de auxiliar no ciclo de vida de outros recursos. Ele não depende de outros providers, pois vem no embutido.
Na documentação, há duas formas de explorar isso:
Usando o recurso
terraform_data
;Usando
replace_triggered_by
no argumento de lifecycle de outro recurso.
No primeiro caso, fica sujeito ao uso de provisioner como local-exec
. O que merece muita atenção. Concorda?
Confeccionando policies
Eu utilizei o The Rego Playground para confeccionar algumas policies REGO.
Na imagem acima, o campo esquerdo é reservado para a policy REGO. No campo Input você cola o conteúdo de $SHOWFILE
gerado anteriormente. Lembra-se? E o sucesso da policy pode ser acompanhado nos campos Data e Output sempre que Evaluate for chamado. Acredite, isso pode facilitar a sua vida!
E agora?
É importante fazer um refinamento para chegar em um número maior de itens a serem verificados.
Mas, não seja limitado aos testes em si. É importante abranger a segurança para as outras camadas de infraestrutura.
Uma tendência é o uso de registry Terraform privado para módulos e/ou providers. Há opções como Citizen, Terrareg etc.
Conhece mais dicas para evitar instruções arbitrárias?
Enfim, espero que tenham gostado e mantenham-se sempre vigilantes.
Referências
Atlantis - https://www.runatlantis.io/
Conftest - https://www.conftest.dev/
OPA - https://www.openpolicyagent.org/docs/latest/terraform/
go-getter - https://pkg.go.dev/github.com/hashicorp/go-getter
The Rego Playground - https://play.openpolicyagent.org/
Terraform Plan RCE - https://alex.kaskaso.li/post/terraform-plan-rce
Regula - https://regula.dev/
HackTricks Cloud - https://cloud.hacktricks.xyz/pentesting-ci-cd/terraform-security#terraform-apply
Atlantis Hardening and Review Fatigue - https://doordash.engineering/2023/12/05/atlantis-hardening-and-review-fatigue/
Use Conftest to Audit IaC - https://www.fairwinds.com/blog/how-to-kubernetes-use-conftest-to-audit-infrastructure-as-code