やろうとしたこと
GoogleCloudのVMにdockerでWebサーバーを立てて、HTTPSでアクセスできるようにしたかった。
HTTPS通信にするためにはロードバランサを使用しSSL/TLS証明書(以下、証明書と呼ぶ)を適用する必要がある。ロードバランサはどう頑張ってもコストがかかるので、できれば使わずに無料でHTTPS化したい。無料でHTTPSを使う、ここ大事!
前提
- 使用するdocker関連ファイルはCTFdを流用
- 使用するドメイン名は取得済み
- HTTPを使ったWebサーバーからのレスポンスは正常なことを確認済み
手順
-
Nginxの設定ファイルを修正する
CTFdだと~/conf/nginx/http.confを修正することになる
worker_processes 4; events { worker_connections 1024; } http { # Configuration containing list of application servers upstream app_servers { server ctfd:8000; } server { # HTTPからHTTPSへのリダイレクト listen 80; server_name <取得しているドメイン名>; # ドメイン名を指定 # リダイレクト設定 return 301 https://$host$request_uri; } server { listen 443 ssl; # SSLを有効にする server_name <取得しているドメイン名>; # ドメイン名を指定 # SSL証明書のパスを指定 ssl_certificate /etc/letsencrypt/live/cyber-ctf.xyz/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/cyber-ctf.xyz/privkey.pem; gzip on; client_max_body_size 4G; # Handle Server Sent Events for Notifications location /events { proxy_pass http://app_servers; proxy_set_header Connection ''; proxy_http_version 1.1; chunked_transfer_encoding off; proxy_buffering off; proxy_cache off; proxy_redirect off; 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-Host $server_name; } # Proxy connections to the application servers location / { proxy_pass http://app_servers; proxy_redirect off; 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-Host $server_name; } } }
-
docker-compose.yamlを修正する
services: ctfd: build: . user: root restart: always ports: - "8000:8000" environment: - UPLOAD_FOLDER=/var/uploads - DATABASE_URL=mysql+pymysql://ctfd:ctfd@db/ctfd - REDIS_URL=redis://cache:6379 - WORKERS=1 - LOG_FOLDER=/var/log/CTFd - ACCESS_LOG=- - ERROR_LOG=- - REVERSE_PROXY=true volumes: - .data/CTFd/logs:/var/log/CTFd - .data/CTFd/uploads:/var/uploads - .:/opt/CTFd:ro depends_on: - db networks: default: internal: nginx: image: nginx:stable restart: always volumes: - ./conf/nginx/http.conf:/etc/nginx/nginx.conf - /etc/letsencrypt:/etc/letsencrypt # 証明書をマウントさせるため追記 ports: - 443:443 # HTTPS接続させるため変更 # 証明書を使うためcertbotの記述を追加 certbot: image: certbot/certbot volumes: - ./certs:/etc/letsencrypt entrypoint: "/bin/sh -c 'trap exit TERM; while :; do certbot renew; sleep 12h & wait $${!}; done;'" depends_on: - ctfd db: image: mariadb:10.11 restart: always environment: - MARIADB_ROOT_PASSWORD=ctfd - MARIADB_USER=ctfd - MARIADB_PASSWORD=ctfd - MARIADB_DATABASE=ctfd - MARIADB_AUTO_UPGRADE=1 volumes: - .data/mysql:/var/lib/mysql networks: internal: # This command is required to set important mariadb defaults command: [mysqld, --character-set-server=utf8mb4, --collation-server=utf8mb4_unicode_ci, --wait_timeout=28800, --log-warnings=0] cache: image: redis:4 restart: always volumes: - .data/redis:/data networks: internal: networks: default: internal: internal: true
-
Certbotを使用して証明書を取得する
sudo docker-compose run --rm certbot certonly --nginx -d <ドメイン名> 又は sudo docker-compose run --rm certbot certonly --standalone -d <ドメイン名>
-
dockerを起動する
sudo docker compose up
これでdocker上のNginxとHTTPS通信が可能になる上、HTTP通信を試みるとHTTPSにリダイレクトされる!
補足(上手くいかない時)
自分の場合は手順3を実施した時に、証明書が何も取得できなかった。これはdockerに適用する前にVMでテストするため以下のコマンドで証明書を既に入手していたから。
sudo apt install python3-certbot-nginx
sudo certbot --nginx
証明書を既に持っているにも関わらずdocker compose upをすると証明書のエラーが出て起動できない状態が続いた。
この場合の原因はdocker-compose.yamlファイルにあった。当初はファイルの記述内容を下記のようにしていた
nginx:
image: nginx:stable
restart: always
volumes:
- ./conf/nginx/http.conf:/etc/nginx/nginx.conf
- ./:/etc/letsencrypt # ここの記述がエラーの原因だった
ports:
- 443:443
証明書が格納されているファイルを調べると/etc/letsencrypt/だった。
つまり、上記のコードだと、証明書がdocker上の/etc/letsencryptにマウントされず証明書エラーが出てしまう。
事前にVM上で証明書を取ったためにエラーが出てしまい時間を溶かしてしまった。
さいごに
思い返せばdockerで出るエラーはほとんどvolumesに関連してる。
他の人はどうなんだろう?
自分は今度からエラーが出たら真っ先にvolumesが正しいか確認することから始めようかな。