Terraform Atlantis Security: Complete Production Deployment Guide 2025

Learn proven steps to harden Terraform Atlantis: auth, secrets, VCS webhooks, least-privilege CI/CD, runtime isolation—your roadmap to secure production IaC.

Atlantis automates Terraform operations through pull requests, making infrastructure-as-code more collaborative. However, this automation introduces security risks that must be addressed for production environments.

Server Security Fundamentals

Securing the Atlantis server itself is the first critical layer of defense, as it runs with privileged access to your infrastructure.

Network Security Configuration

Properly isolate and protect the Atlantis server with these firewall configurations:

# Example iptables rules for Atlantis server
# Allow incoming webhook connections only from VCS IPs
iptables -A INPUT -p tcp -s <GITHUB_WEBHOOK_IPS> --dport 4141 -j ACCEPT
# Allow outgoing connections to infrastructure providers
iptables -A OUTPUT -p tcp -d <AWS_API_ENDPOINT> --dport 443 -j ACCEPT
# Deny all other incoming traffic to Atlantis port
iptables -A INPUT -p tcp --dport 4141 -j DROP

Best practices:

  • Place Atlantis behind a reverse proxy with TLS termination
  • Restrict incoming traffic to only VCS provider IP ranges
  • Configure egress rules to limit outbound connectivity
  • For GitHub, only allow traffic from GitHub's webhook IP ranges

TLS/SSL Implementation

Always enable HTTPS for Atlantis communications:

# Start Atlantis with SSL certificates
atlantis server \
  --ssl-cert-file=/path/to/cert.pem \
  --ssl-key-file=/path/to/key.pem \
  --repo-allowlist="github.com/yourorg/*" \
  --atlantis-url="https://atlantis.example.com"
  • Use valid certificates from trusted CAs
  • Configure strong TLS ciphers and disable older protocols
  • Implement automatic certificate renewal

OS-Level Hardening

Run Atlantis with a minimally privileged account:

# Create a dedicated non-root user
sudo useradd -r -m -s /bin/false atlantis

# Ensure proper permissions on data directory
sudo mkdir -p /var/lib/atlantis
sudo chown atlantis:atlantis /var/lib/atlantis
sudo chmod 700 /var/lib/atlantis
  • Install only necessary packages on the host
  • Configure secure SSH settings (key-based auth, disable root login)
  • Apply system updates and security patches regularly
  • Use a minimal base image if deploying with containers

Container Security (Docker Deployment)

When running Atlantis as a container, implement these security controls:

# Run Atlantis container with security options
docker run --name atlantis \
  --user atlantis \
  --read-only \
  --cap-drop=ALL \
  --security-opt=no-new-privileges \
  --mount type=volume,source=atlantis-data,target=/var/lib/atlantis \
  -p 4141:4141 \
  -e ATLANTIS_GH_TOKEN="$GITHUB_TOKEN" \
  -e ATLANTIS_GH_WEBHOOK_SECRET="$WEBHOOK_SECRET" \
  -e ATLANTIS_REPO_ALLOWLIST="github.com/myorg/*" \
  ghcr.io/runatlantis/atlantis:latest server
  • Run containers as non-root user
  • Use read-only filesystems with specific volume mounts
  • Drop unnecessary capabilities
  • Implement resource limits to prevent DoS attacks

VCS Webhook Security

The connection between version control systems and Atlantis requires strong security controls to prevent unauthorized access and potential exploitation.

Webhook Secrets Implementation

Generate and configure strong webhook secrets to validate incoming requests:

# Generate a random webhook secret
webhook_secret=$(openssl rand -hex 32)

# Set webhook secrets as environment variables
export ATLANTIS_GH_WEBHOOK_SECRET="$webhook_secret"
export ATLANTIS_GITLAB_WEBHOOK_SECRET="$webhook_secret"
export ATLANTIS_BITBUCKET_WEBHOOK_SECRET="$webhook_secret" # For Bitbucket Server
  • Generate secrets at least 24 characters long with high entropy
  • Store secrets securely in environment variables or a secrets manager
  • Rotate webhook secrets periodically

IP Allowlisting for Webhooks

Restrict webhook access to only known VCS provider IP addresses:

