0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Docker編 第11回】CaddyでHTTPS化してTLSと証明書の仕組みを理解する

0
Posted at

はじめに

前回はCaddyをReverse Proxyとして導入し、

http://flask.home.arpa
http://kuma.home.arpa

というHost名でFlaskとUptime Kumaへアクセスできるようにしました。

構成は、

Windows PC
     |
     | HTTP :80
     v
   Caddy
     |
     +----> Flask
     |
     +----> Uptime Kuma

となっています。

今回はこの通信をHTTPS化します。

最終的には、

https://flask.home.arpa
https://kuma.home.arpa

へ証明書警告なしでアクセスできるようにします。


HTTPSにすると何が変わるのか

HTTPではClientとServer間の通信がTLSによって暗号化されていません。

HTTPSでは、

HTTP
 +
TLS
 =
HTTPS

という形でTLSを使って通信を保護します。

今回の構成ではCaddyがHTTPSを担当します。

Windows PC
    |
    | HTTPS
    v
  Caddy
    |
    | HTTP
    v
 Flask

Flask自身をHTTPS化するのではありません。

CaddyでTLS通信を終了し、内部では通常のHTTPでFlaskへReverse Proxyします。

このような構成を**TLS Termination(TLS終端)**と呼びます。


今回はCaddyのInternal CAを使う

今回使用しているHost名は、

flask.home.arpa
kuma.home.arpa

です。

これは家庭内ネットワークで使用している名前であり、公開Webサイト用のドメインではありません。

そこで今回は、Caddyが持つInternal CAを利用します。

イメージとしては、

Caddy Internal CA
       |
       +---- 証明書 ---> flask.home.arpa
       |
       +---- 証明書 ---> kuma.home.arpa

となります。

公開Webサイトで利用するLet's Encryptなどの公開CAとは異なり、今回のCAは自分の環境内で使用するCAです。


CaddyfileをHTTPS対応にする

前回はHTTPSを無効にするため、

http://flask.home.arpa {
    reverse_proxy web:5000
}

http://kuma.home.arpa {
    reverse_proxy uptime-kuma:3001
}

としていました。

これを次のように変更しました。

flask.home.arpa {
    tls internal
    reverse_proxy web:5000
}

kuma.home.arpa {
    tls internal
    reverse_proxy uptime-kuma:3001
}

tls internalによってCaddyのInternal CAを利用します。


443番Portを公開する

HTTPSでは通常443番Portを使用します。

Caddyのcompose.yamlを変更します。

services:
  caddy:
    image: caddy:latest
    container_name: caddy
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile:ro
      - caddy-data:/data
      - caddy-config:/config
    restart: unless-stopped
    networks:
      - default
      - docker-lab
      - uptime-kuma

networks:
  docker-lab:
    external: true
    name: docker-lab_default

  uptime-kuma:
    external: true
    name: uptime-kuma_default

volumes:
  caddy-data:
  caddy-config:

今回は、

- "80:80"
- "443:443"

を公開しています。

80番はHTTP、443番はHTTPSで使用します。


CaddyのデータをVolumeへ保存する

今回新しく、

- caddy-data:/data
- caddy-config:/config

を追加しました。

そして、

volumes:
  caddy-data:
  caddy-config:

としています。

特に/dataには、Caddyが管理する証明書やInternal CAに関係する重要なデータが保存されます。

Containerは再作成できますが、CAまで毎回作り直してしまうと、Windows側が信頼しているCAと一致しなくなる可能性があります。

そのため、CaddyのデータもVolumeへ永続化します。


設定を確認する

Compose設定を確認しました。

sudo docker compose config

問題がなければ反映します。

sudo docker compose up -d

確認します。

sudo docker compose ps

結果として、

0.0.0.0:80->80/tcp
0.0.0.0:443->443/tcp

が確認できました。

Host側でも確認します。

sudo ss -lntp | grep -E ':80 |:443 '

80番と443番の両方がListenしていました。


Caddyが証明書を発行したことを確認する

Caddyのログを確認します。

sudo docker compose logs --tail=50 caddy

ログには、

enabling automatic HTTP->HTTPS redirects

が表示されました。

つまりCaddyがHTTPからHTTPSへのRedirectを自動設定しています。

さらに、

enabling automatic TLS certificate management

も確認できました。

証明書についても、

certificate obtained successfully

となっており、

flask.home.arpa
kuma.home.arpa

の証明書が正常に発行されました。


HTTPSへアクセスしてみる

