TLS と証明書
このページで解決すること
Section titled “このページで解決すること”本番構成は立ち上げた時点で https です。docker compose up -d のあと、ウェブ画面は 443 番にあり、
80 番に来た平文のリクエストはそちらへ転送されます。
証明書は既定ではシステム自身が発行し、自前のものに差し替えることもできます。
このページでは証明書の出どころの選び方、CA を接続元のマシンへ配る手順、
すでに自社の ingress がある場合のつなぎ方、そして TLS と接続チャネルの wss が効いているかの確かめ方を扱います。
- 外向きのホスト名ひとつ。
.envのTLS_DOMAINに書きます。社内 DNS かクライアントの hosts で引ければ十分で、公開登録されたドメインである必要はありません。 .envのPUBLIC_BASE_URL。同じホスト名の https の URL を指します。シングルサインオンのコールバックアドレスはこの値から組み立てます。- 自前の証明書を使うときは、証明書チェーンと秘密鍵(
fullchain.pemとprivkey.pem)。 - 公開するポートは 443(https)と 80(http、転送のみ)の二つで、URL にポートは付きません。ホスト側で既にそれらのポートを使っている場合は
.envのTLS_HTTPS_PORTとTLS_HTTP_PORTを別の組(よく使うのは 8443 と 8088)に変更し、そのときは URL にポートを付けます。
内蔵のプロキシは TLS 1.2 以上、HSTS、そしてターミナルとグラフィック接続に必要な WebSocket の upgrade 転送を提供します。
設定ファイル内のホスト名は起動時に TLS_DOMAIN から差し込まれるので、ファイル自体を編集する必要はありません。
アプリケーション側の受け入れも済んでいます。フロントエンドはページのプロトコルに合わせて ws と wss を切り替え、
アクセストークンは URL ではなく Authorization ヘッダーで渡します。
設定のしかた
Section titled “設定のしかた”既定:システムが署名するローカル CA
Section titled “既定:システムが署名するローカル CA”.env の TLS_MODE は出荷時 selfsigned です。初回起動時に tls/ へローカルの認証局とサーバー証明書を生成し、
以後の起動ではそれを使い続けます。証明書は TLS_DOMAIN のホスト名と、TLS_IP_SAN に並べたアドレスを含みます。
CA の秘密鍵はホストの tls/ca-private/ に残り、プロキシのコンテナには入りません。
TLS_DOMAIN=jumper.example.internalTLS_IP_SAN=10.0.0.5 # アドレスでも到達させるとき。カンマ区切りPUBLIC_BASE_URL=https://jumper.example.internalこの三つが空のときは、bash scripts/quickstart.sh がマシンのホスト名とアドレスから埋め、
最後に開くべき URL と CA 証明書のダウンロード先を表示します。すでに入っている値はそのまま残ります。
docker compose up -d続けて、CA 証明書を接続元のマシンへ配ります。
curl -sk https://jumper.example.internal/custodexa-ca.crt -o custodexa-ca.crtWindows ドメインではグループポリシー(コンピューターの構成 → Windows の設定 → セキュリティの設定 → 公開キーのポリシー → 信頼されたルート証明機関)で配布し、macOS とモバイル端末では MDM の構成プロファイル、 単体のマシンでは手動でシステムの信頼されたルート証明書ストアに取り込みます。 配り終えれば、ブラウザはこのサイトを信頼された接続として表示します。 CA 証明書は公開してよいデータなので、そのまま利用者に渡せます。
自前の証明書に差し替える
Section titled “自前の証明書に差し替える”公開 CA や組織の CA が発行した証明書が手元にあるときは、次のようにします。
- チェーンを
tls/fullchain.pem、秘密鍵をtls/privkey.pemに置きます。 .envでTLS_MODE=provided、TLS_DOMAIN(証明書のホスト名と揃えます)、PUBLIC_BASE_URL=https://<ホスト名>(外向きのポートが 443 以外ならポートも付けます)を設定します。docker compose up -d。
どちらかのファイルが無いと、起動は証明書の準備の段階で止まり、足りないファイルの名前を示します。 プロキシが中途半端な設定のまま立ち上がることはありません。
docker compose logs tls-inittls/ にすでにある証明書は、以後の起動でもそのまま使われます。入れ替えるときはファイルを消してから起動します。
組織の CA で署名する
Section titled “組織の CA で署名する”ホスト側で秘密鍵と署名要求を作り、SAN にホスト名を入れて提出します。
openssl req -newkey rsa:2048 -nodes -keyout tls/privkey.pem \ -out custodexa.csr -subj "/CN=jumper.bnc.prod" \ -addext "subjectAltName=DNS:jumper.bnc.prod"署名されたら、サーバー証明書と中間証明書を順に(リーフを先頭に)tls/fullchain.pem へつなぎ、
TLS_MODE を provided にして再起動します。
その CA に対するクライアント側の信頼は、通常 AD のグループポリシーか MDM で配ります。
社内の ID プロバイダーが同じ CA で https を話す場合は、CA 証明書をバックエンドのコンテナの信頼ストアに追加します。
すでに自社の ingress がある場合
Section titled “すでに自社の ingress がある場合”前段にクラウドのロードバランサーや既存の nginx、Traefik がある構成では、overlay で内蔵プロキシを退かせ、 フロントエンドが自分で http のポートを公開して ingress から受けられるようにします。
docker compose -f docker-compose.yml -f docker-compose.external-ingress.yml up -d.env に COMPOSE_FILE=docker-compose.yml:docker-compose.external-ingress.yml を書いておけば、
以後の up -d、ps、down に毎回 -f を付ける必要はありません。
この形ではフロントエンドが 80 を公開し(.env の HTTP_PORT で変更できます)、外向きの TLS は ingress が担います。
条件は TLS 1.2 以上、信頼された証明書、HTTP から HTTPS への転送、HSTS、そして WebSocket の upgrade 転送です。
ingress からこのホストまでの区間が信頼できないネットワークをまたぐなら、その区間も暗号化します。
PUBLIC_BASE_URL には利用者が実際に見る https の URL を入れます。
フロントエンドへ転送する Host ヘッダーには、利用者が実際に接続する外向きのポートを含めます(443 以外なら付けます)。
CORS_ALLOWED_ORIGINS を設定していないとき、バックエンドは同一オリジンのリクエストだけを受け付け、
その判定はリクエストの Origin と Host の比較で行うため、ポートの欠けた Host では資格情報付きのリクエストがクロスオリジンと見なされます。
TRUSTED_PROXIES には自社の ingress のアドレス、またはそれが属するネットワークを書きます。
これは各リクエストをどの送信元アドレスに帰属させるかを決め、監査記録に残るアドレスと、ログインのレート制限が数える鍵を決めます。
(内蔵プロキシを使う既定の形では bash scripts/quickstart.sh が Docker のサブネットを書き込みます。)
プロキシの設定を自分で書く
Section titled “プロキシの設定を自分で書く”同梱のテンプレートは一般的な用途を賄います。変えたいときは複製し、.env から複製のほうを指し、元のファイルは触りません。
cp docker/reverse-proxy/nginx-tls.conf.template tls/custodexa.conf.templateTLS_NGINX_TEMPLATE=./tls/custodexa.conf.template変えられるのは server_name(既定では TLS_DOMAIN から入ります)、証明書のパス、追加のヘッダー
(HSTS の max-age、includeSubDomains、preload は自社のドメイン方針に合わせます)、
そして upstream(同じ Docker ネットワークならサービス名 frontend:80)です。
docker compose up -d tls-proxy で反映します。
プロキシのコンテナを動かさず、ホストにある既存の nginx を使う場合は、上の ingress overlay でフロントエンドに http のポートを公開させ
(マッピングを 127.0.0.1:8088:80 に変えて loopback に縛るのがおすすめです)、設定は同じテンプレートを流用し、
upstream を 127.0.0.1:8088 に向け、server_name と証明書のパスを自分のものに置き換えます。
nginx 1.25.1 より前のバージョンでは http2 on; の代わりに listen 443 ssl http2; と書きます。
ほかのプロキシ(Caddy、Traefik、クラウドのロードバランサー)も前節の条件を満たせば使えます。
docker/reverse-proxy/Caddyfile.example が同等の Caddy 設定です。
docker compose ps # tls-proxy が Up。tls-init は証明書を用意し終えると終了しますcurl -sI http://localhost/ | head -1 # 301。https へ転送されますcurl -skI https://localhost/ | head -1 # 200curl --cacert tls/ca-public/custodexa-ca.crt -sI https://<TLS_DOMAIN>/ | head -1 # 自己署名モードでは 200四つめは CA 証明書で信頼の連鎖を検証します。200 が返れば証明書とホスト名が一致しています。
そのあとログインして接続をひとつ張ると、ブラウザの開発者ツールの Network タブに wss:// のストリームが見えます。
ログイン状態を保持する条件
Section titled “ログイン状態を保持する条件”ログイン状態の再発行に使うトークンは HttpOnly cookie で運びます。ポリシーページの「接続とアカウント」の節に 「ログイン状態は https 接続でのみ保持する」というスイッチがあり、出荷時は有効です。有効なら cookie は暗号化された接続でしか送られず、 保存すれば次に発行する cookie から新しい値が使われます。再起動は要りません。
ポリシーにまだ値が無いときは、.env の AUTH_REFRESH_COOKIE_SECURE、PUBLIC_BASE_URL のプロトコル、出荷時の既定という順で初期値を取ります。
起動ログには効いている値とその出どころが出ます。
docker compose logs backend | grep "refresh cookie"監査記録のタイムスタンプはホストの時計から取ります。本番ではホストで時刻同期サービスを有効にし、時刻源は UTC にしてください。
監査で見えること
Section titled “監査で見えること”- 通信セキュリティのページにあるチャネル台帳が、各チャネルの現在の暗号化状態を並べます。ウェブ層は導入側の管理と示し、基準からのずれも表示します。
- 台帳はスナップショットとして書き出せます。スナップショットには生成の時刻と生成者が入り、書き出し自体も監査に残ります。
- ポリシーのスイッチを変えると一件の記録が残り、変更者と変更の前後の値が入ります。