Initial skills documentation — 25 categories, all SKILL.md + references + scripts
This commit is contained in:
@@ -0,0 +1,95 @@
|
||||
---
|
||||
name: tailscale-internal-services
|
||||
description: "Deploy internal-only services behind Tailscale for zero public exposure — Tailscale Serve for HTTPS, UFW firewall rules, and service configuration patterns."
|
||||
version: 1.0.0
|
||||
author: ShoNuff
|
||||
platforms: [linux]
|
||||
metadata:
|
||||
hermes:
|
||||
tags: [tailscale, vpn, networking, security, internal-services, docker]
|
||||
---
|
||||
|
||||
# Tailscale Internal Services
|
||||
|
||||
Standard for deploying services that should not be publicly accessible. All internal services live behind Tailscale — no DNS records, no public ports, no Let's Encrypt.
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
Device (iPhone/Mac) app1 (netcup)
|
||||
┌──────────────┐ Tailscale tunnel ┌─────────────────────┐
|
||||
│ Tailscale │◄──────────────────────►│ tailscale serve │
|
||||
│ connected │ encrypted mesh │ ↓ │
|
||||
│ │ │ Docker container │
|
||||
│ localhost │ │ port 8080 │
|
||||
└──────────────┘ └─────────────────────┘
|
||||
│
|
||||
▼
|
||||
https://<hostname>.tailXXXXX.ts.net
|
||||
```
|
||||
|
||||
**Key principle:** Tailscale traffic is already encrypted end-to-end. The Tailscale Serve HTTPS layer is only needed for (a) browser security warnings and (b) apps (like Bitwarden) that reject plain HTTP self-hosted URLs. For API-only services, plain HTTP over the tailnet is fine.
|
||||
|
||||
## Setup
|
||||
|
||||
### 1. Install Tailscale
|
||||
```bash
|
||||
curl -fsSL https://tailscale.com/install.sh | sh
|
||||
tailscale up
|
||||
# Auth URL printed — open in browser to log in
|
||||
```
|
||||
|
||||
### 2. Start a service with Tailscale Serve
|
||||
```bash
|
||||
# Expose a local Docker port via Tailscale HTTPS
|
||||
tailscale serve --bg --https 443 --set-path / http://127.0.0.1:<port>
|
||||
|
||||
# Verify
|
||||
tailscale serve status
|
||||
# → https://<hostname>.tail<random>.ts.net/ → proxy http://127.0.0.1:<port>
|
||||
```
|
||||
|
||||
### 3. Configure the service's DOMAIN
|
||||
Update the container's `DOMAIN` environment variable to match the Tailscale URL so the service's internal redirects and API URLs work correctly.
|
||||
|
||||
### 4. Close the public port
|
||||
```bash
|
||||
ufw deny <port>/tcp
|
||||
ufw reload
|
||||
```
|
||||
|
||||
### 5. Rename the hostname if the random tailnet name has confusing characters
|
||||
```bash
|
||||
tailscale set --hostname <service-name>
|
||||
# Then re-create serve config (it picks up the new hostname)
|
||||
```
|
||||
|
||||
## Application-Specific Notes
|
||||
|
||||
### Vaultwarden / Bitwarden
|
||||
- Use **"Self-hosted environment"** in the Bitwarden app
|
||||
- URL: `https://<hostname>.tail<random>.ts.net`
|
||||
- If the Tailscale hostname has zeroes or letter-O confusables, rename the node: `tailscale set --hostname vaultwarden`
|
||||
- The Bitwarden mobile app may have issues with certain SSL implementations from Tailscale Serve. If the app can't connect, try:
|
||||
- Use the **Custom Environment** option in the app and enter api/identity/web vault URLs separately
|
||||
- Access via pure HTTP on the Tailscale IP (`http://100.x.x.x:8080`) instead of the HTTPS hostname — works fine since tailnet traffic is encrypted
|
||||
|
||||
## UFW Rules
|
||||
|
||||
```bash
|
||||
# Default: ALLOW SSH, HTTP, HTTPS for management
|
||||
ufw allow 22/tcp
|
||||
ufw allow 80/tcp
|
||||
ufw allow 443/tcp
|
||||
|
||||
# Per service: start with port open for testing, then close when Tailscale is verified
|
||||
ufw allow <port>/tcp comment 'Service name (temp)'
|
||||
ufw delete allow <port>/tcp # After verifying Tailscale access
|
||||
```
|
||||
|
||||
## Pitfalls
|
||||
- **Tailscale Serve requires HTTPS** — you can't serve a port on both HTTP and HTTPS via Tailscale Serve simultaneously
|
||||
- **Hostname changes after serve is active** — you need to restart the serve daemon (turn off, then re-enable with the new hostname)
|
||||
- **Safari on iOS shows "Not Secure"** for plain HTTP over tailnet — this is cosmetic, the tunnel is still encrypted
|
||||
- **`tailscale serve status` shows a tailnet-only badge** — verify with `-o json` for automated checks
|
||||
- **Renaming the tailscale node** (e.g. app1→vaultwarden) changes the serve URL immediately without downtime
|
||||
Reference in New Issue
Block a user