Skip to main content

A robust tool to sync AWS ACM certificates to local files for web servers (nginx, apache, haproxy, etc.)

Project description

AWS ACM Certificate Sync Tool

A robust tool to sync AWS ACM certificates to local files for web servers (nginx, apache, haproxy, etc.). Designed for containerized environments and automation workflows.

⚡ Now Possible with ACM Export Feature
As of June 2025, AWS Certificate Manager allows exporting public certificates for use anywhere, making it possible to sync ACM-managed certificates to your own infrastructure while maintaining centralized certificate management and automatic renewals.

Why This Tool?

While AWS ACM provides excellent certificate management within AWS services (ALB, CloudFront, etc.), many scenarios require certificates on your own servers:

  • Hybrid deployments: On-premises servers that need AWS-managed certificates
  • Custom applications: Services running on EC2 that don't integrate directly with ACM
  • Multi-cloud setups: Consistent certificate management across cloud providers
  • Legacy systems: Existing infrastructure that needs modern certificate automation
  • Development environments: Local testing with production-like certificates

This tool bridges that gap by automatically exporting ACM certificates and deploying them to your servers with proper formatting for different web servers.

Features

  • 🔍 Multiple certificate sources: Find certificates by ARN or AWS tags
  • 🎯 Smart certificate selection: When multiple certificates match tags, automatically selects:
    • Valid certificates over expired ones
    • Among valid certificates, the one with longest remaining validity
  • 📦 Multiple output formats: Support for nginx, apache, haproxy certificate formats
  • 🎯 Multiple targets: Deploy the same certificate to different servers/locations
  • ⚡ Smart updates: Only downloads certificates when needed (expiry check, content changes)
  • 🔐 Secure handling: Uses temporary passphrase for ACM export then stores unencrypted/encrypted as needed
  • ⏰ Flexible scheduling: Run once or as daemon with configurable schedule
  • 🐳 Container-ready: Designed for sidecar deployment patterns

Installation

From PyPI

pip install aws_cert_syncer

From Source

git clone https://github.com/koenvo/aws_cert_syncer.git
cd aws_cert_syncer
uv sync

Quick Start

  1. Create configuration file (config.yaml):
aws:
  region: us-east-1

certificates:
  - name: my-web-cert
    arn: "arn:aws:acm:us-east-1:123456789012:certificate/12345678-1234-1234-1234-123456789012"
    targets:
      - base_dir: "/etc/ssl"
        server_type: "nginx"
        reload_command: "systemctl reload nginx"
  1. Run the tool:
# Install from PyPI
aws_cert_syncer --config config.yaml

# Or run from source
python cert_sync.py --config config.yaml

Configuration

Complete Example

aws:
  region: us-east-1

certificates:
  # Method 1: Certificate by ARN
  - name: my-domain-cert
    arn: "arn:aws:acm:us-east-1:123456789012:certificate/12345678-1234-1234-1234-123456789012"
    
    targets:
      # Deploy to nginx
      - base_dir: "/etc/ssl"
        server_type: "nginx"
        passphrase: ""
        reload_command: "systemctl reload nginx"
      
      # Deploy to haproxy (same cert, different format)
      - base_dir: "/opt/haproxy/ssl"
        server_type: "haproxy"
        reload_command: "systemctl reload haproxy"

  # Method 2: Certificate by tags (with smart selection)
  - name: api-cert
    tags:
      Domain: "api.example.com"
      Environment: "production"
    
    targets:
      - base_dir: "/etc/ssl"
        server_type: "apache"
        passphrase: "my-secure-password"
        reload_command: "systemctl reload apache2"

  # Method 3: Custom file paths
  - name: legacy-app
    tags:
      Name: "legacy.company.com"
    
    targets:
      - base_dir: "/opt/app/ssl"
        server_type: "nginx"
        cert_path: "/opt/app/ssl/custom-cert.pem"
        key_path: "/opt/app/ssl/custom-key.pem"
        reload_command: "docker restart legacy-app"

