2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

自己署名SSL証明書を簡単に生成できる Docker イメージを作りました

2
Posted at

毎回 OpenSSL コマンドを調べていませんか?

開発環境でもHTTPSが必要になるケース

最近のWebアプリケーション開発では、以下のような理由で開発環境でもHTTPSが必要になることが増えています:

  • ブラウザーAPIの制約: Service Worker や Web Cryptography API など、HTTPS 環境でしか動作しない API が増加
  • 本番環境との一致: 本番環境が HTTPS の場合、開発環境も同じにしないと挙動が変わる
  • 外部サービス連携: OAuth、Webhook 受信など HTTPS が必須の場合が多い
  • データベースSSL接続: MySQL 9.0 以降では SSL 接続がデフォルトで必須になるなど、データベースの SSL 化が進んでいる

OpenSSLコマンドでの証明書生成の課題

開発環境でHTTPS化するには自己署名SSL証明書が必要ですが、OpenSSL のコマンドは複雑です:

  • 何度調べても覚えられない
  • ワイルドカード証明書の作り方が毎回わからない
  • チーム内で証明書の作り方がバラバラ

解決策

ワイルドカード自己署名 SSL 証明書を生成する Docker イメージを作成しました:
futureys/ssl-certificate - Docker Image

この Docker イメージを使えば、環境変数を指定するだけ
標準的な自己署名 SSL 証明書が生成できます

さらに、Docker Compose と組み合わせることで、
開発環境のセットアップを完全に自動化できます

このイメージを使うメリット

OpenSSL コマンドを覚える必要なし - 環境変数だけで設定完了
チーム全体で統一 - Dockerfile や compose.yml で共有可能
自動化が簡単 - CI/CD や Docker Compose に組み込める
標準的なディレクトリ構成 - Linux 標準の /etc/pki を使用

ユースケース

このツールは以下のようなシーンで活用できます:

  • ローカル開発環境: HTTPS 接続が必要なアプリケーションの開発
  • テスト環境: SSL/TLS 接続のテスト
  • マイクロサービス間通信: コンテナ間のセキュアな通信
  • データベース接続: MySQL や PostgreSQL などの SSL 接続

クイックスタート

最も簡単に試せる例です:

compose.yml
services:
  ssl-certificate:
    environment:
      DOMAIN_NAME: localhost
      SUPPORT_ROOT_DOMAIN: true
    image: futureys/ssl-certificate:latest
    volumes:
      - pki:/etc/pki

  web:
    command:
      - sh
      - -c
      - |
        echo 'server {
          listen 443 ssl;
          ssl_certificate /etc/pki/tls/certs/servercert-localhost.pem;
          ssl_certificate_key /etc/pki/tls/private/serverkey-localhost.pem;
          location / { return 200 "SSL is working!\n"; add_header Content-Type text/plain; }
        }' > /etc/nginx/conf.d/default.conf && nginx -g 'daemon off;'
    depends_on:
      ssl-certificate:
        condition: service_completed_successfully
    image: nginx:alpine
    ports:
      - "443:443"
    volumes:
      - pki:/etc/pki:ro

volumes:
  pki:

起動後、https://localhost にアクセスすると "SSL is working!" が表示されます

この時点ではブラウザーに「信頼されていない証明書」の警告が表示されますが、次の手順で CA 証明書を取り出し、ブラウザーにインストールすることで回避できます:

docker compose cp web:/etc/pki/CA/cacert-localhost.pem ./cacert-localhost.pem

証明書のインストール手順については
後述の「CA 証明書のブラウザーへのインストール」セクションも参照してください

生成されるファイル

CA (認証局) 証明書とサーバー証明書の両方を、
関連する秘密鍵や証明書要求ファイルとともに生成します

6 つのファイルが生成されます:

CA (認証局) 関連

  • CA 秘密鍵: /etc/pki/CA/private/cakey.pem
  • CA 証明書要求: /etc/pki/CA/cacert-<ドメイン名>.csr
  • CA 証明書: /etc/pki/CA/cacert-<ドメイン名>.pem ← ブラウザーにインストールするファイル

サーバー証明書関連

  • サーバー秘密鍵: /etc/pki/tls/private/serverkey-<ドメイン名>.pem
  • サーバー証明書要求: /etc/pki/tls/certs/servercert-<ドメイン名>.csr
  • サーバー証明書: /etc/pki/tls/certs/servercert-<ドメイン名>.pem ← サーバーで使用

基本的な使い方

