Webサーバーの一部に認証をつけたいがBasic認証というのもありきたりなのでクライアント証明書による認証にトライした。実家に置いているRaspberry Piのペットカメラにつけようとして挫折したのでリベンジとなる。環境はUbuntu18.04 LTS(このサーバー)。
ちまたにたくさん似たような記事があるのだが、今回は余計な情報は極力排して最短距離で証明書作成まで行けることを重視した(*1)。
全体的な流れ
全体の流れは下記の通り。矢印の先が生成ファイルとなる。
- ルート証明書の発行 → ca.key, ca.crt
- クライアント証明書の発行 → client.key, client.crt
- p12ファイルの生成 → client.p12
- nginxの設定
サイトによって拡張子が違っていたりするのが個人的には分かりにくい原因の一つだったが、今回は下記で統一することにした。
- *.key: 秘密鍵
- *.csr: Certificate Signing Request. 証明書発行のための申請書
- *.crt: 証明書本体
- *.pem: 上記を固めたやつ
- *.p12: クライアント証明書
ルート証明書の発行
#鍵の生成 $ openssl genrsa -out ca.key 4096 #証明書の生成 $ openssl req -new -x509 -days 365 -key ca.key -out ca.crt
FQDNはサーバーのドメインにすること。例えばこのサーバーだとeminus.tech
クライアント証明書の発行と署名
#鍵の生成 $ openssl genrsa -out client.key 4096 #CSRの生成 $ openssl req -new -key client.key -out client.csr #署名 $ openssl x509 -req -days 365 -in client.csr -CA ca.crt -CAkey ca.key -out client.crt
p12ファイルの生成
#鍵とルート証明書を固める $ cat client.key client.crt ca.crt > client.pem # シリアル番号ファイルを生成←ないとca.srlがないと怒られる $ echo 01 > ca.srl # p12ファイルに変換。p12は配布するので、パスワードは設定必須。 $ openssl pkcs12 -export -out client.p12 -inkey client.key -in client.pem -certfile ca.crt
nginxの設定
/etc/nginx/sites-enabled/defaultの該当serverの中に下記を追記or変更。今回は特定のフォルダだけ認証をつけたい(一般公開ページは認証不要)ので、1行目はoptionalにしている。
ssl_verify_client optional; ssl_verify_depth 2; ssl_client_certificate {path_to_ca.crt}; location {uri_to_require_auth} { if ($ssl_client_verify != SUCCESS) { return 403; } ... }
ちなみに、ちゃんと設定しないとhtmlは認証できるがphpは認証なしで突破できたりするので十分検証する必要がある。詳細は「nginx 優先順位」とかで検索すると出てくる。設定が終わったら
$ sudo nginx -t
でチェック後
$ sudo /etc/init.d/nginx restart
動作確認
生成したp12ファイルをクライアントにSCP等で持ってくる。Windowsの場合はそのままダブルクリックで開いてあとはデフォルトのままインストールする。開いているブラウザがあれば一旦全部閉じて、設定したURLを開くと、正しい証明書がインストールされており、かつnginxの設定が上手くいっていれば証明書を選択するダイアログが出てくる。こうなれば選択すれば開ける。ブラウザを閉じるまで有効。例えば試しに証明書が入っていない別のPC等からアクセスすると、403エラーがでて表示されない。
参考
https://mcilis.medium.com/how-to-create-a-self-signed-client-certificate-with-openssl-c4af9ac03e99
(*1) なんかこのファイル生成したのにつかってなくない?っていうのもあったり