API token
scripts/cloudflare.sh sets up Cloudflare through its API, so it needs an API token. install.sh runs that
script for you. The token is a setup-only credential: nothing that runs day to day uses it, and the scripts
never save it.
Create it#
Create a custom token at dash.cloudflare.com/profile/api-tokens with these permissions:
| Scope | Permission | Access | Used for |
|---|---|---|---|
| Account | Cloudflare Tunnel | Edit | the tunnel and its run token |
| Account | Access: Apps and Policies | Edit | the Access application and its policy |
| Account | Access: Organizations, Identity Providers, and Groups | Read | your team domain and the login method |
| Zone | DNS | Edit | the term.example.com record |
Leaving one out breaks that part of the setup.
Keep the token small:
- Account Resources: include only the account that owns the zone.
- Zone Resources: include only the zone you're using, so the token can't touch your other domains.
- Client IP Address Filtering, optional: only your server's public IP.
- TTL: a day or so. You need the token only while the script runs. Set the start date to today. A start date of tomorrow makes every call fail with "Invalid access token".
Caution
Anyone with this token can change your tunnels and Access policies, and so could open your terminal to themselves. Treat it like a password.
When you need it#
Short version: adding a person needs one, almost nothing else does. See Do I need a token every time? for the table.
Only when you change something on Cloudflare. Everything else runs on your server without a token.
Needs a token
- the first install, and any re-run of
./install.sh - adding a person with
cloudflare-tmux add - changing the hostname,
PORT,TUNNEL_NAMEorACCESS_SESSION - replacing a leaked tunnel token
- tidying the Access policy after removing a person
Never needs a token
- locking someone out:
cloudflare-tmux remove their@email - mapping an already-listed email to a different Linux user
- the two-step code: turning it on, enrolling or removing a phone
- updating the app or
cloudflared - switching to the multi-user install
- restarts, logs, status, uninstall
Each time you need one, create a fresh token, run the command, then delete the token again.
You can also make the same changes by hand in the Zero Trust dashboard. But the next cloudflare.sh run
overwrites the Access application and policy with what's in your config. See
Doing it by hand.
If the login method is missing#
The script uses Zero Trust's Cloudflare login method. If your account doesn't have it yet, the script has to create it, and that one call needs Edit instead of Read on Access: Organizations, Identity Providers, and Groups. The script stops and tells you if so. Either:
- add it once yourself under Zero Trust → Settings → Authentication → Login methods → Add new → Cloudflare, with restrict to account members on, then re-run; or
- give the token Edit for that first run. Read is enough afterwards.
Delete it afterwards#
Once the setup works, delete the token at dash.cloudflare.com/profile/api-tokens. Nothing breaks: the tunnel has its own run token, and the Access application, login method and DNS record live in Cloudflare.
Deleting is better than downgrading it to read-only. A read-only token is still a credential to protect, and the next change needs Edit anyway.
How the scripts take it#
install.sh and cloudflare.sh ask for the token when they need it. The input is hidden, the token stays in
memory for that run, and it's never written to disk. The scripts pass it to curl through a file descriptor,
never on a command line, so other users on the machine can't see it in ps.
For unattended runs, pass it as CF_API_TOKEN from your secret store:
CF_API_TOKEN=... ./install.sh --yes --host term.example.com --identities you@example.com:$USER
If the token is refused#
cloudflare.sh verifies the token before using it. If Cloudflare rejects it, the script says why when it can:
a start date in the future, an expired token, or a scope that doesn't cover the account or zone. See
Troubleshooting.
Check the token#
read -rsp 'Token: ' T; echo
curl -s -H @<(printf 'Authorization: Bearer %s\n' "$T") \
https://api.cloudflare.com/client/v4/user/tokens/verify; unset T
You should see "status": "active".