ボリュームマウントと環境変数を指定してイメージを実行します:

docker run --env DOMAIN_NAME=exampledomain.com \
           -v $(pwd)/CA:/etc/pki/CA \
           -v $(pwd)/tls/certs:/etc/pki/tls/certs \
           -v $(pwd)/tls/private:/etc/pki/tls/private \
           futureys/ssl-certificate

このコマンドを実行すると、exampledomain.com 用の証明書が
現在のディレクトリの CAtls/certstls/privateディレクトリに生成されます

Docker Composeとの統合

Docker Compose を使用すると、アプリケーション起動時に自動的に証明書を生成できます

MySQL データベース接続を保護する例を紹介します

1. compose.yml の準備

compose.yml
services:
  ssl-certificate:
    environment:
      DOMAIN_NAME: database
      SUPPORT_ROOT_DOMAIN: true
    image: futureys/ssl-certificate:latest
    volumes:
      - pki:/etc/pki

  database:
    command:
      - --ssl-ca=/etc/pki/CA/cacert-database.pem
      - --ssl-cert=/etc/pki/tls/certs/servercert-database.pem
      - --ssl-key=/etc/pki/tls/private/serverkey-database.pem
    depends_on:
      ssl-certificate:
        condition: service_completed_successfully
    entrypoint: setup-certificate.sh
    environment:
      MYSQL_ROOT_PASSWORD: ${DATABASE_ROOT_PASSWORD}
    healthcheck:
      test:
        - CMD
        - mysqladmin
        - ping
        - -h
        - localhost
        - -u
        - root
        - -pexamplepass
        - --ssl-mode=REQUIRED
      interval: 5s
      timeout: 10s
      retries: 5
      start_period: 30s
    image: mysql:9.5.0
    volumes:
      - pki:/etc/pki
      - ./setup-certificate.sh:/usr/local/bin/setup-certificate.sh
      # MySQL 9.0.0 未満の場合は以下も必要
      - ./mysql_conf.d:/etc/mysql/conf.d

volumes:
  pki:

2. MySQL 9.0.0 未満の場合の設定

MySQL 9.0.0 未満を使用する場合は次のファイルを作成します:

mysql_conf.d/ssl.cnf
[mysqld]
# SSL 接続を強制
require_secure_transport = ON

3. setup-certificate.sh の準備

証明書のパーミッション設定と MySQL の起動を行うスクリプトを作成します:

setup-certificate.sh
#!/bin/bash
set -eu
SERVERKEY="/etc/pki/tls/private/serverkey-database.pem"
chown root:mysql "${SERVERKEY}"
chmod 640 "${SERVERKEY}"
# docker-entrypoint.sh は公式データベースイメージのデフォルト ENTRYPOINT
exec docker-entrypoint.sh "$@"

重要: スクリプトに実行権限を付与してください:

chmod +x setup-certificate.sh

4. 起動

以下のコマンドで SSL が有効化された MySQL が起動します:

docker compose up

内部的には、次の手順が自動的に行われます:

  1. サービス: ssl-certificate が証明書を生成
  2. サービス: database は証明書生成の完了を待機
  3. setup-certificate.sh が証明書のパーミッションを適切に設定
  4. MySQL が生成された証明書を使用して SSL 接続を有効化

起動時にパラメーターとして与える環境変数

DOMAIN_NAME

証明書を生成するドメイン名を指定します
この環境変数は必須です

例:

DOMAIN_NAME=exampledomain.com

上記の場合、*.exampledomain.comのすべてのサブドメインで使用できます

(ワイルドカード証明書が生成されるため)

SUPPORT_ROOT_DOMAIN

ルートドメインのカバレッジをサブドメインと一緒に有効にするブール値フラグです
trueに設定すると、ルートドメインも証明書に含まれます
デフォルトはfalseです

例:

SUPPORT_ROOT_DOMAIN=true

例えば、DOMAIN_NAME=exampledomain.comの場合:

SUPPORT_ROOT_DOMAIN 対象となるドメイン 説明と例
false
(デフォルト)
*.exampledomain.comのみ api.exampledomain.comwww.exampledomain.comなど
true exampledomain.com*.exampledomain.comの両方 ルートドメインexampledomain.comも含む

CA 証明書のブラウザーへのインストール

自己署名証明書をブラウザーからアクセスする Web サーバーで使用する場合、
CA 証明書をブラウザーにインストールする必要があります

