TrademarkTrademark
Features
Documentation

Securing Terraform Atlantis in production

Step-by-step guide to harden Terraform Atlantis: secure secrets, enforce policies, isolate environments and automate audits for production.
Sebastian StadilMarch 4, 2026Updated March 31, 2026
Securing Terraform Atlantis in production
Key takeaways
  • The most critical action for securing Atlantis is updating to v0.30.0 or later to address CVE-2024-52009, which exposes GitHub credentials in logs.
  • Hardening Atlantis requires defense-in-depth across network isolation, container security, webhook secrets, repository allowlists, and branch protection.
  • Cloud credentials should use IAM AssumeRole or Vault dynamic credentials rather than long-lived secrets, with secrets managed via tools like Kubernetes External Secrets Operator.
  • Production deployments need audit logging, Prometheus metrics, security alerting, and team-based RBAC requiring multi-person approval for production changes.

This post is part of our Atlantis collection.

What Immediate Action Does the Atlantis CVE Require?

CVE-2024-52009: GitHub credential exposure requires urgent patching

Severity: Critical - GitHub tokens are exposed in Atlantis logs during rotation, allowing attackers with log access to impersonate Atlantis and gain administrative privileges.

Immediate Action Required:

# Check your current version
atlantis version
 
# Update to v0.30.0 or later
docker pull ghcr.io/runatlantis/atlantis:v0.30.0
 
# Review logs for exposed tokens
grep -r "ghs_" /var/log/atlantis/

Additional Active Vulnerabilities:

  • Multiple Go runtime CVEs affecting container images
  • Alpine Linux vulnerabilities in base images
  • Design-level risks from malicious Terraform code execution

How Do You Configure Firewall and Network Security for Atlantis?

Implement network isolation with defense-in-depth architecture

Essential Firewall Configuration:

# Core iptables rules
-P INPUT DROP
-P FORWARD DROP
-P OUTPUT DROP
 
# Allow webhook traffic only from VCS providers
-A INPUT -p tcp --dport 4141 -s 140.82.112.0/20 -j ACCEPT  # GitHub
-A INPUT -p tcp --dport 4141 -s 35.232.176.0/20 -j ACCEPT  # GitLab
 
# Essential outbound traffic only
-A OUTPUT -p tcp --dport 443 -j ACCEPT  # HTTPS
-A OUTPUT -p tcp --dport 22 -j ACCEPT   # SSH for Git
-A OUTPUT -p udp --dport 53 -j ACCEPT   # DNS

Kubernetes Network Policy:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: atlantis-network-policy
spec:
  podSelector:
    matchLabels:
      app: atlantis
  policyTypes:
  - Ingress
  - Egress
  ingress:
  - from:
    - namespaceSelector:
        matchLabels:
          name: ingress-controllers
    ports:
    - port: 4141
  egress:
  - to:
    - namespaceSelector: {}
    ports:
    - port: 443
    - port: 22

Container and OS hardening prevents privilege escalation

Secure Kubernetes Deployment:

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: atlantis
spec:
  template:
    spec:
      securityContext:
        runAsNonRoot: true
        runAsUser: 100
        fsGroup: 1000
        seccompProfile:
          type: RuntimeDefault
      containers:
      - name: atlantis
        image: ghcr.io/runatlantis/atlantis:v0.30.0
        securityContext:
          allowPrivilegeEscalation: false
          readOnlyRootFilesystem: true
          runAsNonRoot: true
          capabilities:
            drop:
            - ALL
        resources:
          limits:
            cpu: 2
            memory: 2Gi
          requests:
            cpu: 500m
            memory: 512Mi

SSL/TLS Configuration:

atlantis server \
  --ssl-cert-file="/etc/ssl/certs/atlantis.crt" \
  --ssl-key-file="/etc/ssl/private/atlantis.key" \
  --atlantis-url="https://atlantis.company.com"

How Do You Secure the VCS Integration?

Webhook security prevents unauthorized command execution

GitHub Webhook Configuration:

# Generate secure webhook secret
WEBHOOK_SECRET=$(openssl rand -hex 32)
 
# Configure Atlantis
atlantis server \
  --gh-webhook-secret="$WEBHOOK_SECRET" \
  --repo-allowlist="github.com/company/*,!github.com/company/untrusted-*" \
  --allow-fork-prs=false

Repository Security Configuration (atlantis.yaml):

version: 3
automerge: false
parallel_plan: true
parallel_apply: false
 
projects:
  - name: production
    dir: ./environments/prod
    workflow: secure-production
    apply_requirements: [approved, mergeable]
    import_requirements: [approved, mergeable]
    plan_requirements: [mergeable]
 
workflows:
  secure-production:
    plan:
      steps:
        - init
        - run: |
            # Validate no hardcoded credentials
            if grep -r "AKIA\|secret_key\|password" .; then
              echo "Potential credentials detected"
              exit 1
            fi
        - plan
    apply:
      steps:
        - apply

