chore: initial commit — chat-saiden web chat baseline
This commit is contained in:
@@ -0,0 +1,149 @@
|
||||
# Cloudflare dashboard setup — chat.saiden.dev (token-based)
|
||||
|
||||
Two artefacts get created in the Zero Trust dashboard:
|
||||
1. **A tunnel** (`chat-saiden`) with its public hostname.
|
||||
2. **An Access application** with Google IdP + whitelist.
|
||||
|
||||
Both are token/UI-managed (no local config files for tunnel ingress) to match the
|
||||
existing `cloudflared-mesh` and `cloudflared-tensors-art` pattern on junkpile.
|
||||
|
||||
---
|
||||
|
||||
## Part A — Create the tunnel (5 minutes)
|
||||
|
||||
### 1. Open the Networks → Tunnels page
|
||||
|
||||
Zero Trust → **Networks** → **Tunnels** → **Create a tunnel**.
|
||||
|
||||
### 2. Pick connector type
|
||||
|
||||
Choose **Cloudflared**. Click Next.
|
||||
|
||||
### 3. Name + save
|
||||
|
||||
Tunnel name: `chat-saiden`. Click **Save tunnel**.
|
||||
|
||||
### 4. Get the token (DO NOT close this page)
|
||||
|
||||
The wizard shows install instructions for several platforms. The token is the
|
||||
long base64 string inside the displayed command, e.g.:
|
||||
|
||||
```
|
||||
cloudflared.exe service install eyJhIjoiOTVhZDNiYWEyYTRlY2RhMWUzODM0MmRm... # ← THIS PART
|
||||
```
|
||||
|
||||
**Copy just the token string** (everything after `service install` for the
|
||||
Windows command, or after `--token` for the Linux command — same string either
|
||||
way). Save it for the next step.
|
||||
|
||||
### 5. Place the token on junkpile
|
||||
|
||||
```bash
|
||||
ssh junkpile
|
||||
sudo mkdir -p /etc/cloudflared
|
||||
sudo tee /etc/cloudflared/chat-saiden.env > /dev/null <<'EOF'
|
||||
TUNNEL_TOKEN=PASTE_THE_LONG_TOKEN_HERE
|
||||
EOF
|
||||
sudo chown root:chi /etc/cloudflared/chat-saiden.env
|
||||
sudo chmod 0640 /etc/cloudflared/chat-saiden.env
|
||||
```
|
||||
|
||||
⚠ Verify the file looks right:
|
||||
```bash
|
||||
sudo ls -la /etc/cloudflared/chat-saiden.env # should be -rw-r----- root:chi
|
||||
```
|
||||
|
||||
### 6. Configure the public hostname
|
||||
|
||||
Back in the wizard → **Next** → **Public Hostname** tab → **Add a public hostname**:
|
||||
|
||||
| Field | Value |
|
||||
|-------|-------|
|
||||
| Subdomain | `chat` |
|
||||
| Domain | `saiden.dev` |
|
||||
| Path | (leave blank) |
|
||||
| Type | `HTTP` |
|
||||
| URL | `localhost:7681` |
|
||||
|
||||
**Additional application settings → TLS → No TLS Verify** can stay off (localhost).
|
||||
**Additional application settings → Connection → Disable Chunked Encoding** must stay OFF.
|
||||
|
||||
Click **Save hostname**.
|
||||
|
||||
CF will auto-create the `chat.saiden.dev` proxy CNAME for you.
|
||||
|
||||
### 7. Verify the tunnel page
|
||||
|
||||
The tunnel page should now show:
|
||||
- Connector: **Healthy** (or "No connectors yet" if you haven't started the service)
|
||||
- Public hostname: `chat.saiden.dev → http://localhost:7681`
|
||||
|
||||
---
|
||||
|
||||
## Part B — CF Access application (5 minutes)
|
||||
|
||||
### 1. Add the Access application
|
||||
|
||||
Zero Trust → **Access** → **Applications** → **Add an application** → **Self-hosted**.
|
||||
|
||||
| Field | Value |
|
||||
|-------|-------|
|
||||
| Application name | `chat-saiden` |
|
||||
| Session duration | `24 hours` |
|
||||
| Application domain | `chat.saiden.dev` |
|
||||
| Path | (leave blank) |
|
||||
| Identity providers | Google |
|
||||
| Instant Auth | enabled |
|
||||
|
||||
Save → continue to policies.
|
||||
|
||||
### 2. Add the whitelist policy (THE SECURITY BOUNDARY)
|
||||
|
||||
| Field | Value |
|
||||
|-------|-------|
|
||||
| Policy name | `pilot-whitelist` |
|
||||
| Action | **Allow** |
|
||||
| Include rule | **Emails** = `adam.ladachowski@gmail.com` |
|
||||
|
||||
Save policy.
|
||||
|
||||
### 3. WebSocket support
|
||||
|
||||
Application **Settings → Advanced → WebSocket support** → enable.
|
||||
|
||||
This is mandatory; ttyd is WS-based.
|
||||
|
||||
### 4. Default deny (implicit, but verify)
|
||||
|
||||
With only one Allow rule, anyone not matching is denied by default — no extra
|
||||
deny rule needed. To double-check, look at the policies list: it should show
|
||||
**one** policy (`pilot-whitelist`, Allow) and nothing else.
|
||||
|
||||
---
|
||||
|
||||
## Part C — Google IdP setup (one-time, skip if already done)
|
||||
|
||||
Zero Trust → **Settings → Authentication → Login methods → Add new → Google**.
|
||||
|
||||
OAuth client ID + secret come from
|
||||
https://console.cloud.google.com → APIs & Services → Credentials.
|
||||
|
||||
Authorized redirect URI:
|
||||
```
|
||||
https://<your-team>.cloudflareaccess.com/cdn-cgi/access/callback
|
||||
```
|
||||
|
||||
Replace `<your-team>` with the team domain shown at the top of the Zero Trust
|
||||
dashboard. Save in CF wizard → click **Test**. Must succeed before moving on.
|
||||
|
||||
---
|
||||
|
||||
## Operational hygiene
|
||||
|
||||
- The TUNNEL_TOKEN is a long-lived bearer credential. If junkpile is compromised
|
||||
or you suspect the token leaked: dashboard → tunnel → **Refresh token**.
|
||||
Update `/etc/cloudflared/chat-saiden.env` and `systemctl restart
|
||||
cloudflared-chat-saiden`.
|
||||
- Audit access logs weekly: Zero Trust → **Logs → Access**.
|
||||
- To revoke a whitelist entry: edit `pilot-whitelist` policy, save. Existing
|
||||
sessions are cut on next request (session lifetime ≤ 24h by config).
|
||||
Executable
+68
@@ -0,0 +1,68 @@
|
||||
#!/usr/bin/env bash
|
||||
# provision-tunnel.sh — run on FUJI (or wherever ~/.cloudflared/cert.pem lives).
|
||||
# Creates the chat-saiden tunnel, places the DNS CNAME in the correct zone,
|
||||
# and scp's the credentials JSON to junkpile.
|
||||
#
|
||||
# Idempotent: re-running just verifies + re-syncs credentials.
|
||||
#
|
||||
# Watch out: `cloudflared tunnel route dns` auto-zone-detection is unreliable
|
||||
# across this multi-zone account (saiden.dev + tengu.to share a cert). We
|
||||
# manage the CNAME via flarectl explicitly to dodge it.
|
||||
set -uo pipefail
|
||||
|
||||
TUNNEL_NAME="chat-saiden"
|
||||
HOSTNAME="chat.saiden.dev"
|
||||
ZONE="saiden.dev"
|
||||
JUNKPILE_DEST="/etc/cloudflared/chat-saiden/chat-saiden.json"
|
||||
|
||||
if [[ ! -f "$HOME/.cloudflared/cert.pem" ]]; then
|
||||
echo "ERROR: ~/.cloudflared/cert.pem missing — run 'cloudflared tunnel login' first"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# --- 1. Create tunnel (idempotent) ---
|
||||
echo "[1/3] Tunnel"
|
||||
if cloudflared tunnel list 2>/dev/null | awk '{print $2}' | grep -qx "$TUNNEL_NAME"; then
|
||||
UUID=$(cloudflared tunnel list 2>/dev/null | awk -v n="$TUNNEL_NAME" '$2==n {print $1}')
|
||||
echo " already exists, UUID=$UUID"
|
||||
else
|
||||
cloudflared tunnel create "$TUNNEL_NAME"
|
||||
UUID=$(cloudflared tunnel list 2>/dev/null | awk -v n="$TUNNEL_NAME" '$2==n {print $1}')
|
||||
echo " created, UUID=$UUID"
|
||||
fi
|
||||
CRED_FILE="$HOME/.cloudflared/${UUID}.json"
|
||||
if [[ ! -f "$CRED_FILE" ]]; then
|
||||
echo "ERROR: credentials missing at $CRED_FILE"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# --- 2. DNS CNAME via flarectl ---
|
||||
echo "[2/3] DNS"
|
||||
EXPECTED_TARGET="${UUID}.cfargotunnel.com"
|
||||
EXISTING=$(flarectl dns list --zone "$ZONE" 2>/dev/null | awk -v fqdn="${HOSTNAME}" '$0 ~ fqdn && $3=="CNAME"')
|
||||
if [[ -n "$EXISTING" ]]; then
|
||||
EX_CONTENT=$(echo "$EXISTING" | awk -F'|' '{gsub(/^ +| +$/,"",$5); print $5}')
|
||||
if [[ "$EX_CONTENT" == "$EXPECTED_TARGET" ]]; then
|
||||
echo " CNAME already correct: $HOSTNAME → $EXPECTED_TARGET"
|
||||
else
|
||||
echo " ERROR: CNAME exists for $HOSTNAME but points elsewhere: $EX_CONTENT"
|
||||
echo " expected: $EXPECTED_TARGET — fix manually"
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
flarectl dns create --zone "$ZONE" --type CNAME --name chat --content "$EXPECTED_TARGET" --proxy
|
||||
echo " created: $HOSTNAME → $EXPECTED_TARGET (proxied)"
|
||||
fi
|
||||
|
||||
# --- 3. Copy credentials to junkpile ---
|
||||
echo "[3/3] Credentials → junkpile"
|
||||
scp -q "$CRED_FILE" junkpile:/tmp/chat-saiden.json
|
||||
ssh junkpile "sudo mkdir -p /etc/cloudflared/chat-saiden && sudo mv /tmp/chat-saiden.json $JUNKPILE_DEST && sudo chown root:chi $JUNKPILE_DEST && sudo chmod 0640 $JUNKPILE_DEST"
|
||||
echo " → junkpile:$JUNKPILE_DEST (0640 root:chi)"
|
||||
|
||||
echo
|
||||
echo "Tunnel UUID: $UUID"
|
||||
echo "Hostname: $HOSTNAME"
|
||||
echo "CNAME target: $EXPECTED_TARGET"
|
||||
echo
|
||||
echo "Next: ssh junkpile 'bash ~/chat-saiden/install.sh'"
|
||||
Reference in New Issue
Block a user