今回は、dockerで作成したサービスをLet's Encryptを用いてhttps通信ができる方法についてです。
CI/CDや定期実行ではおなじみのjenkinsをコンテナにて実行する例を参考にご紹介します。

準備

最初に、準備する環境についてです。
1. Dockerを実行できるサーバ
コンテナを実行する上でDockerを実行できるサーバが必要になります。

2. DNS
https化するにあたり、SSL証明書を発行する必要があります。
SSL証明書はドメインに紐づくものなので、DNSへ登録できる環境が必要になります。

ここまでが下準備です。

3. Docker
では、本題のDocker環境についてです。
以下のフォルダ構成でファイルを準備します。

最初に今回利用するdokcer-composeファイルを記載します。(2024年5月時点)

version: '3.7'

services:
  jenkins:
    image: jenkins/jenkins:lts-jdk17
    container_name: jenkins-app
    environment:
      TZ: Asia/Tokyo
    ports:
      - "8080:8080"
    volumes:
      - ./jenkins_home:/var/jenkins_home 

  nginx:
    image: nginx:1.25.5
    container_name: jenkins-nginx
    environment:
      TZ: Asia/Tokyo
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx/nginx.conf:/etc/nginx/nginx.conf
            
      # 証明書
      - ./ssl/letsencrypt:/etc/letsencrypt
      - ./nginx/www/html:/var/www/html
    depends_on:
      - jenkins

  certbot:
        container_name: jenkins-certbot
        image: certbot/certbot:v1.7.0
        environment:
          TZ: Asia/Tokyo
        volumes:
          - ./ssl/letsencrypt:/etc/letsencrypt
          - ./nginx/www/html:/var/www/html
        command: ["--version"]

簡単に記載内容を紹介します。
・イメージについて
公式で提供されているDockerイメージを利用します。
Jenkins: jenkins/jenkins:lts-jdk17
jenkinsというリポジトリがありますが、非推奨となっており以下をオススメされていますのでおとなしく以下を使います。
https://hub.docker.com/r/jenkins/jenkins

nginx: nginx:1.25.5
通信を制御するために、Nginxを設定します。
https://hub.docker.com/_/nginx

Cert-bot: certbot/certbot:v1.7.0
本題の通信をhttpsで行うために、無料で証明書を発行してくれるcert-botを設定します。
https://hub.docker.com/r/certbot/certbot

・マウントについて
Jenkins、Nginxのファイルをホストマシンにディスクマウントを行います。
マウントを行うことで、コンテナを立て直したときでもデータを失うことを防ぐことができます。
Jenkinsでは、ジョブを実行しますが立て直しごとにデータが無くなってしまうと設定が大変面倒です。

・タイムゾーンについて
Jenkinsを実行するとログを出力することができますが、コンテナ内は特に指定しないとUTC(協定世界時)となりイギリスの時間として出力されます。
ログを見たときに見づらいので、東京の時間を設定します。

以上が、docker-composeのご紹介になります。

次にNginxの設定をご紹介します。
nginx.confを記載します。

events {}

http {
    server {
        listen 80;
        server_name hoge-hoge.webike.net;

        root         /var/www/html;

        location     /.well-known/acme-challenge/ {
            root /var/www/html;
        }

        ##return 301 https://$host$request_uri;
    }
# https設定
    # server {
    #     listen 443;
    #     root         /var/www/html;
    #     ##server_name hoge-hoge.webike.net;

    #     # 証明書
    #     ##ssl_certificate      /etc/letsencrypt/live/hoge-hoge.webike.net/fullchain.pem;
    #     ##ssl_certificate_key  /etc/letsencrypt/live/hoge-hoge.webike.net/privkey.pem;

    #     ssl_session_timeout 1d;
    #     ssl_session_cache shared:SSL:10m;

    #     ssl_protocols TLSv1.3 TLSv1.2;
    #     ssl_ciphers 'ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-RSA-AES128-GCM-SHA256';
    #     ssl_prefer_server_ciphers off;

    #     add_header Strict-Transport-Security "max-age=2592000" always;

    #     location / {
    #         proxy_pass http://jenkins:8080;
    #         proxy_set_header Host $host;
    #         proxy_set_header X-Real-IP $remote_addr;
    #         proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    #         proxy_set_header X-Forwarded-Proto $scheme;
    #     }
    # }
}

