TrademarkTrademark
Features
Documentation
All articles

Securing Terraform Atlantis in production (June 2025)

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 (June 2025)

This post is part of a series on Terraform Atlantis: The Complete Guide to GitOps Infrastructure Automation.

This post is part of our Atlantis collection.

tl;dr

Terraform Atlantis automates infrastructure changes through pull requests, making it a critical attack surface that requires comprehensive security measures.

The most critical immediate action is updating to Atlantis v0.30.0+ to address CVE-2024-52009, which exposes GitHub credentials in logs. Beyond patching, organizations must implement defense-in-depth strategies covering network security, authentication, secrets management, and operational monitoring.

Critical Security Vulnerabilities and Immediate Actions

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

Network Security and Infrastructure Hardening

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"

VCS Integration Security

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

Cloud Provider Credential Security

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

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

User Permissions and Access Control

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]

Sensitive Data Protection

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

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

Enterprise Security Configuration Example

#!/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 configuration implements multiple security layers including authentication, authorization, encryption, monitoring, and policy enforcement, providing a robust security posture for production Atlantis deployments.

About the author
Sebastian StadilCEO at Scalr
Sebastian Stadil is the CEO at Scalr. He has over 15 years of devops experience, and started his career with AWS in 2004. Sebastian was also an early advisor to Microsoft Azure and Google Cloud.