自宅鯖SSL問題を解決!OCI NginxとローカルOLSの証明書競合の回避方法
当ブログ「納戸工房」のサーバー構成は、いくつかの技術的な壁を乗り越えた結果、今の形に辿り着きました。
最初はシンプルに WireGuard(OCI) -> OLS(local) -> WordPress という構成でした。しかし、ここで最初の壁にぶつかります。WireGuardのiptables転送では X-Forwarded-For ヘッダーが適切に付与されず、WordPressの高速化に不可欠な QUIC.cloud連携 が正常に動作しなかったのです。
この問題を解決するため、OCI側にリバースプロキシとして Nginx を導入。Nginx(OCI) — WireGuard -> OLS(local) という構成へ進化させました。
しかし、次に待っていたのが「SSL終端」の悩みです。当初はOLS側でSSLを終端させようとしましたが、Nginx越しにうまくトラフィックを通せず、最終的に NginxをSSL終端にする 決断をしました。これにより通信問題は解決しましたが、副産物として 「OLS側でacmeツールを使った証明書の自動更新ができなくなる」 という新たな課題が浮上しました。
SSL更新の「主導権」を奪われたバックエンドサーバー
現状、外部公開用のSSL証明書はOCI上のNginxが管理しています。しかし、自宅内のローカルアクセスやサーバー間通信の整合性を保つためには、バックエンドのOLSにも同じ有効な証明書を配置しなければなりません。
Nginxがポート80を占有しているため、OLSが自分で証明書を更新しに行くことはできません。かといって、3ヶ月ごとに手動で証明書をコピペするのは納戸工房の美学に反します。
「何もしない」を自動化する。年間手動更新回数「0」へ
目指すべきゴールは明確です。
- SSL更新にかける手動作業時間を「年間0分」にする。
- ネットワークの制約(OCI→自宅へのPush不可)を回避し、安全に証明書を同期する。
「構成が複雑だから手動は仕方ない」と諦めるのではなく、複雑な構成のまま自動化を完遂させます。
権限の壁と、ネットワークの「片側通行」
自動化にあたり、2つの大きな課題がありました。
- Permission Denied(権限の壁): Let’s Encryptの証明書(/etc/letsencrypt/)はroot権限で守られています。一般のSSHユーザーでは読み取ることができず、rsyncしようとしても弾かれます。
- Pull型しか選べない制約: WireGuardで繋がっているとはいえ、コンテナ運用かつセキュリティの観点から「OCIから自宅へ送りつける(Push)」ことは困難です。自宅側から「取りに行く(Pull)」仕組みが必須でした。
Deploy Hook ✕ Pull型同期 ✕ Graceful Restart
この課題を、3つのステップで解決しました。
1. OCI側の「お裾分け」準備
本尊の証明書(root所有)は汚さず、更新された時だけ一般ユーザーが読める場所へコピーを作成する「デプロイ・フック」を設定します。
# OCI側のCertbot更新フック設定例 cp -L /etc/letsencrypt/live/your-domain/fullchain.pem /home/ubuntu/certs/ chown ubuntu:ubuntu /home/ubuntu/certs/*.pem
2. 自宅側の Pull型同期スクリプト (copy_ssl_sync.sh)
秘密鍵(id_rsa)を指定し、リンクの実体を追跡(-L)しながら rsync で取得します。また、無駄な再起動を避けるため、ファイルのハッシュ値を比較するロジックを組み込みました。
#!/bin/bash
# 設定エリア
SSH_KEY="./id_rsa"
SSH_USER="ubuntu"
SSH_HOST="<OCI_IP>"
REMOTE_PATH="/home/ubuntu/certs/"
LOCAL_PATH="/home/root/servers/ols-docker-env/"
OLS_CONTAINER="openlitespeed"
# 1. ハッシュ値比較
PRE_HASH=$(md5sum ${LOCAL_PATH}fullchain.pem 2>/dev/null | awk '{print $1}')
# 2. 同期実行 (rsyncはリモート側にもインストールが必要)
rsync -avL -e "ssh -i ${SSH_KEY} -o StrictHostKeyChecking=no" \
${SSH_USER}@${SSH_HOST}:${REMOTE_PATH}fullchain.pem ${LOCAL_PATH}
rsync -avL -e "ssh -i ${SSH_KEY} -o StrictHostKeyChecking=no" \
${SSH_USER}@${SSH_HOST}:${REMOTE_PATH}privkey.pem ${LOCAL_PATH}
# 3. 更新があった場合のみOLSをリロード
POST_HASH=$(md5sum ${LOCAL_PATH}fullchain.pem | awk '{print $1}')
if [ "$PRE_HASH" != "$POST_HASH" ]; then
docker exec $OLS_CONTAINER /usr/local/lsws/bin/lswsctrl restart
fi
3. 再現性を重視した cron.d での自動化
管理のしやすさと再現性を考え、crontab -e ではなく /etc/cron.d/ に設定ファイルを配置しました。
# /etc/cron.d/ssl-sync 00 03 * * * root cd /home/root/servers/ols-docker-env && /bin/bash copy_ssl_sync.sh >> /var/log/ssl_sync.log 2>&1
【まとめ】制約を楽しみ、システムを飼い慣らす
今回のSSL自動更新の完成により、複雑なネットワーク構成という「制約」を、スクリプトという「知恵」で克服することができました。
- X-Forwarded-For問題はNginx導入で解決。
- SSL更新問題はPull型同期スクリプトで解決。
- 運用管理は cron.d によるファイルベース管理で解決。
一見すると遠回りに見える構成ですが、一つ一つの課題に正面から向き合い、自らの手で解を導き出す。これこそが「納戸工房」が大切にしている豊かな想像力と探求心の結果です。
これでようやく、心置きなく次のDIYプロジェクトや技術検証に没頭できそうです。

