Skip to content

Commit f29fb21

Browse files
committed
docs: rewrite README as a user-facing overview
The README opened by describing itself as a proof of concept and led with the WASM rationale, so a reader had to infer what the service actually does. Lead with that instead: Traefik cannot validate Cloudflare Access cookies itself, this answers 200 or 403 for it. Adds a plain-language request walkthrough, the background refresh intervals (24h keys, 1h app list) so operators know it is self-updating, a configuration table clarifying that CF_ORG is the account ID, and a compose example reduced to the variables that actually do something. Also documents a property that was previously undocumented: every AUD in the account is accepted for every protected route, because get_aud() returns the whole list and passes it to set_audience(). Two apps behind one instance can therefore accept each other's tokens, which matters when choosing what to put it in front of.
1 parent 1d9bf51 commit f29fb21

1 file changed

Lines changed: 73 additions & 25 deletions

File tree

README.md

Lines changed: 73 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,48 +1,96 @@
1-
This document serves as a guide for a Proof of Concept (POC) aimed at validating Cloudflare Zero Trust forwards using Traefik's forward-auth service. The rust service is compatible with both Traefik 2.x and Traefik 3.x versions, originally i setout ot build a traefik only plugin but the limitations of the current WASM implementation follows R1 spec and as such is limited.
1+
# Traefik Forward Auth for Cloudflare Access
22

3-
This is a port of: https://github.com/nihaopaul/Traefik-ForwardAuth-Cloudflare-Access
3+
A small service that lets **Traefik** enforce **Cloudflare Zero Trust** access rules on any route it serves.
44

5-
The primary goal is to demonstrate how to authenticate Traefik requests through Cloudflare Zero Trust, ensuring secure access control.
5+
Cloudflare Access puts a login screen in front of your apps and hands the browser a signed `CF_Authorization` cookie. Traefik can't check that cookie by itself. This service does: Traefik asks it about every request, and it answers `200` (allow) or `403` (deny). Works with Traefik 2.x and 3.x.
66

7-
Additionally most configurations have moved to environmental variables which allow you to dockerise it, and since AUDs will come and go along with certificates the certficates refresh every 24hours while the auds refresh every hour using cloudflare APIS.
7+
> Originally attempted as a native Traefik plugin, but Traefik's WASM support follows the R1 spec and is too limited. This is a Rust port of [Traefik-ForwardAuth-Cloudflare-Access](https://github.com/nihaopaul/Traefik-ForwardAuth-Cloudflare-Access).
88
9-
#### setup a read only API token with permission: Account > Access: apps and policies > Read
9+
## How it works
1010

11-
```
12-
CF_TOKEN=_this is your cf token_
13-
```
11+
1. A user hits your app. Traefik pauses the request and asks this service `GET /auth`, forwarding the cookies.
12+
2. The service reads the `CF_Authorization` cookie. No cookie → `403`, and Cloudflare shows the login page.
13+
3. It verifies the cookie's JWT signature against your team's public keys, and checks the token was issued for one of *your* applications (its `aud`).
14+
4. Valid → `200` and Traefik serves the request. Anything else → `403`.
1415

15-
#### take from the cloudflare dashboard url https://dash.cloudflare.com/{your ORG ID}
16+
It keeps itself current in the background, so you don't restart it when things change in Cloudflare:
1617

17-
```
18-
CF_ORG=_this is your ID for cloudflare_
19-
```
18+
- **Public keys** refresh every 24 hours (Cloudflare rotates them).
19+
- **Application list** refreshes every hour (so new apps start working on their own).
2020

21-
#### what you have configured in cloudflare zero trust: team domain
21+
Nothing is stored on disk and no state is kept between requests.
2222

23-
```
24-
CF_DOMAIN=https://{yourdomain}.cloudflareaccess.com
25-
traefik config such as auth.yml
23+
## Quick start
24+
25+
```yaml
26+
services:
27+
forward-auth-rust:
28+
image: nihaopaul/forward-auth-rust:0.4.0
29+
restart: unless-stopped
30+
environment:
31+
CF_DOMAIN: https://yourteam.cloudflareaccess.com
32+
CF_ORG: your-cloudflare-account-id
33+
CF_TOKEN: your-read-only-api-token
34+
PORT: '9001'
35+
ports:
36+
- '9001:9001'
37+
deploy:
38+
resources:
39+
limits:
40+
cpus: '1'
41+
memory: 50M
2642
```
2743
28-
```yml
44+
Then tell Traefik to use it as a middleware:
45+
46+
```yaml
2947
http:
3048
middlewares:
31-
test-auth:
49+
cf-auth:
3250
forwardAuth:
33-
address: "http://IP:PORT/auth"
51+
address: "http://forward-auth-rust:9001/auth"
3452
```
3553
36-
then under the domain specify the provider, you probably dont want to do this on a writable dashboard or API.
54+
And apply it to a route:
3755
38-
```yml
56+
```yaml
3957
http:
4058
routers:
4159
dashboard:
42-
rule: Host(`{your domain}`)
60+
rule: Host(`traefik.example.com`)
4361
service: api@internal
44-
middlewares:
45-
- test-auth
4662
entryPoints:
47-
- "websecure"
63+
- websecure
64+
middlewares:
65+
- cf-auth
4866
```
67+
68+
## Configuration
69+
70+
| Variable | Required | Description |
71+
| --- | --- | --- |
72+
| `CF_DOMAIN` | yes | Your Zero Trust team domain, including `https://` — the hostname where Cloudflare shows your login page. |
73+
| `CF_ORG` | yes | Your Cloudflare **account ID** — the value in your dashboard URL, `https://dash.cloudflare.com/{account-id}`. |
74+
| `CF_TOKEN` | yes | API token with **Account → Access: Apps and Policies → Read**. Read-only is enough. |
75+
| `PORT` | no | Port to listen on. Defaults to `3000`. |
76+
77+
The service listens on `0.0.0.0` and exposes a single endpoint, `GET /auth`.
78+
79+
## Notes
80+
81+
**Protect the right things.** This gates on "is this a valid login for one of my Cloudflare apps" — it does not evaluate per-application policies. Two apps behind the same instance can accept each other's tokens, so think carefully before putting it in front of a writable dashboard or API.
82+
83+
**Only `linux/amd64` images are published.** On arm64 hosts you'll need emulation, or build the image yourself.
84+
85+
**Pin a version.** The example uses `:0.4.0` rather than `:latest` so an unattended `docker compose pull` can't change what you're running.
86+
87+
## Building from source
88+
89+
Requires [devbox](https://www.jetify.com/devbox) (which supplies the Rust toolchain):
90+
91+
```bash
92+
devbox run test # run the test suite
93+
devbox run build # release build
94+
```
95+
96+
The container image is a statically linked musl binary on `distroless/static`, about 12 MB uncompressed, with no shell and no libc in the runtime layer.

0 commit comments

Comments
 (0)