Traefikを使ってLet’s Encryptの証明書を得る
Traefikを使ってLet’s Encryptの証明書を得て、その証明書でウェブサイトを保護する。これをやってみるのだが、あちこちのウェブサイトにあるサンプルを試してみたものの、それらはバージョンが古いのか何なのか、どうにもうまく動かない。結局本家のサンプルが最もうまく行った。
サンプルは以下の二つで、現時点では2.8用である(今後更新されるかもしれない)。
これを見ていくが、それ以前に前提条件がある。
- docker, docker-composeがインストールされていること
- 自身のドメインを持っており、今回のテスト用にDNSがセットアップされていること
- 自身のメールアドレスがあること。
以下では、オリジナルのサンプルとは異なり、ドメインを「fortest.example.com」、メアドを「me@example.com」としてある。
基本的なサンプル
適当なフォルダを作成して、その中にdocker-compose.ymlとして以下を記述する。ただし、fortest.example.comの部分は自分の保持するドメイン(あるいはサブドメイン)にする。なお、fortest.example.comを囲んでいるのはシングルクォート(‘)ではなく、バッククォート(`)であることに注意。
version: "3.3"
services:
traefik:
image: "traefik:v2.8"
container_name: "traefik"
command:
#- "--log.level=DEBUG"
- "--api.insecure=true"
- "--providers.docker=true"
- "--providers.docker.exposedbydefault=false"
- "--entrypoints.web.address=:80"
ports:
- "80:80"
- "8080:8080"
volumes:
- "/var/run/docker.sock:/var/run/docker.sock:ro"
whoami:
image: "traefik/whoami"
container_name: "simple-service"
labels:
- "traefik.enable=true"
- "traefik.http.routers.whoami.rule=Host(`fortest.example.com`)"
- "traefik.http.routers.whoami.entrypoints=web"
上を記述したら、そのフォルダの中で「docker-compose up -d」を行い、http://fortest.example.comにアクセスする。
以下のような表示がされるはずだ。
Hostname: 3adb4ff***
IP: 127.0.0.1
IP: 172.27.0.2
RemoteAddr: 172.27.0.3:50894
GET / HTTP/1.1
Host: fortest.example.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:105.0) Gecko/20100101 Firefox/105.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Encoding: gzip, deflate, br
Accept-Language: ja,en-US;q=0.7,en;q=0.3
Dnt: 1
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: none
Sec-Fetch-User: ?1
Te: trailers
Upgrade-Insecure-Requests: 1
X-Forwarded-For: ****
X-Forwarded-Host: fortest.example.com
X-Forwarded-Port: 443
X-Forwarded-Proto: https
X-Forwarded-Server: 0b98bcc****
X-Real-Ip: ****
Let’ Encryptの証明書を取得する
前のサンプルは「docker-compose down」としてコンテナを停止しておく。
docker-compose.ymlを以下に書き換える。fortest.example.comは自身のドメインに書き換える。me@example.comは自身のメアドに書き換える。
version: "3.3"
services:
traefik:
image: "traefik:v2.8"
container_name: "traefik"
command:
#- "--log.level=DEBUG"
- "--api.insecure=true"
- "--providers.docker=true"
- "--providers.docker.exposedbydefault=false"
- "--entrypoints.web.address=:80"
- "--entrypoints.websecure.address=:443"
- "--certificatesresolvers.myresolver.acme.httpchallenge=true"
- "--certificatesresolvers.myresolver.acme.httpchallenge.entrypoint=web"
#- "--certificatesresolvers.myresolver.acme.caserver=https://acme-staging-v02.api.letsencrypt.org/directory"
- "--certificatesresolvers.myresolver.acme.email=me@example.com"
- "--certificatesresolvers.myresolver.acme.storage=/letsencrypt/acme.json"
ports:
- "80:80"
- "443:443"
- "8080:8080"
volumes:
- "./letsencrypt:/letsencrypt"
- "/var/run/docker.sock:/var/run/docker.sock:ro"
whoami:
image: "traefik/whoami"
container_name: "simple-service"
labels:
- "traefik.enable=true"
- "traefik.http.routers.whoami.rule=Host(`fortest.example.com`)"
- "traefik.http.routers.whoami.entrypoints=websecure"
- "traefik.http.routers.whoami.tls.certresolver=myresolver"
これで「docker-compose up -d」を行い、(この例だと)https://fortest.example.comにアクセスする。すると、Let’s Encryptの証明書で保護されていることがわかる。
Let’s Encryptの証明書は、docker-compose.ymlの下のフォルダletsencryptのacme.jsonに保持されている。このファイルはただのテキストファイルなので、単純に表示すれば、保持されている様子がわかる。
別コンテナに適用するには?
docker-composeに慣れている人なら言わずもがなだろうが、これを別コンテナに適用するにはどうするか?
別々のフォルダで、それぞれ次のようなdocker-compose.ymlを書き、それぞれ起動する。
ネットワークを合わせないといけないので、common-networkを使用している。あらかじめ、「docker network create common-network」として作成しなければならない。
version: "3.3"
services:
traefik:
image: "traefik:v2.8"
container_name: "traefik"
command:
#- "--log.level=DEBUG"
- "--api.insecure=true"
- "--providers.docker=true"
- "--providers.docker.exposedbydefault=false"
- "--entrypoints.web.address=:80"
- "--entrypoints.websecure.address=:443"
- "--certificatesresolvers.myresolver.acme.httpchallenge=true"
- "--certificatesresolvers.myresolver.acme.httpchallenge.entrypoint=web"
#- "--certificatesresolvers.myresolver.acme.caserver=https://acme-staging-v02.api.letsencrypt.org/directory"
- "--certificatesresolvers.myresolver.acme.email=me@example.com"
- "--certificatesresolvers.myresolver.acme.storage=/letsencrypt/acme.json"
ports:
- "80:80"
- "443:443"
- "8080:8080"
volumes:
- "./letsencrypt:/letsencrypt"
- "/var/run/docker.sock:/var/run/docker.sock:ro"
networks:
default:
external:
name: common-network
version: "3.3"
services:
whoami:
image: "traefik/whoami"
container_name: "simple-service"
labels:
- "traefik.enable=true"
- "traefik.http.routers.whoami.rule=Host(`fortest.example.com`)"
- "traefik.http.routers.whoami.entrypoints=websecure"
- "traefik.http.routers.whoami.tls.certresolver=myresolver"
networks:
default:
external:
name: common-network
つまり、任意のサービスについて、labels以下の文言を書けば、簡単にLet’s Encryptでの保護ができるということ。ただし、networkを合わせること。
ただし、fortest.example.comは変更しないといけない。また、”traefik.http.routers.whoamiのwhoami部分は任意であり、他コンテナと競合しなければ何でもよい。
80以外のポートの場合
whoamiは80以外のポートも指定できる。この場合をみてみる。
以下では、whoamiは2001番で待ち受けているが、それを示すことにより、traefikは、80で受けたリクエストをwhoamiの2001番に受け渡す。
version: "3.3"
services:
whoami:
image: "traefik/whoami"
container_name: "whoami"
command:
# It tells whoami to start listening on 2001 instead of 80
- "--port=2001"
labels:
- "traefik.enable=true"
- "traefik.http.routers.whoami.rule=Host(`fortest.example.com`)"
- "traefik.http.routers.whoami.entrypoints=websecure"
- "traefik.http.routers.whoami.tls.certresolver=myresolver"
- "traefik.http.services.whoami.loadbalancer.server.port=2001"
networks:
default:
external:
name: common-network