OCI_WAF(CDN)を構成してみる
Categories:
WAFのEdge PolicyはCDNとしての機能もあるらしいのでそれ目的で構成してみた
OCIリソース
- Oracle Cloud Infrastructure Certificates:
Cert_Blogs_Ash→結局Edge Policyでは使えないことがわかって削除- Reasion:US East(Ashburn)
- Compartment: Blogs
- Oracle Web Application Firewall(WAF)※Edge Policy: WaF_for_Blogs_Ash
Reasion:
US East(Ashburn)→Edge PolicyはグローバルっぽいCompartment: Blogs
SAN証明書
ドメイン毎に証明書を分けようと思ったが、WAFが1つにつき1つの証明書しか含められない、かつWAFが初回インスタンスのみ無料っぽいので、ドメインすべて同じ証明書にくくる
→というかその方法 SAN証明書というやつ取得する必要があるらしく、Traefikの構成を変える必要があるとのこと。
↓下は証明書を分けて抜き出すためのコマンド。WAFのインスタンスポコポコ量産できないので使わない。
# jqインストール
sudo apt -y install jq
# Traefik v2ではA型とBg型の格納方法があるらしい。↓はB型(型によって以下のコマンドが変わってくる)
ubuntu@instance-blogs-ash:~/traefik/letsencrypt$ jq 'keys' ~/traefik/letsencrypt/acme.json
[
"le"
]
ubuntu@instance-blogs-ash:~/traefik/letsencrypt$
# どのドメインの証明書が含まれてるか確認
ubuntu@instance-blogs-ash:~/traefik/letsencrypt$ jq -r '.[].Certificates[].domain.main' ~/traefik/letsencrypt/acme.json
staging.fuku.tokyo
stagingsub.fuku.tokyo
ubuntu@instance-blogs-ash:~/traefik/letsencrypt$
# ドメイン毎に証明書ファイルを分けて出力する方法
jq -c '
.[].Certificates[] |
{dom: .domain.main, cert: .certificate, key: .key}
' acme.json |
while IFS= read -r obj; do
dom=$(jq -r '.dom' <<<"$obj")
jq -r '.cert' <<<"$obj" | base64 -d > "${dom}.crt"
jq -r '.key' <<<"$obj" | base64 -d > "${dom}.key"
done
SAN証明書の構成
traefik.ymlにdomains: セクションを追加
- 今のhttpChallenge:web(CAが80番ポートアクセスして存在を確認)という仕組みでdomainsはサポートしてないらしくdnsChallenge(DNSをチェックするっぽい)にする必要があるらしい
- dnsChallenge providerにociがサポートしているらしい(OCI DNSを使っているのでこれが使える)
- OCI APIキー取得
- 「user settings」> 「API Key」から生成して、各種設定とprivate key fileを保存
- traefik.ymlを変更:
- domainsを指定
websecure: address: ":443" http: tls: options: default certResolver: le # 1 つだけ ACME リゾルバを指定 domains: # ここにまとめたいドメインを列挙 - main: staging.fuku.tokyo sans: - stagingsub.fuku.tokyo
- 今のhttpChallengeをdnsChallengeに変更
dnsChallenge: provider: oraclecloud delayBeforeCheck: 10
- domainsを指定
- docker-compose.yml修正
- secratesを登録して秘密鍵ファイルをコンテナに渡す、トップレベルでsecrets定義(リソースの定義)とサービス側にもsecrets定義(使うよって定義)が必要
- 各OCIのAPI KEY情報を設定。実際の設定は.envに書いてある。
ubuntu@instance-blogs-ash:~/traefik$ cat docker-compose.yml services: traefik: image: traefik:latest container_name: traefik restart: unless-stopped ports: - "80:80" - "443:443" volumes: - ./traefik.yml:/traefik.yml:ro - ./dynamic:/dynamic:ro - ./letsencrypt:/letsencrypt - /etc/localtime:/etc/localtime:ro - /etc/timezone:/etc/timezone:ro secrets: - source: oci_api_key target: oci_api_key.pem environment: - TZ=${TIMEZONE} - OCI_REGION=${OCI_REGION} - OCI_COMPARTMENT_OCID=${OCI_COMPARTMENT_OCID} - OCI_TENANCY_OCID=${OCI_TENANCY_OCID} - OCI_USER_OCID=${OCI_USER_OCID} - OCI_PUBKEY_FINGERPRINT=${OCI_PUBKEY_FINGERPRINT} # ファイル参照 (_FILE) で秘密鍵を安全に渡す - OCI_PRIVKEY_FILE=/run/secrets/oci_api_key.pem - OCI_PRIVKEY_PASS= # パスフレーズ無しなら空文字を明示 depends_on: - socket-proxy networks: - traefik_net socket-proxy: image: tecnativa/docker-socket-proxy:latest container_name: socket-proxy networks: - traefik_net # Traefik と同じネット environment: TZ: ${TIMEZONE} CONTAINERS: 1 # Traefik が必要とする最小限のみ有効化 volumes: - /var/run/docker.sock:/var/run/docker.sock:ro - /etc/localtime:/etc/localtime:ro - /etc/timezone:/etc/timezone:ro networks: traefik_net: external: true secrets: oci_api_key: file: ./secrets/oci_api_key.pem ubuntu@instance-blogs-ash:~/traefik$
- private key fileを置く
- mkdier ~/traefik/secrets
- ファイルアップロード(./secrets/oci_api_key.pem)
- chmod ./secrets/oci_api_key.pem ```
- 今のhttpChallenge:web(CAが80番ポートアクセスして存在を確認)という仕組みでdomainsはサポートしてないらしくdnsChallenge(DNSをチェックするっぽい)にする必要があるらしい
コンテナに定義してある証明書自動取得ラベルを外して代わりにtlsが有効っていうラベルのみにする
↓を消して - "traefik.http.routers.site1.tls.certresolver=le" ↓を入れる - "traefik.http.routers.site1.tls=true"
treaefikを再起動とその他コンテナ再起動
- コンテナ止めて
- acme.jsonを初期化
cp /dev/nul acme.json
- コンテナ起動
- サイトアクセスできればとりあえず成功
- ダメだったら、
docker compose logs |grep ERR
でtraefikのエラーを見る。
一枚にまとまっている確認
#1 と表示される
ubuntu@instance-blogs-ash:~/traefik/letsencrypt$ jq '.le.Certificates | length' acme.json
1
ubuntu@instance-blogs-ash:~/traefik/letsencrypt$
# 1つのエントリ(1行)に複数のサブドメインが含まれている。
ubuntu@instance-blogs-ash:~/traefik/letsencrypt$ jq -r '.le.Certificates[]
| [.domain.main] + .domain.sans
| @tsv' acme.json
staging.fuku.tokyo stagingsub.fuku.tokyo
ubuntu@instance-blogs-ash:~/traefik/letsencrypt$
その他ナレッジ
yqインストール(yamalのパーサ、apt getでインストールもできるがそれ古い奴らしくて使えない。この新しい奴をインストールする)
sudo wget -O /usr/local/bin/yq \
https://github.com/mikefarah/yq/releases/latest/download/yq_linux_arm64
sudo chmod +x /usr/local/bin/yq
Oracle Cloud Infrastructure Certificates
→結局Edge WASでは使えないことが分かった。そのため最終的にはWAFに直接証明書をImportした。
WAF はレガシー版(Edge WAF)とWAF v2(Reagional WAF)の2つがある。
v1はOCI外に配置されてるWAFサーバーっぽい。→CDN用途で使えるのはこっち。
v2はLBにアタッチする形で使うセキュリティーの拡張機能って感じ。
証明書のExport
MAIN=staging.fuku.tokyo jq -r --arg MAIN "$MAIN" ' .le.Certificates[] | select(.domain.main==$MAIN) | .certificate' acme.json | base64 -d > fullchain.pem jq -r --arg MAIN "$MAIN" ' .le.Certificates[] | select(.domain.main==$MAIN) | .key' acme.json | base64 -d > privkey.pem # fullchain.pemとprivkey.pemが出来る。 ubuntu@instance-blogs-ash:~/traefik/letsencrypt$ ls -ltr total 24 -rw------- 1 ubuntu ubuntu 13462 May 14 23:07 acme.json -rw-rw-r-- 1 ubuntu ubuntu 4010 May 14 23:33 fullchain.pem -rw-rw-r-- 1 ubuntu ubuntu 3243 May 14 23:33 privkey.pem ubuntu@instance-blogs-ash:~/traefik/letsencrypt$ # ドメインがすべて含まれていればOK ubuntu@instance-blogs-ash:~/traefik/letsencrypt$ openssl x509 -in fullchain.pem -noout -text | grep DNS DNS:staging.fuku.tokyo, DNS:stagingsub.fuku.tokyo ubuntu@instance-blogs-ash:~/traefik/letsencrypt$ # OCI ではcertificatonとchain別で張らないといけないので、分けて出力 awk ' /-----BEGIN CERTIFICATE-----/ {i++} i==1 {print > "certificate.pem"} # 1枚目だけ i>=2 {print > "chain.pem"} # 2枚目以降をチェーンへ ' fullchain.pem
OCI
Certificates > Certificates > Create Certificate をクリック
Importedを選択して、Certificateにcertificate.pemの内容を、Certificate Chainにfullchain.pemの内容を、Private Keyにprivkey.pemの内容を張る、そのまま進んで、最終的にActiveになればOK
Oracle Web Application Firewall(WAF)
Identity & Security > Web application firewall > Create WAF policy
legacy workflow のリンクをクリックする必要がある。
これでつくるとEdge policy になるらしく、作成も削除もちょっと時間かかる。世界中のWAFサーバーに設定を伝搬しているらしい。
入力はこんな感じ
出来上がったら、Settingsでオプションを設定する。
証明書はOCI Cerficationsを使えないので直接アップロード↓のは一回アップロードした後の画面なのでCertificatieが入っているが、初回はUpload…のほウにチェックして貼り付ける。fullchain.pemとprivkey.pemが必要(fullchainの2件の登録間の空白行はダメらしいので、そこを削って張った)
各オプションの意味
画面に表示される項目 | OCI CLI パラメータ | 何をするか | いつ有効にする? |
---|---|---|---|
Enable response buffering | --is-response-buffering-enabled | オリジンから返ってくるレスポンスをいったん WAF エッジでバッファリングしてからクライアントへ送り出します。‐ ネットワークの瞬断や輻輳時に、途中で転送が切れるリスクを低減。‐ その代わり Time To First Byte (TTFB) がわずかに遅くなる可能性があります。 (Oracle クラウド インフラドキュメント) | 暗号化オブジェクトや大きいファイルを配信していて、回線品質が不安定なケース/WAF 側でレスポンス改変(圧縮・マスキングなど)を行うケース |
Cache control respected | --is-cache-control-respected | オリジン応答の Cache-Control ヘッダー値(例 max-age=120 )をそのまま尊重し、WAF がエッジ・キャッシュとして働きます。独自のキャッシュルールを別途設定している場合はそちらが優先されます。 (Oracle クラウド インフラドキュメント) | オリジン側で細かいキャッシュ制御をしている/既存 CDN から OCI WAF に移行しつつ設定を変えたくない場合 |
Behind CDN | --is-behind-cdn | WAF の前段に CDN がある前提で、CDN が付与するヘッダー(既定は X-Forwarded-For)から「本当のクライアント IP」を取得します。不正アクセスブロックやレート制限を正しい IP 単位で判定できます。ヘッダー名は Client address header で変更可能です。 (Oracle クラウド インフラドキュメント) | Akamai/CloudFront など外部 CDN を経由して OCI WAF を使う場合 |
Enable SNI | --is-sni-enabled | TLS 拡張の Server Name Indication (SNI) を有効化します。WAF→オリジンへの TLS ハンドシェイク時に Host 名 を渡せるため、1 つの IP:Port で複数バーチャルホストを運用しているオリジンでも正しい証明書が返せます。 (Oracle クラウド インフラドキュメント) | オリジンが SNI 必須の構成(例: SSL 証明書を vhost 単位で切り替えている Nginx/Apache)になっている場合 |
設定したら publish all てボタンが出るので押さなきゃダメ。→反映には10分~30分くらいかかるらしい。
DNSの書き換え
CNAME Targetが表示されるのでDNSに登録
Aレコード消して、CNAMEを登録
速度計測
いろいろ計測ツール(Webサービス)があるらしい
- Google PageSpeed Insights
- GTmetrix
- Pingdom Website Speed Test
- WebPageTest
その中で手軽そうなPingdom Website Speed Testで実施
from: Asia/Tokyoで計測
staging.fuku.tokyo(WAF使ってない)
staging.fuku.tokyo(WAF)
stagingsub.fuku.tokyo(WAF) →ページサイズが違くて確認ならなかったが遅いっちゃ遅い
www.fuku.tokyo(Tokyo region)
Chrome のDeveloper Toolsでもいい感じにできる。こっちのほうがいいかな。Shift + F5で計測
staging.fuku.tokyo(WAF使ってない)
staging.fuku.tokyo(WAF)
www.fuku.tokyo(Tokyo region)
結局速くはならなかった。WordPressみたいな動的生成ページにはCDN不向きっていう根本的な話もあるので。
とりあえずしばらくはこれで運用してみようかな。