Cloudflare for SaaS を経由してオリジンに送られる SNI を確認する



# SNI 概要

1つの IP 上で複数のサイトを配信するには、「SNI (Server Name Indication)」と呼ばれる技術を使った `VirtualHost` の仕組みがあります。

> [【図解】TLSのSNIの仕組み ~SANsやCN,ワイルドカードとの違い~ | SEの道標](https://milestone-of-se.nesuke.com/sv-advanced/digicert/wildcard-sans-sni/)
>
> ![img](https://milestone-of-se.nesuke.com/wp-content/uploads/2019/03/cn-4.png.webp)

例えば Apache では、以下のような設定ファイルを配置することで、クライアントから送られる SNI によって、サーバーが異なる SSL 証明書を提示することが可能になります。

```con:apache.conf
NameVirtualHost *:443

<VirtualHost *:443>
ServerName www.yoursite.com
DocumentRoot /var/www/site
SSLEngine on
SSLCertificateFile /path/to/www_yoursite_com.crt
SSLCertificateKeyFile /path/to/www_yoursite_com.key
SSLCertificateChainFile /path/to/DigiCertCA.crt
</VirtualHost>

<VirtualHost *:443>
ServerName www.yoursite2.com
DocumentRoot /var/www/site2
SSLEngine on
SSLCertificateFile /path/to/www_yoursite2_com.crt
SSLCertificateKeyFile /path/to/www_yoursite2_com.key
SSLCertificateChainFile /path/to/DigiCertCA.crt
</VirtualHost>
```

その後、受信したクライアントブラウザは、証明書内の SANs を確認し、SNI とマッチングするものがあるかを確かめます。

そして SSL/TLS の処理後にホストヘッダ等を含む HTTP 通信がおこなわれます。

> [イマドキの証明書は全部マルチドメイン証明書 - Qiita](https://qiita.com/testnin2/items/0fb2211dffae2ffaec7a)

# Cloudflare for SaaS とは

[Cloudflare SSL for SaaS 設定ガイドを少し具体的に補足 - Qiita](https://qiita.com/KeioCF/items/fe3e155aca87902ca22b) が参考になります。

以下の公式ドキュメントも整理されています。

> [Cloudflare for SaaS · Cloudflare for SaaS docs](https://developers.cloudflare.com/cloudflare-for-saas/)
>
> ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101361/689f3b70-b9bb-c770-449b-adc63207de42.png)

# 確認結果まとめ

先に図示して載せておきます。

特に SaaS 業者はオリジンの運用において、SNI が重要になる場合がありますが、

Cloudflare for SaaS を経由した場合に独自ドメインで入ってくる Custom Hostname ごとにそうした制御が求められることもあります。

また、Cloudflare for SaaS を採用した際にオリジンで持つべき証明書にも関係します。

![ssl_for_saas_sni](https://cdn.jsdelivr.net/gh/kyouheicf/image@master/uPic/ssl_for_saas_sni.png)

# オリジン準備

## Docker インストール

Docker を使うので、インストールしておきます。

> [CentOS8にDockerをインストールする方法 - Qiita](https://qiita.com/yasushi-jp/items/976df81fd8ee8ee65f29)

```shell
$ cat /etc/redhat-release 
CentOS Linux release 8.4.2105
```

## GitHub からファイルダウンロード

SNI を表示するオリジンに使うファイルをダウンロードし、設定を書き換えます。

> [obezuk/test-sni: Server written in Node.js to debug SNI and Request Headers](https://github.com/obezuk/test-sni)

```shell
sudo yum install git -y
git clone https://github.com/obezuk/test-sni
cd test-sni
export MY_ZONE='saas\.com'
sed -i -e "s/example\.com/$MY_ZONE/g" csr-configuration.txt
# cat csr-configuration.txt
```

## Docker build & run

そのまま Docker ビルドしてラン（かつデタッチ）します。

> [obezuk/test-sni - Docker Image | Docker Hub](https://hub.docker.com/r/obezuk/test-sni)

```
sudo docker build -t test-sni .
sudo docker run -d -p 80:3080 -p 443:3443 test-sni
```

# 確認に必要な設定

オリジンが自己署名証明書を使うため、SSL/TLS encryption mode を Full にしておきます。

![image-20220807001714289](https://cdn.jsdelivr.net/gh/kyouheicf/image@master/uPic/image-20220807001714289.png)

# SNI を確認する

## 0. Cloudflare for SaaS なし

通常の SaaS ドメインへのアクセスについて、正しく SNI (`servername`) が表示されたことが確認できます。

```shell
% curl https://default-fallback.saas.com -s | jq -r '.|{"host":.headers.host, "servername":.servername}'
{
  "host": "default-fallback.saas.com",
  "servername": "default-fallback.saas.com"
}
% curl https://custom-origin.saas.com -s | jq -r '.|{"host":.headers.host, "servername":.servername}'
{
  "host": "custom-origin.saas.com",
  "servername": "custom-origin.saas.com"
}
```

## 1. Cloudflare for SaaS 経由

Custom Origin を使わない設定だと、デフォルト Fallback Origin に以下の SNI が送られたことが確認できます。

```shell
% curl https://customer.shop -s | jq -r '.|{"host":.headers.host, "servername":.servername}'
{
  "host": "customer.shop",
  "servername": "customer.shop"
}
```

![image-20220807002453647](https://cdn.jsdelivr.net/gh/kyouheicf/image@master/uPic/image-20220807002453647.png)

![image-20220807001224276](https://cdn.jsdelivr.net/gh/kyouheicf/image@master/uPic/image-20220807001224276.png)

## 2. Cloudflare for SaaS 経由 + Custom Origin (Origin server name)

Custom Hostname ごとに Custom Origin を指定して、SNI が Origin server name になったことが確認できます。

```shell
% curl https://customer.shop -s | jq -r '.|{"host":.headers.host, "servername":.servername}'
{
  "host": "customer.shop",
  "servername": "custom-origin.saas.com"
}
```

![image-20220807001323399](https://cdn.jsdelivr.net/gh/kyouheicf/image@master/uPic/image-20220807001323399.png)

## 3. Cloudflare for SaaS 経由 + Custom Origin (Host header)

Custom Origin であっても、SNI を当初の Host header と一致させられたことが確認できます。

```shell
% curl https://customer.shop -s | jq -r '.|{"host":.headers.host, "servername":.servername}'
{
  "host": "customer.shop",
  "servername": "customer.shop"
}
```

![image-20220807001348827](https://cdn.jsdelivr.net/gh/kyouheicf/image@master/uPic/image-20220807001348827.png)

## 4. Cloudflare for SaaS 経由 + Custom Origin (Host header) + Host Header Override

以下のような `Host Header Override` の Page Rule を加えると、SNI もそれに従って反映されたことが確認できます。

```shell
% curl https://customer.shop -s | jq -r '.|{"host":.headers.host, "servername":.servername}'
{
  "host": "override.example.com",
  "servername": "override.example.com"
}
```

![image-20220807001548061](https://cdn.jsdelivr.net/gh/kyouheicf/image@master/uPic/image-20220807001548061.png)

## 5. Cloudflare for SaaS 経由 + Custom Origin (Subdomain of zone)

以下のように SNI を `saas.com` もしくは `subdomain.saas.com` に変更されたことが確認できます。

```shell
% curl https://customer.shop -s | jq -r '.|{"host":.headers.host, "servername":.servername}'
{
  "host": "customer.shop",
  "servername": "subdomain.saas.com"
}
```

![image-20220807001418338](https://cdn.jsdelivr.net/gh/kyouheicf/image@master/uPic/image-20220807001418338.png)

以上。