Dockerでオレオレ認証局を作成〜オレオレ証明書を発行
普段Dockerコンテナを使って開発環境やテスト環境を作っているのですが、Web通信部分はやっぱりHTTPSで行いたい(各所をhttps://→http://に書き換えたりする作業が大変、ということもある)。とはいうものの、テスト環境用にわざわざサーバ証明書を取得するのも面倒。
そこで、Dockerコンテナでテスト環境用のルート認証局(オレオレ認証局)を作成し、それで各テスト用サーバのサーバ証明書を発行することにしました。
インターネット上を「オレオレ認証局」などで検索すると数多くヒットしますが、今回その手順を整理してみました。
オレオレ認証局の作成
オレオレ認証局とするDockerのコンテナを作成します。
Docker、およびdocker-composeが動作する環境を準備しておきます(ここではDockerのインストールまでは解説しません)。
opensslコマンドが使用できればあとは特に何も必要ないので、軽量なAlpine Linuxイメージでサクッと作っていきます。
Dockerfileはこんな感じで良いでしょう。
FROM alpine
RUN apk add --update openssl && \
rm -rf /var/cache/apk/*
秘密鍵・サーバ証明書などを保存するための永続的なファイルシステムが欲しいので、docker-composeを使ってvolumesを指定します。
docker-compose.yml をこんな感じで作成します。
version: "3.7"
services:
myca:
build: ./
container_name: myca
hostname: myca
volumes:
- "./data/pki:/etc/pki"
tty: true
stdin_open: true
docker-compose.yml ファイルのあるフォルダ直下に「data」フォルダを作成し、その下に「pki」フォルダを作成しておきます。
ここまでできたら動かしてみましょう。ターミナル/コマンドプロンプトより上で作成したdocker-compose.ymlがある場所へ移動し、以下を実行します。
% docker-comose up -d
:
Creating myca ... done
コンテナが正常に起動できればコンテナにログインしてみます。
% docker container exec -it myca sh
/ # <------ プロンプトが返ってくればOK
自己署名証明書の作成
次にオレオレ自己署名証明書(ルート証明書)を作成します。
openssl.cnfの準備
opensslコマンド実行時にデフォルト値としての動作を記述したマスタ設定ファイルと呼ばれる「openssl.cnf」ファイルを準備します。
openssl.cnfファイルはLinuxの場合「/etc/ssl」の下にあると思いますので、それをコピーして作業場所に持ってきます。
- 以降のコマンド操作はオレオレ認証局コンテナで行います。
/ # cd /etc/pki
/etc/pki # mkdir cert <------------------ 自己署名証明書などの認証局のデータを作成・保持するディレクトリとします
/etc/pki # cp /etc/ssl/openssl.cnf . <--- openssl.cnfをコピーしてきます
ホスト側にて「data/pki」フォルダを開くと「cert」フォルダができており、その中にコピーされたopenssl.cnfファイルがありますので、エディタで中身を編集します。
- オレオレ認証局コンテナにはvi等のエディタは入っていませんので、ファイルの内容編集は全てホスト側で行うことにします。
:
default_md = sha256 # use public key default MD <---- 77行目 ハッシュ関数方式
:
default_bits = 4096 <----------------- 108行目 公開鍵のビット数
:
countryName_default = JP <------------ 131行目 国コード
:
stateOrProvinceName_default = Fukuoka <--- 136行目 都道府県名
:
localityName = Munakata <--------- 138行目 地域
:
0.organizationName_default = KzStyle <---- 141行目 組織名
:
以降より自己署名証明書(ルート証明書)の実際の作成に入ります。
コマンド1行で一気に作成することもできますが、opensslコマンドのオプションを理解していないと何をしているのかがわかりにくいので、一段階づつ進めていきます。
秘密鍵の作成
オレオレ認証局の秘密鍵を「privkey_cert.pem」という名前で作成します。
/etc/pki/cert # openssl genrsa -aes256 -out privkey_cert.pem 4096
Generating RSA private key, 4096 bit long modulus (2 primes)
..........++++
......................++++
e is 65537 (0x010001)
Enter pass phrase for privkey_cert.pem: <----------------------- パスフレーズを入力
Verifying - Enter pass phrase for privkey_cert.pem: <----------- 同じパスフレーズを入力
自己署名証明書署名要求の発行
次に自己署名証明書署名要求というものを「cacert.csr」という名前で作成します。
先に作成したopenssl.cnfの設定内容がここで反映されます。
/etc/pki/cert # openssl req -new -key privkey_cert.pem -config openssl.cnf -out cacert.csr
Enter pass phrase for privkey_cert.pem:
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [JP]: <------------- そのままEnter
State or Province Name (full name) [Fukuoka]:<--- そのままEnter
Munakata []:<------------------------------------ そのままEnter
Organization Name (eg, company) [KzStyle]:<------ そのままEnter
Organizational Unit Name (eg, section) []:<------ そのままEnter
Common Name (e.g. server FQDN or YOUR name) []:My CA <--- わかりやすい名前であれば何でも可
Email Address []:admin@rootca.local <------------ 未入力でも可
Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []: <----------------------- そのままEnter
An optional company name []: <------------------- そのままEnter
オレオレ自己署名証明書(ルート証明書)を発行
最後にオレオレ自己署名証明書(ルート証明書)を「mycacert.pem」の名前で発行します。
自己署名証明書の有効期間は3950日(-days 3650)にしています。
/etc/pki/cert # openssl x509 -req -in cacert.csr -signkey privkey_cert.pem -days 3650 -extensions v3_ca -out mycacert.pem
Signature ok
subject=C = JP, ST = Fukuoka, O = KzStyle, CN = My CA, emailAddress = admin@rootca.local
Getting Private key
Enter pass phrase for privkey_cert.pem: <---- 秘密鍵のパスフレーズを入力
オレオレ自己署名証明書(ルート証明書)をホストマシンへ登録
作成したオレオレ自己署名証明書は実際にアクセスするPCに登録しないと機能しません。
オレオレ自己署名証明書をPCへ登録する方法を紹介します。
macOSの場合
作成したオレオレ自己署名証明書「mycacert.pem」をダブルクリックすると「キーチェーンアクセス」が開き登録されます。
その後、当該ルート証明書を右クリック > 情報を見る を選択します。
情報表示画面が開くので、信頼 をクリック >
SSL (Secure Sockets Layer) > 常に信頼 に変更
X.509基本ポリシー > 常に信頼 に変更
Windowsの場合
[スタート] > [ファイル名を指定して実行]を選択し、「名前(O)」に「certmgr.msc」と入力し[OK]をクリックします。
証明書の管理ウィンドウが表示されるので、[信頼されたルート証明書] > [証明書] を右クリックし、[すべてのタスク(K)] > [インポート(i)...]を選択します。
証明書のインポートウィザードが開始されるので、[次へ(N)]をクリックします。
インポートするファイルを指定するウィンドウに変わるので、[参照(R)...]ボタンをクリックし、作成したオレオレ自己署名証明書「mycacert.pem」を選択し[次へ(N)]をクリックします。
「証明書をすべて次のストアに配置する(P)」を選択し、[次へ(N)]をクリックします。
「証明書のインポート ウィザードの完了」ウィンドウが表示されるので[完了(F)]をクリックします。
「セキュリティ警告」ウィンドウが表示されるので[はい(Y)]をクリックします。
正しくインポートされた旨のメッセージが表示されるので[OK]をクリックします。
証明書の管理ウィンドウにオレオレ自己署名証明書が追加されたことを確認します。
ダブルクリックすると証明書の詳細が確認できます。
Firefoxの場合
上記macOSの場合、またはWindowsの場合を実行し、PCにオレオレ自己署名証明書を認識させたとしても、Firefoxブラウザは個別に自己署名証明書を認識させる必要があります。
Firefoxでオレオレ自己署名証明書で署名したサーバ証明書を使ったサイトにアクセスすると下記のような警告を表示してしまいます。
Firefoxブラウザ右上のハンバーガーメニューをクリックし、「設定」を選択します。
設定画面が開くので、左側メニューより「プライバシーとセキュリティ」を選択し、[証明書を表示...(C)]をクリックします。
「証明書マネージャー」画面が開くので、「認証局証明書」タブを選択し、[インポート(M)...]をクリックします。
証明書ファイルを選択する画面が表示されるので、作成したオレオレ自己署名証明書「mycacert.pem」を選択します。
確認画面が表示されるので、「この認証局によるウェブサイトの識別を信頼する」をチェックし、[OK]をクリックします。
証明書マネージャー画面にオレオレ自己署名証明書が追加されたことを確認し[OK]をクリックします。
サーバごとのサーバ証明書の発行
次に、オレオレ認証局で署名したオレオレサーバ証明書を作成していきます。
サーバ証明書に必要な秘密鍵、CSRを各サーバで作成しても良いのですが、ここではオレオレ認証局で全ての操作を行なった後、必要なファイルを当該サーバに移すことにします。
従って以降の操作はオレオレ認証局コンテナで行います。
以降の例ではホスト名「example.loc」のサーバ証明書を作成する手順を紹介します。
「example.loc」をたとえば「localhost」に読み替えるとlocalhostのサーバ証明書を作ることができます。
各サーバ用ディレクトリの作成
今からサーバ証明書を作成するサーバ用の作業場所としてディレクトリをオレオレ認証局コンテナの「/etc/pki」以下に作成します。
ここではドメイン「example.loc」のサーバのサーバ証明書を作成するものとします。
/etc/pki # mkdir example.loc
SAN情報ファイルの準備
作成した作業ディレクトリ内に、SAN(Subject Alternative Names)の情報を書いたファイルを「subjectnames.txt」の名前で作成します。
こちらもファイルの編集があるので、コンテナではなくホストマシンにてエディタで作成します。
- Google Chromeブラウザでは自己署名証明書を利用しているサイトにアクセスした場合に「Your connection is not private」エラーを表示するようになりました。証明書内にSAN(Subject Alternative Names)が設定されていないことが原因のようです。
よって、サーバ証明書発行時にSANの情報を入れるために本ファイルを準備します。
ドメイン「example.loc」のサーバを自ホスト内(IPアドレス: 127.0.0.1)で動かす場合、内容は次の通りです。
subjectAltName = DNS:example.loc, IP:127.0.0.1
秘密鍵の作成
サーバ用の秘密鍵を「privkey_example_withpasswd.pem」の名前で作成します。
/etc/pki # cd example.loc
/etc/pki/example.loc # openssl genrsa -aes256 -out privkey_example_withpasswd.pem 4096
Generating RSA private key, 4096 bit long modulus (2 primes)
........++++
..............................................................++++
e is 65537 (0x010001)
Enter pass phrase for privkey_example_withpasswd.pem: <--------------- パスフレーズを入力
Verifying - Enter pass phrase for privkey_example_withpasswd.pem: <--- 同じパスフレーズを入力
CSRの作成
認証局にサーバ証明書の発行を依頼するためのCSR(Certificate Signing Request:署名リクエスト)を「example.csr」の名前で作成します。
openssl.cnfファイルは準備していませんので、各項目には手入力していきます。
/etc/pki/example.loc # openssl req -new -key privkey_example_withpasswd.pem -out example.csr
Enter pass phrase for privkey_example_withpasswd.pem: <---------------- 秘密鍵のパスフレーズを入力
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:JP <------------------------------------- 国コード「JP」を入力
State or Province Name (full name) [Some-State]:Fukuoka <------------------ 都道府県を入力
Locality Name (eg, city) []:Munakata <------------------------------------- 地域を入力
Organization Name (eg, company) [Internet Widgits Pty Ltd]:example.loc <--- 組織名を入力
Organizational Unit Name (eg, section) []: <------------------------------- そのままEnter
Common Name (e.g. server FQDN or YOUR name) []:example.loc <--------------- コモンネームを入力。ドメイン名で可
Email Address []: <-------------------------------------------------------- そのままEnter
Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []: <--------------- そのままEnter
An optional company name []: <----------- そのままEnter
秘密鍵のパスフレーズを外す
サーバ証明書を当該サーバに設定する際に、同時に作成したサーバの秘密鍵も設定します。パスフレーズがついたままの秘密鍵を設定した場合、サーバ(Webサーバ)が再起動する度にパスフレーズを聞いてくることになります。これでは困るので、秘密鍵のパスフレーズを外してしまいましょう。
/etc/pki/example.loc # openssl rsa -in privkey_example_withpasswd.pem -out privkey_example.pem
Enter pass phrase for privkey_example_withpasswd.pem: <--- 秘密鍵のパスフレーズを入力
writing RSA key
サーバ証明書の発行
CSRが作成されたらオレオレ認証局で当該サーバのサーバ証明書を発行します。
この操作はオレオレ認証局のディレクトリ「cert」で行います。
- オレオレ認証局コンテナ内で申請側サーバの操作も行なっているのでディレクトリはどこでも良いのですが、opensslコマンドで指定する各ファイルの場所を記述する際にディレクトリ指定を少なくするために「cert」ディレクトリで行います。
/etc/pki/example.loc # cd ../cert/
/etc/pki/cert # openssl x509 -req -in /etc/pki/example.loc/example.csr -CA mycacert.pem -CAkey privkey_cert.pem -CAcreateserial -extfile /etc/pki/example.loc/subjectnames.txt -days 730 -out /etc/pki/example.loc/server_example.crt
Signature ok
subject=C = JP, ST = Fukuoka, L = Munakata, O = example.loc, CN = example.loc
Getting CA Private Key
Enter pass phrase for privkey_cert.pem: <--- 認証局の秘密鍵のパスフレーズを入力
- 「-extfile」でSANの情報をサーバ証明書内に書き込みます。
- Google Chromeブラウザは長期間の有効期限を持つ証明書では「NET::ERR_CERT_VALIDITY_TOO_LONG」(サーバー証明書の有効期限が長すぎます)の警告を表示するため、サーバ証明書の有効期限を730日(2年)で作成しています。
これでサーバの作業ディレクトリ「example.loc」にサーバ証明書「server_example.crt」が作成されます。
サーバの作業ディレクトリは次のような状態になっています。
/etc/pki/cert # ls ../example.loc
example.csr server_example.crt
privkey_example.pem subjectnames.txt
privkey_example_withpasswd.pem
サーバ証明書の確認
次のopensslコマンドを実行して「Subject Alternative Name」が正しく設定されていることを確認します。
/etc/pki/cert # openssl x509 -in /etc/pki/example.loc/server_example.crt -text -noout
Certificate:
Data:
Version: 3 (0x2)
Serial Number:
16:d2:d6:2e:a6:6b:85:87:50:a4:1d:21:a5:6c:f2:ad:a8:ba:a6:8b
Signature Algorithm: sha256WithRSAEncryption
Issuer: C = JP, ST = Fukuoka, O = K'z Style, CN = My CA, emailAddress = admin@rootca.local
Validity
Not Before: Aug 25 03:03:13 2022 GMT
Not After : Aug 24 03:03:13 2024 GMT
Subject: C = JP, ST = Fukuoka, L = Munakata, O = example.loc, CN = example.loc
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
RSA Public-Key: (4096 bit)
Modulus:
00:ad:b2:83:67:fe:2f:a7:da:73:75:2e:66:69:2c:
:
Exponent: 65537 (0x10001)
X509v3 extensions:
X509v3 Subject Alternative Name:
DNS:example.loc, IP Address:127.0.0.1 <---- これが表示されること
Signature Algorithm: sha256WithRSAEncryption
10:80:27:20:de:5c:e4:53:88:d3:97:2e:ba:9b:3f:5f:9e:9b:
:
サーバへの証明書類の設定
以上で出来上がったサーバの秘密鍵(パスフレーズを除去したほう)とサーバ証明書を当該サーバへコピーします。
- 秘密鍵:privkey_example.pem
- サーバ証明書:server_example.crt
Webサーバにこれら証明書を設定します。以下はapache2.4の例です。
<VirtualHost *:443>
DocumentRoot "/var/www/html"
ServerName example.loc
SSLEngine on
SSLProtocol all -SSLv2
SSLCertificateFile /etc/apache2/ssl/server_example.crt <---- 追記
SSLCertificateKeyFile /etc/apache2/ssl/privkey_example.pem <--- 追記
</VirtualHost>
クライアントPCのhostsに追記
作成したオレオレサーバ証明書を使ったWebサーバ(以降オレオレサーバと呼びます)にアクセスする際、そのままではDNS情報がないためオレオレサーバにアクセスできません。
自由に設定を追加できるDNSサーバを持っている場合、そちらにオレオレサーバの情報を登録しても良いのですが、ここではオレオレサーバにアクセスするクライアントPCの「hosts」ファイルにオレオレサーバの情報を追加することにします。
macOSの場合「/etc/hosts」、Windowsの場合「c:¥Windows¥system32¥drivers¥etc¥hosts」ファイルをエディタで開き、オレオレサーバのホスト名(example.loc)とIPアドレス(127.0.0.1)を追加します。
以下はmacOSの例です。
127.0.0.1 example.com
- hostsファイルは管理者のみ編集できるようにアクセス制限がかかっています。「sudo」付きでエディタを開くか、保存時に「管理者権限で再試行..」などを選択して編集します。
- 例ではオレオレサーバをDockerコンテナでクライアントPC上で動かす想定ですので、IPアドレスを「127.0.0.1」にしています。別のマシンでオレオレサーバを動かし、ネットワーク経由でアクセスする場合はオレオレサーバの正しいIPアドレスを設定します。
参考:
Dockerのコンテナでオレオレ認証局を立ててサーバー証明書に署名する