mTLS user propagation

I’m thinking about using mTLS as a part of client authentication while consuming APIs … at the end, isn’t this what API shield is about?? :wink:

I need to propagate client cert CN or Subject to the receiving server, as I need my developers to take decisions based on the (machine) client performing the request (authorization/profiling). I haven’t set up a testbed yet, but I was wondering if this information is somehow automatically propagated and how.

Many thanks.

See documentation at https://developers.cloudflare.com/api-shield/security/mtls/configure/ for steps to set it up. You’d have to figure out how you want to get the created CF Client TLS certificate onto the client devices doing the requests to your origin server.

Looks like Cloudflare doesn’t have a documented API for creating Cloudflare Client TLS certificates for mTLS authenatication at https://api.cloudflare.com/. You probably need such to programmatically obtain the Cloudflare Client TLS certificate + setup requesting clients’ devices/servers.

But if you go to your CF zone’s SSL/TLS → Client Certificates page https://dash.cloudflare.com/?to=/:account/:zone/ssl-tls/client-certificates and open the web browser developer tools and log a manually created CF Client TLS certificates POST requests you can find out the info and inspect how it was created. The CF dashboard follows closely to Cloudflare API endpoints. So we can deduce how to use the Cloudflare API to create Cloudflare Client TLS certificates for mTLS.

I was curious about this so decided to investigate how you can do this via Cloudflare API using undocumented commands and came up with the below steps. I borrowed Cloudflare’s cfssl tool to make some of this process easier GitHub - cloudflare/cfssl: CFSSL: Cloudflare's PKI and TLS toolkit

First assign variables used for Cloudflare API credentials.

zid=YOUR_CF_ZONE_ID
cfuser=YOUR_CF_EMAIL
cftoken=YOUR_CF_GLOBAL_API_KEY

Then create CF client TLS certificate using your own generated and provided private key and CSR

DOMAIN=Cloudflare
client_tls_cert_dir=YOUR_WORKING_DIRECTORY

cd ${client_tls_cert_dir}/
# create CSR JSOn file for ECDSA 256bit based CF Client TLS certificate
echo "{\"CN\": \"$DOMAIN\",\"key\":{\"algo\":\"ecdsa\",\"size\":256},\"names\":[{\"C\":\"US\"}]}" | jq -r | tee "${client_tls_cert_dir}/cfca-origin-csr-${DOMAIN}.json"
# generate own private key and CSR using cfssl tools
cfssl genkey "${client_tls_cert_dir}/cfca-origin-csr-${DOMAIN}.json" | cfssljson -bare
mv cert.csr "$DOMAIN-cert.csr"
mv cert-key.pem "$DOMAIN-cert-key.pem"
cp -a "$DOMAIN-cert-key.pem" cf-tls-client-key.pem
# create the payload for JSON formatted data which consists of validity_days = 15yrs 
# and the generated and formatted CSR file
CSR_CERT_JSON=$(cat ${client_tls_cert_dir}/$DOMAIN-cert.csr | perl -pe 's/\r?\n/\\n/'|sed -e's/..$//')
CSR_PAYLOAD="{\"validity_days\":5475,\"csr\":\"$CSR_CERT_JSON\"}"
echo "created CSR ${client_tls_cert_dir}/$DOMAIN-cert.csr"
echo "created Private Key ${client_tls_cert_dir}/$DOMAIN-cert-key.pem"

The CSR JSON file contents used to generate the private key and CSR files

echo "{\"CN\": \"$DOMAIN\",\"key\":{\"algo\":\"ecdsa\",\"size\":256},\"names\":[{\"C\":\"US\"}]}" | jq -r | tee "${client_tls_cert_dir}/cfca-origin-csr-${DOMAIN}.json"
{
  "CN": "Cloudflare",
  "key": {
    "algo": "ecdsa",
    "size": 256
  },
  "names": [
    {
      "C": "US"
    }
  ]
}