# Nginx configuration to restrict access by IP
server {
    listen 443 ssl;
    server_name atlantis.example.com;
    
    # Allow GitHub webhook IPs
    allow 192.30.252.0/22;
    allow 185.199.108.0/22;
    allow 140.82.112.0/20;
    
    # Deny all other IPs
    deny all;
    
    location / {
        proxy_pass http://localhost:4141;
    }
}

Note: Bitbucket Cloud doesn't support webhook secrets, so IP allowlisting is essential for Bitbucket integrations.

Repository Allowlist

Restrict which repositories can trigger Atlantis operations:

# Start Atlantis with repository allowlist
atlantis server \
  --repo-allowlist="github.com/myorg/*" \
  --gh-webhook-secret="$WEBHOOK_SECRET"
  • Use specific patterns to limit repository access
  • Never use --repo-allowlist="*" in production

Cloud Provider Credentials Security

Atlantis requires access to cloud provider credentials to execute Terraform operations. Securing these credentials is critical.

Implementing IAM Roles with Least Privilege

For AWS, use IAM roles instead of static access keys:

# Example IAM role for Atlantis with least privilege
resource "aws_iam_role" "atlantis_role" {
  name = "atlantis-terraform-role"
  
  assume_role_policy = jsonencode({
    Version = "2012-10-17",
    Statement = [{
      Action = "sts:AssumeRole",
      Effect = "Allow",
      Principal = {
        Service = "ec2.amazonaws.com"  # If running on EC2
      }
    }]
  })
}

resource "aws_iam_policy" "atlantis_policy" {
  name        = "atlantis-terraform-policy"
  description = "Policy for Atlantis to manage terraform resources"

  policy = jsonencode({
    Version = "2012-10-17",
    Statement = [
      {
        Effect = "Allow",
        Action = [
          "s3:GetObject",
          "s3:PutObject",
          "s3:ListBucket"
        ],
        Resource = [
          "arn:aws:s3:::${var.terraform_state_bucket}",
          "arn:aws:s3:::${var.terraform_state_bucket}/*"
        ]
      },
      # Add only required permissions for specific resources
    ]
  })
}

OIDC Implementation

Use OpenID Connect for federated authentication with cloud providers:

AWS OIDC Example:

# Create an OIDC provider for Atlantis
resource "aws_iam_openid_connect_provider" "atlantis" {
  url             = "https://your-atlantis-domain"
  client_id_list  = ["atlantis"]
  thumbprint_list = ["<certificate-thumbprint>"]
}

resource "aws_iam_role" "atlantis_oidc_role" {
  name = "atlantis-oidc-role"
  
  assume_role_policy = jsonencode({
    Version = "2012-10-17",
    Statement = [{
      Action = "sts:AssumeRoleWithWebIdentity",
      Effect = "Allow",
      Principal = {
        Federated = aws_iam_openid_connect_provider.atlantis.arn
      },
      Condition = {
        StringEquals = {
          "${aws_iam_openid_connect_provider.atlantis.url}:sub": "system:serviceaccount:atlantis:atlantis"
        }
      }
    }]
  })
}

Azure Workload Identity Example:

resource "azuread_application_federated_identity_credential" "atlantis" {
  application_object_id = azuread_application.atlantis.object_id
  display_name          = "atlantis-federation"
  description           = "Federated identity for Atlantis"
  audiences             = ["api://AzureADTokenExchange"]
  issuer                = "https://your-atlantis-domain"
  subject               = "system:serviceaccount:atlantis:atlantis"
}

Avoiding Hardcoded Secrets

Use environment variables or secure vaults instead of hardcoded credentials:

# Example Terraform provider configuration using Vault
provider "vault" {
  address = "https://vault.example.com"
}

data "vault_aws_access_credentials" "aws_creds" {
  backend = "aws"
  role    = "atlantis"
}

provider "aws" {
  access_key = data.vault_aws_access_credentials.aws_creds.access_key
  secret_key = data.vault_aws_access_credentials.aws_creds.secret_key
  region     = var.aws_region
}
  • Implement automated credential rotation
  • Configure alerts for credential usage patterns
  • Enable detailed logging for credential access

Repo-Level Security with atlantis.yaml

Controlling what Atlantis can do at the repository level is critical for preventing misuse and exploits.

Server-Side Configuration

Limit what repositories can override in their atlantis.yaml files:

# repos.yaml (server-side config)
repos:
  - id: /.*/  # Applies to all repositories
    # Explicitly limit what repos can override
    allowed_overrides: [workflow]
    # Don't allow repos to define custom workflows
    allow_custom_workflows: false
    # Define secure defaults
    apply_requirements: [approved, mergeable]

Critical security practices:

  • Never set allow_custom_workflows: true without strict controls
  • Only include necessary keys in allowed_overrides
  • Use explicit repository IDs where possible

Restricting Dangerous Provider Usage

Create a pre-workflow hook script to validate providers:

#!/bin/bash
set -euo pipefail

# Define allowed providers
PROVIDER_ALLOWLIST=(
  'registry.terraform.io/hashicorp/aws'
  'registry.terraform.io/hashicorp/azurerm'
  'registry.terraform.io/hashicorp/google'
  'registry.terraform.io/hashicorp/kubernetes'
  'registry.terraform.io/hashicorp/vault'
  'registry.terraform.io/hashicorp/random'
  'registry.terraform.io/hashicorp/template'
  'registry.terraform.io/hashicorp/null'
)

# Get provider selections from terraform
mapfile -t PROVIDER_SELECTIONS < <(terraform version -json | jq -r '.provider_selections | keys | .[]')

# Validate each provider
for selection in "${PROVIDER_SELECTIONS[@]}"; do
  if ! [[ " ${PROVIDER_ALLOWLIST[*]} " =~ ${selection} ]]; then
    echo "ERROR: Provider '$selection' is not in the allowed list"
    exit 1
  fi
done

echo "All providers validated successfully"
exit 0

Configure this in your server-side repo configuration:

# repos.yaml
repos:
  - id: /.*/
    pre_workflow_hooks:
      - run: /path/to/validate_providers.sh

VCS Branch Protection Integration

Configure branch protection rules in your VCS:

  1. Require pull request reviews before merging
  2. Require approval from specific teams for critical infrastructure
  3. Dismiss stale PR approvals when new commits are pushed
  4. Require status checks to pass before merging

Set up corresponding Atlantis apply requirements:

# atlantis.yaml
version: 3
projects:
  - dir: production
    apply_requirements: [approved, mergeable, undiverged]
  • approved: Requires PR approval before Atlantis can apply changes
  • mergeable: Ensures branch protection rules are satisfied
  • undiverged: Prevents applies if there are changes on base branch

Audit Logging and Monitoring

Establishing comprehensive logging and monitoring for Atlantis activities is essential for security and compliance.

Configuring Atlantis Server Logging

# Set logging level using environment variables
export ATLANTIS_LOG_LEVEL="info"

# Or via command line when starting Atlantis
atlantis server --log-level="info"

For containerized deployments, implement log forwarding:

# Example Docker configuration with log forwarding
docker run \
  --name atlantis \
  --log-driver=fluentd \
  --log-opt fluentd-address=localhost:24224 \
  --log-opt tag=atlantis \
  runatlantis/atlantis:latest server

Monitoring Terraform Atlantis Activities

Track these key metrics to ensure Atlantis operates correctly:

  • Number of plans/applies executed (success vs failure)
  • Duration of plan/apply operations
  • Authentication failures
  • Resource creation/deletion rates
  • Off-hours operation attempts

Example Prometheus configuration:

# Example Prometheus configuration for scraping Atlantis metrics
scrape_configs:
  - job_name: 'atlantis'
    scrape_interval: 15s
    static_configs:
      - targets: ['atlantis:4141']

Setting Up Alerting for Dangerous Operations

Configure alerts for potentially dangerous operations:

# Example alert rule in Prometheus
groups:
- name: atlantis_alerts
  rules:
  - alert: AtlantisHighVolumeResourceDeletion
    expr: rate(atlantis_resource_deletion_total[5m]) > 10
    for: 5m
    labels:
      severity: critical
    annotations:
      summary: "High volume of resource deletions"
      description: "Atlantis is deleting resources at an abnormal rate"

Key operations to monitor and alert on:

  • Mass resource deletions
  • Changes to security groups or IAM roles
  • Modifications to network infrastructure
  • Changes to production environments during off-hours

Handling Sensitive Data

Terraform operations can potentially expose sensitive information in plan outputs and PR comments.

Marking Variables as Sensitive

