Checkov: Encontrando Problemas de Segurança no seu IaC Antes que Vire Dor de Cabeça

Checkov: Encontrando Problemas de Segurança no seu IaC Antes que Vire Dor de Cabeça

Não há nada melhor que aquela sensação de fazer um deploy e descobrir que deixou um bucket S3 público? Ou pior, perceber que suas senhas do banco estão declaradas no código de infraestrutura? Pois é, só quem viveu sabe do frio na barriga que isso dá.

Tenho uma boa notícia! Existem várias formas de evitar esse tipo de problema antes mesmo do código chegar no repositório. Hoje vou falar sobre o Checkov, uma ferramenta Opensource que tem mostrado o seu valor.

O Problema que a Gente Conhece Bem

É inegável que trabalhar com Infrastructure as Code trouxe muitos benefícios, mas também trouxe desafios. Você escreve um manifesto de Kubernetes aqui, um template para OpenTofu, e quando percebe BOOM já tem uma enxurrada de arquivos definindo sua infraestrutura inteira.

Na rotina de escrita, versionamento, commit, deployment, monitoramento, Q&A dentre tantas outras que diariamente temos, é muito fácil cometer deslizes. Esquecer de habilitar criptografia, passar senha de forma explícita no código, deixar portas abertas quando deviam ser restritas, usar imagens de container sem tag específica… São tantos detalhes que passam despercebidos no code review, especialmente quando todo mundo está a mil com suas entregas.

Vários estudos e artigos de segurança apontam que a maioria das vulnerabilidades em nuvem não vem de invasões sofisticadas, mas sim de configurações erradas. É tipo deixar a porta de casa aberta ou deixar seu celular sobre a mesa de um shopping.

Conhecendo o Checkov

Temos falado bastante sobre DevSecOps e esse assunto é realmente importante não somente para desenvolvimento, que é a parte onde é possível ter uma visão maior do prduto pois é entregue ao cliente, e sabemos quando há algo de errado o aviso é instantâneo. Então por quê não dar atenção também à IaC que faz o alicerce para que o produto seja entregue com qualidade e desempenho. Implantar SAST também em sua infraestrutura.

O que é o Checkov? O Checkov é basicamente um analisador estático focado em segurança e compliance para IaC. Ele lê seus arquivos de infraestrutura e verifica se estão seguindo as melhores práticas de segurança.

O legal é que o Checkov não se limita a um formato só. Ele entende OpenTofu, Terraform, CloudFormation, Kubernetes, Dockerfiles, Helm charts, ARM templates, e até GitHub Actions. Praticamente um canivete suiço para tudo que você usa no dia a dia.

Atualmente existem mais de mil políticas pré-configuradas cobrindo os principais provedores cloud (AWS, Azure, GCP) e também frameworks de compliance como CIS, PCI-DSS e HIPAA.

Colocando para Rodar

Bom, antes de mais nada… Nos exemplos a seguir vou usar OpenTofu, o fork opensource do Terraform criado pela Linux Foundation. Como ambos usam a sintaxe HCL, o Checkov funciona perfeitamente com ele e com o Terraform. Se você ainda usa Terraform, pode seguir os exemplos normalmente que tudo vai funcionar igual.

A instalação é bem direta. Se você usa Python, basta um pip install:

pip3 install checkov
Também tem imagem Docker oficial caso prefira não instalar nada localmente
docker pull bridgecrew/checkov

Para quem está no macOS, tem pelo Homebrew também:

brew install checkov

Depois de instalado, você pode testar rodando o Checkov em qualquer diretório que tenha arquivos de IaC:

checkov -d /caminho/do/seu/projeto

Simples assim. Ele vai varrer recursivamente todos os arquivos suportados e mostrar o que encontrou.

Vendo na Prática

Vamos de um exemplo real. Imagine que você tem um arquivo bem básico para criar um bucket S3:

resource "aws_s3_bucket" "data_bucket" {
  bucket = "meu-warehouse-rpg"
  
  tags = {
    Environment = "production"
  }
}

resource "aws_s3_bucket_versioning" "data_bucket_versioning" {
  bucket = aws_s3_bucket.data_bucket.id
  
  versioning_configuration {
    status = "Enabled"
  }
}

resource "aws_s3_bucket_server_side_encryption_configuration" "data_bucket_encryption" {
  bucket = aws_s3_bucket.data_bucket.id

  rule {
    apply_server_side_encryption_by_default {
      sse_algorithm     = "aws:kms"
      kms_master_key_id = aws_kms_key.bucket_key.arn
    }
  }
}

