Skip to content

Certificate Automation

Eric Fitzgerald edited this page Feb 3, 2026 · 1 revision

Certificate Automation

This guide covers automatic TLS certificate provisioning for TMI using Let's Encrypt with OCI Functions and DNS-01 challenges.

Overview

TMI provides automatic certificate management using:

  • Let's Encrypt - Free, automated TLS certificates
  • OCI Functions - Serverless certificate lifecycle management
  • DNS-01 Challenges - Domain validation via DNS TXT records
  • OCI Vault - Secure certificate and key storage
  • OCI Load Balancer - Automatic certificate deployment

Architecture

                                 +------------------+
                                 |  Let's Encrypt   |
                                 |  ACME Server     |
                                 +--------+---------+
                                          |
                                          | ACME Protocol (DNS-01)
                                          v
+--------------------------------------------------------------------------------+
|                              OCI Tenancy                                        |
|                                                                                 |
|  +----------------+         +------------------+                                |
|  |  Scheduler     |-------->|  OCI Function    |                                |
|  |  (External)    |         |  (certmgr)       |                                |
|  +----------------+         +--------+---------+                                |
|                                      |                                          |
|        +-----------------------------+-----------------------------+            |
|        |                             |                             |            |
|        v                             v                             v            |
|  +----------------+     +------------------+     +------------------+           |
|  | DNS Zone (OCI) |     | OCI Vault        |     | OCI Load Balancer|           |
|  | (TXT records)  |     | (cert storage)   |     | (HTTPS:443)      |           |
|  +----------------+     +------------------+     +---------+--------+           |
|                                                           |                     |
|                                                           v                     |
|                                                 +------------------+            |
|                                                 | TMI Application  |            |
|                                                 +------------------+            |
+--------------------------------------------------------------------------------+

Prerequisites

OCI Resources

  1. DNS Zone - Domain must be hosted in OCI DNS
  2. OCI Vault - For storing certificates and ACME account key
  3. Load Balancer - Target for certificate deployment
  4. Compartment - With appropriate IAM permissions

Required IAM Permissions

The certificate manager function requires:

  • DNS: Manage TXT records in the specified zone
  • Vault: Read and write secrets
  • Load Balancer: Update certificates and listeners

These are automatically created when create_dynamic_group = true.

Quick Start

1. Enable Certificate Automation

In your terraform.tfvars:

# Enable certificate automation
enable_certificate_automation = true

# Domain configuration
domain_name        = "tmi.example.com"
dns_zone_id        = "ocid1.dns-zone.oc1..<unique_id>"
acme_contact_email = "[email protected]"

# Start with staging for testing
acme_directory = "staging"

# Certificate manager function image
certmgr_image_url = "us-ashburn-1.ocir.io/<namespace>/certmgr:latest"

2. Build and Push Function Image

# Build the certificate manager function
make fn-build-certmgr

# Push to OCI Container Registry
make fn-deploy-certmgr

3. Deploy Infrastructure

cd terraform/environments/oci-free-tier
terraform init
terraform plan
terraform apply

4. Test with Manual Invocation

# Invoke the function manually
make fn-invoke-certmgr

# Or use OCI CLI directly
terraform output certificate_invoke_command

5. Switch to Production

After verifying the flow works with staging:

acme_directory = "production"

Then terraform apply to update.

Configuration Reference

Terraform Variables

Variable Type Default Description
enable_certificate_automation bool false Enable the certificates module
domain_name string (required) Domain name for TLS certificate
dns_zone_id string (required) OCID of the OCI DNS zone
acme_contact_email string (required) Email for Let's Encrypt notifications
acme_directory string staging ACME directory: staging or production
certificate_renewal_days number 30 Days before expiry to trigger renewal
certmgr_image_url string (required) Container image URL for the function

Function Environment Variables

These are automatically configured by Terraform:

Variable Description
CERTMGR_DOMAIN Domain name for certificate
CERTMGR_DNS_ZONE_ID OCI DNS zone OCID
CERTMGR_ACME_EMAIL Let's Encrypt contact email
CERTMGR_RENEWAL_DAYS Days before expiry to renew
CERTMGR_LB_ID OCI Load Balancer OCID
CERTMGR_VAULT_ID OCI Vault OCID
CERTMGR_VAULT_KEY_ID OCI Vault master key OCID
CERTMGR_COMPARTMENT_ID Compartment OCID
CERTMGR_NAME_PREFIX Prefix for resource names
CERTMGR_ACME_DIRECTORY Full ACME directory URL

Certificate Lifecycle

Initial Issuance

  1. Function starts and loads configuration
  2. Checks for existing certificate in Vault
  3. If no certificate or expired:
    • Generates ACME account key (stored in Vault)
    • Registers account with Let's Encrypt
    • Requests certificate authorization
    • Creates DNS TXT record for challenge
    • Waits for DNS propagation
    • Notifies Let's Encrypt to verify
    • Receives and stores certificate in Vault
    • Updates Load Balancer with certificate
    • Cleans up DNS TXT record

