OCI WAF SSL Certificate Auto-Renewal Guide — certbot + OCI CLI | Data In Hands
OCI WAF SSL Auto-Renewal Architecture Diagram

OCI WAF (Web Application Firewall) provides DDoS protection and automatic HTTP-to-HTTPS redirects. But there's a hidden trap: SSL certificate renewal.

While certbot automatically renews server-side Let's Encrypt certificates, the WAF certificate must be uploaded separately. Forget this, and your site goes down without warning.

This guide walks through a real production incident where a WAF certificate was 2 days from expiry, and how we built full automation with certbot deploy hooks and OCI CLI.

The problem: dual certificate architecture

[Browser] │ ▼ [OCI WAF] ←── WAF SSL cert (manual upload) │ ▼ [Apache Server] ←── Server SSL cert (certbot auto-renew)

Even when certbot renews the server certificate, the WAF certificate remains unchanged. If the WAF cert expires, the site becomes inaccessible — regardless of the server cert status.

⚠️ Real scenario: Server certbot certificate was valid until May 21st, but the WAF certificate was expiring in 2 days (March 22). No deploy hooks, no OCI CLI installed.

Diagnosis

# Server certificate status
sudo certbot certificates
# Expiry Date: 2026-05-21 (VALID: 62 days) ← OK

# Deploy hooks exist?
sudo ls -la /etc/letsencrypt/renewal-hooks/deploy/
# Empty ← No automation

# OCI CLI available?
oci --version 2>/dev/null || echo "OCI CLI not installed"
# Not installed ← Can't call API

WAF certificate status: check in OCI Console → WAF Policies → Policy → SettingsSSL certificate expiration date.

Emergency fix: manual certificate upload

1
Extract server certificate files
sudo cp /etc/letsencrypt/live/domain/fullchain.pem /home/opc/
sudo cp /etc/letsencrypt/live/domain/privkey.pem /home/opc/
sudo chown opc:opc /home/opc/*.pem
2
Download to local machine
scp opc@server-ip:~/fullchain.pem ./
scp opc@server-ip:~/privkey.pem ./
3
Upload via OCI Console

WAF Policy → Settings → Edit → Upload or paste certificate and private key → Files → upload fullchain.pem + privkey.pem → Save changes.

Do not check Self signed certificate — Let's Encrypt is a public CA.

Permanent fix: OCI CLI + certbot deploy hook

Install OCI CLI

# Oracle Linux 8
sudo yum install -y python36-oci-cli
oci --version

Configure OCI CLI

Check for existing config first:

cat ~/.oci/config 2>/dev/null

If missing or authentication fails, run oci setup config. Required values:

FieldWhere to find
User OCIDOCI Console → top-right avatar → My profile → OCID Copy
Tenancy OCIDOCI Console → avatar → Tenancy → OCID Copy
RegionTop-right of console (e.g., ap-chuncheon-1)

Register the generated public key in OCI:

cat ~/.oci/oci_api_key_public.pem
# Copy output → My profile → API keys → Add API key → Paste
💡 API key limit: OCI allows 3 API keys per user. If all 3 slots are taken, delete an unused key first. OCI doesn't show which keys are actively in use — check Audit logs by fingerprint for indirect confirmation.

The deploy hook script

sudo tee /etc/letsencrypt/renewal-hooks/deploy/upload-to-waf.sh << 'SCRIPT'
#!/bin/bash
LOG="/var/log/waf-cert-upload.log"
OCI="/usr/bin/oci --config-file /home/opc/.oci/config"
WAF_ID="<WAF_POLICY_OCID>"

echo "$(date) ==============================" >> "$LOG"
echo "$RENEWED_DOMAINS" | grep -q "yourdomain" || { echo "$(date) Skip" >> "$LOG"; exit 0; }

CERT_PATH="$RENEWED_LINEAGE/fullchain.pem"
KEY_PATH="$RENEWED_LINEAGE/privkey.pem"
COMPARTMENT_ID=$(${OCI} waas waas-policy get \
    --waas-policy-id ${WAF_ID} \
    --query 'data."compartment-id"' --raw-output)

# 1. Create certificate resource
CERT_RESULT=$(${OCI} waas certificate create \
    --compartment-id "$COMPARTMENT_ID" \
    --certificate-data "$(cat ${CERT_PATH})" \
    --private-key-data "$(cat ${KEY_PATH})" \
    --display-name "auto-$(date +%Y%m%d%H%M%S)")

CERT_OCID=$(echo "$CERT_RESULT" | \
    python3 -c "import sys,json; print(json.load(sys.stdin)['data']['id'])")

# 2. Attach to WAF policy
${OCI} waas waas-policy update \
    --waas-policy-id "$WAF_ID" \
    --policy-config "{\"certificateId\":\"${CERT_OCID}\",\"isSniEnabled\":true,\"isHttpsEnabled\":true,\"isHttpsForced\":true,\"tlsProtocols\":[\"TLS_V1_2\",\"TLS_V1_3\"]}" \
    --force >> "$LOG" 2>&1

echo "$(date) Done: $CERT_OCID" >> "$LOG"
SCRIPT

sudo chmod +x /etc/letsencrypt/renewal-hooks/deploy/upload-to-waf.sh

Gotchas

ErrorCauseFix
certificateData is not validUsing file:// formatUse $(cat file) instead
No such option: --configWAAS API uses different param nameUse --policy-config
Update shows no changeAsync processing (up to 15 min)Check with oci waas work-request get
✅ Result: Every time certbot renews a certificate, the deploy hook fires automatically → uploads to OCI WAF. No more manual intervention.
Scroll to Top