Branch protection enforces approval workflows

GitHub Branch Protection Settings:

  • Require pull request reviews (minimum 2 for production)
  • Dismiss stale reviews on new commits
  • Require review from CODEOWNERS
  • Require status checks including Atlantis
  • Include administrators in restrictions

CODEOWNERS Configuration:

# Global infrastructure requires DevOps approval
*.tf @company/devops-team

# Production requires additional security review
/production/ @company/senior-devops @company/security-team

# Security-sensitive resources
**/iam/ @company/security-team
**/security-groups/ @company/security-team

How Should You Secure Cloud Provider Credentials?

IAM roles eliminate long-lived credentials

AWS AssumeRole Configuration:

provider "aws" {
  assume_role {
    role_arn = "arn:aws:iam::${var.account_id}:role/AtlantisRole"
    session_name = "atlantis-${var.atlantis_user}-${var.atlantis_pull_num}"
    duration_seconds = 3600  # 1 hour maximum
  }
}

Trust Policy with Conditions:

{
  "Version": "2012-10-17",
  "Statement": [{
    "Effect": "Allow",
    "Principal": {
      "AWS": "arn:aws:iam::ATLANTIS_ACCOUNT:role/atlantis-ecs-task"
    },
    "Action": "sts:AssumeRole",
    "Condition": {
      "StringEquals": {
        "sts:ExternalId": "unique-external-id"
      }
    }
  }]
}

Vault integration provides dynamic credentials

Vault AWS Dynamic Credentials:

resource "vault_aws_secret_backend_role" "atlantis" {
  backend = vault_aws_secret_backend.aws.path
  name    = "atlantis-role"
  credential_type = "assumed_role"
  role_arns = ["arn:aws:iam::ACCOUNT:role/AtlantisVaultRole"]
  default_sts_ttl = 3600
  max_sts_ttl = 7200
}

Custom Workflow with Vault:

workflows:
  vault-auth:
    plan:
      steps:
      - run: |
          export VAULT_TOKEN=$(vault write -field=token auth/kubernetes/login \
            role=atlantis jwt=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token))
      - init
      - plan

Kubernetes secrets management

External Secrets Operator Integration:

apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: atlantis-credentials
spec:
  refreshInterval: 15m
  secretStoreRef:
    name: vault-backend
    kind: SecretStore
  target:
    name: atlantis-secrets
    creationPolicy: Owner
  data:
  - secretKey: github-token
    remoteRef:
      key: secret/atlantis
      property: github_token
  - secretKey: webhook-secret
    remoteRef:
      key: secret/atlantis
      property: webhook_secret

How Do You Set Up Audit Logging and Monitoring?

Comprehensive logging enables threat detection

Structured Logging Configuration:

atlantis server \
  --log-level="info" \
  --stats-namespace="atlantis-prod" \
  --enable-policy-checks

Essential Prometheus Metrics:

# Critical metrics to monitor
- atlantis_cmd_autoplan_execution_error
- atlantis_cmd_apply_execution_error
- atlantis_webhook_auth_failures
- atlantis_policy_check_failures
- atlantis_active_locks

Security Alerting Rules:

groups:
- name: atlantis-security
  rules:
  - alert: HighAuthenticationFailureRate
    expr: rate(atlantis_webhook_auth_failures[5m]) > 0.1
    labels:
      severity: critical
    annotations:
      summary: "Potential authentication attack detected"
      
  - alert: PolicyViolations
    expr: increase(atlantis_policy_check_failures[1h]) > 5
    labels:
      severity: warning

SIEM integration for security operations

Log Aggregation Pattern:

logging:
  siem_events:
    - authentication_failures
    - policy_check_overrides
    - external_data_source_usage
    - after_hours_production_changes
    - credential_exposure_risks

How Do You Control User Permissions and Access?

Team-based RBAC limits blast radius

GitHub Teams Configuration:

atlantis server \
  --gh-team-allowlist="devops:apply,security:apply,developers:plan" \
  --require-approval=true \
  --require-mergeable=true

Repository-Level Permissions:

# repos.yaml (server-side)
repos:
  - id: /.*production.*/
    apply_requirements: [approved, mergeable]
    allowed_overrides: []  # No overrides for production
    
  - id: /.*staging.*/
    apply_requirements: [mergeable]
    allowed_overrides: [workflow]

Multi-person approval for production changes

Approval Workflow Configuration:

repos:
  - id: "github.com/company/production-infra"
    apply_requirements: [approved, mergeable, undiverged]
    plan_requirements: [approved]
    import_requirements: [approved, mergeable]

How Do You Protect Sensitive Data in Plan Output?

Plan output sanitization prevents credential exposure