Certificate Selection Logic

When using tags and multiple certificates match:

  1. Valid certificates (ISSUED status + not expired) are preferred over invalid/expired ones
  2. Among valid certificates, the one with the longest remaining validity is selected
  3. Detailed logging shows all matches and selection reasoning

Example scenario:

  • Certificate A: Expires in 30 days ✅ Valid
  • Certificate B: Expires in 90 days ✅ Valid
  • Certificate C: Expired ❌ Invalid

Result: Certificate B is selected (longest validity)

Usage

Command Line Options

aws_cert_syncer [options]

Options:
  --config, -c    Path to configuration file (default: /config.yaml)
  --daemon, -d    Run as daemon with scheduling
  --dry-run       Show what would be done without making changes
  --help          Show help message

Standalone Usage

# Run once
aws_cert_syncer --config config.yaml

# Run as daemon (uses SCHEDULE environment variable)
aws_cert_syncer --config config.yaml --daemon

# Dry run (see what would happen)
aws_cert_syncer --config config.yaml --dry-run

Docker Usage

# Pull from registry (when published)
docker pull your-registry/aws_cert_syncer

# Build locally
docker build -t aws_cert_syncer .

# Run once
docker run --rm \
  -v $(pwd)/config.yaml:/config/config.yaml \
  -v ~/.aws:/home/certsync/.aws \
  -v /etc/ssl:/etc/ssl \
  aws_cert_syncer

# Run as daemon
docker run -d \
  -v $(pwd)/config.yaml:/config/config.yaml \
  -v ~/.aws:/home/certsync/.aws \
  -v /etc/ssl:/etc/ssl \
  -e SCHEDULE=02:00 \
  -e DAYS_BEFORE_EXPIRY=30 \
  aws_cert_syncer --daemon

Docker Compose (Sidecar Pattern)

See examples/docker-compose.yml.example for a complete setup with nginx and haproxy.

# Start all services
docker-compose up -d

# View logs
docker-compose logs cert-sync

# Force certificate sync
docker-compose exec cert-sync aws_cert_syncer --config /config/config.yaml

Environment Variables

Variable Description Default
LOG_LEVEL Logging level (DEBUG, INFO, WARNING, ERROR) INFO
DAYS_BEFORE_EXPIRY Days before expiry to trigger renewal 30
SCHEDULE Daemon schedule (see formats below) 02:00

Schedule Formats

  • Time format: 02:00 (daily at 2 AM), 14:30 (daily at 2:30 PM)
  • Interval format: 6h (every 6 hours), 30m (every 30 minutes), 1h (hourly)

Server Types & File Formats

Nginx

  • Files: Separate {name}.crt, {name}.key, {name}-chain.crt
  • Location: /etc/ssl/certs/ and /etc/ssl/private/
  • Key format: Unencrypted (ignores passphrase for automatic startup)
  • Permissions: 644 (cert), 600 (key)

Apache

  • Files: Separate {name}.crt, {name}.key, {name}-chain.crt
  • Location: /etc/ssl/certs/ and /etc/ssl/private/
  • Key format: Supports encrypted keys with passphrase
  • Permissions: 644 (cert), 600 (key)

HAProxy

  • Files: Single combined {name}.pem (cert + key + chain)
  • Location: /etc/ssl/haproxy/
  • Key format: Unencrypted (ignores passphrase)
  • Permissions: 600 (combined file)

AWS Setup

IAM Permissions

Create an IAM policy with these permissions:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "acm:ListCertificates",
                "acm:DescribeCertificate", 
                "acm:ExportCertificate",
                "acm:ListTagsForCertificate"
            ],
            "Resource": "*"
        }
    ]
}

AWS Credentials

The tool uses standard AWS credential chain:

  1. Environment variables: AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY
  2. AWS credentials file: ~/.aws/credentials
  3. IAM instance profile (recommended for EC2/ECS)
  4. IAM roles for service accounts (recommended for Kubernetes)

