Files
stack/hw/TUNNEL-SETUP.md
kert 59eb56f659
Some checks failed
CI / skinny-install (aco) (push) Successful in 1m18s
CI / skinny-install (api) (push) Successful in 40s
CI / skinny-install (bcda) (push) Successful in 35s
CI / skinny-install (bib) (push) Successful in 38s
CI / skinny-install (cli) (push) Successful in 46s
CI / skinny-install (conf) (push) Successful in 36s
CI / skinny-install (opps) (push) Successful in 38s
CI / skinny-install (pfs) (push) Successful in 47s
CI / skinny-install (rex) (push) Successful in 35s
Infra CI / notebooks (push) Successful in 3m17s
CI / lint-test (push) Failing after 3m30s
CI / skinny-install (bls) (push) Successful in 34s
CI / skinny-install (ccw) (push) Successful in 45s
CI / skinny-install (cms) (push) Successful in 32s
CI / skinny-install (perf) (push) Successful in 43s
Deploy / build-scan-report (push) Has been cancelled
Infra CI / docs (push) Failing after 20s
Infra CI / api (push) Successful in 16s
Infra CI / mc (push) Successful in 12s
Package Supply Chain / pkg-supply-chain (push) Successful in 1m27s
Infra CI / zotero (push) Successful in 6m10s
chore: hw provisioning, test coverage, deps
2026-04-09 22:26:31 -04:00

252 lines
7.2 KiB
Markdown

