feat: add Cloudflare tunnel hosting
This commit is contained in:
parent
e6c1b82679
commit
23e087ae4b
15 changed files with 839 additions and 30 deletions
113
docs/hosting.md
Normal file
113
docs/hosting.md
Normal file
|
|
@ -0,0 +1,113 @@
|
|||
# Hosting
|
||||
|
||||
Noisebell is hosted with DigitalOcean as the public service and observability hub, the Raspberry Pi on Tailscale, and Cloudflare Tunnel as the only intended public HTTP entry point.
|
||||
|
||||
## Public Routes
|
||||
|
||||
| Hostname | Target | Access |
|
||||
|---|---|---|
|
||||
| `noisebell.extremist.software` | cache public routes | public via Cloudflare Tunnel |
|
||||
| `rss-noisebell.extremist.software` | RSS service | public via Cloudflare Tunnel |
|
||||
| `grafana-noisebell.extremist.software` | Grafana | public via Cloudflare Tunnel, Grafana login required |
|
||||
|
||||
`discord.noisebell.extremist.software` is not routed through the tunnel. The Discord bot stays local to the DigitalOcean host and receives cache webhooks at `127.0.0.1`.
|
||||
|
||||
## Private Routes
|
||||
|
||||
| Route | Purpose |
|
||||
|---|---|
|
||||
| `jet@noisebell-do` | DO administration over Tailscale SSH |
|
||||
| `pi@noisebell-pi` or `pi@100.66.45.36` | Pi administration over Tailscale SSH |
|
||||
| `noisebell-do:3000/webhook` | Pi state-change webhook to cache |
|
||||
| `noisebell-pi:80` | DO cache polling and Pi app metrics |
|
||||
| `noisebell-pi:8090` | cache to Pi Home Assistant relay |
|
||||
| `noisebell-pi:9100` | Pi node exporter metrics |
|
||||
| `noisebell-do:3100` | Pi journal shipping to Loki |
|
||||
|
||||
## DigitalOcean Firewall
|
||||
|
||||
NixOS firewall policy is in `hosts/noisebell-do/configuration.nix`.
|
||||
|
||||
SSH is Tailscale-only because no public TCP SSH port is opened and `tailscale0` is trusted. Direct public HTTP and HTTPS stay open while `services.noisebell-public-gateway.enable = false` so an accidental pre-tunnel deploy does not break the existing site. Once the Cloudflare Tunnel module is enabled, public TCP ports close and all public web access enters through the tunnel.
|
||||
|
||||
After the tunnel is verified, mirror that policy in the DigitalOcean cloud firewall: close public inbound `22`, `80`, and `443`; keep outbound open; keep Tailscale UDP reachable as needed.
|
||||
|
||||
## Cloudflare Tunnel
|
||||
|
||||
The tunnel module is `hosts/noisebell-do/public-gateway.nix`. It defines ingress for the three public hostnames and returns `404` for everything else.
|
||||
|
||||
The module reconciles Cloudflare through the API when enabled:
|
||||
|
||||
- creates a locally-managed tunnel named `noisebell-do` if it does not exist
|
||||
- writes the tunnel credentials JSON to `/var/lib/noisebell-public-gateway/credentials.json`
|
||||
- upserts proxied CNAME records for the public hostnames
|
||||
- starts `cloudflared` with local Nix-managed ingress rules
|
||||
|
||||
Create two age secrets before enabling the module:
|
||||
|
||||
```sh
|
||||
cd secrets
|
||||
agenix -e cloudflare-api-token.age
|
||||
nix shell nixpkgs#openssl -c openssl rand -base64 32
|
||||
agenix -e cloudflare-tunnel-secret.age
|
||||
```
|
||||
|
||||
Paste the generated base64 value into `cloudflare-tunnel-secret.age`.
|
||||
|
||||
Set `services.noisebell-public-gateway.accountId` and `services.noisebell-public-gateway.zoneId` in `hosts/noisebell-do/configuration.nix`, then flip `services.noisebell-public-gateway.enable = true`.
|
||||
|
||||
Required Cloudflare API token scopes:
|
||||
|
||||
| Resource | Scope |
|
||||
|---|---|
|
||||
| Account | `Cloudflare Tunnel:Edit` or `Cloudflare One Connector: cloudflared:Edit` |
|
||||
| Zone | `DNS:Edit` for `extremist.software` |
|
||||
|
||||
The token should be restricted to the Noisebell Cloudflare account and the `extremist.software` zone.
|
||||
|
||||
## Grafana
|
||||
|
||||
Grafana listens on `127.0.0.1:3030` on the DO host. Public access is through `https://grafana-noisebell.extremist.software/` and requires the Grafana login form.
|
||||
|
||||
The admin user is `admin`. The password is generated on the DO host at first start and stored in `/var/lib/grafana/admin_password`.
|
||||
|
||||
```sh
|
||||
ssh jet@noisebell-do sudo cat /var/lib/grafana/admin_password
|
||||
```
|
||||
|
||||
Two dashboards are provisioned from `hosts/noisebell-do/observability.nix`:
|
||||
|
||||
| Dashboard | Audience | Datasources |
|
||||
|---|---|---|
|
||||
| `Noisebell Full Debug` | authenticated operators | Prometheus and Loki |
|
||||
| `Noisebell Public` | anyone with the shared link | Prometheus only |
|
||||
|
||||
Use Grafana externally shared dashboards for the public-safe view. `noisebell-grafana-public-dashboard.service` creates or refreshes the public share on boot/deploy with a deterministic token.
|
||||
|
||||
The public-safe URL is:
|
||||
|
||||
```text
|
||||
https://grafana-noisebell.extremist.software/public-dashboards/6e6f69736562656c6c7075626c696330
|
||||
```
|
||||
|
||||
The RSS and Grafana hostnames use one label under `extremist.software` so they are covered by Cloudflare Universal SSL. Nested names like `grafana.noisebell.extremist.software` require additional Cloudflare certificate setup.
|
||||
|
||||
The helper script `scripts/share-grafana-public-dashboard jet@noisebell-do` can still be used to repair or print that URL manually. The public dashboard intentionally avoids Loki/raw journal panels and uses stored Prometheus queries only.
|
||||
|
||||
## Pi Hardening
|
||||
|
||||
`scripts/deploy-pios-pi.sh` configures the Raspberry Pi OS host. It now uses `NOISEBELL_ENDPOINT_URL=http://noisebell-do:3000/webhook` by default, so state changes go to the cache over Tailscale instead of the public domain.
|
||||
|
||||
The deploy script applies a persistent firewall service, `noisebell-tailscale-only-firewall.service`, that drops non-Tailscale TCP traffic to `22`, `80`, `8090`, and `9100`. Existing SSH sessions survive because established connections are allowed. New SSH, app, relay, and node exporter access must use Tailscale.
|
||||
|
||||
Deploy the Pi over Tailscale after the first bootstrap:
|
||||
|
||||
```sh
|
||||
HOME_ASSISTANT_BASE_URL=http://10.21.0.43:8123 scripts/deploy-pios-pi.sh pi@100.66.45.36
|
||||
```
|
||||
|
||||
Override the cache webhook only if needed:
|
||||
|
||||
```sh
|
||||
NOISEBELL_CACHE_WEBHOOK_URL=http://noisebell-do:3000/webhook scripts/deploy-pios-pi.sh pi@100.66.45.36
|
||||
```
|
||||
Loading…
Add table
Add a link
Reference in a new issue