Custom Sanitization Workflow:

workflows:
  secure-plan:
    plan:
      steps:
        - init
        - run: |
            terraform plan -out=tfplan -no-color > plan.txt
            # Sanitize AWS keys
            sed -i 's/AKIA[0-9A-Z]\{16\}/AKIA****************/g' plan.txt
            # Sanitize generic secrets
            sed -i 's/\(password\|secret\|key\)\s*=\s*"[^"]*"/\1 = "***REDACTED***"/gi' plan.txt
            cat plan.txt

Variable File Security:

# Restrict variable file access
atlantis server \
  --var-file-allowlist="/secure/tfvars" \
  --restrict-file-list=true

What Belongs on a Production Deployment Security Checklist?

Pre-deployment requirements

  • [ ] Update to Atlantis v0.30.0+ to patch CVE-2024-52009
  • [ ] Generate strong webhook secrets (32+ characters)
  • [ ] Configure SSL/TLS certificates
  • [ ] Set up IAM roles/managed identities (no hardcoded credentials)
  • [ ] Create repository allowlist (never use --repo-allowlist=*)
  • [ ] Enable web UI authentication
  • [ ] Configure structured logging
  • [ ] Set up monitoring and alerting
  • [ ] Implement network segmentation

Operational security

  • [ ] Enable apply requirements for production
  • [ ] Configure branch protection rules
  • [ ] Implement CODEOWNERS for sensitive resources
  • [ ] Set up policy-as-code checks
  • [ ] Configure audit log retention (compliance requirements)
  • [ ] Test incident response procedures
  • [ ] Schedule regular security assessments
  • [ ] Document break-glass procedures

Continuous security

  • [ ] Monitor for new CVEs and security advisories
  • [ ] Regularly update container images
  • [ ] Rotate credentials and secrets quarterly
  • [ ] Review access logs monthly
  • [ ] Conduct security training for teams
  • [ ] Test disaster recovery procedures
  • [ ] Update security policies based on threats

What Does a Full Enterprise Security Configuration Look Like?

#!/bin/bash
# Complete secure Atlantis deployment
 
atlantis server \
  --atlantis-url="https://atlantis.company.com" \
  --repo-allowlist="github.com/company/*,!github.com/company/experimental-*" \
  --gh-webhook-secret="${WEBHOOK_SECRET}" \
  --ssl-cert-file="/etc/ssl/certs/atlantis.crt" \
  --ssl-key-file="/etc/ssl/private/atlantis.key" \
  --web-basic-auth=true \
  --web-username="admin" \
  --web-password="${WEB_PASSWORD}" \
  --gh-team-allowlist="devops:apply,security:apply,developers:plan" \
  --var-file-allowlist="/secure/configs" \
  --restrict-file-list=true \
  --enable-policy-checks=true \
  --silence-fork-pr-errors=true \
  --log-level="info" \
  --stats-namespace="atlantis-prod" \
  --repo-config="/etc/atlantis/repos.yaml"

This config stacks several layers at once: authentication, authorization, encryption, monitoring, and policy enforcement. Together they give a production Atlantis deployment a much stronger security posture. If maintaining all of this yourself is more operational burden than you want, see how self-hosted Atlantis compares to a managed platform.

Frequently asked questions

What should I do about the Atlantis CVE-2024-52009 vulnerability?

Update Atlantis to v0.30.0 or later immediately. The vulnerability exposes GitHub tokens in Atlantis logs during rotation, letting anyone with log access impersonate Atlantis and gain administrative privileges. After patching, review your logs for exposed tokens, for example by grepping for the ghs_ prefix.

How do you secure webhooks and repositories in Atlantis?

Generate a strong webhook secret of 32 or more characters and pass it via the gh-webhook-secret flag so Atlantis can verify requests actually come from your VCS provider. Restrict which repositories Atlantis processes with a repository allowlist, never a wildcard, and disable fork PRs. Branch protection rules and apply requirements like approved and mergeable then enforce approval before any apply.

How should Atlantis authenticate to cloud providers securely?

Avoid long-lived credentials. Use IAM AssumeRole with short session durations and an external ID condition, or pull dynamic short-lived credentials from HashiCorp Vault. On Kubernetes, manage remaining secrets like the GitHub token and webhook secret through a tool such as External Secrets Operator rather than hardcoding them.

How do you prevent Terraform plan output from leaking secrets in Atlantis?

Add a custom plan workflow that sanitizes the output before it gets posted as a PR comment, for example sed rules that redact AWS access key patterns and password or secret assignments. Also restrict which variable files Atlantis can read using the var-file-allowlist and restrict-file-list options.
About the author
Sebastian StadilCEO at Scalr
Sebastian Stadil is the CEO of Scalr with 15+ years of DevOps experience. He started with AWS in 2004 and advised early Microsoft Azure and Google Cloud.