variable "database_password" {
  description = "Password for the database"
  type        = string
  sensitive   = true  # Prevents value from appearing in plan output
}

Using External Secret Providers

Integrate with secret management services to retrieve sensitive data at runtime:

# Example using HashiCorp Vault provider
data "vault_generic_secret" "database_credentials" {
  path = "secret/database/credentials"
}

resource "aws_db_instance" "database" {
  username = data.vault_generic_secret.database_credentials.data["username"]
  password = data.vault_generic_secret.database_credentials.data["password"]
}

Filtering Sensitive Data from Plan Output

Implement a pre-workflow hook to filter sensitive information:

#!/bin/bash
# filter-sensitive-output.sh
# This script filters sensitive data from Terraform plan output

# Create a temporary file for filtered output
TEMP_FILE=$(mktemp)

# Filter sensitive patterns from PLANFILE
cat "$PLANFILE" | \
  sed -E 's/(password|secret|key|token|credential)([[:space:]]*=[[:space:]]*)\".+\"/\1\2\"[REDACTED]\"/gi' > "$TEMP_FILE"

# Replace original plan file with filtered content
cat "$TEMP_FILE" > "$PLANFILE"
rm "$TEMP_FILE"

Configure this in your Atlantis setup:

# repos.yaml
repos:
  - id: github.com/your-org/your-repo
    pre_workflow_hooks:
      - run: scripts/filter-sensitive-output.sh
        description: "Filter sensitive data from plan output"
        commands: plan

Securing State Files

Configure remote state with proper encryption and access controls:

terraform {
  backend "s3" {
    bucket         = "secure-terraform-state"
    key            = "project/terraform.tfstate"
    region         = "us-west-2"
    encrypt        = true  # Enable server-side encryption
    dynamodb_table = "terraform-locks"  # Enable state locking
  }
}

Comprehensive Security Checklist

Server Security

  • [ ] Deploy Atlantis behind a TLS-enabled reverse proxy
  • [ ] Configure firewall rules to restrict inbound/outbound traffic
  • [ ] Run Atlantis as a non-root user with minimal permissions
  • [ ] Apply OS-level hardening and regular security updates
  • [ ] Implement container security best practices if using Docker
  • [ ] Enable web UI authentication with strong credentials

Webhook Security

  • [ ] Generate and configure strong webhook secrets (>24 characters)
  • [ ] Implement IP allowlisting for VCS providers
  • [ ] Use --repo-allowlist to restrict which repositories can trigger Atlantis
  • [ ] Set --allow-fork-prs=false unless explicitly needed
  • [ ] Configure proper authentication for Azure DevOps webhooks

Cloud Provider Credentials

  • [ ] Implement IAM roles with least privilege access
  • [ ] Use OIDC for federated authentication where possible
  • [ ] Avoid storing static credentials in configuration files
  • [ ] Implement automated credential rotation
  • [ ] Enable monitoring and alerting for credential usage

Repo-Level Security

  • [ ] Restrict allowed_overrides in server-side configuration
  • [ ] Never enable allow_custom_workflows without strict controls
  • [ ] Implement branch protection rules in your VCS
  • [ ] Configure appropriate apply_requirements (approved, mergeable)
  • [ ] Implement provider restrictions to prevent arbitrary code execution
  • [ ] Use pre-workflow hooks to validate security requirements

Audit Logging and Monitoring

  • [ ] Configure appropriate log levels for Atlantis
  • [ ] Set up log forwarding to centralized logging system
  • [ ] Implement structured logging for SIEM compatibility
  • [ ] Configure metrics collection with Prometheus or similar
  • [ ] Implement alerting for suspicious operations
  • [ ] Establish baseline metrics for normal operations
  • [ ] Include Atlantis monitoring in security incident response plans

Sensitive Data Protection

  • [ ] Mark all sensitive variables with sensitive = true
  • [ ] Use external secret providers like Vault or cloud provider secret stores
  • [ ] Implement filtering for plan outputs to redact sensitive information
  • [ ] Configure remote state with encryption and access controls
  • [ ] Implement policy checks for detecting sensitive data
  • [ ] Train team members on secure practices for handling sensitive data

By implementing these security measures, you can significantly reduce the risk of security incidents when running Atlantis in production environments. Regular security reviews and updates to your configurations will ensure your Terraform automation remains secure as your infrastructure evolves.