Debian + NginxでプライベートCAを使ったHTTPS環境を構築する
今回は、プライベート認証局(Private CA)を自分で構築し、そのCAでWebサーバの証明書に署名して、NginxでHTTPS通信を行うところまで試してみます。LinuC303の復習や支援士の勉強のために自演してみます。
自己署名サーバ証明書を作るだけではなく、次の3つの役割を分離します。
Private CA
│
│ サーバ証明書へ署名
↓
Web Server(Nginx)
│
│ HTTPS
↓
Client
この構成にすることで、
- CAの秘密鍵
- CA証明書
- Webサーバの秘密鍵
- CSR(証明書署名要求)
- Webサーバ証明書
- クライアントの信頼ストア
の関係を確認します。
環境
3台のPCまたはVMを使用します。
| 役割 | OS | 用途 |
|---|---|---|
| Private CA | Debian 13 | プライベート認証局 |
| Web Server | Debian 13 | Nginx |
| Client | Ubuntu | HTTPS接続確認 |
IPアドレスは環境に合わせて読み替えてください。
この記事では例として次のアドレスを使用します。
Private CA : 192.168.10.10
Web Server : 192.168.10.20
Client : 192.168.10.30
1. Private CAを作成する
Private CAとなるDebianで作業します。
まず作業用ディレクトリを作成しました。
mkdir ~/private-ca
cd ~/private-ca
CAの秘密鍵を作成
openssl genrsa -out ca.key 4096
ca.keyはCAの秘密鍵です。
この秘密鍵が漏えいすると、このCAになりすまして証明書を発行できてしまうため、本来は非常に厳重に管理する必要があります。
CA証明書を作成
openssl req -x509 -new \
-key ca.key \
-sha256 \
-days 3650 \
-out ca.crt
Country NameやOrganization Nameなどを聞かれるので、テスト環境に合わせて入力します。
これでCA側には、
ca.key ← CA秘密鍵
ca.crt ← CA証明書(公開鍵を含む)
が作成されました。
ca.crtは公開しても問題ありませんが、ca.keyはCA外へ持ち出さない、出させないようにします。
2. Web Serverで秘密鍵とCSRを作成する
次にNginxを動かすWeb Serverで作業します。
今回は分かりやすいようにホームディレクトリ配下に作業ディレクトリを用意しました。
mkdir ~/webserver-cert
cd ~/webserver-cert
Webサーバの秘密鍵を作成
openssl genrsa -out webserver.key 2048
続いてCSR(Certificate Signing Request:証明書署名要求)を作成します。
openssl req -new \
-key webserver.key \
-out webserver.csr
これで、
webserver.key ← Webサーバ秘密鍵
webserver.csr ← 証明書署名要求
が作成されます。
CSRにはWebサーバの公開鍵が含まれています。
確認する場合は、
openssl req \
-in webserver.csr \
-noout \
-subject \
-pubkey
とします。
重要なのは、webserver.keyをCAへ渡す必要がないことです。
Web Server
webserver.key
│
├── 公開鍵
↓
webserver.csr
│
│ CAへ送る
↓
Private CA
Webサーバの秘密鍵はWebサーバから外へ出しません。
3. CSRをPrivate CAへ送る
Web ServerからPrivate CAへCSRを送ります。
SSH公開鍵認証をあらかじめ設定しておきます。
scp webserver.csr causer@192.168.10.10:~/private-ca/
scpはSSHを利用してファイルを転送します。
Private CA側で確認します。
cd ~/private-ca
ls -l webserver.csr
CSRの内容も確認できます。
openssl req \
-in webserver.csr \
-noout \
-text
4. Private CAでCSRに署名する
Private CA側で、受け取ったCSRへ署名します。
まずは単純な方法として次のように実行しました。
openssl x509 -req \
-in webserver.csr \
-CA ca.crt \
-CAkey ca.key \
-CAcreateserial \
-days 365 \
-sha256 \
-out webserver.crt
これで、
webserver.crt
が作成されます。
構造としては次のようになります。
Private CA
ca.key
│
│ 署名
↓
webserver.csr ───→ webserver.crt
証明書を確認します。
openssl x509 \
-in webserver.crt \
-noout \
-subject \
-issuer \
-dates
subjectにはWebサーバ側でCSRを作った際の情報、issuerにはPrivate CAの情報が表示されます。
5. SANがないことを確認
ここで作成した証明書を詳しく確認してみます。
openssl x509 \
-in webserver.crt \
-noout \
-ext subjectAltName
今回最初に作った証明書では、
No extensions in certificate
となりました。
つまり、この証明書にはSAN(Subject Alternative Name)が設定されていません。
現在のHTTPSでは、接続先のホスト名やIPアドレスをSANによって確認するため、Webサーバ証明書として使用するならSANを設定しておきます。
今回はIPアドレスでWeb Serverへ接続するため、例えば、
IP:192.168.10.20
をSANへ登録します。
SAN付きCSRを作成する
Web Server側でCSRを作り直します。
秘密鍵webserver.keyはそのまま使用できます。
openssl req -new \
-key webserver.key \
-out webserver-san.csr \
-subj "/C=JP/O=PrivateLab/CN=webserver" \
-addext "subjectAltName=IP:192.168.10.20"
確認します。
openssl req \
-in webserver-san.csr \
-noout \
-text
次のような情報があればSANがCSRへ格納されています。
Requested Extensions:
X509v3 Subject Alternative Name:
IP Address:192.168.10.20
再びPrivate CAへ送ります。
scp webserver-san.csr \
causer@192.168.10.10:~/private-ca/
CA側で署名します。
openssl x509 -req \
-in webserver-san.csr \
-CA ca.crt \
-CAkey ca.key \
-CAcreateserial \
-days 365 \
-sha256 \
-copy_extensions copy \
-out webserver.crt
-copy_extensions copyによって、CSRに含まれているSANを証明書へコピーします。
確認します。
openssl x509 \
-in webserver.crt \
-noout \
-subject \
-issuer \
-ext subjectAltName
例えば次のように表示されます。
X509v3 Subject Alternative Name:
IP Address:192.168.10.20
これでSAN付きのサーバ証明書が作成できました。
6. 証明書をWeb Serverへ戻す
Web Server側から、Private CA上にある証明書を取得します。
scp causer@192.168.10.10:~/private-ca/webserver.crt ./
scpはローカルからリモートへ送るだけでなく、
scp ユーザー@リモート:ファイル ローカル
とすることで、リモート側のファイルを取得することもできます。
7. Nginxへ証明書を配置する
Web Serverで作業します。
証明書用ディレクトリを作成します。
sudo mkdir -p /etc/nginx/ssl
証明書と秘密鍵を配置します。
sudo cp webserver.crt /etc/nginx/ssl/
sudo cp webserver.key /etc/nginx/ssl/
秘密鍵のパーミッションを制限します。
sudo chown root:root /etc/nginx/ssl/webserver.key
sudo chmod 600 /etc/nginx/ssl/webserver.key
sudo chown root:root /etc/nginx/ssl/webserver.crt
sudo chmod 644 /etc/nginx/ssl/webserver.crt
8. NginxでHTTPSを設定する
今回はテスト環境なので、
/etc/nginx/sites-available/default
を編集します。
念のためバックアップします。
sudo cp /etc/nginx/sites-available/default \
/etc/nginx/sites-available/default.bak
編集します。
sudo vim /etc/nginx/sites-available/default
設定例です。
server {
listen 80 default_server;
listen [::]:80 default_server;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl default_server;
listen [::]:443 ssl default_server;
root /var/www/html;
index index.html index.htm;
server_name _;
ssl_certificate /etc/nginx/ssl/webserver.crt;
ssl_certificate_key /etc/nginx/ssl/webserver.key;
ssl_protocols TLSv1.2 TLSv1.3;
location / {
try_files $uri $uri/ =404;
}
}
Nginxが使用するのは、
webserver.crt
webserver.key
です。
CAの秘密鍵ca.keyはWeb Serverには配置しません。
9. Nginxの設定を確認して反映する
設定ファイルに問題がないか確認します。
sudo nginx -t
問題がなければ、
syntax is ok
test is successful
などと表示されます。
設定を反映します。
sudo systemctl reload nginx
443番ポートをLISTENしているか確認します。
sudo ss -lntp | grep nginx
10. ClientからHTTPS接続する
ClientからWeb Serverへアクセスします。
curl -v https://192.168.10.20/
この時点では、証明書の検証に失敗する可能性があります。
これはWeb ServerやNginxの設定がおかしいからではありません。
Clientは、
webserver.crt
│
↓
「誰が署名した?」
│
↓
Private CA
│
↓
「そのCAを私は信頼している?」
│
↓
知らない
│
↓
証明書検証失敗
という状態だからです。
証明書検証を一時的に無効化すると、
curl -k https://192.168.10.20/
でNginxへ接続できることを確認できます。
ただし-kは証明書検証を無効化するため、あくまでテスト用です。
11. Private CAをClientから信頼する
最後に、Private CAのCA証明書、
ca.crt
をClientへ登録します。
ここで配布するのはCA証明書ca.crtだけです。
ca.keyは絶対に配布しません。
Ubuntuの場合、CA証明書を、
sudo cp ca.crt \
/usr/local/share/ca-certificates/private-ca.crt
へ配置します。
続いて、
sudo update-ca-certificates
を実行します。
これでOSの信頼ストアへPrivate CAが追加されます。
再び、
curl https://192.168.10.20/
として、今度は-kなしでHTTPS接続できるか確認します。
12. OpenSSLでも検証する
証明書チェーンを確認してみます。
openssl s_client \
-connect 192.168.10.20:443 \
-verify_ip 192.168.10.20 \
</dev/null
最終的に、
Verify return code: 0 (ok)
となれば、CAの信頼と接続先IPアドレスの検証が成功しています。
今回のポイント
今回の構成を整理すると次のようになります。
Private CA
────────────────────
ca.key 🔐
ca.crt
│
│ CSRに署名
↓
webserver.crt
│
│ 証明書だけ返す
↓
Web Server
────────────────────
webserver.key 🔐
webserver.crt
│
│ HTTPS
↓
Client
────────────────────
ca.crtを信頼
特に重要なのは、2つの秘密鍵です。
ca.key
→ Private CAから出さない
webserver.key
→ Web Serverから出さない
ネットワークを移動するのは、
Web Server → Private CA
CSR
Private CA → Web Server
サーバ証明書
Private CA → Client
CA証明書
だけです。
CSRが存在する理由も理解できた
実際に別々のマシンで作業してみると、CSRの存在理由が分かりやすくなりました。
Web Serverは秘密鍵をCAへ渡す必要がありません。
webserver.key
│
├── 公開鍵
↓
webserver.csr
│
│ CSRだけCAへ渡す
↓
Private CA
│
│ ca.keyで署名
↓
webserver.crt
Private CAもCA秘密鍵を外部へ渡す必要がありません。
この仕組みによって、それぞれの秘密鍵をそれぞれのホスト内に保持したまま、CAによって署名されたサーバ証明書を発行できます。
CAの信頼と接続先の確認は別
今回もう一つ理解できたのがSANです。
TLSでは大きく、
サーバ証明書
│
┌───────────┴───────────┐
↓ ↓
発行者を信頼できるか? 接続先は正しいか?
│ │
CA・電子署名 SAN
│ │
└───────────┬───────────┘
↓
OK
という確認が行われます。
「信頼できるCAが署名した証明書だからOK」だけではなく、その証明書が実際に接続しようとしているWebサーバ用の証明書なのかも確認する必要があります。
今回、自分でPrivate CA、Web Server、Clientを分離して構築したことで、証明書・公開鍵・秘密鍵・CSR・CA・信頼ストアの関係を実際の通信として確認できました。
以上になります。またお会いしましょう



