-
Notifications
You must be signed in to change notification settings - Fork 2
Certificate Automation
This guide covers automatic TLS certificate provisioning for TMI using Let's Encrypt with OCI Functions and DNS-01 challenges.
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
+------------------+
| 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 | |
| +------------------+ |
+--------------------------------------------------------------------------------+
- DNS Zone - Domain must be hosted in OCI DNS
- OCI Vault - For storing certificates and ACME account key
- Load Balancer - Target for certificate deployment
- Compartment - With appropriate 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.
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 = "admin@example.com"
# Start with staging for testing
acme_directory = "staging"
# Certificate manager function image
certmgr_image_url = "us-ashburn-1.ocir.io/<namespace>/certmgr:latest"The Makefile targets wrap the Fn Project CLI (fn). The FN_APP environment variable must be set to your OCI Function Application name.
# Build the certificate manager function (runs `fn build`)
make fn-build-certmgr
# Deploy to OCI Functions (runs `fn deploy --app $FN_APP`)
FN_APP=tmi-certmgr make fn-deploy-certmgr# Use oci-private or oci-public depending on your deployment
cd terraform/environments/oci-private
terraform init
terraform plan
terraform apply# Invoke the function manually
make fn-invoke-certmgr
# Or use the OCI CLI command shown in Terraform outputs
terraform output -module=certificates invoke_commandAfter verifying the flow works with staging:
acme_directory = "production"Then terraform apply to update.
| 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 (7-60) |
certmgr_image_url |
string | (required) | Container image URL for the function |
| Variable | Type | Default | Description |
|---|---|---|---|
compartment_id |
string | (required) | OCI compartment OCID |
tenancy_ocid |
string | (required) | OCI tenancy OCID (for dynamic group creation) |
name_prefix |
string | tmi |
Prefix for resource names |
subnet_id |
string | (required) | Subnet OCID for the Function Application |
load_balancer_id |
string | (required) | OCID of the OCI Load Balancer |
vault_id |
string | (required) | OCID of the OCI Vault |
vault_key_id |
string | (required) | OCID of the Vault encryption key |
function_memory_mb |
number | 256 |
Function memory in MB (128, 256, 512, or 1024) |
function_timeout_seconds |
number | 300 |
Function timeout in seconds (30-300) |
create_dynamic_group |
bool | true |
Create dynamic group and IAM policies |
existing_dynamic_group_name |
string | null |
Existing dynamic group name (if create_dynamic_group is false) |
tags |
map(string) | {} |
Freeform tags for all resources |
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 |
CERTMGR_DRY_RUN |
Set to true to skip actual operations (testing only) |
- Function starts and loads configuration (4-minute internal timeout)
- Checks for existing certificate in Vault
- If no certificate or expiring within
certificate_renewal_days:- Gets or generates ACME account key (ECDSA P-256, stored in Vault)
- Registers account with Let's Encrypt
- Requests certificate authorization (DNS-01 challenge)
- Creates
_acme-challenge.<domain>TXT record in OCI DNS (TTL 60s) - Waits 60 seconds for DNS propagation
- Accepts the challenge and waits for authorization
- Generates a new ECDSA P-256 certificate key and CSR
- Finalizes the order and receives the certificate chain
- Stores certificate and private key in Vault
- Creates (or replaces) certificate on the Load Balancer
- Updates the HTTPS listener to reference the new certificate
- Cleans up the DNS TXT record (deferred, runs even on failure)
- Function retrieves certificate from Vault
- Checks certificate expiry date
- If expiring within
certificate_renewal_days:- Triggers renewal flow (same as initial issuance)
- If not expiring: logs status and exits
OCI does not have a native Terraform resource for function scheduling. Set up daily invocation via one of these methods:
- Navigate to Resource Scheduler in OCI Console
- 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)
On a machine with OCI CLI configured:
# Add to crontab
0 2 * * * oci fn function invoke --function-id <function-ocid> --file - --body ''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/configure-oci-cli@v1.3.0
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 ''All targets except fn-build-certmgr require the FN_APP environment variable to be set to the OCI Function Application name. All targets require the Fn Project CLI (fn) to be installed.
| Target | Description |
|---|---|
fn-build-certmgr |
Build the certificate manager function (fn build) |
fn-deploy-certmgr |
Build and deploy function to OCI Functions (fn deploy --app $FN_APP) |
fn-invoke-certmgr |
Manually invoke the function (fn invoke $FN_APP certmgr) |
fn-logs-certmgr |
View function logs (fn logs $FN_APP certmgr) |
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)
-
Check function logs:
make fn-logs-certmgr
-
Verify function has correct IAM policies:
- Dynamic group includes the function
- Policies grant DNS, Vault, and LB access
-
Check function timeout (default: 300s should be sufficient)
- Verify DNS zone OCID is correct
- Check that the zone contains the target domain
- Ensure IAM policy allows DNS record management
- Check DNS propagation:
dig TXT _acme-challenge.tmi.example.com
- Verify function completed successfully (check logs)
- Check Load Balancer has HTTPS listener configured
- Verify IAM policy allows Load Balancer updates
- Check certificate in Vault contains valid PEM data
Let's Encrypt has rate limits:
- Staging: No practical limits (for testing)
- Production: 50 certificates per domain per week
If rate limited:
- Wait for rate limit window to reset (1 week)
- Use staging for testing
- Consider wildcard certificate for subdomains
The function has a 5-minute (300 second) timeout, which is the OCI Functions maximum. The code enforces a 4-minute internal context deadline to allow for cleanup. Typical execution:
- DNS propagation wait: 60 seconds (hardcoded)
- ACME challenge verification: ~30 seconds
- Load Balancer work requests: ~30 seconds
- Total: ~2-3 minutes
If timing out:
- The default
function_timeout_secondsis already 300 (the OCI maximum). Verify it has not been reduced. - Check DNS zone TTL settings
- Review function logs for delays in ACME or Load Balancer work requests
| 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.
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.
The function uses OCI Resource Principal for authentication:
- No static credentials in configuration
- Automatic credential rotation by OCI
- Scoped to specific compartment and resources
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
- ACME account key stored encrypted in Vault
- Private key never leaves OCI (Vault → Load Balancer)
- Certificate chain stored for transparency
The certificate automation described above deploys certificates to the OCI Load Balancer, which terminates TLS before forwarding traffic to the TMI application. The TMI server itself also supports direct TLS termination, configured via these environment variables:
| Variable | Description |
|---|---|
TMI_SERVER_TLS_ENABLED |
Set to true to enable TLS on the server |
TMI_SERVER_TLS_CERT_FILE |
Path to PEM-encoded certificate file |
TMI_SERVER_TLS_KEY_FILE |
Path to PEM-encoded private key file |
TMI_SERVER_TLS_SUBJECT_NAME |
Expected TLS subject name |
TMI_SERVER_HTTP_TO_HTTPS_REDIRECT |
Set to true to redirect HTTP to HTTPS |
When using OCI Load Balancer TLS termination (the recommended approach for OCI deployments), these server-level TLS settings are not required. The Load Balancer handles HTTPS and forwards plain HTTP to the TMI container.
- Using TMI for Threat Modeling
- Accessing TMI
- Authentication
- Creating Your First Threat Model
- Understanding the User Interface
- Working with Data Flow Diagrams
- Managing Threats
- Collaborative Threat Modeling
- Using Notes and Documentation
- Timmy AI Assistant
- Metadata and Extensions
- Planning Your Deployment
- Terraform Deployment (AWS, OCI, GCP, Azure)
- Deploying TMI Server
- OCI Container Deployment
- Certificate Automation
- Deploying TMI Web Application
- Setting Up Authentication
- Database Setup
- Component Integration
- Post-Deployment
- Branding and Customization
- Monitoring and Health
- Cloud Logging
- Database Operations
- Security Operations
- Performance and Scaling
- Maintenance Tasks
- Getting Started with Development
- Architecture and Design
- API Integration
- Testing
- Contributing
- Extending TMI
- Dependency Upgrade Plans
- DFD Graphing Library Reference
- Migration Instructions