Windowsから、

https://flask.home.arpa

へアクセスしました。

しかし、この段階ではブラウザに証明書警告が表示されました。

Caddyが証明書を発行できているのに、なぜ警告されるのでしょうか。

理由は、

Caddy
 |
 | 「この証明書は私が保証します」
 v
Windows

「そのCAをまだ信用していない」

という状態だからです。

証明書を発行できることと、Clientがその発行元を信頼することは別です。


CaddyのRoot CAを確認する

Caddy Container内を確認しました。

sudo docker compose exec caddy \
  ls -l /data/caddy/pki/authorities/local/

次のファイルが確認できました。

intermediate.crt
intermediate.key
root.crt
root.key

ここで非常に重要なのが、

root.crt

と、

root.key

の違いです。

root.crtはRoot CAの公開証明書です。

一方、

root.key

は秘密鍵です。

秘密鍵が漏れると、そのCAを使って不正な証明書を作られる可能性があります。

そのため、

root.crt → Clientへ配布してよい
root.key → 外へ出さない

という扱いが必要です。


Root CAをWindowsへコピーする

Caddy ContainerからRaspberry Piへroot.crtをコピーしました。

sudo docker cp \
  caddy:/data/caddy/pki/authorities/local/root.crt \
  ./root.crt

sudo docker cpで作成したため、必要に応じて所有者と権限を変更しました。

sudo chown pi:pi ~/caddy/root.crt
chmod 644 ~/caddy/root.crt

Windowsからscpを使って取得します。

scp pi@192.168.1.93:/home/pi/caddy/root.crt .\root.crt

重要なのは、コピーするのはroot.crtだけという点です。

root.keyはコピーしません。


WindowsにRoot CAを信頼させる

管理者としてPowerShellを起動し、Root CAをWindowsの信頼されたRoot証明書ストアへ登録しました。

certutil -addstore -f Root "$env:USERPROFILE\root.crt"

これによって、

Caddy Internal CA
        |
        | root.crt
        v
Windows
「このCAを信頼する」

という状態になります。

ブラウザを再起動して、

https://flask.home.arpa

へアクセスすると、今度は証明書警告が表示されませんでした。

https://kuma.home.arpa

も同様にアクセスできました。


HTTPからHTTPSへ自動転送される

次に、

http://flask.home.arpa

へアクセスしました。

すると、自動的に、

https://flask.home.arpa

へ移動しました。

これはCaddyのAutomatic HTTPSによるRedirectです。

通信の流れは、

http://flask.home.arpa
        |
        | HTTP :80
        v
      Caddy
        |
        | HTTPSへRedirect
        v
https://flask.home.arpa
        |
        | HTTPS :443
        v
      Caddy

となります。


実際の証明書をOpenSSLで確認する

HTTPSで使われている証明書をOpenSSLで確認しました。

openssl s_client \
  -connect 127.0.0.1:443 \
  -servername flask.home.arpa \
  </dev/null 2>/dev/null \
| openssl x509 -noout -subject -issuer -dates

結果は、

subject=
issuer=CN=Caddy Local Authority - ECC Intermediate
notBefore=Aug 30 05:30:39 2026 GMT
notAfter=Aug 30 17:30:39 2026 GMT

となりました。

issuerを見ると、

Caddy Local Authority - ECC Intermediate

となっています。

つまりCaddyのInternal CAによって発行された証明書です。


証明書の有効期限が約12時間だった

今回確認した証明書は、

notBefore=Aug 30 05:30:39 2026 GMT
notAfter=Aug 30 17:30:39 2026 GMT

となっており、有効期間は約12時間でした。

ここで、

12時間後にHTTPSが使えなくなるのでは?

と思いました。

しかし、Caddyは証明書を自動管理します。

そのため、通常は有効期限が来る前にCaddyが新しい証明書へ更新します。

つまり、

証明書の有効期限が12時間

だからといって、

12時間後にHTTPSが使えなくなる

という意味ではありません。

今回caddy-data Volumeを永続化していることも重要です。

Caddy Container
       |
       X 再作成
       |
       v
caddy-data Volume
       |
       +---- Internal CA
       +---- 証明書関連データ

Containerを再作成しても、CaddyのPKI関連データを保持できます。

ただし、caddy-data Volumeまで削除してInternal CAを作り直した場合は注意が必要です。

新しいRoot CAが作られると、Windowsへ登録した古いRoot CAとは別のCAになる可能性があります。