The output from cfssl tool used to generate the private key and CSR file passing the CSR JSON file contents

cfssl genkey "${client_tls_cert_dir}/cfca-origin-csr-${DOMAIN}.json" | cfssljson -bare
2022/03/24 16:48:07 [INFO] generate received request
2022/03/24 16:48:07 [INFO] received CSR
2022/03/24 16:48:07 [INFO] generating key: ecdsa-256
2022/03/24 16:48:07 [INFO] encoded CSR

The $CSR_PAYLOAD I am sending to Cloudflare API for zones/$zid/client_certificates endpoint.

echo $CSR_PAYLOAD  | jq -r
{
  "validity_days": 5475,
  "csr": "-----BEGIN CERTIFICATE REQUEST-----\nMIHdMIGEAgEAMCIxCzAJBgNVXXXTAlVTMRMwEQYDVQQDEwpDbG91ZGZsYXJlMFkw\nEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEVbg5BWe6RrVCwbsIUWKikw/MqWEY50qD\nUsBdLteI6/VOkdaeKKPHOxFWeWllf1HIO0d4DIGYR3Hy0g0H4JVbZKAAMAoGCCqG\nSM49BAMCA0gAMEUCIQDMSCtUxU4QgQtnXzBe3wvn1wAT0Ni2PtjPeFqpTAfi/QIg\nVzadKQOC25V0k1zjzN3rj5SZBQmPf1Rvl91EeGbwPg0=\n-----END CERTIFICATE REQUEST-----"
}

Obtain CF client TLS certificate using $CSR_PAYLOAD provided generated CSR with 15yr validity using Cloudflare API.

ENDPOINT="zones/$zid/client_certificates"
curl -4sX POST "https://api.cloudflare.com/client/v4/${ENDPOINT}" \
     -H "X-Auth-Email: $cfuser" -H "X-Auth-Key: $cftoken" -H "Content-Type: application/json" \
     --data "$CSR_PAYLOAD" | jq -r | tee ${client_tls_cert_dir}/cf-tls-client-certificate.json

