OCI WAF(Web Application Firewall)를 사용하면 DDoS 방어와 HTTP→HTTPS 리다이렉트를 자동으로 얻을 수 있습니다. 하지만 SSL 인증서 갱신이라는 함정이 숨어 있습니다.
서버의 certbot은 Let's Encrypt 인증서를 자동 갱신하지만, WAF에 업로드된 인증서는 별도로 갱신해야 합니다. 이를 잊으면 어느 날 갑자기 사이트가 접속 불가 상태가 됩니다.
이 글에서는 실제 운영 환경에서 겪은 WAF 인증서 만료 위기를 해결하고, certbot deploy hook과 OCI CLI로 완전 자동화하는 과정을 공유합니다.
certbot이 서버 인증서를 갱신해도, WAF 인증서는 그대로입니다. 두 인증서의 만료일이 다르기 때문에 서버 인증서가 유효해도 WAF 인증서가 만료되면 사이트 접속이 불가합니다.
sudo certbot certificates
# Expiry Date: 2026-05-21 (VALID: 62 days) ← 정상
sudo ls -la /etc/letsencrypt/renewal-hooks/deploy/
# 비어있음 ← 자동화 없음
oci --version 2>/dev/null || echo "OCI CLI 미설치"
# OCI CLI 미설치 ← API 호출 불가
OCI 콘솔 → WAF Policies → 정책 선택 → Settings → General settings에서 SSL certificate expiration date 확인.
sudo cp /etc/letsencrypt/live/도메인/fullchain.pem /home/opc/
sudo cp /etc/letsencrypt/live/도메인/privkey.pem /home/opc/
sudo chown opc:opc /home/opc/*.pem
scp opc@서버IP:~/fullchain.pem ./
scp opc@서버IP:~/privkey.pem ./
WAF Policy → Settings → Edit → Upload or paste certificate and private key → Files 선택 → fullchain.pem + privkey.pem 업로드 → Save changes
Self signed certificate는 체크하지 않음 — Let's Encrypt는 공인 CA입니다.
# Oracle Linux 8
sudo yum install -y python36-oci-cli
oci --version
기존 설정이 있을 수 있으니 먼저 확인:
cat ~/.oci/config 2>/dev/null
설정이 없거나 인증 실패하면 oci setup config를 실행합니다. 필요한 값:
| 항목 | 확인 위치 |
|---|---|
| User OCID | OCI 콘솔 → 우측 상단 사람 아이콘 → My profile → OCID Copy |
| Tenancy OCID | OCI 콘솔 → 사람 아이콘 → Tenancy → OCID Copy |
| Region | 콘솔 우측 상단 (예: ap-chuncheon-1) |
설정 완료 후 생성된 공개키를 OCI에 등록:
cat ~/.oci/oci_api_key_public.pem
# 출력 내용 복사 → 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 "도메인" || { echo "$(date) 스킵" >> "$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. 인증서 리소스 생성
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. WAF 정책에 연결
${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) 완료: $CERT_OCID" >> "$LOG"
SCRIPT
sudo chmod +x /etc/letsencrypt/renewal-hooks/deploy/upload-to-waf.sh
| 증상 | 원인 | 해결 |
|---|---|---|
| certificateData is not valid | file:// 형식 전달 | $(cat 파일)로 변경 |
| No such option: --config | WAAS API 파라미터명 상이 | --policy-config 사용 |
| 업데이트 후 변경 안 됨 | 비동기 처리 (최대 15분) | oci waas work-request get으로 확인 |