Terraform, repositórios mono e conformidade como código

Olá a todos. A OTUS abriu o recrutamento para um novo grupo no curso “Plataforma de infraestrutura baseada no Kubernetes”. Nesse contexto, preparamos uma tradução de material interessante sobre o tema.




Você pode ser um daqueles que usam terraform para Infraestrutura como um código e está se perguntando como usá-lo de maneira mais produtiva e segura. Em geral, recentemente, isso incomodou muitos. Todos nós escrevemos configurações e códigos usando diferentes ferramentas, idiomas e gastamos uma quantidade considerável de tempo, tornando-os mais legíveis, extensíveis e escaláveis.


Talvez o problema esteja em nós mesmos?

O código escrito deve criar valor ou resolver um problema, além de ser reutilizável para desduplicação. Normalmente, esse tipo de discussão termina com "Vamos usar os módulos" . Todos nós usamos módulos terraform, certo? Eu poderia escrever muitas histórias com problemas devido à modularidade excessiva, mas essa é uma história completamente diferente e não o farei.


Não eu não vou. Não insista, não ... Ok, talvez mais tarde.

Existe uma prática bem conhecida - marcar código ao usar módulos para bloquear o módulo raiz, a fim de garantir sua operação, mesmo ao alterar o código do módulo. Essa abordagem deve se tornar um princípio de equipe, no qual os módulos apropriados são marcados e usados ​​apropriadamente.

... mas e as dependências? E se eu tiver 120 módulos em 120 repositórios diferentes e a alteração de um módulo afetar outros 20 módulos. Isso significa que precisamos fazer 20 + 1 solicitações pull? Se o número mínimo de revisores for 2, isso significa 21 x 2 = 44Reveja. A sério! Simplesmente paralisamos o comando “mudando um módulo” e todos começarão a enviar memes ou gifs do Senhor dos Anéis, e o resto do dia será perdido.


Um PR para notificar a todos, um PR para reunir todos, algemar e mergulhar na escuridão

Vale a pena trabalhar? Devemos reduzir o número de revisores? Ou, talvez, faça uma exceção para os módulos e não exija PR se a alteração tiver um grande impacto. Mesmo? Você quer andar cegamente em uma floresta escura e profunda? Ou reuni-los todos, perfurar e mergulhar na escuridão ?

Não, não altere a ordem da revisão. Se você acha que trabalhar com relações públicas é correto, cumpra-as. Se você possui pipelines inteligentes ou se esforça para dominar, continue com essa abordagem.

Nesse caso, o problema não é "como você trabalha" , mas "qual é a estrutura dos seus repositórios git" .



É semelhante ao que senti quando apliquei a sugestão abaixo.Vamos

voltar ao básico. Quais são os requisitos gerais para um repositório com módulos terraform?

  1. Ele deve ser marcado para que não haja alterações de interrupção.
  2. Qualquer alteração deve poder testar.
  3. As alterações devem passar por uma revisão mútua.

Sugiro o seguinte - NÃO use micro-repositórios para módulos de terraform. Use um repositório mono .

  • Você pode marcar todo o repositório quando houver uma alteração / requisito
  • Qualquer alteração, PR ou push pode ser testada.
  • Qualquer alteração pode passar pela revisão.


Eu tenho força!

Ok, mas qual será a estrutura deste repositório? Nos últimos quatro anos, tive muitas falhas relacionadas a isso e cheguei à conclusão de que um diretório separado para o módulo seria a melhor solução.


Estrutura de diretório de exemplo para um repositório mono. Veja a alteração tags_override?

Assim, uma mudança de módulo que afeta 20 outros módulos é de apenas 1 PR ! Mesmo se você adicionar 5 revisores a este PR, a revisão será muito rápida em comparação com os micro-repositórios. Se você usa o Github, isso é ainda melhor! Você pode usar CODEOWNERS para módulos que possuem mantenedores / proprietários, e quaisquer alterações nesses módulos DEVEM ser aprovadas por esse proprietário.

Ótimo, mas como usar esse módulo, localizado no diretório de repositório único?

Fácil:

module "from_mono_repo" {
 source = "git::ssh://.../<org>/<repo>.git//<my_module_dir>"
 ...
}
module "from_mono_repo_with_tags" {
  source = "git::ssh://..../<org>/<repo>.git//<mod_dir>?ref=1.2.4"
  ...
}
module "from_micro_repo" {
  source = "git::ssh://.../<org>/<mod_repo>.git"
  ...
}
module "from_micro_repo_with_tags" {
  source = "git::ssh://.../<org>/<mod_repo>.git?ref=1.2.4"
  ...
}

Quais são as desvantagens desse tipo de estrutura? Bem, se você tentar testar "todos os módulos" com PR / alteração, poderá obter 1,5 horas de pipelines de CI. Você precisa encontrar os módulos modificados no PR. Eu faço assim:

changed_modules=$(git diff --name-only $(git rev-parse origin/master) HEAD | cut -d "/" -f1 | grep ^aws- | uniq)

Há outra desvantagem: sempre que você executa o "terraform init", ele carrega todo o repositório no diretório .terraform. Mas nunca tive problemas com isso, pois executo meus pipes em contêineres escaláveis ​​do AWS CodeBuild. Se você usa o Jenkins e o Jenkins Slaves persistente, pode ter esse problema.


Não nos faça chorar.

Com um mono-repositório, você ainda tem todas as vantagens dos micro-repositórios, mas como bônus, reduz o custo de manutenção de seus módulos.