{
  "success": true,
  "errors": [],
  "messages": [],
  "result": {
    "id": "63ffa8ff-bb00-4d68-887a-ac23d1408XXX",
    "certificate_authority": {
      "id": "c3006d2d-5d9f-49e9-bcec-517a2552xxxx",
      "name": "Cloudflare Managed CA for account CF_ACC_ID"
    },
    "certificate": "-----BEGIN CERTIFICATE-----\nMIIDSjCCAjKgAwIBAgIUVw2oj2pAXw6VrgsspAl96DrDu+swDQYJKoZIhvcNAQEL\nBQAwgagxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQH\nEw1TYW4gRnJhbmNpc2NvMRkwFwYDVQQKExBDbG91ZGZsYXJlLCBJbmMuMRswGQYD\nVQQLExJ3d3cuY2xvdWRmbGFyZS5jb20xNDAyBgNVBAMTK01hbmFnZWQgQ0EgMmM4\nYjFiMDg5NGVmYTk5NDBlYjU1MGNlMDYwMjc4MTAwHhcNMjIwMzI0MTY0ODAwWhcN\nMzcwMzIwMTY0ODAwWjAiMQswCQYDVQQGEwJVUzETMBEGA1UEAxMKQ2xvdWRmbGFy\nZTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABFW4OQVnuka1QsG7CFFiopMPzKlh\nGOdKg1LAXS7XiOv1TpHWniijxzsRVnlpZX9RyDtHeAyBmEdx8tINB+CVW2Sjgbsw\ngbgwEwYDVR0lBAwwCgYIKwYBBQUHAwIwDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQU\nJXucyrWx/UKMNkdtkao3zX2INEUwHwYDVR0jBBgwFoAUHshBBVjPFHy9P2U7P9p4\nPevX/rswUwYDVR0fBEwwSjBIoEagRIZCaHR0cDovL2NybC5jbG91ZGZsYXJlLmNv\nbS9jMzAwNmQyZC01ZDlmLTQ5ZTktYmNlYy01MTdhMjU1MmFkNDIuY3JsMA0GCSqG\nSIb3DQEBCwUAA4IBAQAsTT190Ny8JHaDD51gU0DjhYVtc4FgIWtVuvJbEYt+F328\nwTt6WY4WSqwpLMrINaO+x/Hb6ZKcmdFnDtKJWfo+eQJXjw2y13KodLRTVnM0iaFN\nQrT0lFbUMS7OLhcgh+ChR/qS/9As+y2O1/x7I2aqqrLp/3lf4DFnIcB0sv8MyTA6\nFZaKaY36nJTo0WrKq6mPRsXdVC8DVGnY1TBuIlxYu2Vd3OldyuQnUaFLmUixk6J8\n0dRj278bU3bZJNezWWN7wSb7BUXlLwZ6kKQ2fp9AnXG3U4DwVCfc6tQIKFDMaZZ3\nbVLRRN389WnlOydjVERTxGCddGTZh6+zWU4Oxxxx\n-----END CERTIFICATE-----\n",
    "csr": "-----BEGIN CERTIFICATE REQUEST-----\nMIHdMIGEAgEAMCIxCzAJBgNVXXXTAlVTMRMwEQYDVQQDEwpDbG91ZGZsYXJlMFkw\nEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEVbg5BWe6RrVCwbsIUWKikw/MqWEY50qD\nUsBdLteI6/VOkdaeKKPHOxFWeWllf1HIO0d4DIGYR3Hy0g0H4JVbZKAAMAoGCCqG\nSM49BAMCA0gAMEUCIQDMSCtUxU4QgQtnXzBe3wvn1wAT0Ni2PtjPeFqpTAfi/QIg\nVzadKQOC25V0k1zjzN3rj5SZBQmPf1Rvl91EeGbwPg0=\n-----END CERTIFICATE REQUEST-----",
    "ski": "257b9ccab5b1fd428c36476d91aa37cd7d88XXXXS",
    "serial_number": "570da88f6a405f0e95ae0b2ca4097de83ac3XXXXS",
    "signature": "SHA256WithRSA",
    "common_name": "Cloudflare",
    "country": "US",
    "expires_on": "2037-03-20T16:48:00Z",
    "issued_on": "2022-03-24T16:48:00Z",
    "fingerprint_sha256": "1429f056bd8ec7f5d1437f87a620053b42fb4182abf5525d29624f50820bxxxx",
    "validity_days": 5475,
    "status": "active"
  }
}

Inspect the created Client TLS certificate and CSR file

cat cf-tls-client-certificate.json | jq -r '.result.certificate' > ${client_tls_cert_dir}/cf-tls-client.pem
cat cf-tls-client-certificate.json | jq -r '.result.csr' > ${client_tls_cert_dir}/cf-tls-client.csr