その場合は、新しいroot.crtをWindowsへ登録し直す必要があります。


SANを確認する

証明書が本当にflask.home.arpa用なのか確認しました。

openssl s_client \
  -connect 127.0.0.1:443 \
  -servername flask.home.arpa \
  </dev/null 2>/dev/null \
| openssl x509 -noout -text \
| grep -A2 "Subject Alternative Name"

結果は、

X509v3 Subject Alternative Name: critical
    DNS:flask.home.arpa
Signature Algorithm: ecdsa-with-SHA256

となりました。

SANは、

Subject Alternative Name

の略です。

ここに、

DNS:flask.home.arpa

が入っています。

ブラウザは証明書の名前とアクセス先のHost名が一致しているかを確認します。

アクセス先
flask.home.arpa

        ↓ 比較

証明書SAN
DNS:flask.home.arpa

        ↓

一致

この確認も、HTTPSでServerを検証するための重要な仕組みです。


CAの信頼関係

今回の証明書の信頼関係を簡略化すると、

Caddy Root CA
      |
      v
Caddy Intermediate CA
      |
      v
flask.home.arpa用証明書

となります。

WindowsにはRoot CAを信頼させました。

そのため、

Windows
   |
   | Root CAを信頼
   v
Caddy Root CA
   |
   v
Intermediate CA
   |
   v
flask.home.arpa

という信頼のChainを検証できます。


TLSで何が行われているのか

細かな仕組みは非常に複雑ですが、今回の実験で重要な部分を簡略化すると、

1. ClientがServerへ接続する

2. Serverが証明書を提示する

3. Clientが証明書を検証する

4. TLSで暗号化通信に必要な情報を安全に確立する

5. 暗号化されたHTTP通信を行う

という流れになります。

証明書には単に「暗号化する」という役割だけでなく、

接続しているServerが意図したHost名のServerなのかを検証する

という重要な役割があります。


TLS Termination

今回の構成では、HTTPSを処理しているのはFlaskではありません。

Caddyです。

Windows PC
     |
     | HTTPS / TLS
     v
   Caddy
     |
     | HTTP
     v
   Flask

CaddyでTLS通信を終了しているため、

TLS Termination

と呼ばれます。

Flask側ではこれまで通り、

web:5000

でHTTP Serverを動かしています。

Caddyが、

  • 証明書
  • HTTPS
  • TLS
  • Redirect

をまとめて担当します。

複数のWeb Applicationがある場合でも、それぞれに個別でTLS設定を実装する必要がなく、Reverse Proxy側へ集約できます。


今回分かったこと

今回の実験では、次のことを確認できました。

  • HTTPSではTLSを利用する
  • CaddyはInternal CAを持てる
  • Caddyが証明書を自動発行できる
  • Client側がCAを信頼していなければ証明書警告が出る
  • Root CAをWindowsへ登録すると信頼できる
  • root.crtroot.keyはまったく扱いが違う
  • HTTPからHTTPSへ自動Redirectできる
  • 証明書には有効期限がある
  • Caddyは証明書を自動管理・更新する
  • SANに証明書の対象Host名が入っている
  • CaddyでTLS Terminationできる
  • Flask自身をHTTPS化しなくてもClientからHTTPSでアクセスできる

現在の構成

最終的な通信経路は次のようになりました。

Windows PC
     |
     | HTTP :80
     v
   Caddy
     |
     | Redirect
     v
   HTTPS :443
     |
     | TLS
     v
   Caddy
     |
     | Docker Network / HTTP
     |
     +----> Flask :5000
     |
     +----> Uptime Kuma :3001

ClientからCaddyまではHTTPSで暗号化されています。

Caddyから各ContainerまではDocker Network内をHTTPで通信しています。


おわりに

前回はReverse Proxyを使ってWebサービスの入口をCaddyへまとめました。

今回はさらにCaddyへTLSを担当させ、

http://

から、

https://

へ移行しました。

実際に証明書警告が出る状態から始めて、Root CAをWindowsへ登録すると警告が消えるところまで確認したことで、

証明書がある

だけでは不十分で、

その証明書を発行したCAをClientが信頼できる

ことが重要だと分かりました。

Dockerの学習から始めましたが、Reverse Proxyを導入したことで、

Docker Network
Reverse Proxy
DNS / hosts
HTTP
HTTPS
TLS
CA
Certificate

がどのようにつながっているのかも見えてきました。

次回は、この環境をさらに実運用へ近づけるための構成を試していきます。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?