Cloudflare Zero Trust で Client certificate のデバイスポスチャチェックを試す

# 目的
以下のドキュメントにある手順を確認します。

https://developers.cloudflare.com/cloudflare-one/identity/devices/warp-client-checks/client-certificate/

```mermaid:
graph BT

subgraph PC [Local PC]
    WARP
    Cert[Client Certificate]
    WARP --> |Device Posture Check|Cert
end

subgraph Cloudflare [Cloudflare Zero Trust]
    API["/mtls_certificates<br>API"]
end

CA[Root CA]
CA ---> |Issue|Cert
CA --> |"Upload<br>(Bring Your Own CA)"|API
WARP --> |Valid Cert by CA?|API
```

また、証明書の作成にあたり、以下の記事も参考にして進めます。

https://qiita.com/khayama/items/a8f7bc16e5554c9ccd51#%E8%AA%8D%E8%A8%BC%E5%B1%80-ca-%E7%BD%B2%E5%90%8D%E3%82%92%E8%A1%8C%E3%81%86%E5%A0%B4%E5%90%88

# Root CA の作成
`ca-csr.json` と `ca-config.json` を準備します。

```shell:
cat << EOS > ca-csr.json
{
    "CN": "khayama",
    "hosts": [""],
    "key": {
        "algo": "ecdsa",
        "size": 256
    },
    "names": [
        {
            "O": "khayama",
            "OU": "Sales",
            "L": "Shibuya",
            "ST": "Tokyo",
            "C": "JP"
        }
    ],
    "ca": {
        "expiry": "876000h"
    }
}
EOS
```

```shell:
cat << EOS > ca-config.json
{
    "signing": {
        "default": {
            "expiry": "87600h",
                "usages": [
                    "signing",
                    "key encipherment",
                    "server auth",
                    "client auth"
                    ]
        }
    }
}
EOS
```

以下のコマンドで `ca.csr` と `ca.pem` と `ca-key.pem` を作成します。

```shell
cfssl genkey -initca ca-csr.json | cfssljson -bare ca   
```

# Root CA のアップロード
以下のコマンドで Root CA をアップロードします。


https://www.kevinsimper.dk/posts/how-to-convert-privatekey-into-singleline

```shell:
export EMAIL='YOUR_EMAIL'
export APIKEY='YOUR_APIKEY'
export ACCOUNT_ID='YOUR_ACCOUNT_ID'

export CA_CERT=$(awk -v ORS='\\n' '1' ca.pem)
export CA_KEY=$(awk -v ORS='\\n' '1' ca-key.pem)

curl -s "https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/mtls_certificates" \
--header "X-Auth-Email: $EMAIL" \
--header "X-Auth-Key: $APIKEY" \
--header "Content-Type: application/json" \
--data '{
  "name": "client_cert_check_ca",
  "certificates": "'"$CA_CERT"'",
  "private_key": "'"$CA_KEY"'",
  "ca": true
}' | jq
```

その後、`result.id` として返ってくる UUID を控えておきます。

```shell:
export CA_UUID=$(curl -s "https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/mtls_certificates" \
--header "X-Auth-Email: $EMAIL" \
--header "X-Auth-Key: $APIKEY" \
--header "Content-Type: application/json" | jq -r '.result[] | select (.name == "client_cert_check_ca") | .id')
echo $CA_UUID
```

# Client Certificate の作成
`client.json` を準備します。

```shell:
cat << EOS > client.json
{
    "CN": "khayama",
    "hosts": [""],
    "key": {
        "algo": "ecdsa",
        "size": 256
    },
    "names": [
        {
            "O": "khayama",
            "OU": "Sales",
            "L": "Shibuya",
            "ST": "Tokyo",
            "C": "JP"
        }
    ]
}
EOS
```

以下のコマンドで `client.csr` と `client-key.pem` を作成します。


```shell:
cfssl genkey client.json | cfssljson -bare client
```

以下のコマンドで `client.pem` を作成します。

```shell:
cfssl sign -ca ca.pem -ca-key ca-key.pem -config ca-config.json client.csr | cfssljson -bare client
```

# macOS 

## Client Certificate の登録
以下の記事を参考にします。

https://qiita.com/khayama/items/d9eee0b7e8c2f9c4873a#%E3%82%AF%E3%83%A9%E3%82%A4%E3%82%A2%E3%83%B3%E3%83%88%E8%A8%BC%E6%98%8E%E6%9B%B8%E3%81%82%E3%82%8A%E3%81%A7%E3%83%96%E3%83%A9%E3%82%A6%E3%82%B6%E3%81%A7%E3%82%A2%E3%82%AF%E3%82%BB%E3%82%B9

以下のコマンドで `client.p12` を作成します。

```shell:
openssl pkcs12 -export -legacy \
-in client.pem \
-inkey client-key.pem \
-out client.p12  \
-passout pass:password \
-name "Client Cert for CF ZT Device Posture Check"
```

