opensslで自己署名証明書を1発で作る


opensslの`req`はCSRを作るコマンドですが、`-newkey`をつけると秘密鍵の生成、`-x509`をつけるとCSRを作った後にそれで署名した証明書を作ってくれるので、コマンド１発で作れます

また、`-config`で作成時のオプションなどをファイルで管理できます。以下の例はファイル名に`-`を指定することで標準入力から読ませていますが、ファイルにしてファイルパスを指定することもできます。Subject Alternative Name(SAN)に対応する場合はファイルで。一応、SANにはWildcardもzone apexなドメインも指定できます。

`-days 3650`は署名する際に設定される期間です。自己署名なので適当に長くしてください。

configの読み方/書き方は次のドキュメントが役に立つと思います。

- https://www.openssl.org/docs/man1.0.2/man1/openssl-req.html
- https://www.openssl.org/docs/man1.0.2/man5/x509v3_config.html

以下はSANで2つのドメインに対応したSSL通信用の証明書を作成する例です

``` sh
# req_distinguished_nameやalt_namesは適宜書き換える
openssl req -x509 -days 3650 -nodes -newkey rsa:4096 -keyout /etc/nginx/server.key -out /etc/nginx/server.crt -config - << __EOF__
[req]
distinguished_name = req_distinguished_name
x509_extensions = v3_req
prompt = no

[req_distinguished_name]
C = JP
ST = Tokyo
L = Chuo-ku
O = MyCompany
OU = MyDivision
CN = mydomain.example

[v3_req]
keyUsage = keyEncipherment, dataEncipherment
extendedKeyUsage = serverAuth
subjectAltName = @alt_names

[alt_names]
DNS.1 = mydomain.example
DNS.2 = sub.mydomain.example
__EOF__

# 証明書の内容を確認
openssl x509 -in /etc/nginx/server.crt -noout -text
```

以上。



### （おまけ）Nginxコンテナ内でSSL証明書を作成して利用する

DockerのNginxイメージは起動時に「/docker-entrypoint.d/」の配下にあるスクリプトをenvsubstで変数の置き換えをして配置し、それを実行してからnginxを起動するということを行ってくれます。

- https://github.com/nginxinc/docker-nginx/blob/master/stable/alpine/docker-entrypoint.sh#L13

これを利用して、Nginxコンテナが起動したときに自己署名証明書を作るようにします。

docker-compose の例です。appというアプリケーションの前にnginxを置きたいと考えます。

``` docker-compose.yml
version: '3.8'

services:
  nginx:
    image: nginx:1.18-alpine
    volumes:
     - ./docker/nginx/nginx.conf:/etc/nginx/nginx.conf
     - ./docker/nginx/docker-entrypoint.d/50-self-signed-certificate.sh:/docker-entrypoint.d/50-self-signed-certificate.sh
    ports:
     - "443:443"
     - "80:80"
    environment:
     - NGINX_HOST=mydomain.example
    links:
     - app:app
  app:
    # 省略

```

``` docker/nginx/docker-entrypoint.d/50-self-signed-certificate.sh
#!/bin/sh

set -ue

# nginx:1.18-alpine なので apk add で openssl コマンドをインストールする
apk add openssl

# req_distinguished_nameやalt_namesは適宜書き換える
openssl req -x509 -days 3650 -nodes -newkey rsa:4096 -keyout /etc/nginx/server.key -out /etc/nginx/server.crt -config - << __EOF__
[req]
distinguished_name = req_distinguished_name
x509_extensions = v3_req
prompt = no
[req_distinguished_name]
C = JP
ST = Tokyo
L = Chuo-ku
O = MyCompany
OU = MyDivision
CN = ${NGINX_HOST}
[v3_req]
keyUsage = keyEncipherment, dataEncipherment
extendedKeyUsage = serverAuth
subjectAltName = @alt_names
[alt_names]
DNS.1 = ${NGINX_HOST}
DNS.2 = sub.${NGINX_HOST}
__EOF__

openssl x509 -in /etc/nginx/server.crt -noout -text
```

これでコンテナ内に`/etc/nginx/server.crt`と`/etc/nginx/server.key`が出来上がるので、`/etc/nginx/nginx.conf`はそのファイルパスを使ってSSLの設定すればよいという感じになります。


自己署名証明書をインストールしたい場合、証明書を取り出す必要があるので次のようにして取り出します。

``` sh
docker exec "(NginxのCONTAINER_ID)" cat /etc/nginx/server.crt 
```

当然ですがこのままではNginxを起動するたびに新しい自己署名証明書が作られます。それが不便に感じる場合は以下のいずれかでしょう。（このおまけが台無しですが）

- 最初からADDしてCOPYしてRUNして含めたイメージを作る
- ホスト側で作っておいてADDで含めたイメージを作る
- runするときにVolumeでファイルをマウントする

自己署名証明書なので安全ではないのですが、httpsの動作の検証でサクッとほしくなる時があるのでとりあえず作るならこういう手もありますよということで。