resource "aws_s3_bucket_logging" "data_bucket_logging" {
  bucket = aws_s3_bucket.data_bucket.id

  target_bucket = aws_s3_bucket.log_bucket.id
  target_prefix = "logs/"
}

resource "aws_s3_bucket_public_access_block" "data_bucket_pab" {
  bucket = aws_s3_bucket.data_bucket.id

  block_public_acls       = true
  block_public_policy     = true
  ignore_public_acls      = true
  restrict_public_buckets = false  # Ops, esquecemos esse aqui
}

resource "aws_s3_bucket_lifecycle_configuration" "data_bucket_lifecycle" {
  bucket = aws_s3_bucket.data_bucket.id

  rule {
    id     = "archive-old-objects"
    status = "Enabled"

    transition {
      days          = 90
      storage_class = "GLACIER"
    }

    expiration {
      days = 365
    }
    # Faltou o abort_incomplete_multipart_upload
  }
}

# Comentamos temporariamente a replicação durante os testes
# resource "aws_s3_bucket_replication_configuration" "data_bucket_replication" {
#   bucket = aws_s3_bucket.data_bucket.id
#   role   = aws_iam_role.replication.arn
#
#   rule {
#     id     = "replicate-all"
#     status = "Enabled"
#
#     destination {
#       bucket        = aws_s3_bucket.replica_bucket.arn
#       storage_class = "STANDARD_IA"
#     }
#   }
# }

# Ainda não implementamos as notificações
# resource "aws_s3_bucket_notification" "data_bucket_notification" {
#   bucket = aws_s3_bucket.data_bucket.id
#
#   topic {
#     topic_arn = aws_sns_topic.s3_events.arn
#     events    = ["s3:ObjectCreated:*", "s3:ObjectRemoved:*"]
#   }
# }

À primeira vista parece ok, certo? Agora roda o Checkov nesse arquivo:

checkov -f bucket.t

E olha o que ele retorna:

[ terraform framework ]: 100%|████████████████████|[1/1], Current File Scanned=bucket.tf
[ secrets framework ]: 100%|████████████████████|[1/1], Current File Scanned=bucket.tf

       _               _
   ___| |__   ___  ___| | _______   __
  / __| '_ \ / _ \/ __| |/ / _ \ \ / /
 | (__| | | |  __/ (__|   < (_) \ V /
  \___|_| |_|\___|\___|_|\_\___/ \_/

By Prisma Cloud | version: 3.2.483 

terraform scan results:

Passed checks: 12, Failed checks: 4, Skipped checks: 0

Check: CKV_AWS_93: "Ensure S3 bucket policy does not lockout all but root user. (Prevent lockouts needing root account fixes)"
	PASSED for resource: aws_s3_bucket.data_bucket
	File: /bucket.tf:1-7
	Guide: https://docs.prismacloud.io/en/enterprise-edition/policy-reference/aws-policies/s3-policies/bc-aws-s3-24
Check: CKV_AWS_53: "Ensure S3 bucket has block public ACLS enabled"
	PASSED for resource: aws_s3_bucket_public_access_block.data_bucket_pab
	File: /bucket.tf:35-42
	Guide: https://docs.prismacloud.io/en/enterprise-edition/policy-reference/aws-policies/s3-policies/bc-aws-s3-19
Check: CKV_AWS_54: "Ensure S3 bucket has block public policy enabled"
	PASSED for resource: aws_s3_bucket_public_access_block.data_bucket_pab
	File: /bucket.tf:35-42
	Guide: https://docs.prismacloud.io/en/enterprise-edition/policy-reference/aws-policies/s3-policies/bc-aws-s3-20
Check: CKV_AWS_55: "Ensure S3 bucket has ignore public ACLs enabled"
	PASSED for resource: aws_s3_bucket_public_access_block.data_bucket_pab
	File: /bucket.tf:35-42
	Guide: https://docs.prismacloud.io/en/enterprise-edition/policy-reference/aws-policies/s3-policies/bc-aws-s3-21
Check: CKV_AWS_57: "S3 Bucket has an ACL defined which allows public WRITE access."
	PASSED for resource: aws_s3_bucket.data_bucket
	File: /bucket.tf:1-7
	Guide: https://docs.prismacloud.io/en/enterprise-edition/policy-reference/aws-policies/s3-policies/s3-2-acl-write-permissions-everyone
Check: CKV2_AWS_61: "Ensure that an S3 bucket has a lifecycle configuration"
	PASSED for resource: aws_s3_bucket.data_bucket
	File: /bucket.tf:1-7
	Guide: https://docs.prismacloud.io/en/enterprise-edition/policy-reference/aws-policies/aws-logging-policies/bc-aws-2-61
Check: CKV_AWS_18: "Ensure the S3 bucket has access logging enabled"
	PASSED for resource: aws_s3_bucket.data_bucket
	File: /bucket.tf:1-7
	Guide: https://docs.prismacloud.io/en/enterprise-edition/policy-reference/aws-policies/s3-policies/s3-13-enable-logging
Check: CKV_AWS_20: "S3 Bucket has an ACL defined which allows public READ access."
	PASSED for resource: aws_s3_bucket.data_bucket
	File: /bucket.tf:1-7
	Guide: https://docs.prismacloud.io/en/enterprise-edition/policy-reference/aws-policies/s3-policies/s3-1-acl-read-permissions-everyone
Check: CKV_AWS_145: "Ensure that S3 buckets are encrypted with KMS by default"
	PASSED for resource: aws_s3_bucket.data_bucket
	File: /bucket.tf:1-7
	Guide: https://docs.prismacloud.io/en/enterprise-edition/policy-reference/aws-policies/aws-general-policies/ensure-that-s3-buckets-are-encrypted-with-kms-by-default
Check: CKV2_AWS_6: "Ensure that S3 bucket has a Public Access block"
	PASSED for resource: aws_s3_bucket.data_bucket
	File: /bucket.tf:1-7
	Guide: https://docs.prismacloud.io/en/enterprise-edition/policy-reference/aws-policies/aws-networking-policies/s3-bucket-should-have-public-access-blocks-defaults-to-false-if-the-public-access-block-is-not-attached
Check: CKV_AWS_21: "Ensure all data stored in the S3 bucket have versioning enabled"
	PASSED for resource: aws_s3_bucket.data_bucket
	File: /bucket.tf:1-7
	Guide: https://docs.prismacloud.io/en/enterprise-edition/policy-reference/aws-policies/s3-policies/s3-16-enable-versioning
Check: CKV_AWS_19: "Ensure all data stored in the S3 bucket is securely encrypted at rest"
	PASSED for resource: aws_s3_bucket.data_bucket
	File: /bucket.tf:1-7
	Guide: https://docs.prismacloud.io/en/enterprise-edition/policy-reference/aws-policies/s3-policies/s3-14-data-encrypted-at-rest
Check: CKV_AWS_56: "Ensure S3 bucket has 'restrict_public_buckets' enabled"
	FAILED for resource: aws_s3_bucket_public_access_block.data_bucket_pab
	File: /bucket.tf:35-42
	Guide: https://docs.prismacloud.io/en/enterprise-edition/policy-reference/aws-policies/s3-policies/bc-aws-s3-22

		35 | resource "aws_s3_bucket_public_access_block" "data_bucket_pab" {
		36 |   bucket = aws_s3_bucket.data_bucket.id
		37 | 
		38 |   block_public_acls       = true
		39 |   block_public_policy     = true
		40 |   ignore_public_acls      = true
		41 |   restrict_public_buckets = false  # Ops, esquecemos esse aqui
		42 | }

Check: CKV_AWS_300: "Ensure S3 lifecycle configuration sets period for aborting failed uploads"
	FAILED for resource: aws_s3_bucket_lifecycle_configuration.data_bucket_lifecycle
	File: /bucket.tf:44-61
	Guide: https://docs.prismacloud.io/en/enterprise-edition/policy-reference/aws-policies/aws-general-policies/bc-aws-300

		44 | resource "aws_s3_bucket_lifecycle_configuration" "data_bucket_lifecycle" {
		45 |   bucket = aws_s3_bucket.data_bucket.id
		46 | 
		47 |   rule {
		48 |     id     = "archive-old-objects"
		49 |     status = "Enabled"
		50 | 
		51 |     transition {
		52 |       days          = 90
		53 |       storage_class = "GLACIER"
		54 |     }
		55 | 
		56 |     expiration {
		57 |       days = 365
		58 |     }
		59 |     # Faltou o abort_incomplete_multipart_upload
		60 |   }
		61 | }

Check: CKV2_AWS_62: "Ensure S3 buckets should have event notifications enabled"
	FAILED for resource: aws_s3_bucket.data_bucket
	File: /bucket.tf:1-7
	Guide: https://docs.prismacloud.io/en/enterprise-edition/policy-reference/aws-policies/aws-logging-policies/bc-aws-2-62

		1 | resource "aws_s3_bucket" "data_bucket" {
		2 |   bucket = "meu-warehouse-rpg"
		3 |   
		4 |   tags = {
		5 |     Environment = "production"
		6 |   }
		7 | }

Check: CKV_AWS_144: "Ensure that S3 bucket has cross-region replication enabled"
	FAILED for resource: aws_s3_bucket.data_bucket
	File: /bucket.tf:1-7
	Guide: https://docs.prismacloud.io/en/enterprise-edition/policy-reference/aws-policies/aws-general-policies/ensure-that-s3-bucket-has-cross-region-replication-enabled

		1 | resource "aws_s3_bucket" "data_bucket" {
		2 |   bucket = "meu-warehouse-rpg"
		3 |   
		4 |   tags = {
		5 |     Environment = "production"
		6 |   }
		7 | }

Ele realizou um total de 12 checagens e identificou quatro problemas de segurança que provavelmente passariam batido em um code review normal. Cada check vem com uma explicação do problema e geralmente um link com da docuemtanção de como corrigir.

Corrigindo os problemas principais, o código ficaria assim:

resource "aws_s3_bucket" "data_bucket" {
  bucket = "meu-warehouse-rpg"
  
  tags = {
    Environment = "production"
  }
}

# Versionamento habilitado
resource "aws_s3_bucket_versioning" "data_bucket_versioning" {
  bucket = aws_s3_bucket.data_bucket.id
  
  versioning_configuration {
    status = "Enabled"
  }
}

# Criptografia com KMS
resource "aws_s3_bucket_server_side_encryption_configuration" "data_bucket_encryption" {
  bucket = aws_s3_bucket.data_bucket.id

  rule {
    apply_server_side_encryption_by_default {
      sse_algorithm     = "aws:kms"
      kms_master_key_id = aws_kms_key.bucket_key.arn
    }
  }
}

# Logging habilitado
resource "aws_s3_bucket_logging" "data_bucket_logging" {
  bucket = aws_s3_bucket.data_bucket.id

  target_bucket = aws_s3_bucket.log_bucket.id
  target_prefix = "logs/"
}

# Bloqueio de acesso público
resource "aws_s3_bucket_public_access_block" "data_bucket_pab" {
  bucket = aws_s3_bucket.data_bucket.id

  block_public_acls       = true
  block_public_policy     = true
  ignore_public_acls      = true
  restrict_public_buckets = true
}

# Política de lifecycle para gerenciamento de custos
resource "aws_s3_bucket_lifecycle_configuration" "data_bucket_lifecycle" {
  bucket = aws_s3_bucket.data_bucket.id

  rule {
    id     = "archive-old-objects"
    status = "Enabled"

    transition {
      days          = 90
      storage_class = "GLACIER"
    }

    expiration {
      days = 365
    }

    # Aborta uploads multipart incompletos após 7 dias
    abort_incomplete_multipart_upload {
      days_after_initiation = 7
    }
  }
}

# Replicação cross-region para disaster recovery
resource "aws_s3_bucket_replication_configuration" "data_bucket_replication" {
  bucket = aws_s3_bucket.data_bucket.id
  role   = aws_iam_role.replication.arn

  rule {
    id     = "replicate-all"
    status = "Enabled"

    destination {
      bucket        = aws_s3_bucket.replica_bucket.arn
      storage_class = "STANDARD_IA"
    }
  }
}

# Notificações de eventos (opcional, mas recomendado)
resource "aws_s3_bucket_notification" "data_bucket_notification" {
  bucket = aws_s3_bucket.data_bucket.id

  topic {
    topic_arn = aws_sns_topic.s3_events.arn
    events    = ["s3:ObjectCreated:*", "s3:ObjectRemoved:*"]
  }
}

Agora sim, muito mais robusto… Lindo! Rodando o Checkov novamente, todos os checks críticos passam. Claro que você pode ajustar as configurações conforme sua necessidade, mas pelo menos agora você sabe o que está desabilitando e por quê.

Indo Além do Básico

Depois que você pega o jeito, dá para fazer coisas mais interessantes. Uma que eu uso bastante é filtrar por severidade:

checkov -f bucket.tf --compact --quiet --check CRITICAL,HIGH

Isso mostra só os problemas críticos e altos, ignorando os avisos menores. Útil quando você está começando e não quer se sobrecarregar com centenas de warnings.

Outro recurso legal é criar suas próprias políticas customizadas. Você pode escrever checks em Python ou YAML. Por exemplo, digamos que na sua empresa existe uma regra de que todos os recursos precisam ter uma tag específica de centro de custo:

metadata:
  name: "Ensure all resources have cost center tag"
  id: "CUSTOM_AWS_1"
  category: "CONVENTION"
  
definition:
  cond_type: "attribute"
  resource_types:
  - "aws_*"
  attribute: "tags.CostCenter"
  operator: "exists"

Salva isso em um arquivo .yaml e passa o caminho para o Checkov. Agora você tem compliance com as políticas internas da empresa, não só com as best practices gerais.

Integrando no Pipeline

O Checkov brilha mesmo quando você coloca ele no CI/CD. Exemplo de configuração para GitHub Actions:

name: Checkov Security Scan

on: [pull_request]

jobs:
  checkov:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      
      - name: Run Checkov
        uses: bridgecrewio/checkov-action@master
        with:
          directory: opentofu/
          framework: terraform
          soft_fail: false

Com soft_fail: false, o build quebra se encontrar algum problema. Você pode ajustar isso se quiser começar mais suave, deixando como true para apenas reportar sem bloquear.

O legal é que o Checkov funciona bem com qualquer ferramenta de CI/CD. GitLab CI, Jenkins, Azure DevOps, todos suportam. E como tem imagem Docker, basta um docker run e ser feliz.

Lidando com Falsos Positivos

Nem tudo são flore e também nem tudo que o Checkov aponta é necessariamente um problema no seu contexto. Por exemplo, a replicação cross-region faz sentido para buckets de produção com dados críticos, mas pode ser overkill para um bucket de logs temporários.

Para esses casos, você pode suprimir checks específicos usando comentários inline:

# checkov:skip=CKV_AWS_144:Bucket de desenvolvimento, não requer replicação cross-region
resource "aws_s3_bucket" "dev_bucket" {
  bucket = "meu-bucket-dev"
  
  tags = {
    Environment = "development"
  }
}

Ou se você tem vários checks para suprimir no mesmo recurso:

resource "aws_s3_bucket" "temp_logs" {
  # checkov:skip=CKV_AWS_144:Logs temporários, não requer replicação
  # checkov:skip=CKV2_AWS_61:Logs são deletados automaticamente pela aplicação
  # checkov:skip=CKV2_AWS_62:Não precisa de notificações para logs temporários
  
  bucket = "temp-application-logs"
  
  tags = {
    Environment = "production"
    DataType    = "temporary"
  }
}

Sempre inclua uma justificativa no comentário. Isso ajuda quem for revisar o código depois a entender o contexto da decisão. Durante code review, se você vir um checkov:skip sem explicação, peça para a pessoa adicionar o motivo.

Vale a Pena?

Depois de alguns tempo usando o Checkov, posso dizer que a ferramenta me surpreendeu várias vezes. De problemas que definitivamente causariam incidentes em produção mas também como ferramenta para aprendizado de escrita.

O tempo de scan é bem rápido, geralmente alguns segundos para projetos médios. E como roda localmente, você pode validar suas mudanças antes mesmo de fazer commit.

Se você trabalha com IaC e ainda não usa nenhuma ferramenta de análise estática, recomendo muito o Checkov. É Opensource, bem documentado e tem uma comunidade ativa. Comece rodando em algum projeto menor, veja os resultados, e depois vai expandindo para o resto da infraestrutura.

No final das contas, é muito melhor descobrir um problema de segurança na sua IDE do que receber um alerta no meio da madrugada porque alguém encontrou uma brecha em produção.

Me conte aqui, já conhecia esta ferramenta?

Seguem algumas referências:

 

Anterior OpenClaw na prática: 10 ideias reais para usar no dia a dia

About author

Jeovany Batista
Jeovany Batista 14 posts

Formado em Segurança da Informação, trabalha com tecnologia há 11 anos, atualmente é Analista de Infraestrutura e Monitoramento na 4Linux, nas horas vagas se aventura na culinária e nos games. Entusiasta em opensource tools e no momento curtindo a distro OpenSuse!

View all posts by this author →

Você pode gostar também