`/Library/Keychains/System.keychain` に `sudo` で証明書をインポートします。

```shell:
sudo security -v import client.p12 -k /Library/Keychains/System.keychain -P "password"
```

以下のコマンドで表示が確認できれば、登録完了です。

```
security find-certificate -c "khayama" -p /Library/Keychains/System.keychain 
```

## デバイスポスチャの設定
[Root CA のアップロード](#root-ca-のアップロード) で控えた UUID を Certificate ID に入力して、以下のように設定します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101361/9ab0c332-1389-199b-b685-5e26afbe3dc4.png)

## 確認
macOS の WARP クライアントでは以下のようにデバイスポスチャを確認できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101361/3039c074-28b2-8e45-d5de-2b0e8921edc9.png)

# Windows
## Client Certificate の登録
以下の手順で登録します。

https://learn.microsoft.com/ja-jp/biztalk/adapters-and-accelerators/accelerator-swift/adding-certificates-to-the-certificates-store-on-the-client

[macOS Client Certificate の登録](#client-certificate-の登録) と同じ手順で `client.p12` ファイルの作成が必要です。

### User trust store
以下から証明書のインポートウィザードを起動し、`client.p12` ファイルをインポートします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101361/be079515-bd1a-b3fe-000b-2aa24064aff0.png)

以下のコマンドで結果得られれば、問題なく登録されたことが確認できます。

```powershell:
PS C:\Windows\system32> Get-ChildItem Cert:\CurrentUser\My\ | where{$_.Subject -like "*khayama*"}


   PSParentPath: Microsoft.PowerShell.Security\Certificate::CurrentUser\My

Thumbprint                                Subject
----------                                -------
3FDD10D889775FDA11906FAB8EFABC07F03A8F38  CN=khayama, OU=Sales, O=khayama, L=Shibuya, S=Tokyo, C=JP
```

### Local machine trust store
以下から証明書のインポートウィザードを起動し、`client.p12` ファイルをインポートします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101361/71016707-4741-cf6b-bdf7-f56737c3fbeb.png)

以下のコマンドで結果得られれば、問題なく登録されたことが確認できます。

```powershell:
PS C:\Windows\system32> Get-ChildItem Cert:\LocalMachine\My\ | where{$_.Subject -like "*khayama*"}


   PSParentPath: Microsoft.PowerShell.Security\Certificate::LocalMachine\My

Thumbprint                                Subject
----------                                -------
3FDD10D889775FDA11906FAB8EFABC07F03A8F38  CN=khayama, OU=Sales, O=khayama, L=Shibuya, S=Tokyo, C=JP
```

## デバイスポスチャの設定
[Root CA のアップロード](#root-ca-のアップロード) で控えた UUID を Certificate ID に入力して、以下のように設定します。
Windows OS では `Local machine trust store` と `User trust store` を選択できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101361/ee95d4b6-ee7d-8340-1455-7fbade48a714.png)

## 確認
Windows の WARP クライアントでは以下のようにデバイスポスチャを確認できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101361/70a24c60-aeda-244c-a67d-f64cd32911e2.png)

# ログ確認

Device posture logs では `SUCCESS` のログが確認できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101361/1107afcd-e24b-aded-ed9a-0f7ec084e03b.png)

以下は Logpush によるログのサンプルです。

https://developers.cloudflare.com/logs/reference/log-fields/account/device_posture_results/

```json:logpush.json
{
  "ClientVersion": "2024.6.416",
  "DeviceID": "0bd7ef7a-3e5e-11ef-ba25-fa495d0d6c56",
  "DeviceManufacturer": "",
  "DeviceModel": "Mac15,7",
  "DeviceName": "xxx",
  "DeviceSerialNumber": "xxx",
  "DeviceType": "mac",
  "Email": "non_identity@xxx.cloudflareaccess.com",
  "OSVersion": "14.5.0",
  "PolicyID": "fadeead5-ff81-49de-b943-f93bab1f21ea",
  "PostureCheckName": "macOS Client Cert",
  "PostureCheckType": "client_certificate_v2",
  "PostureEvaluatedResult": true,
  "PostureExpectedJSON": {
    "certificate_id": "79cf0334-ada9-416f-841a-0f24ec7aefae",
    "cn": "khayama",
    "check_private_key": true,
    "locations": {
      "trust_stores": [
        "system"
      ]
    }
  },
  "PostureReceivedJSON": {
    "certificate_id": "79cf0334-ada9-416f-841a-0f24ec7aefae",
    "exists": true,
    "error": null
  },
  "Timestamp": "2024-07-19T16:39:18Z",
  "UserUID": "b7761faf-27e6-4d08-a35a-802db584b739"
}
```

# まとめ
このように設定することで ID 認証に加えて、クライアント証明書がデバイスにインストールされているかどうかのデバイスポスチャチェックを追加して、アプリケーションへのアクセスを制御できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101361/12720b67-1185-be93-500a-7d203198cc7d.png)