Security & Access¶
Cloudflare Access model¶
Three Access applications gate the fleet (terraform/modules/access/main.tf):
| Application | Domain | Who | Notes |
|---|---|---|---|
| Dashboard | *.tagai.uk |
Admins + Users | Google IdP, email OTP, or admin group |
| Node-RED editor | *-nodered.tagai.uk |
Admins only | GitHub org team, Google, admin group |
| SSH | *-ssh.tagai.uk |
Admins only | GitHub org team, Google, admin group |
Admins authenticate via email or GitHub org team membership
(var.github_org_team); regular users authenticate via email only. See
reference/email-otp-authentication.md
and reference/access-control-reference.md
for the full policy/IdP detail.
There is no VPN and no exposed port on any device — every path in (SSH, Node-RED, dashboard) goes through a Cloudflare Tunnel, and every hostname is gated by an Access application before it reaches the tunnel.
SSH access¶
One-time setup on your machine:
brew install cloudflare/cloudflare/cloudflared
cat >> ~/.ssh/config <<EOF
Host *.tagai.uk
ProxyCommand cloudflared access ssh --hostname %h
EOF
Then:
ssh tagai@rpi-prod-<serial>-ssh.tagai.uk
Ansible itself defaults to connecting over the device's LAN IP, not the
tunnel — pass -e use_tunnel=true (or TUNNEL=true to the relevant task
ansible:* command) to route through Cloudflare Access instead, for
off-network management.
MQTT mTLS¶
Devices authenticate to EMQX with per-device client certificates, not passwords — see mqtt-emqx.md.
Secrets handling¶
- Ansible Vault encrypts
ansible/inventory/production/group_vars/vault.yml(password atansible/.vault-password, never committed) - Tunnel credentials, MQTT certs, and SSH deploy keys are all git-ignored —
see
.gitignorefor the exact patterns docs/All connections.yaml(an MQTTX export that previously contained a plaintext MQTT password) has been removed from the repo; if you still have that password in use anywhere, rotate it in EMQX
Known caveat: Cloudflare Pages fallback domain¶
The team wiki (this site) is built in GitHub Actions and deployed to
Cloudflare Pages by direct upload (not GitHub Pages — this repo is private
and our GitHub org plan doesn't include private-repo GitHub Pages).
docs.tagai.uk is a Cloudflare Pages custom domain, gated by the docs
Access application same as everything else.
Every Cloudflare Pages project also gets a default <project>.pages.dev
fallback domain, which is not confirmed to be gated by Cloudflare
Access — Cloudflare's own "protect preview deployments" toggle is
dashboard-only (not exposed via Terraform) and by Cloudflare's own
documentation only covers per-deployment preview URLs, not necessarily the
project's general .pages.dev domain. Until that's explicitly verified
(check Settings → Access on the gaisro-technika-docs Pages project in
the Cloudflare dashboard), treat gaisro-technika-docs.pages.dev as
potentially reachable without Cloudflare Access. Mitigations in place
regardless: the site is marked noindex, and every device link the wiki
publishes (*-nodered.tagai.uk, *-ssh.tagai.uk) is itself independently
gated by its own Access application, so even an unauthenticated hit on the
fallback domain only exposes the wiki's own low-sensitivity content, not
device access.