cfssl-certinfo -cert ${client_tls_cert_dir}/cf-tls-client.pem
{
  "subject": {
    "common_name": "Cloudflare",
    "country": "US",
    "names": [
      "US",
      "Cloudflare"
    ]
  },
  "issuer": {
    "common_name": "Managed CA CF_ACC_ID",
    "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 CF_ACC_ID"
    ]
  },
  "serial_number": "496986790414902344661337995713576054503231437xxx",
  "not_before": "2022-03-24T16:48:00Z",
  "not_after": "2037-03-20T16:48:00Z",
  "sigalg": "SHA256WithRSA",
  "authority_key_id": "1E:C8:41:05:58:CF:14:7C:BD:3F:65:3B:3F:DA:78:3D:EB:D7:FE:BB",
  "subject_key_id": "25:7B:9C:CA:B5:B1:FD:42:8C:36:47:6D:91:AA:37:CD:7D:88:34:XX",
  "pem": "-----BEGIN CERTIFICATE-----\nMIIDSjCCAjKgAwIBAgIUVw2oj2pAXw6VrgsspAl96DrDu+swDQYJKoZIhvcNAQEL\nBQAwgagxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQH\nEw1TYW4gRnJhbmNpc2NvMRkwFwYDVQQKExBDbG91ZGZsYXJlLCBJbmMuMRswGQYD\nVQQLExJ3d3cuY2xvdWRmbGFyZS5jb20xNDAyBgNVBAMTK01hbmFnZWQgQ0EgMmM4\nYjFiMDg5NGVmYTk5NDBlYjU1MGNlMDYwMjc4MTAwHhcNMjIwMzI0MTY0ODAwWhcN\nMzcwMzIwMTY0ODAwWjAiMQswCQYDVQQGEwJVUzETMBEGA1UEAxMKQ2xvdWRmbGFy\nZTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABFW4OQVnuka1QsG7CFFiopMPzKlh\nGOdKg1LAXS7XiOv1TpHWniijxzsRVnlpZX9RyDtHeAyBmEdx8tINB+CVW2Sjgbsw\ngbgwEwYDVR0lBAwwCgYIKwYBBQUHAwIwDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQU\nJXucyrWx/UKMNkdtkao3zX2INEUwHwYDVR0jBBgwFoAUHshBBVjPFHy9P2U7P9p4\nPevX/rswUwYDVR0fBEwwSjBIoEagRIZCaHR0cDovL2NybC5jbG91ZGZsYXJlLmNv\nbS9jMzAwNmQyZC01ZDlmLTQ5ZTktYmNlYy01MTdhMjU1MmFkNDIuY3JsMA0GCSqG\nSIb3DQEBCwUAA4IBAQAsTT190Ny8JHaDD51gU0DjhYVtc4FgIWtVuvJbEYt+F328\nwTt6WY4WSqwpLMrINaO+x/Hb6ZKcmdFnDtKJWfo+eQJXjw2y13KodLRTVnM0iaFN\nQrT0lFbUMS7OLhcgh+ChR/qS/9As+y2O1/x7I2aqqrLp/3lf4DFnIcB0sv8MyTA6\nFZaKaY36nJTo0WrKq6mPRsXdVC8DVGnY1TBuIlxYu2Vd3OldyuQnUaFLmUixk6J8\n0dRj278bU3bZJNezWWN7wSb7BUXlLwZ6kKQ2fp9AnXG3U4DwVCfc6tQIKFDMaZZ3\nbVLRRN389WnlOydjVERTxGCddGTZh6+zWU4Oxxxx\n-----END CERTIFICATE-----\n"
}
openssl req -text -noout -verify -in ${client_tls_cert_dir}/cf-tls-client.csr

verify OK
Certificate Request:
    Data:
        Version: 0 (0x0)
        Subject: C=US, CN=Cloudflare
        Subject Public Key Info:
            Public Key Algorithm: id-ecPublicKey
                Public-Key: (256 bit)
                pub: 
                    04:55:b8:39:05:67:ba:46:b5:42:c1:bb:08:51:62:
                    a2:93:0f:cc:a9:61:18:e7:4a:83:52:c0:5d:2e:d7:
                    88:eb:f5:4e:91:d6:9e:28:a3:c7:3b:11:56:79:69:
                    65:7f:51:c8:3b:47:78:0c:81:98:47:71:f2:d2:0d:
                    07:e0:95:5b:64
                ASN1 OID: prime256v1
                NIST CURVE: P-256
        Attributes:
            a0:00
    Signature Algorithm: ecdsa-with-SHA256
         30:45:02:21:00:cc:48:2b:54:c5:4e:10:81:0b:67:5f:30:5e:
         df:0b:e7:d7:00:13:d0:d8:b6:3e:d8:cf:78:5a:a9:4c:07:e2:
         fd:02:20:57:36:9d:29:03:82:db:95:74:93:5c:e3:cc:dd:eb:
         8f:94:99:05:09:8f:7f:54:6f:97:dd:44:78:66:f0:3e:0d