Development

Setup

git clone https://github.com/koenvo/aws_cert_syncer.git
cd aws_cert_syncer
uv sync

Testing

# Run all tests
uv run pytest

# Run with coverage
uv run pytest --cov=cert_sync

# Run specific test
uv run pytest tests/test_cert_sync.py::TestCertificateRetriever::test_find_certificate_by_tags_multiple_matches_prefers_valid

Code Quality

# Format code
uv run ruff format .

# Lint code  
uv run ruff check .

# Fix auto-fixable issues
uv run ruff check . --fix

Examples

Complete configuration examples are available in the examples/ directory:

Troubleshooting

Common Issues

Certificate not found:

  • Verify ARN is correct and certificate exists in the specified region
  • Check that tags match exactly (case-sensitive)
  • Ensure AWS credentials have acm:ListCertificates permission

Permission denied writing files:

  • Check that target directories are writable by the user running the tool
  • Verify parent directories exist
  • In containers, ensure proper volume mounts

Reload command fails:

  • Test reload commands manually first
  • Check that the service is installed and running
  • Verify the user has permission to run the reload command
  • Consider using sudo in reload commands if needed

Multiple certificates found:

  • This is normal - the tool automatically selects the best one
  • Check logs to see which certificate was selected and why
  • Use more specific tags if you want to target a specific certificate

Debug Mode

# Enable debug logging
LOG_LEVEL=DEBUG aws_cert_syncer --config config.yaml

# Dry run to see what would happen
aws_cert_syncer --config config.yaml --dry-run

Monitoring

The tool logs all operations with structured messages:

  • Certificate selection decisions
  • File operations and permissions
  • Reload command execution
  • Error details with context

Integrate with your logging infrastructure to monitor certificate sync operations.

Contributing

  1. Fork the repository
  2. Create a feature branch
  3. Make your changes
  4. Add tests for new functionality
  5. Ensure all tests pass
  6. Submit a pull request

License

MIT License - see LICENSE file for details.

Security

  • Report security vulnerabilities via GitHub security advisories
  • Private keys are handled securely and never logged
  • Temporary files are cleaned up automatically
  • File permissions are set restrictively by default

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

aws_cert_syncer-0.1.0.tar.gz (15.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

aws_cert_syncer-0.1.0-py3-none-any.whl (12.4 kB view details)

Uploaded Python 3

File details

Details for the file aws_cert_syncer-0.1.0.tar.gz.

File metadata

  • Download URL: aws_cert_syncer-0.1.0.tar.gz
  • Upload date:
  • Size: 15.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.12.9

File hashes

Hashes for aws_cert_syncer-0.1.0.tar.gz
Algorithm Hash digest
SHA256 eebb1fc7940805ec326fd94cd6bffdeee050b9356c18d18222ebd4f559777558
MD5 451f2870ec9d714634b31e41fe6de859
BLAKE2b-256 ead48c1b2afc32d6e625c875bc20b65571121ae88444a5fd4bee28450acba4a4

See more details on using hashes here.

Provenance

The following attestation bundles were made for aws_cert_syncer-0.1.0.tar.gz:

Publisher: release.yml on koenvo/aws_cert_syncer

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file aws_cert_syncer-0.1.0-py3-none-any.whl.

File metadata

File hashes

Hashes for aws_cert_syncer-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 0d06e17fc70affe794edf567ca022a5388462b9192d9c15ba143758b2f8144d0
MD5 7c8e06c0e1481ec613e56afe051772ba
BLAKE2b-256 c04e99eab19f69a7a9625b869a4b90a26500648f0447bd2e7e3160f88b8349b2

See more details on using hashes here.

Provenance

The following attestation bundles were made for aws_cert_syncer-0.1.0-py3-none-any.whl:

Publisher: release.yml on koenvo/aws_cert_syncer

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page