はじめに

SAN付きの localhost に対する自己証明書について学びたかったため、備忘録として記載する。

環境

1
2
3
4
5
Windows 10 Professional
WSL2 - (Ubuntu22.04 LTS)
Docker desktop 4.19.0 (106363)
Docker version 23.0.5
Docker Compose version v2.17.3

準備

まずは、適当なwebサーバを用意したいので、nginxをDockerで構築する。

nginxをDockerで構築する

ディレクトリ構造

最終的なツリー構造は下記になる。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
.
├── compose.yml
└── nginx
    ├── conf
    │   └── certs
    │       ├── san.txt
    │       ├── server.crt
    │       ├── server.csr
    │       └── server.key
    ├── conf.d
    │   ├── default.conf
    │   └── ssl.conf
    └── html
        └── index.html

compose.yml の作成

1つずつ作成する。 まずは、 compose.ymlを下記のように作成する。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
services:
  nginx: 
    image: nginx:1.25.1
    volumes:
      - ./nginx/conf.d:/etc/nginx/conf.d
      - ./nginx/conf/certs:/etc/nginx/certs
      - ./nginx/html:/usr/share/nginx/html:ro
    ports:
     - "80:80"
     - "443:443"

80, 443は空けておきアクセスできるようにする。
volume には、設定ファイル、証明書、確認できるように htmlをマウントしておく。

html の作成

次に、確認用の htmlを作成する。
nginx/html フォルダを作成し、その中に index.htmlを作成する。
中身は下記のようにした。

1
<p>Hello,World</p>

確認用のため最小限にした。

設定ファイルの作成

次に、設定ファイルを作成する。
nginx/conf.dフォルダを作成し、その中に default.conf および ssl.confを作成する。
まずは、default.conf

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
server {
    listen       80;
    listen  [::]:80;
    server_name  localhost;

    location / {
        root   /usr/share/nginx/html;
        index  index.html index.htm;
    }

    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }
}

次に、ssl.conf

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
server {
    listen 443 ssl;
    server_name localhost;
    ssl_certificate /etc/nginx/certs/server.crt;
    ssl_certificate_key /etc/nginx/certs/server.key;

    location / {
        root   /usr/share/nginx/html;
    }
}

これでOK。

SAN付き自己証明書の作成

最後に証明書を作成する。
nginx/conf/certs フォルダを作成し、中に移動する。

まずは、 今回の肝である san.txtを作成する。 san.txtは下記のように作成。

1
subjectAltName = DNS:localhost, IP:127.0.0.1

その後、 key, crt, csrを作成するため、下記で証明書を作成するコマンドを実行する。

秘密鍵の作成

1
openssl genrsa -out server.key 2048

CSRの作成

1
openssl req -out server.csr -key server.key -new

下記のようにほとんど適当にやった。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
Country Name (2 letter code) [AU]:JP
State or Province Name (full name) [Some-State]:Tokyo
Locality Name (eg, city) []:
Organization Name (eg, company) [Internet Widgits Pty Ltd]:
Organizational Unit Name (eg, section) []:
Common Name (e.g. server FQDN or YOUR name) []:localhost
Email Address []:

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:

証明書の作成

1
openssl x509 -req -days 3650 -signkey server.key -in server.csr -out server.crt -extfile san.txt

最後にSANが付与されているかをチェックする。

1
openssl x509 -text -in server.crt -noout

下記のような記述があればOK.

1
2
3
        X509v3 extensions:
            X509v3 Subject Alternative Name: 
                DNS:localhost, IP Address:127.0.0.1

ここまでで準備は完了。

実際に下記のコマンドで立ち上げてみる。

1
docker compose up -d

↓httpはOKですね!(http://localhost/)
nginx-http-ok

↓httpsもOK! (https://localhost/)
nginx-https-ok

証明書の確認は下記でチェックする。
「鍵マーク」をクリック→鍵のマーク(この接続は~)→「証明書~」
chrome-certs-san

つけたDNS名、IPアドレスがあることを確認できた。

自己証明書をルート証明書としてインポート

先程のセクションで確認したhttpsの表示、
この保護されていない通信って表示されるのは心理的に嫌な気持ちになる。
なので、下記の手順でブラウザに証明書をインポートし、信頼できる証明書とするようにしてみる。

  1. Chrome の設定を開き、証明書と検索し、「セキュリティ」を押す。 chrome-setting-security

  2. ページ下部にある、「デバイス証明書の管理」を押す。 chrome-setting-security-certs

  3. 管理している証明書の一覧が出てくるので、「信頼されたルート証明機関」のタブを開き、「インポート」を押す。
    trusted-root-certs-import

  4. 証明書のインポートウィザードが表示されるので、「次へ」を押す。 trusted-root-certs-import-step1

  5. 先ほど作成した、証明書ファイルを「参照」から選択する。 ※このときファイルマネージャで表示されているファイルが制限されている可能性があるため注意
    trusted-root-certs-import-step2

  6. 証明書をすべて次のストアに配置するで、証明書ストア:信頼されたルート証明機関となっていることを確認し、「次へ」を押す trusted-root-certs-import-step3

  7. 完了 trusted-root-certs-import-step4

  8. 完了後、証明書インストールの表示が出るため、「はい」を押す。 trusted-root-certs-import-step5

インストールできているかの確認

「信頼されたルート証明機関」に localhost が入っていればOK
trusted-root-certs-import-localhost

ブラウザでの確認

※証明書の読み込みにブラウザを再起動する必要があるかもしれない

https://localhost にアクセスすると下記のように鍵マークが割れていないことを確認できる。
trusted_root_installed_browser_chrome

ちなみに、ブラウザごとに証明書を管理しているため、Firefoxでは信頼されていないことを確認できる。
trusted_root_not_installed_browser_firefox

参考

おわりに

証明書のトレンドはいつの間にか変わっていることが多い。
というのも、証明書の更新は1年に1回だったり、3年に1回だったりで期間が空いてしまい、なおかつ1回だけということもあるからだ。
期間が空く定常したタスクほど調べるのが億劫になってしまうのは嫌だなと思いつつも、やってしまう。
最近だと、Let’s Encryptなどで自分で証明書を発行するということもないので、よりこのあたりの情報がないので定期的に知識をアップデートしないとなと思う。