LoginSignup
0
0

More than 1 year has passed since last update.

Cloudflare API Shield の mTLS を確認する

Last updated at Posted at 2021-10-14

Cloudflare API Shield とは

こちらのブログが参考になります。

Introducing API Shield

公式ドキュメントはこちらです。全プランで使える機能になります。

API Shield™ · Cloudflare Firewall Rules docs

API Shield (mTLS) 有効化前

TLS ハンドシェイクの詳細です。

HOSTNAME=mtls.example.com
CFIP=$(dig +short @1.1.1.1 $HOSTNAME | head -n 1)

% openssl s_client -verify 5 -connect $CFIP:443 -servername $HOSTNAME -quiet -state
verify depth is 5
SSL_connect:before SSL initialization
SSL_connect:SSLv3/TLS write client hello
SSL_connect:SSLv3/TLS write client hello
SSL_connect:SSLv3/TLS read server hello
SSL_connect:TLSv1.3 read encrypted extensions
depth=2 C = IE, O = Baltimore, OU = CyberTrust, CN = Baltimore CyberTrust Root
verify return:1
depth=1 C = US, O = "Cloudflare, Inc.", CN = Cloudflare Inc ECC CA-3
verify return:1
depth=0 C = US, ST = California, L = San Francisco, O = "Cloudflare, Inc.", CN = example.com
verify return:1
SSL_connect:SSLv3/TLS read server certificate
SSL_connect:TLSv1.3 read server certificate verify
SSL_connect:SSLv3/TLS read finished
SSL_connect:SSLv3/TLS write change cipher spec
SSL_connect:SSLv3/TLS write finished
SSL_connect:SSL negotiation finished successfully
SSL_connect:SSL negotiation finished successfully
SSL_connect:SSLv3/TLS read server session ticket
SSL_connect:SSL negotiation finished successfully
SSL_connect:SSL negotiation finished successfully
SSL_connect:SSLv3/TLS read server session ticket
SSL3 alert read:warning:close notify
SSL3 alert write:warning:close notify

API Shield (mTLS) 有効化後

以下のダッシュボードでホスト名を追加すると、

Enable mTLS · Cloudflare SSL docs

image-20211014020328520

以下のようにクライアント証明書を要求するように TLS ハンドシェイクが変わります。

% openssl s_client -verify 5 -connect $CFIP:443 -servername $HOSTNAME -quiet -state
verify depth is 5
SSL_connect:before SSL initialization
SSL_connect:SSLv3/TLS write client hello
SSL_connect:SSLv3/TLS write client hello
SSL_connect:SSLv3/TLS read server hello
SSL_connect:TLSv1.3 read encrypted extensions
SSL_connect:SSLv3/TLS read server certificate request   <-- クライアント証明書を要求
depth=2 C = IE, O = Baltimore, OU = CyberTrust, CN = Baltimore CyberTrust Root
verify return:1
depth=1 C = US, O = "Cloudflare, Inc.", CN = Cloudflare Inc ECC CA-3
verify return:1
depth=0 C = US, ST = California, L = San Francisco, O = "Cloudflare, Inc.", CN = example.com
verify return:1
SSL_connect:SSLv3/TLS read server certificate
SSL_connect:TLSv1.3 read server certificate verify
SSL_connect:SSLv3/TLS read finished
SSL_connect:SSLv3/TLS write change cipher spec
SSL_connect:SSLv3/TLS write client certificate
SSL_connect:SSLv3/TLS write finished
SSL_connect:SSL negotiation finished successfully
SSL_connect:SSL negotiation finished successfully
SSL_connect:SSLv3/TLS read server session ticket
SSL_connect:SSL negotiation finished successfully
SSL_connect:SSL negotiation finished successfully
SSL_connect:SSLv3/TLS read server session ticket
SSL3 alert read:warning:close notify
SSL3 alert write:warning:close notify

ファイアウォールルールの作成

こちらの手順に従って、作成できます。

Create a mTLS rule · Cloudflare Firewall Rules docs

image-20211014021231798

クライアント証明書なしでcurlでアクセス

以下のようなエラーになります。

% curl -svo /dev/null  https://mtls.example.com
*   Trying 104.18.14.139...
* TCP_NODELAY set
* Connected to mtls.example.com (104.18.14.139) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
*   CAfile: /etc/ssl/cert.pem
  CApath: none
