
OCI WAF (Web Application Firewall) Edge policies (WAAS) use a separately managed certificate when HTTPS is configured. HTTP-to-HTTPS redirection must be enabled in the policy; using WAF alone does not enable it.
Certbot renews the server certificate when a scheduled renewal runs and validation succeeds. A separately uploaded Edge-policy certificate is not replaced by server renewal alone. Check the certificate and expiry status at both deployment points.
This guide describes a certificate-expiry case and an example linking certbot deploy hooks with OCI CLI. The historical account is not evidence that the current environment has been verified. The hook still requires separate validation of error handling, policy selection and asynchronous deployment.
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.
# 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 → Settings → SSL certificate expiration date.
CERT_NAME with the verified Certbot certificate name; do not assume it equals the domain. Review this procedure independently of the later renewal hook.
/etc/letsencrypt/live/CERT_NAME/fullchain.pem
/etc/letsencrypt/live/CERT_NAME/privkey.pem
fullchain.pem contains the public server certificate and intermediate chain; privkey.pem is the corresponding secret private key. Certbot's live paths track renewed files. Do not weaken ownership or permissions on originals or archive files, or delete originals, to enable a manual upload.
Skip server staging if an existing approved and verified transfer method can safely read the originals with administrator privileges. Ordinary SFTP/scp does not automatically gain sudo privileges. If staging is necessary, create a new dedicated directory outside synchronization, backups and web serving, accessible only to the approved transfer account: directory 0700, both copied files 0600 from creation. Parent paths must be trusted; do not overwrite existing files or symbolic links.
/home/TRANSFER_USER/.dih-waf-export-UNIQUE/fullchain.pem
/home/TRANSFER_USER/.dih-waf-export-UNIQUE/privkey.pem
Replace TRANSFER_USER and UNIQUE with the reviewed account and a new per-task name. Limit ownership assignments to these two copied files and their dedicated directory; never use wildcard or recursive changes. Do not create exposed copies and restrict them afterward.
First prepare a dedicated folder on a managed, encrypted PC, excluded from synchronization, sharing and backups. On POSIX systems use directory 0700 and files 0600, owned by the authorized local user. On Windows use NTFS ACLs for the authorized user and necessary system administrators, checking inherited broad access. Do not assume Windows/WSL chmod alone restricts Windows access.
/home/LOCAL_USER/.dih-waf-upload-UNIQUE/fullchain.pem
/home/LOCAL_USER/.dih-waf-upload-UNIQUE/privkey.pem
These are POSIX templates; choose a separately reviewed absolute Windows path. Verify the server host key and transfer only these two files over an encrypted connection to their exact destinations. Avoid the current directory, Downloads and OneDrive. Once transfer is confirmed, remove any server staging copies by their two exact paths and then the empty dedicated directory. Keep key contents out of terminal output, clipboard, chat, logs and Git.
After confirming the target Edge policy and change approval, select fullchain.pem for SSL certificate and privkey.pem for Private key in the Console. Both use PEM; the leaf certificate precedes intermediates. The WAAS input private key cannot be passphrase-protected. Stop if the format or matching pair is uncertain; do not improvise conversion or upload.
Leave Self signed certificate unchecked for Let's Encrypt. Follow the separately approved certificate-change procedure for saving, policy application and result verification. This guidance itself is neither change approval nor evidence of successful deployment.
When the upload attempt ends or work stops, remove the two exact local temporary files and the empty dedicated folder, and confirm no server staging copies remain. Include partial transfers. Certbot originals, archive files and existing OCI certificate resources are not cleanup targets. Check handling of tool-created temporary files, recycle bins and synchronization/backup copies. Ordinary deletion cannot guarantee erasure from SSDs or snapshots, so minimize copies and retention from the outset. Record paths, times and cleanup outcomes without attaching key contents.
# Oracle Linux 8
sudo yum install -y python36-oci-cli
oci --version
Check for existing config first:
cat ~/.oci/config 2>/dev/null
If missing or authentication fails, run oci setup config. Required values:
| Field | Where to find |
|---|---|
| User OCID | OCI Console → top-right avatar → My profile → OCID Copy |
| Tenancy OCID | OCI Console → avatar → Tenancy → OCID Copy |
| Region | Top-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
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
| Error | Cause | Fix |
|---|---|---|
| certificateData is not valid | Using file:// format | Use $(cat file) instead |
| No such option: --config | WAAS API uses different param name | Use --policy-config |
| Update shows no change | Async processing (up to 15 min) | Check with oci waas work-request get |