Renewal Check

  1. Function retrieves certificate from Vault
  2. Checks certificate expiry date
  3. If expiring within certificate_renewal_days:
    • Triggers renewal flow (same as initial issuance)
  4. If not expiring: logs status and exits

Scheduling

OCI does not have a native Terraform resource for function scheduling. Set up daily invocation via one of these methods:

Option 1: OCI Console Resource Scheduler

  1. Navigate to Resource Scheduler in OCI Console
  2. Create a new scheduled job:
    • Type: Function
    • Function: Select {name_prefix}-certmgr
    • Schedule: Daily at desired time (e.g., 0 2 * * * for 2 AM UTC)

Option 2: External Cron

On a machine with OCI CLI configured:

# Add to crontab
0 2 * * * oci fn function invoke --function-id <function-ocid> --file - --body ''

Option 3: GitHub Actions

name: Certificate Renewal Check
on:
  schedule:
    - cron: '0 2 * * *'  # Daily at 2 AM UTC

jobs:
  check-certificate:
    runs-on: ubuntu-latest
    steps:
      - name: Setup OCI CLI
        uses: oracle-actions/[email protected]
        with:
          user: ${{ secrets.OCI_USER_OCID }}
          tenancy: ${{ secrets.OCI_TENANCY_OCID }}
          fingerprint: ${{ secrets.OCI_FINGERPRINT }}
          private_key: ${{ secrets.OCI_PRIVATE_KEY }}
          region: ${{ secrets.OCI_REGION }}

      - name: Invoke Certificate Manager
        run: |
          oci fn function invoke \
            --function-id ${{ secrets.CERTMGR_FUNCTION_ID }} \
            --file - --body ''

Makefile Targets

Target Description
fn-build-certmgr Build the certificate manager function
fn-deploy-certmgr Deploy function to OCI Functions
fn-invoke-certmgr Manually invoke the function
fn-logs-certmgr View function logs

Secrets Storage

The certificates module creates these secrets in OCI Vault:

Secret Name Content Purpose
{name_prefix}-acme-account-key PEM-encoded private key ACME account authentication
{name_prefix}-certificate PEM-encoded certificate TLS certificate chain
{name_prefix}-private-key PEM-encoded private key TLS private key

All secrets are:

  • Encrypted with the Vault master key (AES-256)
  • Accessible only via function Resource Principal
  • Version-controlled (previous versions retained)

Troubleshooting

Function Invocation Fails

  1. Check function logs:

    make fn-logs-certmgr
  2. Verify function has correct IAM policies:

    • Dynamic group includes the function
    • Policies grant DNS, Vault, and LB access
  3. Check function timeout (default: 300s should be sufficient)

DNS Challenge Fails

  1. Verify DNS zone OCID is correct
  2. Check that the zone contains the target domain
  3. Ensure IAM policy allows DNS record management
  4. Check DNS propagation:
    dig TXT _acme-challenge.tmi.example.com

Certificate Not Appearing on Load Balancer

  1. Verify function completed successfully (check logs)
  2. Check Load Balancer has HTTPS listener configured
  3. Verify IAM policy allows Load Balancer updates
  4. Check certificate in Vault contains valid PEM data

ACME Rate Limits

Let's Encrypt has rate limits:

  • Staging: No practical limits (for testing)
  • Production: 50 certificates per domain per week

If rate limited:

  1. Wait for rate limit window to reset (1 week)
  2. Use staging for testing
  3. Consider wildcard certificate for subdomains

Function Timeout

The function has a 5-minute timeout. Typical execution:

  • DNS propagation: 1-3 minutes
  • ACME challenge: ~30 seconds
  • Total: 2-4 minutes

If timing out:

  1. Increase function_timeout_seconds (max 300)
  2. Check DNS zone TTL settings
  3. Review function logs for delays

Staging vs Production

Aspect Staging Production
URL acme-staging-v02.api.letsencrypt.org acme-v02.api.letsencrypt.org
Rate Limits Very generous 50 certs/domain/week
Browser Trust Not trusted Trusted
Use Case Testing, development Production deployments

Best Practice: Always test with staging first, then switch to production.

Free Tier Impact

The certificate automation uses these Free Tier resources:

Resource Free Tier Limit Usage
Functions 2M invocations/month ~30/month (daily)
Functions Memory 400K GB-seconds ~1.3 GB-sec/month
Vault Secrets 20 secrets +3 secrets
DNS Queries 1000/day per zone ~5/day

Impact: Minimal - well within Free Tier limits.

Security Considerations

Resource Principal Authentication

The function uses OCI Resource Principal for authentication:

  • No static credentials in configuration
  • Automatic credential rotation by OCI
  • Scoped to specific compartment and resources

Least Privilege IAM

The created policies grant only necessary permissions:

  • DNS: Only TXT records in specified zone
  • Vault: Only secrets in specified vault
  • Load Balancer: Only specified load balancer

Secret Protection

  • ACME account key stored encrypted in Vault
  • Private key never leaves OCI (Vault → Load Balancer)
  • Certificate chain stored for transparency

Related Documentation

Clone this wiki locally