# Cloudflare Tunnel + Certs Setup for rig.fhirworx.io
## Architecture
```
Internet Your Server (rig)
┌──────────────────────────────┐
Users ──→ Cloudflare Edge │ │
(TLS termination) │ cloudflared ←──outbound──→ CF Edge
*.rig.fhirworx.io │ │ │
│ ├─→ rustfs:9000 (S3 API)│
│ └─→ rustfs:9001 (Console)│
│ │
Cloudflare WARP │ warp-svc (DNS/Zero Trust) │
└──────────────────────────────┘
```
All connections are OUTBOUND from your server. No inbound ports needed
except SSH (22) for YubiKey management.
## Step 1: Cloudflare Dashboard — DNS
1. Log into https://dash.cloudflare.com
2. Select **fhirworx.io** zone
3. Go to **DNS → Records**
4. You do NOT need to add A/AAAA records manually — the tunnel creates
CNAME records automatically. But verify the zone exists and is active.
## Step 2: Create the Tunnel
1. Go to https://one.dash.cloudflare.com (Zero Trust dashboard)
2. **Networks → Tunnels → Create a tunnel**
3. Tunnel name: `rig`
4. Choose **Cloudflared** connector
5. You'll get a tunnel token — it looks like:
```
eyJhIjoiNGY4...very-long-base64-string
```
6. **Copy this token** — you'll need it in Step 4
## Step 3: Configure Tunnel Routes (Public Hostnames)
Still in the tunnel config, add these public hostnames:
| Public Hostname | Service | Notes |
|----------------|---------|-------|
| `s3.rig.fhirworx.io` | `http://rustfs:9000` | S3 API endpoint |
| `console.rig.fhirworx.io` | `http://rustfs:9001` | Web console |
| `rig.fhirworx.io` | `http://rustfs:9000` | Default/root domain → S3 |
For each route:
- **Type**: HTTP (not HTTPS — cloudflared handles the tunnel encryption,
the local connection to the container is plaintext over Docker network)
- **TLS → Origin Server Name**: leave blank
- **No TLS Verify**: Yes (local traffic, no cert needed)
### Optional: Add SSH access through tunnel
| Public Hostname | Service | Notes |
|----------------|---------|-------|
| `ssh.rig.fhirworx.io` | `ssh://localhost:22` | Browser SSH or cloudflared access |
For SSH through tunnel, on the **Access** tab:
- Create an Access Application for `ssh.rig.fhirworx.io`
- Add an Access Policy (e.g., email allowlist, one-time PIN)
- This gives you browser-based SSH as a backup to direct SSH
## Step 4: Install the Token on Your Server
After `yubikey-unlock`, edit the env file:
```sh
rw
nano /opt/rustfs/.env
```
Replace `PASTE_YOUR_TOKEN_HERE` with the actual token from Step 2:
```
TUNNEL_TOKEN=eyJhIjoiNGY4...
```
Save and:
```sh
ro
cd /opt/rustfs && docker compose up -d
```
Verify the tunnel connects:
```sh
docker logs cloudflared
# Should show: "Connection registered" and "Tunnel is connected"
```
## Step 5: SSL/TLS Mode
1. In Cloudflare dashboard → **fhirworx.io** → **SSL/TLS → Overview**
2. Set mode to: **Full**
- NOT "Full (Strict)" — unless you set up an origin cert (see below)
- NOT "Flexible" — that's insecure
- With tunnels, "Full" is fine because the tunnel itself is encrypted
If you want "Full (Strict)" (belt AND suspenders), do Step 6.
## Step 6: Origin Certificate (Optional)
Only needed if:
- You want Full (Strict) SSL mode, OR
- You want direct HTTPS access on your LAN to rig.fhirworx.io
### Option A: Cloudflare Origin CA (simplest, 15-year validity)
1. Dashboard → **fhirworx.io** → **SSL/TLS → Origin Server**
2. **Create Certificate**
3. Settings:
- Key type: **ECDSA**
- Hostnames: `rig.fhirworx.io`, `*.rig.fhirworx.io`
- Validity: **15 years**
4. Copy the PEM certificate and private key
On the server:
```sh
rw
# Paste the certificate
nano /opt/certs/origin.pem
# Paste the private key
nano /opt/certs/origin-key.pem
chmod 644 /opt/certs/origin.pem
chmod 600 /opt/certs/origin-key.pem
ro
```
These certs are ONLY trusted by Cloudflare's edge — browsers connecting
directly will see a cert warning. That's expected and correct for origin certs.
### Option B: Let's Encrypt (trusted everywhere, auto-renews)
Use this if you also want trusted HTTPS directly on your LAN.
1. Create a Cloudflare API token:
- https://dash.cloudflare.com/profile/api-tokens
- **Create Token → Edit zone DNS** template
- Zone: `fhirworx.io`
- Copy the token
2. On the server:
```sh
rw
# Save the API token
cat > /opt/certs/cf-credentials.ini << EOF
dns_cloudflare_api_token = YOUR_TOKEN_HERE
EOF
chmod 600 /opt/certs/cf-credentials.ini
# Request the cert
apk add certbot-dns-cloudflare
certbot certonly \
--dns-cloudflare \
--dns-cloudflare-credentials /opt/certs/cf-credentials.ini \
-d "rig.fhirworx.io" \
-d "*.rig.fhirworx.io" \
--preferred-challenges dns-01 \
--non-interactive \
--agree-tos \
-m you@fhirworx.io
ro
```
Certs land at:
- `/etc/letsencrypt/live/rig.fhirworx.io/fullchain.pem`
- `/etc/letsencrypt/live/rig.fhirworx.io/privkey.pem`
Auto-renewal cron (add after setup):
```sh
rw
echo "0 3 * * * root mount -o remount,rw / && certbot renew --quiet && mount -o remount,ro /" >> /etc/crontabs/root
ro
```
## Step 7: Cloudflare WARP (Zero Trust DNS/VPN)
The WARP container gives you:
- DNS-over-HTTPS for all container traffic
- Option to route through Cloudflare's network
- Zero Trust device posture (if configured)
First run enrollment:
```sh
docker exec -it warp-svc warp-cli registration new
docker exec -it warp-svc warp-cli connect
```
To use WARP as the default DNS for the host:
```sh
rw
echo "nameserver 127.0.0.1" > /etc/resolv.conf
ro
```
## Step 8: Verify Everything
```sh
# Tunnel status
docker logs cloudflared
# Test S3 endpoint externally
curl -I https://s3.rig.fhirworx.io
# Should return 403 (no auth) or 200 with health check
# Test console
curl -I https://console.rig.fhirworx.io
# Should return 200 or 302 redirect to login
# Test from server locally
curl http://127.0.0.1:9000/minio/health/live
curl http://127.0.0.1:9001
# WARP status
docker exec warp-svc warp-cli status
```
## DNS Records (auto-created by tunnel)
After the tunnel connects, Cloudflare automatically creates:
```
s3.rig.fhirworx.io CNAME <tunnel-id>.cfargotunnel.com
console.rig.fhirworx.io CNAME <tunnel-id>.cfargotunnel.com
rig.fhirworx.io CNAME <tunnel-id>.cfargotunnel.com
```
You don't need to create these manually.
## Summary: What Needs a Cert and What Doesn't
| Connection | Encrypted By | Cert Needed? |
|-----------|-------------|-------------|
| User → Cloudflare Edge | Cloudflare Universal SSL | No (automatic) |
| Cloudflare Edge → cloudflared | Tunnel encryption (built-in) | No |
| cloudflared → RustFS container | Docker internal network (localhost) | No |
| Direct LAN → RustFS | Nothing (HTTP) or your own cert (HTTPS) | Only if you want LAN HTTPS |
**For your setup: you need ZERO certificates.** The tunnel handles everything.
Origin certs are a nice-to-have for defense in depth or direct LAN access.