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 CADebian 13プライベート認証局
Web ServerDebian 13Nginx
ClientUbuntuHTTPS接続確認

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・信頼ストアの関係を実際の通信として確認できました。

以上になります。またお会いしましょう

鹿児島県の出水市という所に住んでいまして、インターネット周辺で色々活動して行きたいと思ってるところです。 Webサイト作ったり、サーバ設定したり、プログラムしたりしている、釣りと木工好きなMacユーザです。 今はデータサイエンスに興味を持って競馬AI予想を頑張ってます。

コメントを残す

メールアドレスが公開されることはありません。 ※ が付いている欄は必須項目です


reCaptcha の認証期間が終了しました。ページを再読み込みしてください。

コメントする

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください。