経緯
openssl
コマンド を叩くことが難しい環境にオレオレ証明書をインポートするために、オレオレ証明書を発行する為だけの Docker Compose を作りました。
結果としてはインポートこそ成功したものの、結局https通信はできなかったのであまり効果はなかったですが……。
コード
ディレクトリ階層
PROJECT_ROOT/
├ cert/
│
├ template/
│ └ subjectnames-template.txt // SANs 設定用の設定テンプレートファイル
│
├ workspace/
│ └ entrypoint.sh // エントリポイント
│
├ .env // docker-compose.yml 用の環境変数
├ Dockerfile
└ docker-compose.yml
構造は比較的シンプル。
docker-compose.yml
version: '3.8'
services:
oc:
build:
context: .
dockerfile: Dockerfile
args:
FILE_DIRECTORY: $FILE_DIRECTORY
HOST_NAME: $HOST_NAME
DNS_NAME: $DNS_NAME
IP_ADDR: $IP_ADDR
SSL_C: $SSL_C
SSL_ST: $SSL_ST
SSL_L: $SSL_L
SSL_O: $SSL_O
SSL_OU: $SSL_OU
labels:
certificate.staying.overnight: "Sign this overnight certificate"
volumes:
# workspace
- ./workspace:/workspace
# template
- ./template:/template
# SSL
- ./cert:/etc/ssl/private
tty: true
entrypoint: bash -c "bash /workspace/entrypoint.sh $FILE_DIRECTORY $HOST_NAME $DNS_NAME $IP_ADDR $SSL_C $SSL_ST $SSL_L $SSL_O $SSL_OU && /bin/bash"
SSL証明書を出力するディレクトリをボリュームとしてマウントしているので、 docker-dompose up -d
すると .env
のパラメータに従って証明書が生成される、という挙動を意図しています。
処理自体は Docokerfile とエントリポイントで。
.env
FILE_DIRECTORY=self_sign
HOST_NAME=www.lvh.me
DNS_NAME=lvh.me
IP_ADDR=127.0.0.1
SSL_C=JP
SSL_ST=Hokkaido
SSL_L=SapporoCity
SSL_O=DummyTestOrganization
SSL_OU=
証明書に必要なパラメータと出力するディレクトリ名を指定しています。
subjectnames-template.txt
subjectAltName = DNS:HOST_NAME, IP:IP_ADDR
単純に SANs に指定するホスト名とIP をテンプレートとして持っているだけのファイルです。
Dockerfile
FROM almalinux:8
# args
ARG FILE_DIRECTORY
# timezone
RUN \cp -pf /usr/share/zoneinfo/Japan /etc/localtime
# dnf install
RUN dnf -y update && dnf -y install \
epel-release \
sudo \
less \
procps \
# network ss (instaed of netstat)
iproute \
# SSL
openssl \
mod_ssl
# SSL
RUN mkdir -p /etc/pki/CA/newcerts
RUN echo 00 > /etc/pki/CA/serial
RUN touch /etc/pki/CA/index.txt
RUN mkdir -p /var/certtemp
# volume directory
RUN mkdir -p /etc/ssl/private
RUN mkdir /template
RUN mkdir /workspace
証明書発行の処理に必要なディレクトリを作っておくのが主な役割。後はパッケージのインストールですね。
entrypoint.sh
#!/bin/bash
sed -e "s/HOST_NAME/${2}/gi" -e "s/IP_ADDR/${4}/gi" /template/subjectnames-template.txt > /var/certtemp/subjectnames.txt
# prepare
## delete exists files
if [ -e /etc/ssl/private/${1} ]; then
rm -Rf /etc/ssl/private/${1}
fi
## make cert directory
mkdir /etc/ssl/private/${1}
# gen key & certificate
## .env domain
# Generate private key for CA and Server.
openssl genrsa 2048 > /etc/ssl/private/${1}/server.key
# Generate certificate sigining requests from same key.
openssl req -new -key /etc/ssl/private/${1}/server.key -subj "/C=${5}/ST=${6}/L=${7}/O=${8}/OU=${9}/CN=${2}" > /etc/ssl/private/${1}/ca.csr
openssl req -new -key /etc/ssl/private/${1}/server.key -subj "/C=${5}/ST=${6}/L=${7}/O=${8}/OU=${9}/CN=${2}" > /etc/ssl/private/${1}/server.csr
# Generate CA certificate.
openssl ca -batch -extensions v3_ca -out /etc/ssl/private/${1}/ca.crt -in /etc/ssl/private/${1}/ca.csr -selfsign -keyfile /etc/ssl/private/${1}/server.key
# Generate server certificate with CA certificate.
openssl x509 -days 3650 -req -extfile /var/certtemp/subjectnames.txt -CA /etc/ssl/private/${1}/ca.crt -CAkey /etc/ssl/private/${1}/server.key -set_serial 1 < /etc/ssl/private/${1}/server.csr > /etc/ssl/private/${1}/server.crt
証明書発行のコマンドを全てエントリポイントに含ませています。
今回は /etc/ssl/private/
下 に証明書ごとにさらにサブディレクトリを切って生成するようにしましたが、ここは docker-compose.yml
で指定した通りボリュームとしてマウントされ、ホスト側と共有されます。
が、 Dockerfile 実行時はこの領域は参照できないので、最初の mkdir /etc/ssl/private/{FILE_DIRECTORY}
のコマンドを Dockerfile 内に記述しておくと失敗します。そのため、このディレクトリを掘るコマンドだけはエントリポイント側に持たせました。
後は .env
のパラメータに従って証明書を発行しています。
これで証明書の生成の挙動は問題ないことを確認しました。
結果としては冒頭に記した通りあまり役に立ちませんでしたが……。
余談
例によって名前の由来。証明書と言われて思い付いた単語が外泊証明書だったので。「起きてこれにサインしろ!!」
参考
オレオレ証明書の発行
- Chromeで使えるオレオレ証明書を作成する方法 – Qiita
- GitHub – t-kuni\/self-sign-cert: 自己署名証明書生成ツール
- OpenSSLによるオレオレ認証局が署名した証明書の作成 – Qiita
- 5.4.3 SSL通信のための証明書ファイルを作成する : JP1\/Data Highway – Server 構築・運用ガイド