Nagios HTTP plugin --sniを理解する
Nagios Pluginsのcheck_httpを使いこなそう
とりあえずEC2にインストール
git
automake
gcc
ソースインストールしたものはデフォルトで暗号化機能がoffになっていそう
なのでどこかのリポジトリから持ってくるのが良さそう
amazon linux 2023だとepelリポジトリが使えない
yumでNagiosをインストールできない
なのでamazon linux2でサーバを立て直し
yum -y install nagios nagios-plugins-all でできた
code:ok
HTTP OK: HTTP/1.1 200 OK - 770 bytes in 0.029 second response time |time=0.029258s;;;0.000000 size=770B;;;0
HTTP OK: HTTP/1.1 200 OK - 17290 bytes in 0.054 second response time |time=0.054227s;;;0.000000 size=17290B;;;0
HTTP OK: HTTP/1.1 200 OK - 17290 bytes in 0.050 second response time |time=0.050320s;;;0.000000 size=17290B;;;0
HTTP OK: HTTP/1.1 200 OK - 770 bytes in 0.019 second response time |time=0.018741s;;;0.000000 size=770B;;;0
HTTP OK: HTTP/1.1 200 OK - 578402 bytes in 0.524 second response time |time=0.524076s;;;0.000000 size=578402B;;;0
ECH
SNIを暗号化する
RFC
Proposed Standard
IESG stateはPublication Requested
nagios-pluginsのソースインストールで--sslを有効化するには?
OpenSSLをインストールしたのちにconfigureする
make test時間かかる......
やらなくていいかな
yum install opensslできないのか?
yum install openssl-develしたら--with-openssl: yesになった......
この辺は効果なしかな
結論
yum install openssl-develでOpenSSLをインストールしてから./configureすれば良い
code:source install version
HTTP OK: HTTP/1.1 200 OK - 17290 bytes in 0.059 second response time |time=0.058764s;;;0.000000 size=17290B;;;0
これでソースが読めるか?
gdbとかでデバッグできるかな
できる
しかしタイムアウトの時間を長くしておかないとSocket timeoutで止まる
デバッグしてるとうまくレスポンスを受け取れない
ただ、今回知りたいのは--sniをセットするとどうなるかということ
--sniありなしでリクエストの違いがわかれば良い
リクエストはどれだ
SSL_set_tlsext_host_nameでhost_nameをセットしている
これはOpenSSLのマクロかな
ということはOpenSSLを読む必要があるのか......
デバッグできたが結構難解かもしれない......
code:debug
gdb plugins/check_http
set args -H matac.info -f follow --sni --ssl -t 5000
おおよその追い方
check_http.cのcheck_httpから
code:oi
b check_http
SSL_set_tlsext_host_nameあった(力技)
code:grep
/usr/local/src/openssl-3.4.0/include/openssl/tls1.h:# define SSL_set_tlsext_host_name(s,name) \
sはSSLのメタデータっぽい
バージョン情報とか
SSL_CTRL_SET_TLSEXT_HOSTNAMEとあるから拡張のHOSTNAMEにnameをセットする感じなんだろう
SSL_ctrlを見に行きたい
code:/usr/local/src/openssl-3.4.0/include/openssl/tls1.h
# define SSL_set_tlsext_host_name(s,name) \
SSL_ctrl(s,SSL_CTRL_SET_TLSEXT_HOSTNAME,TLSEXT_NAMETYPE_host_name,\
(void *)name)
こっちは関数なのでデバッグで追いたいなぁ......
code:/usr/local/src/openssl-3.4.0/ssl/ssl_lib.c
long SSL_ctrl(SSL *s, int cmd, long larg, void *parg)
{
return ossl_ctrl_internal(s, cmd, larg, parg, /*no_quic=*/0);
}
ossl_ctrl_internalにSSL_CTRL_SET_TLSEXT_HOSTNAMEが見当たらないな
もしかしてバージョン間違えたとか?
もしくはこのdefaultか?
デバッグしたいなぁ
code:/usr/local/src/openssl-3.4.0/ssl/ssl_lib.c
default:
if (IS_QUIC(s))
return SSL_ctrl((SSL *)sc, cmd, larg, parg);
else
return s->method->ssl_ctrl(s, cmd, larg, parg);
}
ここにssl_ctrlが定義されている?
またか
BIOってなんだ
BIO_ctrlどこだ......!
というかSSL_CTRL_SET_TLSEXT_HOSTNAMEでgrepすれば良くね......
code:/usr/local/src/openssl-3.4.0/ssl/bio_ssl.c
default:
ret = BIO_ctrl(SSL_get_rbio(ssl), cmd, num, ptr);
break;
}
あったあった
サーバ側でも使えてしまうみたいなことが書いてある......
理解すべきはOPENSSL_free
他は例外処理っぽい
多分仕様から外れる場合なんだろう
何かしらの長さの制約がありそうだな
freeしてしまっているのか?allocじゃなくて?
あ!大事なのはこっちか
sc->ext.hostname = OPENSSL_strdup((char *)parg))
OPENSSL_strdup
実態はCRYPTO_strdup((str),__FILE__,__LINE__)
なのでやってることはCRYPTO_malloc
暗号化してファイルに書き込んでる
じゃあこのファイルは何?最終的にどう使われる?
parg
実際の名前(server name)が入ってそう
code:/usr/local/src/openssl-3.4.0/ssl/s3_lib.c
case SSL_CTRL_SET_TLSEXT_HOSTNAME:
/*
* This API is only used for a client to set what SNI it will request
* from the server, but we currently allow it to be used on servers
* as well, which is a programming error. Currently we just clear
* the field in SSL_do_handshake() for server SSLs, but when we can
* make ABI-breaking changes, we may want to make use of this API
* an error on server SSLs.
*/
if (larg == TLSEXT_NAMETYPE_host_name) {
size_t len;
OPENSSL_free(sc->ext.hostname);
sc->ext.hostname = NULL;
ret = 1;
if (parg == NULL)
break;
len = strlen((char *)parg);
if (len == 0 || len > TLSEXT_MAXLEN_host_name) {
ERR_raise(ERR_LIB_SSL, SSL_R_SSL3_EXT_INVALID_SERVERNAME);
return 0;
}
if ((sc->ext.hostname = OPENSSL_strdup((char *)parg)) == NULL) {
ERR_raise(ERR_LIB_SSL, ERR_R_INTERNAL_ERROR);
return 0;
}
} else {
ERR_raise(ERR_LIB_SSL, SSL_R_SSL3_EXT_INVALID_SERVERNAME_TYPE);
return 0;
}
break;
code:strdup
char *CRYPTO_strdup(const char *str, const char* file, int line)
{
char *ret;
if (str == NULL)
return NULL;
ret = CRYPTO_malloc(strlen(str) + 1, file, line);
if (ret != NULL)
strcpy(ret, str);
return ret;
}
あれ?SNIの流れを追ってたらCRYPTO_mallocにたどりついた
ということはSNIはメモリに載った時点で暗号化されている?
ESNI?
いや、strcpy(ret, str);してるぞ?暗号化されていないかも
CRYPTO_mallocは何をしている?mallocと何が違う?
普通にmallocしてるっぽい
最終的にTLSパケットになるはず......(セグメント?データグラム?)
Wiresharkで見てみる?
これやればopensslまでデバッグできるはず
yumの場合はsudo debuginfo-install glibc-2.26-64.amzn2.0.3.x86_64でいけた
sourceは
sudo yum install rpmdevtools
いや、いらないのか
debuginfo-installでソースの情報まで持ってきていそう
2025/02/09
Nginxを使って何か再現してみよう
とりあえずSNIを使った設定を作ってみたい
そのためにはSSL化が必要だな......
EC2 nginx-sandboxを作った
ひとまず監視でOKが返るようにしよう
nagios-sandboxから監視コマンド実行
もうできた。何もする必要なかった
code:ok
HTTP OK: HTTP/1.1 200 OK - 848 bytes in 0.003 second response time |time=0.003465s;;;0.000000 size=848B;;;0
とりあえず何かしらコンテンツを表示しておきたい
監視文字列の設定ができた(なんとなくやってみたかっただけ)
code:ok
HTTP OK: HTTP/1.1 200 OK - 368 bytes in 0.002 second response time |time=0.002120s;;;0.000000 size=368B;;;0
HTTP CRITICAL: HTTP/1.1 200 OK - string 'SNI SANDBOXx' not found on 'http://103.4.10.60:80/' - 368 bytes in 0.003 second response time |time=0.003393s;;;0.000000 size=368B;;;0 このページをSSL化したい
方法
テスト用のドメインを使う
自己証明書?
やってみるか
code:oreore
下記設定を追記
code:nginx
listen 443 ssl;
ssl_certificate /home/ec2-user/server.crt;
ssl_certificate_key /home/ec2-user/server.key;
反映
code:start
Enter PEM pass phrase:
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
pemにパスフレーズが設定されているのがダメそう
code:error
Feb 09 10:01:23 ip-172-31-15-185.ap-northeast-1.compute.internal systemd1: Starting The nginx HTTP and reverse proxy server... Feb 09 10:01:23 ip-172-31-15-185.ap-northeast-1.compute.internal nginx3831: Enter PEM pass phrase: Feb 09 10:01:23 ip-172-31-15-185.ap-northeast-1.compute.internal nginx3831: nginx: emerg cannot load certificate key "/home/ec2-user/server.key": PEM_read_bio_PrivateKey() f... Feb 09 10:01:23 ip-172-31-15-185.ap-northeast-1.compute.internal nginx3831: nginx: configuration file /etc/nginx/nginx.conf test failed Feb 09 10:01:23 ip-172-31-15-185.ap-northeast-1.compute.internal systemd1: nginx.service: control process exited, code=exited status=1 Feb 09 10:01:23 ip-172-31-15-185.ap-northeast-1.compute.internal systemd1: Failed to start The nginx HTTP and reverse proxy server. 作り直し
動いたっぽい
code:ok
Generating RSA private key, 2048 bit long modulus
.........................................................................................+++
......................................+++
e is 65537 (0x10001)
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) XX: State or Province Name (full name) []:
Organizational Unit Name (eg, section) []:
Common Name (eg, your name or your server's hostname) []:example.com
Email Address []:
Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:
Signature ok
subject=/C=XX/L=Default City/O=Default Company Ltd/CN=example.com
Getting Private key
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
● nginx.service - The nginx HTTP and reverse proxy server
Loaded: loaded (/usr/lib/systemd/system/nginx.service; enabled; vendor preset: disabled)
Active: active (running) since Sun 2025-02-09 10:04:17 UTC; 5s ago
Process: 3858 ExecStart=/usr/sbin/nginx (code=exited, status=0/SUCCESS)
Process: 3854 ExecStartPre=/usr/sbin/nginx -t (code=exited, status=0/SUCCESS)
Process: 3853 ExecStartPre=/usr/bin/rm -f /run/nginx.pid (code=exited, status=0/SUCCESS)
Main PID: 3860 (nginx)
CGroup: /system.slice/nginx.service
├─3860 nginx: master process /usr/sbin/nginx
└─3861 nginx: worker process
Feb 09 10:04:17 ip-172-31-15-185.ap-northeast-1.compute.internal systemd1: Starting The nginx HTTP and reverse proxy server... Feb 09 10:04:17 ip-172-31-15-185.ap-northeast-1.compute.internal nginx3854: nginx: the configuration file /etc/nginx/nginx.conf syntax is ok Feb 09 10:04:17 ip-172-31-15-185.ap-northeast-1.compute.internal nginx3854: nginx: configuration file /etc/nginx/nginx.conf test is successful Feb 09 10:04:17 ip-172-31-15-185.ap-northeast-1.compute.internal systemd1: Started The nginx HTTP and reverse proxy server. 監視コマンド実行
-IでIPアドレス指定してcsrで入力したCommon Nameを利用するように
通った
code:ok
HTTP OK: HTTP/1.1 200 OK - 368 bytes in 0.009 second response time |time=0.008956s;;;0.000000 size=368B;;;0
次にSNIの設定をする
SNI有効ではあるらしい
ということは設定するだけで動く
code:SNI
nginx version: nginx/1.22.1
built by gcc 7.3.1 20180712 (Red Hat 7.3.1-17) (GCC)
built with OpenSSL 1.1.1g FIPS 21 Apr 2020 (running with OpenSSL 1.1.1y FIPS 4 Jun 2024)
TLS SNI support enabled
SNIは証明書切り替えのためにあるはずだから、もう一個ドメインを作る
cert1.example.com
できたっぽいが--sniつけなくても監視コマンドが通ってしまう
一旦返すコンテンツを分けるか
--sniつけようがつけまいがOKが返ってくる
-vでコンテンツ表示したらしっかり振り分けされているからNginxの動作は問題なさそう
じゃあ結局--sniは何をしている?
もしかして-I -Hで明示的にホストを指定すると--sniは必要ない?
/etc/hostsに名前登録してやっても変わらなかった
code:?
HTTP OK: HTTP/1.1 200 OK - 380 bytes in 0.009 second response time |time=0.008646s;;;0.000000 size=380B;;;0
HTTP OK: HTTP/1.1 200 OK - 380 bytes in 0.007 second response time |time=0.007498s;;;0.000000 size=380B;;;0
一応証明書が振り分けされているかも確認する
されている
code:cert
depth=0 C = XX, L = Default City, O = Default Company Ltd, CN = cert1.example.com
verify error:num=18:self signed certificate
verify return:1
depth=0 C = XX, L = Default City, O = Default Company Ltd, CN = cert1.example.com
verify return:1
^C
depth=0 C = XX, L = Default City, O = Default Company Ltd, CN = example.com
verify error:num=18:self signed certificate
verify return:1
depth=0 C = XX, L = Default City, O = Default Company Ltd, CN = example.com
verify return:1
じゃあなんでCloudflareやCloudFrontはさんでるサーバは--sniつけないといけないんだ?
予想
Nginxはservernameがなくてもいい感じに読み取ってくれる
SSL層でNginxはやっかいしないのでは?
proxyで使うと挙動が変わる?
301を発生させる
リクエストがどのドメイン向けかをすぐに決定できない場合、CloudFront は接続を中断します。
この挙動かなぁ
Nginxはどのドメインかをすぐに決定できなくてもどうにかしてくれるが、CloudFrontは中断するということか?
パケットを除いてservernameが無い状態であることを確かめたいな
-vでみえるのは多分HTTPヘッダだからSSLの段階でのservernameは確認できないかも
NginxがservernameなくてもどうにかSNIしているということを確かめたい
SSL Client HELLOにSNIが含まれないことを確かめる
→NginxがどのようにSNIによるルーティング?を実現しているか確かめる
SNIがセットされていない場合拒否する設定があるかもしれない
Wiresharkの出番かも
Apacheでもやってみたいかも
Apache立てる
443はNginxが使ってるから10443にして立ち上げた
code:er
(98)Address already in use: AH00072: make_sock: could not bind to address :::443 sudo vim /etc/httpd/conf.d/ssl.conf
よし。SSLの設定しよう
code:ok
HTTP OK: HTTP/1.1 200 OK - 315 bytes in 0.003 second response time |time=0.003224s;;;0.000000 size=315B;;;0
その前にセキュリティグループのインバウンドルール厳しくしておこう......
code:virtualhost
<VirtualHost *:10443>
ServerName example.com
DocumentRoot /var/www/site
SSLEngine on
SSLCertificateFile /home/ec2-user/server.crt
SSLCertificateKeyFile /home/ec2-user/server.key
</VirtualHost>
<VirtualHost *:10443>
ServerName cert1.example.com
DocumentRoot /var/www/site1
SSLEngine on
SSLCertificateFile /home/ec2-user/server1.crt
SSLCertificateKeyFile /home/ec2-user/server1.key
</VirtualHost>
--sniなくても動くな......
code:ok
HTTP OK: HTTP/1.1 200 OK - 308 bytes in 0.008 second response time |time=0.007795s;;;0.000000 size=308B;;;0
HTTP OK: HTTP/1.1 200 OK - 310 bytes in 0.008 second response time |time=0.008330s;;;0.000000 size=310B;;;0
再現できた!
SSLStrictSNIVHostCheckをonにすればよかったんだ
これonにしないとSNI使ってない時は......どうなるんだ?SNIは使わずにどうにかしているってことだよね。
普通に考えたらHTTP層でなんかやってるんだろう
デフォルトはoffらしい
code:ng
HTTP WARNING: HTTP/1.1 403 Forbidden - 531 bytes in 0.008 second response time |time=0.007739s;;;0.000000 size=531B;;;0
HTTP OK: HTTP/1.1 200 OK - 310 bytes in 0.010 second response time |time=0.009635s;;;0.000000 size=310B;;;0
ここで SSLStrictSNIVHostCheck off はSNI未対応ブラウザの挙動を制御する設定となりoffであればDefault Virtual Hostへリダイレクトされることを意味する。onであれば、接続自体を拒否する。
本当にそうか?
SSLStrictSNIVHostCheckの挙動を確かめる前に
NginxにもApacheのSSLStrictSNIVHostCheck on相当の設定はあるか確認しよう
下記でできるらしい
こっちもデフォルトはoffらしい
code:nginx
server {
listen 443 ssl;
ssl_reject_handshake on;
}
できた
code:ng
CRITICAL - Cannot make SSL connection.
140119084476352:error:14077458:SSL routines:SSL23_GET_SERVER_HELLO:tlsv1 unrecognized name:s23_clnt.c:769:
HTTP OK: HTTP/1.1 200 OK - 368 bytes in 0.007 second response time |time=0.007203s;;;0.000000 size=368B;;;0
ここで疑問なのがなんでSNIセットされていない時は拒否する必要があるのかだ
普通に考えたら、別の証明書が使われてしまっていることに気づきやすくなるため?
Cloudflareも試して再現したい
--sniが無い場合、Apacheは403を返し、NginxはCannot make SSL connection.となる
CloudFrontはCannot make SSL connection.を返す
Cloudflareは?403か?
403返すのは一応HTTP層まで行ってるからだよな
Cannot make SSL connectionもNginxが返しているという意味ではHTTP層か?
SSL層で早々に終わらせてる感じ?
どういう場合に--sniが必要なのかをまとめることはできそうだ
2025/02/11
デフォルトの設定を追加して、--sniついてない場合はデフォルトの証明書が使われるか試す
検証でよく使いそうなので別ページ作った
Apacheでやるか
できた
code:default
SSL initialized
GET / HTTP/1.1
User-Agent: check_http/v2.4.12.15.g7c74 (nagios-plugins 2.4.12)
Connection: close
Host: hoge.example.com:10443
Accept: */*
STATUS: HTTP/1.1 200 OK
**** HEADER ****
Date: Tue, 11 Feb 2025 10:44:27 GMT
Server: Apache/2.4.62 () OpenSSL/1.0.2k-fips
Upgrade: h2,h2c
Connection: Upgrade, close
Last-Modified: Tue, 11 Feb 2025 10:43:33 GMT
ETag: "8-62ddb7f19ab83"
Accept-Ranges: bytes
Content-Length: 8
Content-Type: text/html; charset=UTF-8
**** CONTENT ****
DEFAULT
HTTP OK: HTTP/1.1 200 OK - 307 bytes in 0.009 second response time |time=0.009089s;;;0.000000 size=307B;;;0
うーん
Defaultなんちゃらはあまり意味のある検証ではなさそう
適当なホスト名にするとDefaultVirtualHostが使われるので、証明書もdefault.example.comのものが使われる
servernameがcert2.example.comなのに証明書はdefault.example.comのもの、という状態
code:cert
depth=0 C = XX, L = Default City, O = Default Company Ltd, CN = default.example.com
verify error:num=18:self signed certificate
verify return:1
depth=0 C = XX, L = Default City, O = Default Company Ltd, CN = default.example.com
verify return:1
この挙動を作るためにSNIが必要なんだな
cert2.example.comの証明書を期待するが、VirtualHostにcert2.example.comは無いためDefaultVirtualHostのdefault.example.comの証明書が使われる
SSL接続段階で期待しない証明書が使われることがわかるので--verify-hostするとCRITICAL - Hostname mismatch.となる
もし、SNIが無い場合は「cert2.example.comのサイトなのにdefault.example.comの証明書が使われている」という事態が発生する可能性がある
それじゃあ証明書の意味がないじゃないか、ということになる
他人の保険証で処方箋をもらう、みたいな
この表現合ってるか?
Nginxではそういうことは発生しない?
SSL接続段階で別の証明書が使われてしまっていることに気づく仕組みが必要
それを実現するのががSNI
違うかも
code:missmatch
CRITICAL - Hostname mismatch.
SSL initialized
単純にServerName毎に証明書を分けたいからSNIを使うんだろう
--verify-hostオプション使えば指定した証明書が使えていることを示せるが......
ちょっとわかりづらい
--verify-host
Verify SSL certificate is for the -H hostname (with --sni and -S)
DefaultVirtualHostを下記のように設定
code:default
<VirtualHost *:10443>
DocumentRoot /var/www/default
SSLEngine on
SSLCertificateFile /home/ec2-user/default.crt
SSLCertificateKeyFile /home/ec2-user/default.key
</VirtualHost>
その上でCNとは違うホスト名で接続してみる
なんと通る
code:check
HTTP OK: HTTP/1.1 200 OK - 307 bytes in 0.009 second response time |time=0.009081s;;;0.000000 size=307B;;;0
--verify-hostしてみる
CRITICAL - Hostname mismatch.
code:verify
CRITICAL - Hostname mismatch.
SNIによる名前ベースのバーチャルサーバの挙動はNginxも同じっぽい
SNIがない時デフォルトのVirtualHostが使用されるという挙動を再現したい
なお、SNIが無効であるとき、あるいはクライアントがSNIに対応していないときには、デフォルトサーバのバーチャルサーバが適応され、そのサーバ証明書が使われます。
これ本当に?
本当だ......
でもコンテンツはそのドメインのものが使われる
code:honto
depth=0 C = XX, L = Default City, O = Default Company Ltd, CN = default.example.com
verify error:num=18:self signed certificate
verify return:1
depth=0 C = XX, L = Default City, O = Default Company Ltd, CN = default.example.com
verify return:1
HTTP OK: HTTP/1.1 200 OK - 310 bytes in 0.008 second response time |time=0.007652s;;;0.000000 size=310B;;;0
SSL initialized
GET / HTTP/1.1
User-Agent: check_http/v2.4.12.15.g7c74 (nagios-plugins 2.4.12)
Connection: close
Host: cert1.example.com:10443
Accept: */*
STATUS: HTTP/1.1 200 OK
**** HEADER ****
Date: Tue, 11 Feb 2025 12:05:13 GMT
Server: Apache/2.4.62 () OpenSSL/1.0.2k-fips
Upgrade: h2,h2c
Connection: Upgrade, close
Last-Modified: Sun, 09 Feb 2025 11:34:44 GMT
ETag: "a-62db3fa7445f3"
Accept-Ranges: bytes
Content-Length: 10
Content-Type: text/html; charset=UTF-8
**** CONTENT ****
SNI cert1
HTTP OK: HTTP/1.1 200 OK - 310 bytes in 0.009 second response time |time=0.008620s;;;0.000000 size=310B;;;0
Cloudflareでは上記のように意図しない証明書を使って欲しくないから、SNI設定されていない場合は接続拒否する設定になっていると予想する
CDNはでかいVirtualHostみたいな感じ......
2025/02/15
パケットキャプチャしたい
tshark使ってみるか
code:install
sudo yum install -y wireshark-cli
https://gyazo.com/81f559b2be957f4f6bc1f2bdee704417
rsyncで持ってくるためにchmod
code:cap
sudo tshark -i eth0 -f "host 35.78.169.213" -w /tmp/hoge.cap
chmod 644 /tmp/hoge.cap
できた
https://gyazo.com/5031dc38eaae295f5e2783cec9339a29
それぞれ説明したい
Server Name list length
これは予想だけど「011example.com」の長さかな
つまりこれがServer Name list?
Server Name Type
これはなに?
0以外にもある?
Server Name length
「example.com」の長さ
Server Name
これはそのまま
ExtensionのType
server_name = SNI
ExtensionのLength
16(2)+ SNI「011example.com」(14) かな
code:server_name
Extension: server_name (len=16) name=example.com
Type: server_name (0)
Length: 16
Server Name Indication extension
Server Name list length: 14
Server Name Type: host_name (0)
Server Name length: 11
Server Name: example.com
予想が立ったのでRFC見てみよう
code:rfc
struct {
NameType name_type; 1bytes
select (name_type) {
case host_name: HostName;
} name; 11bytes + 2bytes(HostNamelength)
} ServerName;
enum {
host_name(0), (255)
} NameType;
opaque HostName<1..2^16-1>;
struct {
ServerName server_name_list<1..2^16-1>
} ServerNameList; 14bytes + 2bytes(ServerNameListlength)
Server Name Typeはhost_nameのみ設定されている
00 00
server_name(0)
00 10
Length(16)
00 0e
Server Name list length(14)
00
Server Name Type(0(host_name))
00 0b
Server Name length(11)
65 78 61 6d 70 6c 65 2e 63 6f 6d
example.com
11bytes
長さの情報以外だと以下三つ(シンプル)
Extension Type
Server Name
Server Name Type(host_nameのみ)
host_name
Server Name
example.com
これでSNIが具体的にわかった
Server Name Typeは何か追加される予定はあるのか?
予定はわからないけどDNSに依存する仕様では無いということらしい
つまりhost_nameはDNSで利用する名前をさしている
opensslのこの辺りの意味がわかった
s: SSL構造体
SSL_CTRL_SET_TLSEXT_HOSTNAME: SNIの設定するよ〜
TLSEXT_NAMETYPE_host_name: SNIのNameTypeはhost_nameで設定するよ〜
name: ホスト名
code:openssl
# define SSL_set_tlsext_host_name(s,name) \
SSL_ctrl(s,SSL_CTRL_SET_TLSEXT_HOSTNAME,TLSEXT_NAMETYPE_host_name,\
(void *)name)
SSLの設定は色々あるけど一旦SSL_ctrlで抽象化している
より具体がSSL_CTRL_SET_TLSEXT_HOSTNAME
SSL_CTRL_SET_TLSEXT_SERVERNAMEとしてないのはなぜ?
まあ、NameTypeはhost_nameしかないからそれで問題ない
あとは異常終了をパケットキャプチャしてみたい
check_httpで--sniをとって実行してみる
Fatal LevelのUnrecognized Name(112)のAlertが送られている(Nginxでssl_reject_handshake onの場合)
RFCの仕様通りに実装されているということ
https://gyazo.com/9b84556d9d2872d7716abc3018ceaffc
これで流石にSNIは理解し切っただろう?
他のTLS拡張機能も気になるところ
というかSNIは拡張機能の0番なんだね