!!!OpenBSD Webサーバ の SSL(TSL) 接続 (httpd on OpenBSD 7.7) {{category OpenBSD,nolink}}OpenBSD の Webサーバ httpd(relayd) で、SSL(TSL) 接続する。 自己証明書(オレオレ証明書) と ACME client を用いての Let's Encrypt の証明書の取得と更新をする。 httpd 自体を動かす設定は、[[OpenBSD/httpd/77(relayd)]] を参照。 * 関連 man → [httpd(8)|https://man.openbsd.org/httpd.8], [httpd.conf(5)|https://man.openbsd.org/httpd.conf.5], [openssl(1)|https://man.openbsd.org/openssl.1], [acme-client(1)|https://man.openbsd.org/acme-client.1] !!!自己証明書で SSL(TSL) 接続 !!秘密鍵と証明書の作成 LibreSSL の openssl コマンドを使って、サーバの秘密鍵と証明書(自己署名証明書)を作成する。 !秘密鍵の作成 bbb# openssl genrsa -out /etc/ssl/private/server.key 2048 Generating RSA private key, 2048 bit long modulus ......................... ................................ e is 65537 (0x010001) !証明書署名要求(CSR)の作成 -subj オプションの内容は、対象のサーバに合わせて変える。指定しなければ、対話形式で入力となる。 特に CN については、実際のホスト名(URL の FDQN)に合わせる。 bbb# openssl req -new -key /etc/ssl/private/server.key -out /etc/ssl/private/server.csr -subj "/C=JP/ST=Tokyo/L=Chuo-Ku/CN=example.com" !証明書(CRT)の作成 bbb# openssl x509 -days 3650 -req -signkey /etc/ssl/private/server.key -in /etc/ssl/private/server.csr -out /etc/ssl/server.crt Signature ok subject=/C=JP/ST=Tokyo/L=Chuo-Ku/CN=example.com !秘密鍵ファイルの内容を確認 bbb# openssl rsa -text -noout -in /etc/ssl/private/server.key RSA Private-Key: (2048 bit) modulus:   :以下略 !証明書署名要求の内容を確認 bbb# openssl req -text -noout -in /etc/ssl/private/server.csr Certificate Request: Data: Version: 1 (0x0) Subject: C=JP, ST=Tokyo, L=Chuo-Ku, CN=example.com Subject Public Key Info: Public Key Algorithm: rsaEncryption RSA Public-Key: (2048 bit) Modulus:   :以下略 !証明書ファイルの内容を確認 bbb# openssl x509 -text -noout -in /etc/ssl/server.crt Certificate: Data: Version: 1 (0x0) Serial Number: xx:xx:xx:xx:xx:xx:xx:xx Signature Algorithm: sha256WithRSAEncryption Issuer: C=JP, ST=Tokyo, L=Chuo-Ku, CN=example.com Validity Not Before: Jul 8 12:58:32 2025 GMT Not After : Jul 6 12:58:32 2035 GMT Subject: C=JP, ST=Tokyo, L=Chuo-Ku, CN=example.com Subject Public Key Info: Public Key Algorithm: rsaEncryption RSA Public-Key: (2048 bit) Modulus:   :以下略 !! Webサーバ(httpd) の設定 '''/etc/httpd.conf''' に設定する。 TSL(HTTP over SSL/TLS)接続で待つポート番号と、作成したキーファイル /etc/ssl/private/server.key 、証明書 /etc/ssl/server.crt を指定する。 server "default" { listen on * port 80 listen on * tls port 443 tls { certificate "/etc/ssl/server.crt" key "/etc/ssl/private/server.key" } # :中略 } !! HTTPS通信の疎通確認 まぁ、ブラウザでアクセスして、期待するページが表示されれば OK なんだけど。 ! curl curl コマンドでアクセスして確認する。 自己証明書なので '''-k''' (--insecure) オプションをつけて、安全でないSSL接続も許可するようにする。 bbb# curl -svk https://localhost:443/ 1> /dev/null * Host localhost:443 was resolved. * IPv6: ::1 * IPv4: 127.0.0.1 * Trying [::1]:443... * ALPN: curl offers h2,http/1.1 * TLSv1.3 (OUT), TLS handshake, Client hello (1): } [304 bytes data] * TLSv1.3 (IN), TLS handshake, Server hello (2): { [122 bytes data] * TLSv1.3 (IN), TLS handshake, Unknown (8): { [10 bytes data] * TLSv1.3 (IN), TLS handshake, Certificate (11): { [795 bytes data] * TLSv1.3 (IN), TLS handshake, CERT verify (15): { [264 bytes data] * TLSv1.3 (IN), TLS handshake, Finished (20): { [36 bytes data] * TLSv1.3 (OUT), TLS handshake, Finished (20): } [36 bytes data] * SSL connection using TLSv1.3 / TLS_CHACHA20_POLY1305_SHA256 / [blank] / UNDEF * ALPN: server did not agree on a protocol. Uses default. * Server certificate: * subject: C=JP; ST=Tokyo; L=Chuo-Ku; CN=example.com * start date: Jul 8 12:58:32 2025 GMT * expire date: Jul 6 12:58:32 2035 GMT * issuer: C=JP; ST=Tokyo; L=Chuo-Ku; CN=example.com * SSL certificate verify result: self signed certificate (18), continuing anyway. * Certificate level 0: Public key type ? (2048/112 Bits/secBits), signed using sha256WithRSAEncryption * Connected to localhost (::1) port 443 * using HTTP/1.x > GET / HTTP/1.1 > Host: localhost > User-Agent: curl/8.13.0 > Accept: */* > < HTTP/1.1 200 OK < Connection: keep-alive < Content-Length: 170 < Content-Type: text/html < Date: Tue, 08 Jul 2025 13:03:41 GMT < Last-Modified: Wed, 13 Jan 2021 15:54:49 GMT < Server: OpenBSD httpd < { [170 bytes data] * Connection #0 to host localhost left intact ==また、'''--tlsv1'''(-1) または、'''--sslv2'''(-2)、'''--sslv3'''(-3) を指定することで、== ==TLSv1.0 以上 または、SSLv2、SSLv3 で接続する。もっとも、LibreSSL は SSLv2 および SSLv3 をサポートしない。== ==さらに、'''--tlsv1.0''' や '''--tlsv1.1'''、'''--tlsv1.2'''、'''--tlsv1.3''' で、TLSv1.0 以上、TLSv1.1 以上… となる。== curl 8.13.0 では、 --sslv2 や --sslv3 を指定しても TLSv1.3 で接続されてしまう。 ! openssl s_client LibreSSL の openssl コマンドの s_client を利用して、TSL接続を確認する。 '''-connect''' ''localhost:443'' で接続先のホストとポート番号を指定し、 '''-showcerts''' でサーバ証明書を表示する。 また、SNI(Server Name Indication) の場合、'''-servername''' ''localhost'' でサーバ名も指定する。 s_client は、SSL/TSL通信クライアントなので、サーバにコマンドを送るために標準入力の入力待ちとなる。 エンターキーを押すか、nullディバイスを標準入力に渡す('''< /dev/null''')。 bbb# openssl s_client -showcerts -connect localhost:443 -servername localhost < /dev/null CONNECTED(00000003) depth=0 C = JP, ST = Tokyo, L = Chuo-Ku, CN = example.com verify error:num=18:self signed certificate verify return:1 --- Certificate chain 0 s:/C=JP/ST=Tokyo/L=Chuo-Ku/CN=example.com i:/C=JP/ST=Tokyo/L=Chuo-Ku/CN=example.com -----BEGIN CERTIFICATE-----   :中略 -----END CERTIFICATE----- --- Server certificate subject=/C=JP/ST=Tokyo/L=Chuo-Ku/CN=example.com issuer=/C=JP/ST=Tokyo/L=Chuo-Ku/CN=example.com --- No client certificate CA names sent Server Temp Key: ECDH, X25519, 253 bits --- SSL handshake has read 1326 bytes and written 359 bytes --- New, TLSv1/SSLv3, Cipher is TLS_CHACHA20_POLY1305_SHA256 Server public key is 2048 bit Secure Renegotiation IS NOT supported Compression: NONE Expansion: NONE No ALPN negotiated SSL-Session: Protocol : TLSv1.3 Cipher : TLS_CHACHA20_POLY1305_SHA256 Session-ID: Session-ID-ctx: Master-Key: Start Time: 1751980299 Timeout : 7200 (sec) Verify return code: 18 (self signed certificate) --- DONE bbb# 使用するTLSのバージョン指定(-tls1 | -tls1_1 | -tls1_2 | -tls1_3)、制限(-no_tls1 | -no_tls1_1 | -no_tls1_2 | -no_tls1_3)、 使用する暗号スイートを指定(-cipher cipherlist)(リストは ciphers コマンドで確認)ができる。 bbb$ openssl ciphers -v TLS_CHACHA20_POLY1305_SHA256 TLSv1.3 Kx=TLSv1.3 Au=TLSv1.3 Enc=ChaCha20-Poly1305 Mac=AEAD   :中略 AES256-SHA256 TLSv1.2 Kx=RSA Au=RSA Enc=AES(256) Mac=SHA256   :中略 AES128-SHA256 TLSv1.2 Kx=RSA Au=RSA Enc=AES(128) Mac=SHA256   :中略 bbb$ bbb$ openssl s_client -showcerts -connect localhost:443 -servername localhost -tls1_2 -cipher AES128-SHA256 < /dev/null CONNECTED(00000003) depth=0 C = JP, ST = Tokyo, L = Chuo-Ku, CN = example.com verify error:num=18:self signed certificate verify return:1 ---   :中略 --- New, TLSv1/SSLv3, Cipher is AES128-SHA256 Server public key is 2048 bit Secure Renegotiation IS supported Compression: NONE Expansion: NONE No ALPN negotiated SSL-Session: Protocol : TLSv1.2 Cipher : AES128-SHA256 Session-ID: Session-ID-ctx: Master-Key: 797EB977E812761F0BD926078C332CEB7D9DFC1CF495FC9299DEECB58DD5E3DF39CA557D7A622B9BC54C725425F4946F Start Time: 1751980581 Timeout : 7200 (sec) Verify return code: 18 (self signed certificate) --- DONE bbb$ !!! Let's Encrypt の証明書で SSL(TSL) 接続 OpenBSD 6.1 から追加された ACME クライアントを利用して、Let's Encrypt の取得・更新を行い、SSL(TSL) 接続する。 !! acme-client の設定 '''/etc/acme-client.conf''' に設定する。サンプルが /etc/examples/acme-client.conf にある。 bbb# cp -p /etc/examples/acme-client.conf /etc/ サンプルをコピーして、末尾の domain 部分の example.com 等を自分のドメインに変更する。 複数のドメイン名に対応する Subject Alternative Names(SANs) を利用する場合は、'''alternative names''' にカンマまたはスペース区切りで列挙する。 domain example.com { #alternative names { secure.example.com } domain key "/etc/ssl/private/example.com.key" domain full chain certificate "/etc/ssl/example.com.fullchain.pem" sign with letsencrypt } !! Webサーバ(httpd) の設定 '''/etc/httpd.conf''' に設定する。サンプルが /etc/examples/httpd.conf にある。 必要になるのは、ドメイン名の検証を行う HTTP-01 チャレンジ の応答を返すための設定と、取得した証明書(とその秘密鍵)ファイルを参照するようにする。 server "default" { listen on * port 80 listen on * tls port 443 tls { certificate "/etc/ssl/example.com.fullchain.pem" key "/etc/ssl/private/example.com.key" } location "/.well-known/acme-challenge/*" { root "/acme" request strip 2 } } !! 証明書の取得 acme-client を実行して、証明書が取得できたら、httpd を再起動して、秘密鍵と証明書を読み込み直す。 bbb# rcctl restart httpd httpd(ok) httpd(ok) bbb# acme-client -v example.com acme-client: /etc/ssl/private/example.com.key: generated RSA domain key acme-client: /etc/acme/letsencrypt-privkey.pem: generated RSA account key acme-client: https://acme-v02.api.letsencrypt.org/directory: directories acme-client: acme-v02.api.letsencrypt.org: DNS: 172.65.32.248 acme-client: acme-v02.api.letsencrypt.org: DNS: 2606:4700:60:0:f53d:5624:85c7:3a2c account key: https://acme-v02.api.letsencrypt.org/acme/acct/2515807891 acme-client: account key: https://acme-v02.api.letsencrypt.org/acme/acct/2515807891 acme-client: dochngreq: https://acme-v02.api.letsencrypt.org/acme/authz/2515807891/549382395411 acme-client: challenge, token: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx, uri: https://acme-v02.api.letsencrypt.org/acme/chall/2515807891/549382395411/0Ld-_w, status: 0 acme-client: /var/www/acme/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx: created acme-client: https://acme-v02.api.letsencrypt.org/acme/chall/2515807891/549382395411/0Ld-_w: challenge acme-client: order.status 0 acme-client: dochngreq: https://acme-v02.api.letsencrypt.org/acme/authz/2515807891/549382395411 acme-client: challenge, token: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx, uri: https://acme-v02.api.letsencrypt.org/acme/chall/2515807891/549382395411/0Ld-_w, status: 0 acme-client: /var/www/acme/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx: created acme-client: https://acme-v02.api.letsencrypt.org/acme/chall/2515807891/549382395411/0Ld-_w: challenge acme-client: order.status 0 acme-client: dochngreq: https://acme-v02.api.letsencrypt.org/acme/authz/2515807891/549382395411 acme-client: challenge, token: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx, uri: https://acme-v02.api.letsencrypt.org/acme/chall/2515807891/549382395411/0Ld-_w, status: 0 acme-client: /var/www/acme/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx: created acme-client: https://acme-v02.api.letsencrypt.org/acme/chall/2515807891/549382395411/0Ld-_w: challenge acme-client: order.status 0 acme-client: dochngreq: https://acme-v02.api.letsencrypt.org/acme/authz/2515807891/549382395411 acme-client: challenge, token: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx, uri: https://acme-v02.api.letsencrypt.org/acme/chall/2515807891/549382395411/0Ld-_w, status: 2 acme-client: order.status 1 acme-client: https://acme-v02.api.letsencrypt.org/acme/finalize/2515807891/404143812031: certificate acme-client: order.status 3 acme-client: https://acme-v02.api.letsencrypt.org/acme/cert/zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz: certificate acme-client: /etc/ssl/example.com.crt: created bbb# bbb# rcctl restart httpd httpd(ok) httpd(ok) bbb# !! 証明書の自動更新 Let's Encrypt の証明書の有効期限は '''90日''' で作成される。このため、証明書を維持するには、定期的に更新が必要となる。 acme-client は、有効期限が30日を切ると更新を行うようになるので、 ''acme-client example.com && rcctl reload httpd'' とコマンドをつなげて、証明書の更新と再起動を行うように、cron に設定するか、''/etc/daily.local'' や ''/etc/weekly.local'' にコマンドを記載して実行させる。 bbb# cat /etc/daily.local # update Let's Encrypt certificate. acme-client example.com && rcctl reload httpd bbb#