* TLSv1.2 (OUT), TLS handshake, Client hello (1):
} [234 bytes data]
* TLSv1.2 (IN), TLS handshake, Server hello (2):
{ [100 bytes data]
* TLSv1.2 (IN), TLS handshake, Certificate (11):
{ [2327 bytes data]
* TLSv1.2 (IN), TLS handshake, Server key exchange (12):
{ [115 bytes data]
* TLSv1.2 (IN), TLS handshake, Request CERT (13):
{ [374 bytes data]
* TLSv1.2 (IN), TLS handshake, Server finished (14):
{ [4 bytes data]
* TLSv1.2 (OUT), TLS handshake, Certificate (11):
} [7 bytes data]
* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
} [37 bytes data]
* TLSv1.2 (OUT), TLS change cipher, Change cipher spec (1):
} [1 bytes data]
* TLSv1.2 (OUT), TLS handshake, Finished (20):
} [16 bytes data]
* TLSv1.2 (IN), TLS change cipher, Change cipher spec (1):
{ [1 bytes data]
* TLSv1.2 (IN), TLS handshake, Finished (20):
{ [16 bytes data]
* SSL connection using TLSv1.2 / ECDHE-ECDSA-AES128-GCM-SHA256
* ALPN, server accepted to use h2
* Server certificate:
*  subject: C=US; ST=California; L=San Francisco; O=Cloudflare, Inc.; CN=example.com
*  start date: Aug  4 00:00:00 2021 GMT
*  expire date: Aug  3 23:59:59 2022 GMT
*  subjectAltName: host "mtls.example.com" matched cert's "*.example.com"
*  issuer: C=US; O=Cloudflare, Inc.; CN=Cloudflare Inc ECC CA-3
*  SSL certificate verify ok.
* Using HTTP2, server supports multi-use
* Connection state changed (HTTP/2 confirmed)
* Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0
* Using Stream ID: 1 (easy handle 0x7fa3ff80d600)
> GET / HTTP/2
> Host: mtls.example.com
> User-Agent: curl/7.64.1
> Accept: */*
> 
* Connection state changed (MAX_CONCURRENT_STREAMS == 256)!
< HTTP/2 403 
< date: Wed, 13 Oct 2021 17:15:12 GMT
< content-type: text/plain; charset=UTF-8
< content-length: 16
< x-frame-options: SAMEORIGIN
< referrer-policy: same-origin
< cache-control: private, max-age=0, no-store, no-cache, must-revalidate, post-check=0, pre-check=0
< expires: Thu, 01 Jan 1970 00:00:01 GMT
< expect-ct: max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"
< set-cookie: __cf_bm=OQ5zBTND0KtNG6simpoFUFbb52zxMhyll5Dnj2OHmBA-1634145312-0-AUtQQfUj4/luQhwAaUZEbEz/u4V0zMvsXqNYWqbIHf+DiLyRJVU5ya9kCpyWRzWkyRVmfJl+chiylIswSRk4PPQ=; path=/; expires=Wed, 13-Oct-21 17:45:12 GMT; domain=.example.com; HttpOnly; Secure; SameSite=None
< server: cloudflare
< cf-ray: 69da356c6ca00ad0-NRT
< 
{ [16 bytes data]
* Connection #0 to host mtls.example.com left intact
* Closing connection 0

クライアント証明書なしでブラウザでアクセス

以下のようにブロックされます。

image-20211013131029649

クライアント証明書の作成

ダッシュボードからクライアント証明書を作成します。

image-20211014022138498

「Cloudflare Managed CA」が発行する証明書となります。

image-20211014022209781

発行されたクライアント証明書と秘密鍵をローカルファイル cf_managed_client.pem, cf_managed_client.key に保存します。

image-20211014022352868

クライアント証明書の内容確認

以下のコマンドで確認できます。

Cloudflare の PKI ツール cfssl で証明書を作成する - Qiita

% cfssl certinfo -cert cf_managed_client.pem 
{
  "subject": {
    "common_name": "Cloudflare",
    "country": "US",
    "names": [
      "US",
      "Cloudflare"
    ]
  },
  "issuer": {
    "common_name": "Managed CA b5341236aea624bd7e2a0770ee3fb5e5",
    "country": "US",
    "organization": "Cloudflare, Inc.",
    "organizational_unit": "www.cloudflare.com",
    "locality": "San Francisco",
    "province": "California",
    "names": [
      "US",
      "California",
      "San Francisco",
      "Cloudflare, Inc.",
      "www.cloudflare.com",
      "Managed CA b5341236aea624bd7e2a0770ee3fb5e5"
    ]
  },
  "serial_number": "593413451556153623200318414325279173937034532728",
  "not_before": "2021-10-13T06:05:00Z",
  "not_after": "2031-10-11T06:05:00Z",
  "sigalg": "SHA256WithRSA",
  "authority_key_id": "B1:D9:A6:61:71:04:AB:A9:1C:1B:88:51:CC:20:1F:01:B9:88:98:50",
  "subject_key_id": "D7:6F:64:16:92:F6:29:29:14:F3:FB:91:3F:BF:7A:C3:FB:20:90:78",
  "pem": "-----BEGIN CERTIFICATE-----\nxxx\n-----END CERTIFICATE-----\n"
}

クライアント証明書ありでcurlでアクセス

先ほど保存したファイルを使って検証できます。

Troubleshooting · Cloudflare SSL docs

% curl -svo /dev/null --cert cf_managed_client.pem --key cf_managed_client.key https://mtls.example.com
*   Trying 104.18.14.139...
* TCP_NODELAY set
* Connected to mtls.example.com (104.18.14.139) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
*   CAfile: /etc/ssl/cert.pem
  CApath: none
* TLSv1.2 (OUT), TLS handshake, Client hello (1):
} [234 bytes data]
* TLSv1.2 (IN), TLS handshake, Server hello (2):
{ [100 bytes data]
* TLSv1.2 (IN), TLS handshake, Certificate (11):
{ [2327 bytes data]
* TLSv1.2 (IN), TLS handshake, Server key exchange (12):
{ [116 bytes data]
* TLSv1.2 (IN), TLS handshake, Request CERT (13):
{ [374 bytes data]
* TLSv1.2 (IN), TLS handshake, Server finished (14):
{ [4 bytes data]
* TLSv1.2 (OUT), TLS handshake, Certificate (11):
} [1059 bytes data]
* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
} [37 bytes data]
* TLSv1.2 (OUT), TLS handshake, CERT verify (15):
} [264 bytes data]
* TLSv1.2 (OUT), TLS change cipher, Change cipher spec (1):
} [1 bytes data]
* TLSv1.2 (OUT), TLS handshake, Finished (20):
} [16 bytes data]
* TLSv1.2 (IN), TLS change cipher, Change cipher spec (1):
{ [1 bytes data]
* TLSv1.2 (IN), TLS handshake, Finished (20):
{ [16 bytes data]
* SSL connection using TLSv1.2 / ECDHE-ECDSA-AES128-GCM-SHA256
* ALPN, server accepted to use h2
* Server certificate:
*  subject: C=US; ST=California; L=San Francisco; O=Cloudflare, Inc.; CN=example.com
*  start date: Aug  4 00:00:00 2021 GMT
*  expire date: Aug  3 23:59:59 2022 GMT
*  subjectAltName: host "mtls.example.com" matched cert's "*.example.com"
*  issuer: C=US; O=Cloudflare, Inc.; CN=Cloudflare Inc ECC CA-3
*  SSL certificate verify ok.
* Using HTTP2, server supports multi-use
* Connection state changed (HTTP/2 confirmed)
* Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0
* Using Stream ID: 1 (easy handle 0x7f9e3680d600)
> GET / HTTP/2
> Host: mtls.example.com
> User-Agent: curl/7.64.1
> Accept: */*
> 
* Connection state changed (MAX_CONCURRENT_STREAMS == 256)!
< HTTP/2 200 
< date: Wed, 13 Oct 2021 17:26:28 GMT
< content-type: text/html; charset=UTF-8
< x-powered-by: PHP/7.2.24
< x-apache-logname: -
< x-apache-username: -
< cf-cache-status: DYNAMIC
< expect-ct: max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"
< set-cookie: __cf_bm=sG5jNcimJrNvRrCI8QCJwN9hX1kvZ_VMEBbBhtM9REg-1634145988-0-AV1Z6zj22fs71zMX16Ej+lPfqo2JmBiWSlAmmTMun2yQXIfhUtfCYCRNybz2oZEKATtxDLTi4RZa8czZztjJaF4=; path=/; expires=Wed, 13-Oct-21 17:56:28 GMT; domain=.example.com; HttpOnly; Secure; SameSite=None
< server: cloudflare
< cf-ray: 69da45ec0cd234f3-NRT
< 
{ [665 bytes data]
* Connection #0 to host mtls.example.com left intact
* Closing connection 0

クライアント証明書ありでブラウザでアクセス

macOS の場合、クライアント証明書をキーチェーンアクセスにインポートすることでブラウザから利用できるようになります。

まず、先ほど保存したクライアント証明書と秘密鍵を p12 形式にします。

openssl pkcs12 -export -nodes \
-in cf_managed_client.pem \
-inkey cf_managed_client.key \
-out cf_managed_client.p12  \
-passout pass:password \
-name "Cloudflare Client Certificate for API Shield"

その後、キーチェーンアクセスにインポートします。

% security -v import cf_managed_client.p12 -k ~/Library/Keychains/login.keychain-db -P "password"
import "cf_managed_client.p12" "-k" "/Users/kyouhei/Library/Keychains/login.keychain-db" "-P" "password"
1 identity imported.

% security -v find-identity ~/Library/Keychains/login.keychain-db
find-identity "/Users/kyouhei/Library/Keychains/login.keychain-db"

Policy: X.509 Basic
  Matching identities
  1) 90CABE912B699A9FC863B609366181022B647568 "Cloudflare" (CSSMERR_TP_NOT_TRUSTED)
     1 identities found

  Valid identities only
     0 valid identities found

# 消したいとき
# security -v delete-identity -c Cloudflare ~/Library/Keychains/login.keychain-db

Chrome でアクセスしてみます。

open -a "Google Chrome" --args --incognito "https://mtls.example.com"  

このポップアップ画面でクライアント証明書を選択すれば、アクセスできます。

image-20211014121557628

参考:Safari 11.0 より古い場合

こちらにある対応が必要になります。

Why is Safari not detecting my Trusted Endpoints certificate?

security set-identity-preference -c Cloudflare -s mtls.example.com
security get-identity-preference -s mtls.example.com

参考リンク

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0