Skip to content

Commit 9e20051

Browse files
committed
suggested updates
1 parent 1235267 commit 9e20051

File tree

2 files changed

+207
-218
lines changed

2 files changed

+207
-218
lines changed
Lines changed: 207 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,207 @@
1+
---
2+
updated: 2024-09-26
3+
category: 🔐 Access
4+
difficulty: Advanced
5+
pcx_content_type: tutorial
6+
title: Send device posture to Cloudflare Access-protected origins
7+
---
8+
9+
import { Render, GlossaryTooltip } from "~/components"
10+
11+
This tutorial demonstrates how to customize the user identity attributes that Cloudflare Access sends to your origin server.
12+
13+
**Time to complete:**
14+
15+
45 minutes
16+
17+
## Authentication flow
18+
19+
[Cloudflare Access](/cloudflare-one/policies/access/) is an authentication proxy in charge of validating a user's identity before they connect to your application. As shown in the diagram below, Access inserts a [JWT](/cloudflare-one/identity/authorization-cookie/application-token/) into the request, which can then be [verified](/cloudflare-one/identity/authorization-cookie/validating-json/#validate-jwts) by the origin server.
20+
21+
![Standard authentication flow for a request to an Access application](~/assets/images/cloudflare-one/applications/access-standard-flow.png)
22+
23+
You can extend this functionality by using a [Cloudflare Worker](/workers/) to insert additional HTTP headers into the request. In this example, we will add the [device posture attributes](/cloudflare-one/identity/devices/#enforce-device-posture) `firewall_activated` and `disk_encrypted`, but you can include any attributes that Cloudflare Access collects from the authentication event.
24+
25+
![Extended authentication flow uses a Worker to pass additional request headers to the origin](~/assets/images/cloudflare-one/applications/access-extended-flow-serverless.png)
26+
27+
## Benefits
28+
29+
This approach allows you to:
30+
31+
- **Enhance security**: By incorporating additional information from the authentication event, you can implement more robust security measures. For example, you can use device posture data to enforce access based on device compliance.
32+
- **Improve user experience**: You can personalize the user experience by tailoring content or functionality based on user attributes. For example, you can display different content based on the user's role or location.
33+
- **Simplify development**: By using Cloudflare Workers, you can easily extend your Cloudflare Access configuration without modifying your origin application code.
34+
35+
36+
## Before you begin
37+
38+
- Add a [self-hosted application](/cloudflare-one/applications/configure-apps/self-hosted-apps/) to Cloudflare Access.
39+
- Enable the [Disk encryption](/cloudflare-one/identity/devices/warp-client-checks/disk-encryption/) and [Firewall](/cloudflare-one/identity/devices/warp-client-checks/firewall/) device posture checks.
40+
- Install [Wrangler](/workers/wrangler/install-and-update/) on your local machine.
41+
42+
## 1. Create the Worker
43+
44+
1. Create a new Workers project:
45+
46+
```sh
47+
npm create cloudflare@latest -- device-posture-worker
48+
```
49+
50+
<Render file="c3-post-run-steps" product="workers" params={{ one: "Hello World example", two: "Hello World Worker", three: "JavaScript" }} />
51+
52+
2. Change to the project directory:
53+
54+
```sh
55+
$ cd device-posture-worker
56+
```
57+
58+
3. Copy-paste the following code into `src/index.js`. Be sure to replace `<your-team-name>` with your Zero Trust <GlossaryTooltip term="team name">team name</GlossaryTooltip>.
59+
60+
```js title="index.js"
61+
62+
import { parse } from "cookie";
63+
export default {
64+
async fetch(request, env, ctx) {
65+
// The name of the cookie
66+
const COOKIE_NAME = "CF_Authorization";
67+
const CF_GET_IDENTITY = "https://<your-team-name>.cloudflareaccess.com/cdn-cgi/access/get-identity";
68+
const cookie = parse(request.headers.get("Cookie") || "");
69+
if (cookie[COOKIE_NAME] != null) {
70+
try {
71+
let id = await (await fetch(CF_GET_IDENTITY, request)).json()
72+
let diskEncryptionStatus = false;
73+
let firewallStatus = false;
74+
75+
for (const checkId in id.devicePosture) {
76+
const check = id.devicePosture[checkId];
77+
if (check.type === "disk_encryption") {
78+
console.log(check.type)
79+
diskEncryptionStatus = check.success;
80+
}
81+
if (check.type === "firewall") {
82+
console.log(check.type)
83+
firewallStatus = check.success;
84+
break;
85+
}
86+
}
87+
//clone request (immutable otherwise) and insert posture values in new header set
88+
let newRequest = await new Request(request)
89+
newRequest.headers.set("Cf-Access-Firewall-Activated", firewallStatus)
90+
newRequest.headers.set("Cf-Access-Disk-Encrypted", firewallStatus)
91+
92+
//sent modified request to origin
93+
return await fetch(newRequest)
94+
95+
} catch (e) {
96+
console.log(e)
97+
return await fetch(request)
98+
}
99+
}
100+
return await fetch(request)
101+
},
102+
};
103+
```
104+
105+
## 2. View the user's identity
106+
107+
The script in `index.js` uses the [`get-identity`](/cloudflare-one/identity/authorization-cookie/application-token/#user-identity) endpoint to fetch a user's complete identity from a Cloudflare Access authentication event. To view a list of available data fields, log in to your Access application and append `/cdn-cgi/access/get-identity` to the URL. For example, if `www.example.com` is behind Access, go to `https://www.example.com/cdn-cgi/access/get-identity`.
108+
109+
Below is an example of a user identity that includes the `disk_encryption` and `firewall` posture checks. The Worker inserts the posture check results into the request headers **Cf-Access-Firewall-Activated** and **Cf-Access-Disk-Encrypted**.
110+
111+
```json title="Example user identity" {33,52}
112+
{
113+
"id": "P51Tuu01fWHMBjIBvrCK1lK-eUDWs2aQMv03WDqT5oY",
114+
"name": "John Doe",
115+
"email": "[email protected]",
116+
"amr": [
117+
"pwd"
118+
],
119+
"oidc_fields": {
120+
"principalName": "XXXXXX_cloudflare.com#EXT#@XXXXXXcloudflare.onmicrosoft.com"
121+
},
122+
"groups": [
123+
{
124+
"id": "fdaedb59-e9be-4ab7-8001-3e069da54185",
125+
"name": "XXXXX"
126+
}
127+
],
128+
"idp": {
129+
"id": "b9f4d68e-dac1-48b0-b728-ae05a5f0d4b2",
130+
"type": "azureAD"
131+
},
132+
"geo": {
133+
"country": "FR"
134+
},
135+
"user_uuid": "ce40d564-c72f-475f-a9b8-f395f19ad986",
136+
"account_id": "121287a0c6e6260ec930655e6b39a3a8",
137+
"iat": 1724056537,
138+
"devicePosture": {
139+
"f6f9391e-6776-4878-9c60-0cc807dc7dc8": {
140+
"id": "f6f9391e-6776-4878-9c60-0cc807dc7dc8",
141+
"schedule": "5m",
142+
"timestamp": "2024-08-19T08:31:59.274Z",
143+
"description": "",
144+
"type": "disk_encryption",
145+
"check": {
146+
"drives": {
147+
"C": {
148+
"encrypted": true
149+
}
150+
}
151+
},
152+
"success": false,
153+
"rule_name": "Disk Encryption - Windows",
154+
"input": {
155+
"requireAll": true,
156+
"checkDisks": []
157+
},
158+
"a0a8e83d-be75-4aa6-bfa0-5791da6e9186": {
159+
"id": "a0a8e83d-be75-4aa6-bfa0-5791da6e9186",
160+
"schedule": "5m",
161+
"timestamp": "2024-08-19T08:31:59.274Z",
162+
"description": "",
163+
"type": "firewall",
164+
"check": {
165+
"firewall": false
166+
},
167+
"success": false,
168+
"rule_name": "Local Firewall Check - Windows",
169+
"input": {
170+
"enabled": true
171+
}
172+
}
173+
...
174+
}
175+
```
176+
177+
## 3. Route the Worker to your application
178+
179+
In `wrangler.toml`, [set up a route](/workers/configuration/routing/routes/) that maps the Worker to your Access application domain:
180+
181+
```toml
182+
route = { pattern= "app.example.com/*", zone_name="example.com"}
183+
```
184+
185+
## 4. Deploy the Worker
186+
187+
```sh
188+
npx wrangler deploy
189+
```
190+
191+
The Worker will now insert the **Cf-Access-Firewall-Activated** and **Cf-Access-Disk-Encrypted** headers into requests that pass your application's Access policies.
192+
193+
```json title="Example request headers" {7,8}
194+
{
195+
"headers": {
196+
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
197+
"Accept-Encoding": "gzip",
198+
"Accept-Language": "en-US,en;q=0.9,fr-FR;q=0.8,fr;q=0.7,en-GB;q=0.6",
199+
"Cf-Access-Authenticated-User-Email": "[email protected]",
200+
"Cf-Access-Disk-Encrypted": "false",
201+
"Cf-Access-Firewall-Activated": "false",
202+
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36"
203+
}
204+
}
205+
```
206+
207+
You can verify that these headers are received by the origin server.

0 commit comments

Comments
 (0)