トップ 履歴 一覧 カテゴリ ソース 検索 ヘルプ RSS ログイン

OpenBSD/httpd/77(relayd+tsl)

INDEX

OpenBSD Webサーバ の SSL(TSL) 接続 (httpd on OpenBSD 7.7)

OpenBSD の Webサーバ httpd(relayd) で、SSL(TSL) 接続する。

自己証明書(オレオレ証明書) と ACME client を用いての Let's Encrypt の証明書の取得と更新をする。

httpd 自体を動かす設定は、OpenBSD/httpd/77(relayd) を参照。

自己証明書で 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#

最終更新時間:2025年07月08日 22時45分49秒 指摘や意見などあればSandBoxのBBSへ。