(ブラウザーでの「信頼されていない証明書」の警告を防ぐため)

  1. コンテナから生成された CA 証明書 (cacert-<ドメイン名>.pem) を取得
  2. ブラウザーの設定画面を開く
  3. 証明書管理セクションに移動
  4. CA 証明書としてインポート・信頼

1. CA 証明書ファイルをコンテナから取り出す

docker compose cp web:/etc/pki/CA/cacert-<ドメイン名>.pem ./cacert-<ドメイン名>.pem

例: DOMAIN_NAME=exampledomain.com の場合:

docker compose cp web:/etc/pki/CA/cacert-exampledomain.com.pem ./cacert-exampledomain.com.pem

2. Chrome への証明書インストール手順

  1. Chrome の設定を開く (chrome://settings)
  2. 「プライバシーとセキュリティ」→「セキュリティ」を選択
  3. 「証明書の管理」をクリック

以降は OS ごとに異なります

Mac の場合

  1. 「Mac からインポートした証明書を管理する」をクリック
  2. 「キーチェーンアクセスを開く」をクリック
  3. 名前とパスワードの入力を求められたら、このコンピュータの管理者ユーザの名前とパスワードを入力します
  4. 「システムキーチェーン」の「システム」をクリック
  5. 証明書ファイルをキーチェーンアクセスアプリ上にドラッグします
  6. 名前とパスワードの入力を求められたら、このコンピュータの管理者ユーザの名前とパスワードを入力します
  7. インポートした証明書をダブルクリック
  8. 「信頼」セクションを展開
  9. 「この証明書を使用する際:」のドロップダウンメニューで「常に信頼」を選択
  10. ウィンドウを閉じて、名前とパスワードの入力を求められたら、このコンピュータの管理者ユーザの名前とパスワードを入力します

参考: Mac でキーチェーンアクセスを使用してキーチェーンに証明書を追加する - Apple サポート (日本)

Windows の場合

  1. 「Windows からインポートした証明書を管理する」をクリック
  2. 「認証局」タブを選択
  3. 「インポート」をクリック
  4. ダウンロードした cacert-localhost.pem を選択
  5. 「この証明書を使って Web サイトを識別する際に信頼する」にチェック
  6. 「OK」をクリック

参考: How do I deal with NET:ERR_CERT_AUTHORITY_INVALID in Chrome?

3. ドメイン名が localhost でない場合の hosts ファイル設定

ドメイン名を localhost 以外に設定した場合、hosts ファイルに設定を追加する必要があります

例: DOMAIN_NAME=exampledomain.com の場合:

127.0.0.1 local.exampledomain.com
  • Windows: C:\Windows\System32\drivers\etc\hosts
  • Mac/Linux: /etc/hosts

トラブルシューティング

「Permission denied」エラーが出る

setup-certificate.sh に実行権限がない可能性があります:

chmod +x setup-certificate.sh

ブラウザーで「信頼されていない」と表示される

以下の原因が考えられます:

原因1: CA 証明書がブラウザーにインストールされていない

CA 証明書 (cacert-<ドメイン名>.pem) をブラウザーにインストールしてください
詳細は前述の「CA 証明書のブラウザーへのインストール」セクションを参照してください

原因2: アクセスしているドメイン名と証明書のドメイン名が一致していない

ドメイン名を localhost 以外に設定しているのに
https://localhost にアクセスしていませんか?
ブラウザーからアクセスする際は
DOMAIN_NAME で指定したドメイン名(またはそのサブドメイン)を使用する必要があります

確認方法
  • Docker Compose の環境変数 DOMAIN_NAME の値を確認
  • ブラウザーでアクセスしている URL のドメイン名が
    DOMAIN_NAME の値(またはそのサブドメイン)と一致しているか確認
対処法
  1. DOMAIN_NAME で指定したドメイン名でアクセス
    • 例: DOMAIN_NAME=exampledomain.com の場合、
      https://local.exampledomain.com にアクセス
  2. Hosts ファイルで特定のドメイン名をローカル IP アドレスにマッピング
    • 「CA 証明書のブラウザーへのインストール」の
      「3. ドメイン名が localhost でない場合の hosts ファイル設定」を参照

証明書が生成されない

ボリュームマウントのパスが正しいか確認してください
docker runの場合は$(pwd)、Docker Composeの場合は
相対パスまたは名前付きボリュームを使用します

参考資料

詳細な使い方やソースコードは GitHub リポジトリをご覧ください:

yukihiko-shinoda/dockerfile-ssl-certificate

2
2
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
2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?