commit 2ffa06ac80077061e9132502ac3bd532d6cb665e Author: Timmy Date: Fri Apr 10 09:17:12 2026 +0800 Initial commit: Tailscale Docker deployment with Headscale Docker Compose 部署 Tailscale 連線至自架 Headscale 控制伺服器, 包含子網路由、出口節點、iptables NAT 設定及完整中文文件。 Co-Authored-By: Claude Opus 4.6 (1M context) diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..93ad088 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.env +.omc/ diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..fc16392 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,42 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Overview + +Docker-based Tailscale deployment using a self-hosted Headscale control server (`https://headscale.lotimmy.com`). Runs a `tailscale/tailscale:latest` container in host network mode with subnet routing (192.168.88.0/24) and exit node capability. + +## Commands + +```bash +# Start the Tailscale container +./run.sh # docker-compose up -d + +# Stop the container +./stop.sh # docker-compose down + +# Connect to Headscale (advertise subnet routes + exit node) +./docker_exec_tailscale_up.sh + +# Check status / get IP +./docker_exec_tailscale_status.sh +./docker_exec_tailscale_ip.sh + +# Run arbitrary tailscale commands inside the container +./tailscale # wrapper for: docker exec -i tailscale tailscale "$@" + +# Reset iptables and configure NAT/forwarding for Tailscale routing (run on host) +./iptables_reset_and_configure.sh +``` + +## Architecture + +- **docker-compose.yml** - Single service (`tailscale`) running `tailscale/tailscale:latest` with `NET_ADMIN`/`SYS_MODULE` caps, host networking, TUN device mount, and persistent state at `/opt/tailscale`. +- **`.env`** - Contains `TS_AUTHKEY` for pre-authenticated registration with Headscale. +- **iptables_reset_and_configure.sh** - Configures host firewall: allows SSH/HTTP/HTTPS/ICMP, sets up MASQUERADE NAT for Tailscale CGNAT range (100.64.0.0/10) via eth0, and enables forwarding between `tailscale0` and `eth0`. +- Watchtower auto-update is enabled via container label. + +## Notes + +- Documentation (`README`) is in Traditional Chinese. +- Hostname convention: `ip-192-168-88-XX` matching the host's LAN IP. diff --git a/OPERATIONS.md b/OPERATIONS.md new file mode 100644 index 0000000..e567ae0 --- /dev/null +++ b/OPERATIONS.md @@ -0,0 +1,93 @@ +# Operations Guide + +## 日常操作 + +### 啟動服務 + +```bash +./run.sh +``` + +啟動 Tailscale Docker 容器(背景執行)。 + +### 停止服務 + +```bash +./stop.sh +``` + +停止並移除容器。 + +### 連線至 Headscale + +```bash +./docker_exec_tailscale_up.sh +``` + +使用預設的 Auth Key 連線至 `https://headscale.lotimmy.com`,並: +- 廣播子網路 `192.168.88.0/24` +- 啟用出口節點(Exit Node) +- 接受其他節點的路由 + +### 檢查狀態 + +```bash +./docker_exec_tailscale_status.sh # 查看連線狀態 +./docker_exec_tailscale_ip.sh # 查詢 Tailscale IP +``` + +### 執行任意 Tailscale 指令 + +```bash +./tailscale status +./tailscale ping +./tailscale netcheck +``` + +`./tailscale` 是 `docker exec -i tailscale tailscale` 的包裝腳本。 + +## 防火牆與路由設定 + +在主機上執行: + +```bash +sudo ./iptables_reset_and_configure.sh +``` + +此腳本會: +1. 清除所有現有 iptables 規則 +2. 允許 SSH (22)、HTTP (80)、HTTPS (443)、ICMP +3. 設定 Tailscale CGNAT 範圍 (100.64.0.0/10) 的 NAT MASQUERADE +4. 啟用 `tailscale0` ↔ `eth0` 之間的封包轉送 +5. 將規則儲存至 `/etc/iptables/rules.v4` + +## 環境變數 + +| 變數 | 說明 | 位置 | +|------|------|------| +| `TS_AUTHKEY` | Headscale 預認證金鑰 | `.env` | + +## 持久化資料 + +- Tailscale 狀態檔案:主機的 `/opt/tailscale` 掛載至容器的 `/var/lib/tailscale` +- iptables 規則:`/etc/iptables/rules.v4` + +## 故障排除 + +```bash +# 確認容器正在執行 +docker ps | grep tailscale + +# 查看容器日誌 +docker logs tailscale + +# 確認 TUN 裝置存在 +ls -la /dev/net/tun + +# 確認 IP 轉送已啟用 +cat /proc/sys/net/ipv4/ip_forward + +# 登出並重新連線 +./tailscale logout +./docker_exec_tailscale_up.sh +``` diff --git a/QUICKSTART.md b/QUICKSTART.md new file mode 100644 index 0000000..62e302f --- /dev/null +++ b/QUICKSTART.md @@ -0,0 +1,44 @@ +# Quick Start + +## 前置需求 + +- Docker 與 Docker Compose +- `/dev/net/tun` 裝置(主機需支援 TUN/TAP) +- 具備 `NET_ADMIN` 權限的環境 + +## 步驟 + +### 1. 設定 Auth Key + +在 `.env` 檔案中填入 Headscale 預認證金鑰: + +``` +TS_AUTHKEY=your-preauth-key-here +``` + +### 2. 啟動容器 + +```bash +./run.sh +``` + +### 3. 連線至 Headscale + +```bash +./docker_exec_tailscale_up.sh +``` + +### 4. 設定防火牆(如需子網路由/出口節點) + +```bash +sudo ./iptables_reset_and_configure.sh +``` + +### 5. 驗證 + +```bash +./docker_exec_tailscale_status.sh +./docker_exec_tailscale_ip.sh +``` + +看到 Tailscale IP 和節點列表即代表連線成功。 diff --git a/README b/README new file mode 100644 index 0000000..6aba218 --- /dev/null +++ b/README @@ -0,0 +1,35 @@ +Tailscale 操作筆記(使用 Headscale) + +登出目前 Tailscale 使用者 +tailscale logout + +登入 Headscale 自架登入伺服器 +tailscale up --login-server=https://headscale.lotimmy.com + +使用預先產生的 Auth Key 進行登入 +tailscale up --login-server=https://headscale.lotimmy.com --authkey <你的-preauth-key> + +範例:使用實際的 Auth Key +tailscale up --login-server=https://headscale.lotimmy.com --authkey tskey-xxxxxxx + +正式使用 Auth Key 登入(可取代上方範例) +tailscale up --login-server=https://headscale.lotimmy.com --authkey d07067c4bcc950e7f1b02d4b492859b84ca464350d86273e + +登入並接受子網路路由(子網路代理模式) +tailscale up --login-server=https://headscale.lotimmy.com --authkey d07067c4bcc950e7f1b02d4b492859b84ca464350d86273e --accept-routes + +完整範例:廣播子網段、設為出口節點、指定主機名稱 +tailscale up \ + --accept-routes \ + --advertise-routes=192.168.88.0/24 \ + --advertise-exit-node \ + --login-server=https://headscale.lotimmy.com \ + --authkey=d07067c4bcc950e7f1b02d4b492859b84ca464350d86273e \ + --hostname=ip-192-168-88-81 + +檢查目前連線狀態 +tailscale status + +查詢本機 Tailscale IP +tailscale ip + diff --git a/README.md b/README.md new file mode 100644 index 0000000..3591ad4 --- /dev/null +++ b/README.md @@ -0,0 +1,67 @@ +# Tailscale Docker 部署(Headscale) + +使用 Docker Compose 部署 Tailscale,連線至自架的 Headscale 控制伺服器。 + +## 功能 + +- 透過 Docker 容器執行 Tailscale,使用 host 網路模式 +- 連線至 `https://headscale.lotimmy.com` +- 子網路由廣播(192.168.88.0/24) +- 出口節點(Exit Node)支援 +- iptables NAT/轉送自動設定 +- 透過 Watchtower 自動更新容器映像 + +## 檔案結構 + +| 檔案 | 說明 | +|------|------| +| `docker-compose.yml` | Docker 服務定義 | +| `.env` | 環境變數(TS_AUTHKEY) | +| `run.sh` | 啟動容器 | +| `stop.sh` | 停止容器 | +| `docker_exec_tailscale_up.sh` | 連線至 Headscale(含路由與出口節點設定) | +| `docker_exec_tailscale_status.sh` | 查看連線狀態 | +| `docker_exec_tailscale_ip.sh` | 查詢 Tailscale IP | +| `tailscale` | Tailscale CLI 包裝腳本 | +| `iptables_reset_and_configure.sh` | 防火牆與 NAT 設定 | + +## 快速開始 + +```bash +# 1. 設定 .env 中的 TS_AUTHKEY +# 2. 啟動 +./run.sh + +# 3. 連線至 Headscale +./docker_exec_tailscale_up.sh + +# 4. 設定防火牆(如需路由功能) +sudo ./iptables_reset_and_configure.sh + +# 5. 驗證 +./docker_exec_tailscale_status.sh +``` + +## 網路架構 + +``` +Tailscale 節點 ──► Headscale (headscale.lotimmy.com) + │ + ▼ + tailscale0 介面 (100.64.0.0/10) + │ + NAT MASQUERADE + │ + ▼ + eth0 ──► LAN (192.168.88.0/24) +``` + +## 主機名稱慣例 + +使用 `ip-192-168-88-XX` 格式,對應主機的區域網路 IP。 + +## 相關文件 + +- [QUICKSTART.md](QUICKSTART.md) — 快速開始指南 +- [OPERATIONS.md](OPERATIONS.md) — 維運操作手冊 +- [SUMMARY.md](SUMMARY.md) — 專案摘要 diff --git a/SUMMARY.md b/SUMMARY.md new file mode 100644 index 0000000..47cb2fb --- /dev/null +++ b/SUMMARY.md @@ -0,0 +1,25 @@ +# Summary + +## 專案摘要 + +此專案是一個 Docker 化的 Tailscale 部署方案,用於將主機連線至自架的 Headscale 控制伺服器(`https://headscale.lotimmy.com`)。 + +## 核心元件 + +- **Docker Compose 服務**:以 `tailscale/tailscale:latest` 映像執行,採用 host 網路模式,具備 `NET_ADMIN` 和 `SYS_MODULE` 權限。 +- **Headscale 整合**:透過預認證金鑰(Pre-auth Key)自動註冊節點。 +- **子網路由**:廣播 `192.168.88.0/24` 網段,讓遠端 Tailscale 節點可存取區域網路。 +- **出口節點**:允許其他節點透過此主機路由所有流量。 +- **iptables 設定**:NAT MASQUERADE 處理 Tailscale CGNAT 流量,並啟用 `tailscale0` ↔ `eth0` 轉送。 + +## 技術規格 + +| 項目 | 值 | +|------|---| +| 容器映像 | `tailscale/tailscale:latest` | +| 網路模式 | host | +| 控制伺服器 | `https://headscale.lotimmy.com` | +| 廣播子網 | `192.168.88.0/24` | +| Tailscale CGNAT | `100.64.0.0/10` | +| 狀態持久化 | `/opt/tailscale` → `/var/lib/tailscale` | +| 自動更新 | Watchtower(透過 container label) | diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..2536a4e --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,23 @@ +services: + tailscale: + image: tailscale/tailscale:latest + container_name: tailscale + hostname: virmach-ubuntu + restart: unless-stopped + cap_add: + - NET_ADMIN + - SYS_MODULE + network_mode: host + # privileged: true + environment: + - TS_AUTHKEY=${TS_AUTHKEY} + volumes: + - /dev/net/tun:/dev/net/tun + - /opt/tailscale:/var/lib/tailscale + labels: + - "com.centurylinklabs.watchtower.enable=true" + # command: > + # tailscaled + command: > + sh -c "env && tailscaled" + diff --git a/docker_exec_tailscale_ip.sh b/docker_exec_tailscale_ip.sh new file mode 100755 index 0000000..e45aec1 --- /dev/null +++ b/docker_exec_tailscale_ip.sh @@ -0,0 +1,4 @@ +#!/bin/bash +# docker exec -it tailscale tailscale ip +tailscale ip + diff --git a/docker_exec_tailscale_status.sh b/docker_exec_tailscale_status.sh new file mode 100755 index 0000000..c4051e2 --- /dev/null +++ b/docker_exec_tailscale_status.sh @@ -0,0 +1,2 @@ +docker exec -it tailscale tailscale status + diff --git a/docker_exec_tailscale_up.sh b/docker_exec_tailscale_up.sh new file mode 100755 index 0000000..c4c73b9 --- /dev/null +++ b/docker_exec_tailscale_up.sh @@ -0,0 +1,9 @@ +#!/bin/bash +tailscale up \ + --authkey d07067c4bcc950e7f1b02d4b492859b84ca464350d86273e \ + --advertise-routes=192.168.88.0/24 \ + --accept-routes \ + --advertise-exit-node \ + --login-server=https://headscale.lotimmy.com \ + --hostname=ip-192-168-88-82 + diff --git a/iptables_reset_and_configure.sh b/iptables_reset_and_configure.sh new file mode 100755 index 0000000..b64e282 --- /dev/null +++ b/iptables_reset_and_configure.sh @@ -0,0 +1,44 @@ +#!/bin/bash + +echo "🧹 清除所有規則並重設策略..." +iptables -F +iptables -t nat -F +iptables -X + +# 設定預設策略 +# iptables -P INPUT DROP +iptables -P INPUT ACCEPT +iptables -P FORWARD ACCEPT +# iptables -P FORWARD DROP +iptables -P OUTPUT ACCEPT + +echo "✅ 設定 INPUT 安全規則..." + +# 允許已建立與相關的連線(像是回應封包) +iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT -m comment --comment "Allow established/related" + +# 允許 loopback +iptables -A INPUT -i lo -j ACCEPT -m comment --comment "Allow loopback" + +# 允許 ICMP (ping) +iptables -A INPUT -p icmp --icmp-type echo-request -j ACCEPT -m comment --comment "Allow ping" + +# 允許 SSH +iptables -A INPUT -p tcp --dport 22 -j ACCEPT -m comment --comment "Allow SSH" + +# 可選:允許 HTTP/HTTPS(如果有 Web 應用) +iptables -A INPUT -p tcp --dport 80 -j ACCEPT -m comment --comment "Allow HTTP" +iptables -A INPUT -p tcp --dport 443 -j ACCEPT -m comment --comment "Allow HTTPS" + +echo "🔧 設定 Tailscale 出口 NAT(MASQUERADE)" +iptables -t nat -A POSTROUTING -s 100.64.0.0/10 -o eth0 -j MASQUERADE -m comment --comment "Tailscale NAT" + +echo "🔧 允許 Tailscale → LAN 轉送" +iptables -A FORWARD -i tailscale0 -o eth0 -j ACCEPT -m comment --comment "Tailscale → LAN" +iptables -A FORWARD -i eth0 -o tailscale0 -m state --state RELATED,ESTABLISHED -j ACCEPT -m comment --comment "LAN → Tailscale return" + +echo "📦 儲存規則(iptables-persistent)" +iptables-save > /etc/iptables/rules.v4 + +echo "✅ 防火牆與 NAT 重設完成!" + diff --git a/run.sh b/run.sh new file mode 100755 index 0000000..2a67cfd --- /dev/null +++ b/run.sh @@ -0,0 +1,2 @@ +#!/bin/bash +docker-compose up -d diff --git a/stop.sh b/stop.sh new file mode 100755 index 0000000..e4fab3e --- /dev/null +++ b/stop.sh @@ -0,0 +1,4 @@ +#!/bin/bash +# docker stop caddy +# docker system prune -f +docker-compose down diff --git a/tailscale b/tailscale new file mode 100755 index 0000000..0549696 --- /dev/null +++ b/tailscale @@ -0,0 +1,3 @@ +#!/bin/bash +docker exec -i tailscale tailscale "$@" +