Created files for certificate, CSR file, private key and .json file.

ls -lahrt | grep cf-tls-client
-rw-------  1 root root  227 Mar 24 16:48 cf-tls-client-key.pem
-rw-r--r--  1 root root 2.4K Mar 24 16:53 cf-tls-client-certificate.json
-rw-r--r--  1 root root 1.2K Mar 24 16:53 cf-tls-client.pem
-rw-r--r--  1 root root  375 Mar 24 16:53 cf-tls-client.csr

List the CF Client TLS certificate with the 63ffa8ff-bb00-4d68-887a-ac23d1408XXX id from created CF Client TLS certificate.

ENDPOINT="zones/$zid/client_certificates/63ffa8ff-bb00-4d68-887a-ac23d1408XXX"
curl -4sX GET "https://api.cloudflare.com/client/v4/${ENDPOINT}" \
     -H "X-Auth-Email: $cfuser" -H "X-Auth-Key: $cftoken" -H "Content-Type: application/json" | jq -r | tee cf-tls-client-certs-details.json

{
  "success": true,
  "errors": [],
  "messages": [],
  "result": {
    "id": "63ffa8ff-bb00-4d68-887a-ac23d1408XXX",
    "certificate_authority": {
      "id": "c3006d2d-5d9f-49e9-bcec-517a2552xxxx",
      "name": "Cloudflare Managed CA for account CF_ACC_ID"
    },
    "certificate": "-----BEGIN CERTIFICATE-----\nMIIDSjCCAjKgAwIBAgIUVw2oj2pAXw6VrgsspAl96DrDu+swDQYJKoZIhvcNAQEL\nBQAwgagxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQH\nEw1TYW4gRnJhbmNpc2NvMRkwFwYDVQQKExBDbG91ZGZsYXJlLCBJbmMuMRswGQYD\nVQQLExJ3d3cuY2xvdWRmbGFyZS5jb20xNDAyBgNVBAMTK01hbmFnZWQgQ0EgMmM4\nYjFiMDg5NGVmYTk5NDBlYjU1MGNlMDYwMjc4MTAwHhcNMjIwMzI0MTY0ODAwWhcN\nMzcwMzIwMTY0ODAwWjAiMQswCQYDVQQGEwJVUzETMBEGA1UEAxMKQ2xvdWRmbGFy\nZTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABFW4OQVnuka1QsG7CFFiopMPzKlh\nGOdKg1LAXS7XiOv1TpHWniijxzsRVnlpZX9RyDtHeAyBmEdx8tINB+CVW2Sjgbsw\ngbgwEwYDVR0lBAwwCgYIKwYBBQUHAwIwDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQU\nJXucyrWx/UKMNkdtkao3zX2INEUwHwYDVR0jBBgwFoAUHshBBVjPFHy9P2U7P9p4\nPevX/rswUwYDVR0fBEwwSjBIoEagRIZCaHR0cDovL2NybC5jbG91ZGZsYXJlLmNv\nbS9jMzAwNmQyZC01ZDlmLTQ5ZTktYmNlYy01MTdhMjU1MmFkNDIuY3JsMA0GCSqG\nSIb3DQEBCwUAA4IBAQAsTT190Ny8JHaDD51gU0DjhYVtc4FgIWtVuvJbEYt+F328\nwTt6WY4WSqwpLMrINaO+x/Hb6ZKcmdFnDtKJWfo+eQJXjw2y13KodLRTVnM0iaFN\nQrT0lFbUMS7OLhcgh+ChR/qS/9As+y2O1/x7I2aqqrLp/3lf4DFnIcB0sv8MyTA6\nFZaKaY36nJTo0WrKq6mPRsXdVC8DVGnY1TBuIlxYu2Vd3OldyuQnUaFLmUixk6J8\n0dRj278bU3bZJNezWWN7wSb7BUXlLwZ6kKQ2fp9AnXG3U4DwVCfc6tQIKFDMaZZ3\nbVLRRN389WnlOydjVERTxGCddGTZh6+zWU4Oxxxx\n-----END CERTIFICATE-----\n",
    "csr": "-----BEGIN CERTIFICATE REQUEST-----\nMIHdMIGEAgEAMCIxCzAJBgNVXXXTAlVTMRMwEQYDVQQDEwpDbG91ZGZsYXJlMFkw\nEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEVbg5BWe6RrVCwbsIUWKikw/MqWEY50qD\nUsBdLteI6/VOkdaeKKPHOxFWeWllf1HIO0d4DIGYR3Hy0g0H4JVbZKAAMAoGCCqG\nSM49BAMCA0gAMEUCIQDMSCtUxU4QgQtnXzBe3wvn1wAT0Ni2PtjPeFqpTAfi/QIg\nVzadKQOC25V0k1zjzN3rj5SZBQmPf1Rvl91EeGbwPg0=\n-----END CERTIFICATE REQUEST-----",
    "ski": "257b9ccab5b1fd428c36476d91aa37cd7d88XXXXS",
    "serial_number": "570da88f6a405f0e95ae0b2ca4097de83ac3XXXXS",
    "signature": "SHA256WithRSA",
    "common_name": "Cloudflare",
    "country": "US",
    "expires_on": "2037-03-20T16:48:00Z",
    "issued_on": "2022-03-24T16:48:00Z",
    "fingerprint_sha256": "1429f056bd8ec7f5d1437f87a620053b42fb4182abf5525d29624f50820bxxxx",
    "validity_days": 5475,
    "status": "active"
  }
}