コメントアウトしている部分が多いですが、最初はそのままで、、、
設定はhttp, httpsの2つになります。
httpではcert-botを利用する上で、"/.well-known/acme-challenge/"の設定が証明書を取得する上で重要となるので、忘れず記載をしましょう。

httpsでは、取得したLet's Encryptの証明書をNginxに設定、SSL周りの設定となります。
また、jenkinsと通信を行うために、proxy_passを設定しています。

以上で、準備が整いました。
いよいよ、証明書を取得しましょう。

証明書を取得する

1. コンテナを実行
compose upを実行して、各コンテナが立ち上がっていることを確認しましょう。
★cert-botのコンテナはプロセスリストには表示されません。

もし立ち上がらない場合、dockerが出力しているログを確認してエラーとなっている部分を確認しましょう。

docker-compose up -d
docker ps
 # 立ち上がらない時
docker-compose logs

無事立ち上げることができると、http::8080でjenkinsの初期画面を見ることができます

2. SSL証明書を発行
このコマンドを実行する前に、DNSサービスであらかじめドメインを設定しましょう。
DNS設定後、以下のコマンドを実行することで管理者のメールアドレスなどの入力が求められます。
受信できるメールアドレスにすることで後ほどLet's Encryptからメールが届きます。

docker-compose run --rm certbot certonly --webroot -w /var/www/html -d hoge-hoge.webike.net

無事発行ができると
"- Congratulations! Your certificate and chain have been saved at:"
のように、表示がされます。

マウントしたフォルダ(./ssl/letsencrypt)を確認すると、証明書を確認することができます。

3. SSL証明書のNginxへ設定
先に登録したnginx.confを編集し、コメントアウトした部分を削除し以下の状態にします。

events {}

http {
    server {
        listen 80;
        server_name hoge-hoge.webike.net;

        root         /var/www/html;

        location     /.well-known/acme-challenge/ {
            root /var/www/html;
        }

        ##return 301 https://$host$request_uri;
    }
# https設定
     server {
         listen 443;
         root         /var/www/html;
         server_name hoge-hoge.webike.net;

        # 証明書
        ssl_certificate      /etc/letsencrypt/live/hoge-hoge.webike.net/fullchain.pem;
        ssl_certificate_key  /etc/letsencrypt/live/hoge-hoge.webike.net/privkey.pem;

        ssl_session_timeout 1d;
        ssl_session_cache shared:SSL:10m;

        ssl_protocols TLSv1.3 TLSv1.2;
        ssl_ciphers 'ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-RSA-AES128-GCM-SHA256';
        ssl_prefer_server_ciphers off;

        add_header Strict-Transport-Security "max-age=2592000" always;

         location / {
             proxy_pass http://jenkins:8080;
             proxy_set_header Host $host;
             proxy_set_header X-Real-IP $remote_addr;
             proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
             proxy_set_header X-Forwarded-Proto $scheme;
         }
     }
}

編集後、Nginxのコンテナの設定を再読み込みを行います

docker-compose exec nginx nginx -s reload

設定が反映されていない場合、コンテナを再度docker-compose downし、docker-compose up -dし直してみましょう。

5. 設定の確認
上記の設定が完了後、設定したドメインへブラウザを利用してアクセスしてみます。
https通信ができていれば、無事jenkinsサーバをhttps化する目的を達成することができました。

今後の改善

Let's Encryptの証明書は、無料で簡単に取得することができ便利です。しかし有効期間が3か月と有料の証明書などと比べ更新する頻度が高くなります。
現在の設定だと、手動で更新するオペレーションが残ってしまい手間がかかる仕組みとなっています。
定期的に更新ができるような仕組みを組み込んでいきたいです。