Honestamente, depois de trabalhar nesse modo, a proposta de usar micro-repositórios para módulos de terraform deve ser considerada um crime.

Ótimo, e o teste de unidade? Você realmente precisa disso? ... Em geral, o que exatamente você quer dizer com teste de unidade. Você realmente vai verificar se o recurso da AWS foi criado corretamente? De quem é a responsabilidade: terraform ou uma API que lida com a criação de recursos? Talvez devêssemos nos concentrar mais em testes negativos e na idempotência do código .

Para verificar a idempotência do código, o terraform fornece um excelente parâmetro chamado -detailed-exitcode. Apenas corra:

> terraform plan -detailed-exitcode

Depois disso, corra terraform applye é isso. Pelo menos, você terá certeza de que seu código é idempotente e não cria novos recursos devido a uma sequência aleatória ou outra coisa.

E o teste negativo? O que, em geral, é um teste negativo? Na verdade, isso não é muito diferente do teste de unidade, mas você deve prestar atenção em situações negativas.

Por exemplo, ninguém tem permissão para criar um bucket S3 público e não criptografado .

Portanto, em vez de verificar se um depósito S3 está realmente sendo criado, você, de fato, com base em um conjunto de políticas, verifica se o seu código cria um recurso. Como fazer isso? O Terraform Enterprise fornece uma ótima ferramenta para isso, o Sentinel .

... mas também existem alternativas de código aberto. Atualmente, existem muitas ferramentas para análise estática do código HCL. Essas ferramentas, baseadas em práticas recomendadas comuns, não permitirão que você faça algo indesejado, mas e se não tiver o teste que você precisa ... ou, pior ainda, se sua situação for um pouco diferente. Por exemplo, você deseja permitir que alguns buckets do S3 sejam tornados públicos com base em determinadas condições, o que, de fato, será um bug de segurança para essas ferramentas.

É aqui que a conformidade com terraform aparece. Essa ferramenta não apenas permitirá que você escreva seus próprios testes nos quais você pode determinar o que deseja como política da sua empresa, mas também ajuda a separar segurança e desenvolvimento movendo a segurança para a esquerda. Parece bastante controverso, certo? Não. Enquanto que?


Logotipo de conformidade com terraform

Primeiro, a conformidade com terraform usa o Behavior Driven Development (BDD).

Feature: Ensure that we have encryption everywhere.

    Scenario: Reject if an S3 bucket is not encrypted
        Given I have aws_s3_bucket defined
        Then it must contain server_side_encryption_configuration

Verifique se a criptografia está ativada.Se

isso não for suficiente, você pode escrever com mais detalhes:

Feature: Ensure that we have encryption everywhere.

    Scenario: Reject if an S3 bucket is not encrypted with KMS
        Given I have aws_s3_bucket defined
        Then it must contain server_side_encryption_configuration
        And it must contain rule
        And it must contain apply_server_side_encryption_by_default
        And it must contain sse_algorithm
        And its value must match the "aws:kms" regex

Aprofundamos e verificamos se o KMS é usado para criptografia.O

código de terraform para este teste:

resource "aws_kms_key" "mykey" {
 description             = "This key is used to encrypt bucket objects"
 deletion_window_in_days = 10
}

resource "aws_s3_bucket" "mybucket" {
 bucket = "mybucket"

 server_side_encryption_configuration {
   rule {
     apply_server_side_encryption_by_default {
       kms_master_key_id = "${aws_kms_key.mykey.arn}"
       sse_algorithm     = "aws:kms"
     }
   }
 }
}

Assim, os testes são entendidos literalmente por TUDO na sua organização. Aqui você pode delegar a gravação desses testes em um serviço de segurança ou em desenvolvedores com conhecimento suficiente de segurança. Essa ferramenta também permite armazenar arquivos BDD em outro repositório. Isso ajudará a diferenciar a responsabilidade quando as alterações no código e nas políticas de segurança associadas ao seu código forem duas entidades diferentes. Podem ser equipes diferentes com diferentes ciclos de vida. Incrível né? Bem, pelo menos para mim foi.

Para obter mais informações sobre conformidade com terraform, consulte esta apresentação .

Resolvemos muitos problemas com a conformidade com terraform, especialmente onde os serviços de segurança estão bastante distantes das equipes de desenvolvimento e podem não entender o que os desenvolvedores estão fazendo. Você já adivinha o que está acontecendo nessas organizações. Normalmente, o serviço de segurança começa a bloquear tudo o que é suspeito e cria um sistema de segurança baseado na segurança do perímetro. Oh Deus ...

Em muitas situações, usar apenas terraform e conformidade com terraform para equipes que projetam (ou acompanham) a infraestrutura ajudaram a colocar essas duas equipes diferentes na mesma mesa. Quando sua equipe de segurança começa a desenvolver algo com feedback imediato de todos os pipelines de desenvolvimento, ela geralmente obtém a motivação para fazer cada vez mais. Bem, geralmente ...

Portanto, ao usar terraform, estruturamos repositórios git da seguinte maneira:



É claro que isso é bastante arrogante. Mas tive sorte (ou não?) De trabalhar com uma estrutura mais detalhada em várias organizações. Infelizmente, tudo isso não terminou muito bem. O final feliz deve estar oculto no número 3.

Deixe-me saber se você tem alguma história de sucesso com micro repositórios, estou realmente interessado!



Convidamos você a uma lição gratuita na qual consideraremos os componentes de uma futura plataforma de infraestrutura e veremos como entregar nosso aplicativo corretamente .

All Articles