Using cfssljson tool to output certificate and CSR contents.

ENDPOINT="zones/$zid/client_certificates/63ffa8ff-bb00-4d68-887a-ac23d1408XXX"
curl -4sX GET "https://api.cloudflare.com/client/v4/${ENDPOINT}" \
     -H "X-Auth-Email: $cfuser" -H "X-Auth-Key: $cftoken" -H "Content-Type: application/json" | cfssljson -stdout

-----BEGIN CERTIFICATE-----
MIIDSjCCAjKgAwIBAgIUVw2oj2pAXw6VrgsspAl96DrDu+swDQYJKoZIhvcNAQEL
BQAwgagxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQH
Ew1TYW4gRnJhbmNpc2NvMRkwFwYDVQQKExBDbG91ZGZsYXJlLCBJbmMuMRswGQYD
VQQLExJ3d3cuY2xvdWRmbGFyZS5jb20xNDAyBgNVBAMTK01hbmFnZWQgQ0EgMmM4
YjFiMDg5NGVmYTk5NDBlYjU1MGNlMDYwMjc4MTAwHhcNMjIwMzI0MTY0ODAwWhcN
MzcwMzIwMTY0ODAwWjAiMQswCQYDVQQGEwJVUzETMBEGA1UEAxMKQ2xvdWRmbGFy
ZTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABFW4OQVnuka1QsG7CFFiopMPzKlh
GOdKg1LAXS7XiOv1TpHWniijxzsRVnlpZX9RyDtHeAyBmEdx8tINB+CVW2Sjgbsw
gbgwEwYDVR0lBAwwCgYIKwYBBQUHAwIwDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQU
JXucyrWx/UKMNkdtkao3zX2INEUwHwYDVR0jBBgwFoAUHshBBVjPFHy9P2U7P9p4
PevX/rswUwYDVR0fBEwwSjBIoEagRIZCaHR0cDovL2NybC5jbG91ZGZsYXJlLmNv
bS9jMzAwNmQyZC01ZDlmLTQ5ZTktYmNlYy01MTdhMjU1MmFkNDIuY3JsMA0GCSqG
SIb3DQEBCwUAA4IBAQAsTT190Ny8JHaDD51gU0DjhYVtc4FgIWtVuvJbEYt+F328
wTt6WY4WSqwpLMrINaO+x/Hb6ZKcmdFnDtKJWfo+eQJXjw2y13KodLRTVnM0iaFN
QrT0lFbUMS7OLhcgh+ChR/qS/9As+y2O1/x7I2aqqrLp/3lf4DFnIcB0sv8MyTA6
FZaKaY36nJTo0WrKq6mPRsXdVC8DVGnY1TBuIlxYu2Vd3OldyuQnUaFLmUixk6J8
0dRj278bU3bZJNezWWN7wSb7BUXlLwZ6kKQ2fp9AnXG3U4DwVCfc6tQIKFDMaZZ3
bVLRRN389WnlOydjVERTxGCddGTZh6+zWU4Oxxxx
-----END CERTIFICATE-----

