feat: replace Tailscale VPN gate with simple access code
Remove the vpn-setup/ directory (Headscale/Tailscale scripts, CoreDNS, onboarding) and add a lightweight access code gate instead. Visitors must enter a code to view the site — validated via API route with an httpOnly cookie persisted for 1 year. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
9ea2a0ff9f
commit
228f3c6658
|
|
@ -0,0 +1,22 @@
|
||||||
|
import { NextResponse } from "next/server"
|
||||||
|
|
||||||
|
const ACCESS_CODE = process.env.ACCESS_CODE || "42069"
|
||||||
|
|
||||||
|
export async function POST(request: Request) {
|
||||||
|
const { code } = await request.json()
|
||||||
|
|
||||||
|
if (code !== ACCESS_CODE) {
|
||||||
|
return NextResponse.json({ error: "Invalid code" }, { status: 401 })
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = NextResponse.json({ success: true })
|
||||||
|
response.cookies.set("jefflix-access", "granted", {
|
||||||
|
httpOnly: true,
|
||||||
|
secure: true,
|
||||||
|
sameSite: "lax",
|
||||||
|
maxAge: 60 * 60 * 24 * 365, // 1 year
|
||||||
|
path: "/",
|
||||||
|
})
|
||||||
|
|
||||||
|
return response
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,67 @@
|
||||||
|
"use client"
|
||||||
|
|
||||||
|
import { useState } from "react"
|
||||||
|
import { useRouter } from "next/navigation"
|
||||||
|
import { JefflixLogo } from "@/components/jefflix-logo"
|
||||||
|
|
||||||
|
export default function GatePage() {
|
||||||
|
const [code, setCode] = useState("")
|
||||||
|
const [error, setError] = useState("")
|
||||||
|
const [loading, setLoading] = useState(false)
|
||||||
|
const router = useRouter()
|
||||||
|
|
||||||
|
async function handleSubmit(e: React.FormEvent) {
|
||||||
|
e.preventDefault()
|
||||||
|
setError("")
|
||||||
|
setLoading(true)
|
||||||
|
|
||||||
|
const res = await fetch("/api/verify-code", {
|
||||||
|
method: "POST",
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
body: JSON.stringify({ code }),
|
||||||
|
})
|
||||||
|
|
||||||
|
if (res.ok) {
|
||||||
|
router.push("/")
|
||||||
|
router.refresh()
|
||||||
|
} else {
|
||||||
|
setError("Wrong code. Try again.")
|
||||||
|
setLoading(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="min-h-screen bg-background flex items-center justify-center">
|
||||||
|
<div className="absolute inset-0 bg-gradient-to-br from-yellow-50/80 via-background/90 to-green-50/80" />
|
||||||
|
<div className="relative w-full max-w-md mx-auto px-4">
|
||||||
|
<div className="text-center space-y-8">
|
||||||
|
<JefflixLogo />
|
||||||
|
<p className="text-lg text-muted-foreground">
|
||||||
|
Enter the access code to continue
|
||||||
|
</p>
|
||||||
|
<form onSubmit={handleSubmit} className="space-y-4">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
inputMode="numeric"
|
||||||
|
value={code}
|
||||||
|
onChange={(e) => setCode(e.target.value)}
|
||||||
|
placeholder="Access code"
|
||||||
|
autoFocus
|
||||||
|
className="w-full text-center text-2xl tracking-widest px-4 py-3 rounded-lg border-2 border-border bg-card focus:outline-none focus:ring-2 focus:ring-ring"
|
||||||
|
/>
|
||||||
|
{error && (
|
||||||
|
<p className="text-destructive font-medium">{error}</p>
|
||||||
|
)}
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
disabled={loading || !code}
|
||||||
|
className="w-full py-3 px-6 rounded-lg bg-primary text-primary-foreground font-bold text-lg hover:opacity-90 transition-opacity disabled:opacity-50"
|
||||||
|
>
|
||||||
|
{loading ? "Checking..." : "Enter"}
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
import { NextResponse } from "next/server"
|
||||||
|
import type { NextRequest } from "next/server"
|
||||||
|
|
||||||
|
export function middleware(request: NextRequest) {
|
||||||
|
const hasAccess = request.cookies.get("jefflix-access")?.value === "granted"
|
||||||
|
|
||||||
|
if (!hasAccess) {
|
||||||
|
return NextResponse.redirect(new URL("/gate", request.url))
|
||||||
|
}
|
||||||
|
|
||||||
|
return NextResponse.next()
|
||||||
|
}
|
||||||
|
|
||||||
|
export const config = {
|
||||||
|
matcher: [
|
||||||
|
/*
|
||||||
|
* Match all paths except:
|
||||||
|
* - /gate (the code entry page)
|
||||||
|
* - /api/verify-code (the verification endpoint)
|
||||||
|
* - /_next (Next.js internals)
|
||||||
|
* - /favicon.ico, /icon*, /apple-icon*, /og-image* (static assets)
|
||||||
|
*/
|
||||||
|
"/((?!gate|api/verify-code|_next|favicon\\.ico|icon|apple-icon|og-image).*)",
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
@ -1,37 +0,0 @@
|
||||||
# Jefflix VPN Setup — Headscale + Tailscale
|
|
||||||
|
|
||||||
Protects all `*.jefflix.lol` services behind the existing Headscale VPN at `vpn.jeffemmett.com`.
|
|
||||||
|
|
||||||
## How It Works
|
|
||||||
|
|
||||||
```
|
|
||||||
Before (public):
|
|
||||||
Browser → Cloudflare → Tunnel → Traefik → Jellyfin/etc
|
|
||||||
|
|
||||||
After (VPN-only):
|
|
||||||
Browser → Tailscale (WireGuard) → Traefik → Jellyfin/etc
|
|
||||||
(Only works if connected to the tailnet)
|
|
||||||
```
|
|
||||||
|
|
||||||
Traefik still routes by Host header — the only change is how traffic reaches it.
|
|
||||||
|
|
||||||
## Quick Start
|
|
||||||
|
|
||||||
SSH into the server and follow the phases in order:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
ssh netcup
|
|
||||||
```
|
|
||||||
|
|
||||||
Then run `setup.sh` (or follow the manual steps below).
|
|
||||||
|
|
||||||
## Files
|
|
||||||
|
|
||||||
| File | Purpose |
|
|
||||||
|------|---------|
|
|
||||||
| `setup.sh` | Full setup script (run on Netcup) |
|
|
||||||
| `coredns/Corefile` | CoreDNS config — resolves *.jefflix.lol to Tailscale IP |
|
|
||||||
| `coredns/docker-compose.yml` | CoreDNS container definition |
|
|
||||||
| `headscale-config-patch.yaml` | Split DNS addition for Headscale config |
|
|
||||||
| `cloudflared-config-clean.yml` | Cloudflare tunnel config with jefflix entries removed |
|
|
||||||
| `rollback.sh` | Emergency rollback script |
|
|
||||||
|
|
@ -1,99 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
set -euo pipefail
|
|
||||||
|
|
||||||
# ============================================================
|
|
||||||
# Jefflix VPN Cutover Script
|
|
||||||
# Removes public access to *.jefflix.lol
|
|
||||||
# Run ONLY after setup.sh and testing VPN access works
|
|
||||||
# ============================================================
|
|
||||||
|
|
||||||
echo "========================================"
|
|
||||||
echo " Jefflix Public Access Cutover"
|
|
||||||
echo "========================================"
|
|
||||||
echo ""
|
|
||||||
|
|
||||||
# Pre-flight check: verify VPN access works
|
|
||||||
TAILSCALE_IP=$(tailscale ip -4 2>/dev/null || echo "")
|
|
||||||
if [ -z "$TAILSCALE_IP" ]; then
|
|
||||||
echo "ERROR: Tailscale not running on this server. Run setup.sh first."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "Server Tailscale IP: $TAILSCALE_IP"
|
|
||||||
echo ""
|
|
||||||
|
|
||||||
# Check DNS works
|
|
||||||
DIG_RESULT=$(dig +short @${TAILSCALE_IP} movies.jefflix.lol 2>/dev/null || echo "FAILED")
|
|
||||||
if [ "$DIG_RESULT" != "$TAILSCALE_IP" ]; then
|
|
||||||
echo "ERROR: CoreDNS not resolving correctly. Got: $DIG_RESULT (expected $TAILSCALE_IP)"
|
|
||||||
echo "Fix CoreDNS before proceeding."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
echo "✓ CoreDNS resolving correctly"
|
|
||||||
|
|
||||||
echo ""
|
|
||||||
echo "This will REMOVE public access to all *.jefflix.lol services."
|
|
||||||
echo "Users will need Tailscale connected to vpn.jeffemmett.com to access Jefflix."
|
|
||||||
echo ""
|
|
||||||
read -p "Continue? [y/N] " -n 1 -r
|
|
||||||
echo ""
|
|
||||||
|
|
||||||
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
|
||||||
echo "Aborted."
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
# --- Remove jefflix entries from Cloudflare tunnel ---
|
|
||||||
echo ""
|
|
||||||
echo "[Cutover] Removing *.jefflix.lol from Cloudflare tunnel..."
|
|
||||||
|
|
||||||
TUNNEL_CONFIG="/root/cloudflared/config.yml"
|
|
||||||
|
|
||||||
# Backup current config (timestamped)
|
|
||||||
cp "$TUNNEL_CONFIG" "${TUNNEL_CONFIG}.pre-cutover-$(date +%Y%m%d-%H%M%S)"
|
|
||||||
|
|
||||||
# Remove all jefflix.lol hostname entries (hostname + service lines)
|
|
||||||
# This removes the "- hostname: *.jefflix.lol" and its " service:" line
|
|
||||||
python3 -c "
|
|
||||||
import yaml, sys
|
|
||||||
|
|
||||||
with open('$TUNNEL_CONFIG', 'r') as f:
|
|
||||||
config = yaml.safe_load(f)
|
|
||||||
|
|
||||||
original_count = len(config.get('ingress', []))
|
|
||||||
|
|
||||||
# Filter out jefflix.lol entries
|
|
||||||
config['ingress'] = [
|
|
||||||
entry for entry in config.get('ingress', [])
|
|
||||||
if not (isinstance(entry.get('hostname', ''), str) and 'jefflix.lol' in entry.get('hostname', ''))
|
|
||||||
]
|
|
||||||
|
|
||||||
removed = original_count - len(config['ingress'])
|
|
||||||
|
|
||||||
with open('$TUNNEL_CONFIG', 'w') as f:
|
|
||||||
yaml.dump(config, f, default_flow_style=False, sort_keys=False)
|
|
||||||
|
|
||||||
print(f' Removed {removed} jefflix.lol entries from tunnel config')
|
|
||||||
"
|
|
||||||
|
|
||||||
# Restart cloudflared
|
|
||||||
echo " Restarting cloudflared..."
|
|
||||||
docker restart cloudflared
|
|
||||||
echo " ✓ Cloudflared restarted"
|
|
||||||
|
|
||||||
# Wait and verify
|
|
||||||
sleep 5
|
|
||||||
echo ""
|
|
||||||
echo "========================================"
|
|
||||||
echo " Cutover Complete"
|
|
||||||
echo "========================================"
|
|
||||||
echo ""
|
|
||||||
echo "Public access to *.jefflix.lol is now REMOVED."
|
|
||||||
echo ""
|
|
||||||
echo "Verify from a non-VPN device:"
|
|
||||||
echo " curl -I https://movies.jefflix.lol (should fail/404)"
|
|
||||||
echo ""
|
|
||||||
echo "Verify from a VPN device:"
|
|
||||||
echo " curl http://movies.jefflix.lol (should work)"
|
|
||||||
echo ""
|
|
||||||
echo "To ROLLBACK: run rollback.sh"
|
|
||||||
|
|
@ -1,66 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
set -euo pipefail
|
|
||||||
|
|
||||||
# ============================================================
|
|
||||||
# Generate a Jefflix VPN invite for a new user
|
|
||||||
# Usage: ./onboard-user.sh [username]
|
|
||||||
# ============================================================
|
|
||||||
|
|
||||||
USERNAME="${1:-}"
|
|
||||||
|
|
||||||
if [ -z "$USERNAME" ]; then
|
|
||||||
echo "Usage: ./onboard-user.sh <username>"
|
|
||||||
echo " Creates a pre-auth key for the user and prints setup instructions."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Generate a single-use pre-auth key (7 day expiry)
|
|
||||||
echo "Generating pre-auth key for user: $USERNAME"
|
|
||||||
PREAUTH_KEY=$(docker exec headscale headscale preauthkeys create \
|
|
||||||
--user jefflix \
|
|
||||||
--reusable=false \
|
|
||||||
--expiration 168h 2>&1 | tail -1)
|
|
||||||
|
|
||||||
echo ""
|
|
||||||
echo "============================================================"
|
|
||||||
echo " Send the following to $USERNAME:"
|
|
||||||
echo "============================================================"
|
|
||||||
echo ""
|
|
||||||
cat <<MSG
|
|
||||||
Hey! Here's how to connect to Jefflix:
|
|
||||||
|
|
||||||
1. Install Tailscale on your device:
|
|
||||||
- Windows/Mac/Linux: https://tailscale.com/download
|
|
||||||
- iOS: Search "Tailscale" in the App Store
|
|
||||||
- Android: Search "Tailscale" in Google Play
|
|
||||||
|
|
||||||
2. Connect to the Jefflix VPN:
|
|
||||||
|
|
||||||
Desktop (open a terminal and run):
|
|
||||||
tailscale up --login-server=https://vpn.jeffemmett.com --authkey=$PREAUTH_KEY
|
|
||||||
|
|
||||||
iOS: Settings → tap account → "..." → Use custom coordination server
|
|
||||||
Server: https://vpn.jeffemmett.com
|
|
||||||
Then log in with this key: $PREAUTH_KEY
|
|
||||||
|
|
||||||
Android: Settings → tap account → "..." → Use alternate server
|
|
||||||
Server: https://vpn.jeffemmett.com
|
|
||||||
Then log in with this key: $PREAUTH_KEY
|
|
||||||
|
|
||||||
3. Once connected, open your browser and go to:
|
|
||||||
- http://movies.jefflix.lol (Watch movies & shows)
|
|
||||||
- http://requests.jefflix.lol (Request new content)
|
|
||||||
- http://upload.jefflix.lol (Upload your own content)
|
|
||||||
- http://music.jefflix.lol (Listen to music)
|
|
||||||
|
|
||||||
Tailscale runs in the background — you only need to set it up once!
|
|
||||||
|
|
||||||
NOTE: This invite key expires in 7 days. Let me know if you need a new one.
|
|
||||||
MSG
|
|
||||||
|
|
||||||
echo ""
|
|
||||||
echo "============================================================"
|
|
||||||
echo " Key: $PREAUTH_KEY"
|
|
||||||
echo " Expires: 7 days"
|
|
||||||
echo " User: jefflix"
|
|
||||||
echo "============================================================"
|
|
||||||
|
|
@ -1,40 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
set -euo pipefail
|
|
||||||
|
|
||||||
# ============================================================
|
|
||||||
# Jefflix VPN Rollback Script
|
|
||||||
# Restores public access to *.jefflix.lol
|
|
||||||
# ============================================================
|
|
||||||
|
|
||||||
echo "========================================"
|
|
||||||
echo " Jefflix VPN Rollback"
|
|
||||||
echo "========================================"
|
|
||||||
echo ""
|
|
||||||
|
|
||||||
# Restore Cloudflare tunnel config
|
|
||||||
if [ -f /root/cloudflared/config.yml.backup-jefflix-vpn ]; then
|
|
||||||
cp /root/cloudflared/config.yml.backup-jefflix-vpn /root/cloudflared/config.yml
|
|
||||||
docker restart cloudflared
|
|
||||||
echo "✓ Cloudflare tunnel config restored and restarted"
|
|
||||||
else
|
|
||||||
echo "⚠ No backup found at /root/cloudflared/config.yml.backup-jefflix-vpn"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Restore Headscale config
|
|
||||||
if [ -f /opt/apps/headscale-deploy/config/config.yaml.backup-jefflix-vpn ]; then
|
|
||||||
cp /opt/apps/headscale-deploy/config/config.yaml.backup-jefflix-vpn /opt/apps/headscale-deploy/config/config.yaml
|
|
||||||
cd /opt/apps/headscale-deploy && docker compose restart headscale
|
|
||||||
echo "✓ Headscale config restored and restarted"
|
|
||||||
else
|
|
||||||
echo "⚠ No Headscale backup found"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Stop CoreDNS (optional — it doesn't hurt to leave it running)
|
|
||||||
if docker ps --format '{{.Names}}' | grep -q jefflix-dns; then
|
|
||||||
cd /opt/apps/jefflix-dns && docker compose down
|
|
||||||
echo "✓ CoreDNS stopped"
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo ""
|
|
||||||
echo "Rollback complete. Public access to *.jefflix.lol should be restored."
|
|
||||||
echo "Verify: curl -I https://movies.jefflix.lol"
|
|
||||||
|
|
@ -1,151 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
set -euo pipefail
|
|
||||||
|
|
||||||
# ============================================================
|
|
||||||
# Jefflix VPN Setup Script
|
|
||||||
# Run this on the Netcup RS 8000 server (ssh netcup)
|
|
||||||
# ============================================================
|
|
||||||
|
|
||||||
echo "========================================"
|
|
||||||
echo " Jefflix VPN Setup (Headscale/Tailscale)"
|
|
||||||
echo "========================================"
|
|
||||||
echo ""
|
|
||||||
|
|
||||||
# --- Phase 1: Backups ---
|
|
||||||
echo "[Phase 1] Creating backups..."
|
|
||||||
cp /root/cloudflared/config.yml /root/cloudflared/config.yml.backup-jefflix-vpn
|
|
||||||
cp /opt/apps/headscale-deploy/config/config.yaml /opt/apps/headscale-deploy/config/config.yaml.backup-jefflix-vpn
|
|
||||||
cp -r /root/traefik/config/ /root/traefik/config-backup-jefflix-vpn/ 2>/dev/null || true
|
|
||||||
echo " ✓ Backups created"
|
|
||||||
|
|
||||||
# --- Phase 1b: Create Headscale users ---
|
|
||||||
echo ""
|
|
||||||
echo "[Phase 1b] Creating Headscale users..."
|
|
||||||
docker exec headscale headscale users create server 2>/dev/null || echo " (user 'server' already exists)"
|
|
||||||
docker exec headscale headscale users create jefflix 2>/dev/null || echo " (user 'jefflix' already exists)"
|
|
||||||
echo " ✓ Users ready"
|
|
||||||
|
|
||||||
# --- Phase 2: Install Tailscale ---
|
|
||||||
echo ""
|
|
||||||
echo "[Phase 2] Installing Tailscale..."
|
|
||||||
if command -v tailscale &>/dev/null; then
|
|
||||||
echo " Tailscale already installed: $(tailscale version)"
|
|
||||||
else
|
|
||||||
curl -fsSL https://tailscale.com/install.sh | sh
|
|
||||||
echo " ✓ Tailscale installed"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Generate pre-auth key and join tailnet
|
|
||||||
echo ""
|
|
||||||
echo " Generating pre-auth key..."
|
|
||||||
PREAUTH_KEY=$(docker exec headscale headscale preauthkeys create --user server --reusable=false --expiration 1h 2>&1 | tail -1)
|
|
||||||
echo " Pre-auth key: $PREAUTH_KEY"
|
|
||||||
|
|
||||||
echo ""
|
|
||||||
echo " Joining tailnet..."
|
|
||||||
tailscale up \
|
|
||||||
--login-server=https://vpn.jeffemmett.com \
|
|
||||||
--authkey="$PREAUTH_KEY" \
|
|
||||||
--hostname=netcup-rs8000 \
|
|
||||||
--accept-dns=false
|
|
||||||
|
|
||||||
# Get the Tailscale IP
|
|
||||||
TAILSCALE_IP=$(tailscale ip -4)
|
|
||||||
echo " ✓ Joined tailnet with IP: $TAILSCALE_IP"
|
|
||||||
|
|
||||||
# --- Phase 3: Deploy CoreDNS ---
|
|
||||||
echo ""
|
|
||||||
echo "[Phase 3] Deploying CoreDNS for *.jefflix.lol..."
|
|
||||||
mkdir -p /opt/apps/jefflix-dns
|
|
||||||
|
|
||||||
# Write Corefile with the actual Tailscale IP
|
|
||||||
cat > /opt/apps/jefflix-dns/Corefile <<COREFILE_EOF
|
|
||||||
jefflix.lol {
|
|
||||||
template IN A {
|
|
||||||
answer "{{ .Name }} 60 IN A ${TAILSCALE_IP}"
|
|
||||||
}
|
|
||||||
log
|
|
||||||
}
|
|
||||||
COREFILE_EOF
|
|
||||||
|
|
||||||
cat > /opt/apps/jefflix-dns/docker-compose.yml <<COMPOSE_EOF
|
|
||||||
services:
|
|
||||||
coredns:
|
|
||||||
image: coredns/coredns:latest
|
|
||||||
container_name: jefflix-dns
|
|
||||||
restart: unless-stopped
|
|
||||||
command: -conf /etc/coredns/Corefile
|
|
||||||
volumes:
|
|
||||||
- ./Corefile:/etc/coredns/Corefile:ro
|
|
||||||
ports:
|
|
||||||
- "${TAILSCALE_IP}:53:53/udp"
|
|
||||||
- "${TAILSCALE_IP}:53:53/tcp"
|
|
||||||
cap_drop:
|
|
||||||
- ALL
|
|
||||||
cap_add:
|
|
||||||
- NET_BIND_SERVICE
|
|
||||||
security_opt:
|
|
||||||
- no-new-privileges:true
|
|
||||||
COMPOSE_EOF
|
|
||||||
|
|
||||||
cd /opt/apps/jefflix-dns && docker compose up -d
|
|
||||||
echo " ✓ CoreDNS deployed"
|
|
||||||
|
|
||||||
# Test DNS
|
|
||||||
echo ""
|
|
||||||
echo " Testing DNS resolution..."
|
|
||||||
sleep 2
|
|
||||||
DIG_RESULT=$(dig +short @${TAILSCALE_IP} movies.jefflix.lol 2>/dev/null || echo "FAILED")
|
|
||||||
if [ "$DIG_RESULT" = "$TAILSCALE_IP" ]; then
|
|
||||||
echo " ✓ DNS test passed: movies.jefflix.lol -> $TAILSCALE_IP"
|
|
||||||
else
|
|
||||||
echo " ⚠ DNS test returned: $DIG_RESULT (expected $TAILSCALE_IP)"
|
|
||||||
echo " Check CoreDNS logs: docker logs jefflix-dns"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# --- Phase 4: Configure Headscale split DNS ---
|
|
||||||
echo ""
|
|
||||||
echo "[Phase 4] Configuring Headscale split DNS..."
|
|
||||||
|
|
||||||
HEADSCALE_CONFIG="/opt/apps/headscale-deploy/config/config.yaml"
|
|
||||||
|
|
||||||
# Check if split DNS is already configured
|
|
||||||
if grep -q "jefflix.lol" "$HEADSCALE_CONFIG"; then
|
|
||||||
echo " Split DNS for jefflix.lol already in config, skipping"
|
|
||||||
else
|
|
||||||
# Add split DNS section to the nameservers block
|
|
||||||
# This uses sed to add the split DNS config after the global nameservers
|
|
||||||
sed -i '/nameservers:/,/^[^ ]/ {
|
|
||||||
/global:/,/^ [^ ]/ {
|
|
||||||
/- 1.0.0.1/a\ split:\n jefflix.lol:\n - '"${TAILSCALE_IP}"'
|
|
||||||
}
|
|
||||||
}' "$HEADSCALE_CONFIG"
|
|
||||||
echo " ✓ Split DNS added to Headscale config"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Restart Headscale
|
|
||||||
cd /opt/apps/headscale-deploy && docker compose restart headscale
|
|
||||||
echo " ✓ Headscale restarted"
|
|
||||||
sleep 3
|
|
||||||
|
|
||||||
# Verify Headscale is healthy
|
|
||||||
docker exec headscale headscale nodes list >/dev/null 2>&1 && echo " ✓ Headscale healthy" || echo " ⚠ Headscale may need attention"
|
|
||||||
|
|
||||||
echo ""
|
|
||||||
echo "========================================"
|
|
||||||
echo " Setup Complete (Phases 1-4)"
|
|
||||||
echo "========================================"
|
|
||||||
echo ""
|
|
||||||
echo "Server Tailscale IP: $TAILSCALE_IP"
|
|
||||||
echo "CoreDNS: running on $TAILSCALE_IP:53"
|
|
||||||
echo "Split DNS: jefflix.lol -> $TAILSCALE_IP"
|
|
||||||
echo ""
|
|
||||||
echo "NEXT STEPS:"
|
|
||||||
echo " 1. Connect YOUR device to the tailnet and test:"
|
|
||||||
echo " tailscale up --login-server=https://vpn.jeffemmett.com"
|
|
||||||
echo " dig movies.jefflix.lol (should return $TAILSCALE_IP)"
|
|
||||||
echo " curl http://movies.jefflix.lol (should return Jellyfin)"
|
|
||||||
echo ""
|
|
||||||
echo " 2. Once confirmed working, run cutover.sh to remove public access"
|
|
||||||
echo " 3. Then onboard users with pre-auth keys"
|
|
||||||
echo ""
|
|
||||||
Loading…
Reference in New Issue