-----BEGIN CERTIFICATE REQUEST-----
MIHdMIGEAgEAMCIxCzAJBgNVXXXTAlVTMRMwEQYDVQQDEwpDbG91ZGZsYXJlMFkw
EwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEVbg5BWe6RrVCwbsIUWKikw/MqWEY50qD
UsBdLteI6/VOkdaeKKPHOxFWeWllf1HIO0d4DIGYR3Hy0g0H4JVbZKAAMAoGCCqG
SM49BAMCA0gAMEUCIQDMSCtUxU4QgQtnXzBe3wvn1wAT0Ni2PtjPeFqpTAfi/QIg
VzadKQOC25V0k1zjzN3rj5SZBQmPf1Rvl91EeGbwPg0=
-----END CERTIFICATE REQUEST-----

Hope that helps.

Looks like the developer docs has CF API commands for creating Client TLS certs https://developers.cloudflare.com/ssl/client-certificates/configure-your-mobile-app-or-iot-device/#step-2--create-cloudflare-issued-certificates but not CF API docs.

This is fantastic information, that I would need afterwards.
Up to now, I need to extract the client certificate’s CN information on the target webserver.
I can’t see anything valuable in the header.
Any idea?

I found the answer myself, but I’d love to share it as it might be useful to someone else.
In short, configuring mTLS is not enough, you need to configure Access/Zero Trust as well.

mTLS will just enforce the use of a valid certificate on the client side to be able to perform the request. The check is done at Cloudflare level. But that’s it. No information is ever propagated to the downstream webserver/application about the client performing the request, except the usual headers (Cf-Connecting-Ip, Cf-Ipcountry, …)

In no way, the client certificate is presented to the actual target webserver. If you enabled Authenticated Origin Pulls, that certificate will be presented instead.

This is very not useful if you need to make decisions based on the client that is doing the requests. As I’m helping architecting a multi-tenant application, knowing which m2m edge server is performing the request is key.

But an extra step is needed. You need to setup Access/Zero Trust with a policy to allow a “Non Identity” decision to the target website. (for the sake of defence-in-depth, I also specified the valid CNs allowed to make the requests).
At that point, the “standard” Cloudflare Access JWT token is populated with a valid JWT containing the common name, both as a “common_name” and “sub” claims.
This comes, as standard, both as cookie as the “Cf-Access-Jwt-Assertion” header. The example of the decoded JWT token below, with the client cert CN poplated.

{
  "kid": "bd5a946f1c08f42b631f412036183ce94c98766fea336a025c0be61bd56ccf85",
  "alg": "RS256",
  "typ": "JWT"
}.{
  "type": "app",
  "aud": "5853e129ed38547fa33eeddf9fa57397b9db39f92dbd652f60d8138eb75608cf",
  "exp": 1648797569,
  "iss": "https://customer.cloudflareaccess.com",
  "common_name": "DDB1D482-CFCE-4167-BC12-19992734D613",
  "iat": 1648711169,
  "sub": "CN=DDB1D482-CFCE-4167-BC12-19992734D613,O=Customer.,C=US"
}
1 Like