From ce660b2674ea657a7d8bb6adb98b60439ce399ef Mon Sep 17 00:00:00 2001 From: arch3rPro Date: Fri, 24 Apr 2026 11:54:16 +0800 Subject: [PATCH] =?UTF-8?q?feat(tailscale):=20=E6=B7=BB=E5=8A=A0=20Tailsca?= =?UTF-8?q?le=20=E5=BA=94=E7=94=A8=E9=85=8D=E7=BD=AE=E5=92=8C=E6=96=87?= =?UTF-8?q?=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 添加 Tailscale 应用的完整配置,包括: - 基础数据配置 (data.yml) - Docker Compose 部署文件 - 详细的中英文使用文档 - 应用 logo 图片 支持通过认证密钥或交互式登录两种认证方式,并提供子网路由、出口节点等高级功能配置 --- apps/tailscale/1.96.5/data.yml | 62 +++++++++++++ apps/tailscale/1.96.5/docker-compose.yml | 28 ++++++ apps/tailscale/README.md | 112 +++++++++++++++++++++++ apps/tailscale/README_en.md | 112 +++++++++++++++++++++++ apps/tailscale/data.yml | 29 ++++++ apps/tailscale/latest/data.yml | 62 +++++++++++++ apps/tailscale/latest/docker-compose.yml | 28 ++++++ apps/tailscale/logo.png | Bin 0 -> 16121 bytes 8 files changed, 433 insertions(+) create mode 100644 apps/tailscale/1.96.5/data.yml create mode 100644 apps/tailscale/1.96.5/docker-compose.yml create mode 100644 apps/tailscale/README.md create mode 100644 apps/tailscale/README_en.md create mode 100644 apps/tailscale/data.yml create mode 100644 apps/tailscale/latest/data.yml create mode 100644 apps/tailscale/latest/docker-compose.yml create mode 100644 apps/tailscale/logo.png diff --git a/apps/tailscale/1.96.5/data.yml b/apps/tailscale/1.96.5/data.yml new file mode 100644 index 0000000..c8e20af --- /dev/null +++ b/apps/tailscale/1.96.5/data.yml @@ -0,0 +1,62 @@ +additionalProperties: + formFields: + - default: "" + edit: true + envKey: TS_AUTHKEY + labelEn: Tailscale Auth Key + labelZh: Tailscale 认证密钥 + required: false + rule: paramCommon + type: password + description: + zh: 从 https://login.tailscale.com/admin/settings/keys 获取认证密钥,留空则需要在容器内执行 tailscale up 进行交互登录 + en: Get auth key from https://login.tailscale.com/admin/settings/keys, leave empty for interactive login via tailscale up + - default: "false" + edit: true + envKey: TS_USERSPACE + labelEn: Userspace Mode + labelZh: 用户空间模式 + required: false + type: select + values: + - label: "false (Kernel Networking)" + value: "false" + - label: "true (Userspace)" + value: "true" + description: + zh: "false 使用内核网络(推荐),true 使用用户空间网络" + en: "false for kernel networking (recommended), true for userspace" + - default: "" + edit: true + envKey: TS_ROUTES + labelEn: Subnet Routes (Optional) + labelZh: 子网路由(可选) + required: false + rule: paramCommon + type: text + description: + zh: 要广播的子网路由,如 192.168.1.0/24,10.0.0.0/8 + en: Subnet routes to advertise, e.g. 192.168.1.0/24,10.0.0.0/8 + - default: "true" + edit: true + envKey: TS_ACCEPT_DNS + labelEn: Accept DNS + labelZh: 接受 DNS + required: false + type: select + values: + - label: "true" + value: "true" + - label: "false" + value: "false" + - default: "" + edit: true + envKey: TS_EXTRA_ARGS + labelEn: Extra Arguments (Optional) + labelZh: 额外参数(可选) + required: false + rule: paramCommon + type: text + description: + zh: tailscale up 命令的额外参数,如 --accept-routes + en: Extra arguments for tailscale up command, e.g. --accept-routes diff --git a/apps/tailscale/1.96.5/docker-compose.yml b/apps/tailscale/1.96.5/docker-compose.yml new file mode 100644 index 0000000..e2dacec --- /dev/null +++ b/apps/tailscale/1.96.5/docker-compose.yml @@ -0,0 +1,28 @@ +services: + tailscaled: + container_name: ${CONTAINER_NAME} + image: tailscale/tailscale:v1.96.5 + hostname: ${HOSTNAME:-tailscale} + restart: always + volumes: + - ./data/var/lib:/var/lib + - /dev/net/tun:/dev/net/tun + cap_add: + - NET_ADMIN + - SYS_MODULE + network_mode: host + privileged: true + environment: + - TS_AUTHKEY=${TS_AUTHKEY} + - TS_USERSPACE=${TS_USERSPACE:-false} + - TS_STATE_DIR=/var/lib + - TS_ROUTES=${TS_ROUTES:-} + - TS_ACCEPT_DNS=${TS_ACCEPT_DNS:-true} + - TS_EXTRA_ARGS=${TS_EXTRA_ARGS:-} + - TS_AUTH_ONCE=${TS_AUTH_ONCE:-false} + command: containerboot + labels: + createdBy: "Apps" +networks: + 1panel-network: + external: true diff --git a/apps/tailscale/README.md b/apps/tailscale/README.md new file mode 100644 index 0000000..79d2090 --- /dev/null +++ b/apps/tailscale/README.md @@ -0,0 +1,112 @@ +# Tailscale + +最简单、最安全的 WireGuard 私有网络组网工具。Tailscale 让您能够轻松创建安全的 mesh 网络,将您的设备连接在一起,无论它们位于何处。 + +## 功能特点 + +- **基于 WireGuard**: 使用现代化的 WireGuard 协议,提供高性能和强安全性 +- **自动 NAT 穿透**: 自动处理复杂的网络环境,实现设备间直连 +- **零配置**: 无需手动配置,登录即可使用 +- **端到端加密**: 所有通信都经过端到端加密 +- **多平台支持**: 支持 Linux、Windows、macOS、iOS、Android 等平台 +- **子网路由**: 可将整个子网路由到 Tailscale 网络 +- **魔法 DNS**: 自动为设备分配易记的 DNS 名称 +- **访问控制**: 精细的访问控制策略 + +## 使用说明 + +### 认证方式 + +Tailscale 支持两种认证方式: + +#### 方式一:使用认证密钥(推荐) + +1. 访问 [Tailscale 管理控制台](https://login.tailscale.com/admin/settings/keys) +2. 点击 "Generate auth key" 创建认证密钥 +3. 复制生成的密钥,在部署时填写到 "Tailscale 认证密钥" 字段 +4. 部署后自动完成认证,无需额外操作 + +**注意**:认证密钥有过期时间,请在生成后尽快使用。 + +#### 方式二:交互登录 + +1. 部署时留空 "Tailscale 认证密钥" 字段 +2. 部署完成后,在 1Panel 容器列表中点击 "Tailscale" 容器的 "终端" 按钮 +3. 在终端中执行以下命令: + ```bash + tailscale up + ``` +4. 命令会输出一个认证 URL,复制该 URL 到浏览器打开 +5. 登录您的 Tailscale 账号并授权该设备 +6. 认证成功后,容器会自动连接到 Tailscale 网络 + +### 部署参数说明 + +- **Tailscale 认证密钥**(可选):从管理控制台获取的认证密钥,留空则使用交互登录 +- **用户空间模式**: + - `false` (推荐): 使用内核网络模式,性能更好 + - `true`: 使用用户空间网络模式,兼容性更好 +- **子网路由** (可选): 要广播的子网,如 `192.168.1.0/24,10.0.0.0/8` +- **接受 DNS**: 是否接受 Tailscale 的 DNS 配置 +- **额外参数** (可选): tailscale up 命令的额外参数,如 `--accept-routes` + +### 部署后操作 + +1. 部署完成后,访问 [Tailscale 管理控制台](https://login.tailscale.com/admin/machines) 查看设备状态 +2. 在其他设备上安装 Tailscale 客户端并登录同一账号 +3. 设备间可以通过 Tailscale IP 或魔法 DNS 名称互相访问 + +### 高级配置 + +#### 配置子网路由器 + +如果您希望将本地网络路由到 Tailscale: + +1. 在 `子网路由` 参数中填写要广播的子网,如 `192.168.1.0/24` +2. 部署后在管理控制台批准该子网路由 +3. 其他 Tailscale 设备即可访问该子网内的设备 + +#### 使用出口节点 + +将 Tailscale 配置为出口节点,让其他设备通过此设备访问互联网: + +1. 在 `额外参数` 中添加 `--advertise-exit-node` +2. 部署后在管理控制台批准出口节点 +3. 其他设备可以选择使用此出口节点 + +#### 仅首次认证 + +如果希望只在首次启动时使用认证密钥,后续启动使用已有状态: + +- 设置环境变量 `TS_AUTH_ONCE=true`(需要在 docker-compose.yml 中手动添加) + +### 数据目录 + +应用数据存储在 `./data/var/lib` 目录,包含 Tailscale 的状态信息。该目录已自动挂载到容器的 `/var/lib`。 + +## 环境变量说明 + +- `TS_AUTHKEY`: Tailscale 认证密钥(可选,支持交互登录) +- `TS_USERSPACE`: 是否使用用户空间网络模式 +- `TS_STATE_DIR`: 状态文件存储目录(固定为 /var/lib) +- `TS_ROUTES`: 要广播的子网路由 +- `TS_ACCEPT_DNS`: 是否接受 Tailscale DNS 配置 +- `TS_EXTRA_ARGS`: tailscale up 命令的额外参数 +- `TS_AUTH_ONCE`: 是否仅在首次启动时认证(默认 false) + +## 注意事项 + +- 需要 `privileged` 权限和 `NET_ADMIN`、`SYS_MODULE` 能力 +- 使用 `host` 网络模式以直接管理网络接口 +- 需要访问 `/dev/net/tun` 设备 +- 首次运行时需要有效的认证密钥或进行交互登录 +- 建议在防火墙中允许 UDP 41641 端口(WireGuard 默认端口) +- 容器重启策略固定为 `always`,由 1Panel 统一管理 + +## 相关链接 + +- 官方网站:https://tailscale.com +- GitHub: https://github.com/tailscale/tailscale +- 文档:https://tailscale.com/kb +- 管理控制台:https://login.tailscale.com/admin +- 下载客户端:https://tailscale.com/download diff --git a/apps/tailscale/README_en.md b/apps/tailscale/README_en.md new file mode 100644 index 0000000..f6af893 --- /dev/null +++ b/apps/tailscale/README_en.md @@ -0,0 +1,112 @@ +# Tailscale + +The easiest, most secure way to use WireGuard. Tailscale enables you to create secure mesh networks that connect your devices together, no matter where they are located. + +## Features + +- **WireGuard-based**: Uses modern WireGuard protocol for high performance and strong security +- **Automatic NAT Traversal**: Automatically handles complex network environments for direct device connections +- **Zero Configuration**: No manual setup required, just login and use +- **End-to-End Encryption**: All communications are encrypted end-to-end +- **Multi-Platform Support**: Supports Linux, Windows, macOS, iOS, Android and more +- **Subnet Routing**: Route entire subnets to your Tailscale network +- **Magic DNS**: Automatically assigns friendly DNS names to devices +- **Access Control**: Fine-grained access control policies + +## Usage Instructions + +### Authentication Methods + +Tailscale supports two authentication methods: + +#### Method 1: Using Auth Key (Recommended) + +1. Visit [Tailscale Admin Console](https://login.tailscale.com/admin/settings/keys) +2. Click "Generate auth key" to create an authentication key +3. Copy the generated key and fill it in the "Tailscale Auth Key" field during deployment +4. Authentication completes automatically after deployment + +**Note**: Auth keys have an expiration time, please use them as soon as possible after generation. + +#### Method 2: Interactive Login + +1. Leave the "Tailscale Auth Key" field empty during deployment +2. After deployment, click the "Terminal" button for the "Tailscale" container in 1Panel +3. Execute the following command in the terminal: + ```bash + tailscale up + ``` +4. The command will output an authentication URL, copy it to your browser +5. Log in to your Tailscale account and authorize the device +6. After successful authentication, the container will automatically connect to the Tailscale network + +### Deployment Parameters + +- **Tailscale Auth Key** (Optional): Authentication key from admin console, leave empty for interactive login +- **Userspace Mode**: + - `false` (Recommended): Use kernel networking mode for better performance + - `true`: Use userspace networking mode for better compatibility +- **Subnet Routes** (Optional): Subnets to advertise, e.g., `192.168.1.0/24,10.0.0.0/8` +- **Accept DNS**: Whether to accept Tailscale DNS configuration +- **Extra Args** (Optional): Additional arguments for tailscale up command, e.g., `--accept-routes` + +### Post-Deployment Steps + +1. After deployment, visit [Tailscale Admin Console](https://login.tailscale.com/admin/machines) to check device status +2. Install Tailscale clients on other devices and login with the same account +3. Devices can access each other via Tailscale IP or MagicDNS names + +### Advanced Configuration + +#### Configure Subnet Router + +If you want to route your local network to Tailscale: + +1. Fill in the subnets to advertise in `Subnet Routes` parameter, e.g., `192.168.1.0/24` +2. Approve the subnet route in admin console after deployment +3. Other Tailscale devices can then access devices in that subnet + +#### Configure Exit Node + +Configure Tailscale as an exit node to allow other devices to access the internet through it: + +1. Add `--advertise-exit-node` to `Extra Args` parameter +2. Approve the exit node in admin console after deployment +3. Other devices can choose to use this exit node + +#### First-Time Authentication Only + +If you want to use the auth key only on first startup and use existing state on subsequent starts: + +- Set environment variable `TS_AUTH_ONCE=true` (needs to be manually added in docker-compose.yml) + +### Data Directory + +Application data is stored in `./data/var/lib` directory, containing Tailscale state information. This directory is automatically mounted to `/var/lib` in the container. + +## Environment Variables + +- `TS_AUTHKEY`: Tailscale authentication key (optional, supports interactive login) +- `TS_USERSPACE`: Whether to use userspace networking mode +- `TS_STATE_DIR`: State file storage directory (fixed to /var/lib) +- `TS_ROUTES`: Subnet routes to advertise +- `TS_ACCEPT_DNS`: Whether to accept Tailscale DNS configuration +- `TS_EXTRA_ARGS`: Additional arguments for tailscale up command +- `TS_AUTH_ONCE`: Whether to authenticate only on first start (default false) + +## Notes + +- Requires `privileged` mode and `NET_ADMIN`, `SYS_MODULE` capabilities +- Uses `host` network mode to directly manage network interfaces +- Requires access to `/dev/net/tun` device +- A valid auth key or interactive login is required for first run +- Recommended to allow UDP port 41641 (WireGuard default port) in firewall +- Container restart policy is fixed to `always`, managed by 1Panel + +## Links + +- Website: https://tailscale.com +- GitHub: https://github.com/tailscale/tailscale +- Documentation: https://tailscale.com/kb +- Admin Console: https://login.tailscale.com/admin +- Downloads: https://tailscale.com/download diff --git a/apps/tailscale/data.yml b/apps/tailscale/data.yml new file mode 100644 index 0000000..125f510 --- /dev/null +++ b/apps/tailscale/data.yml @@ -0,0 +1,29 @@ +name: Tailscale +tags: + - 网络工具 + - 安全 +title: 私有 WireGuard 网络组网工具 +description: 最简单的 WireGuard 私有网络组网解决方案,支持多设备安全互联 +additionalProperties: + key: tailscale + name: Tailscale + tags: + - Networking + - Security + shortDescZh: 私有 WireGuard 网络组网工具 + shortDescEn: Private WireGuard networking tool + description: + en: The easiest, most secure way to use WireGuard. Create private networks with secure mesh connectivity between devices. + zh: 最简单、最安全的 WireGuard 使用方式。创建设备间安全 mesh 连接的私有网络。 + type: tool + crossVersionUpdate: true + limit: 0 + recommend: 90 + website: https://tailscale.com + github: https://github.com/tailscale/tailscale + document: https://tailscale.com/kb + architectures: + - amd64 + - arm64 + - arm/v7 + hostMode: true diff --git a/apps/tailscale/latest/data.yml b/apps/tailscale/latest/data.yml new file mode 100644 index 0000000..c8e20af --- /dev/null +++ b/apps/tailscale/latest/data.yml @@ -0,0 +1,62 @@ +additionalProperties: + formFields: + - default: "" + edit: true + envKey: TS_AUTHKEY + labelEn: Tailscale Auth Key + labelZh: Tailscale 认证密钥 + required: false + rule: paramCommon + type: password + description: + zh: 从 https://login.tailscale.com/admin/settings/keys 获取认证密钥,留空则需要在容器内执行 tailscale up 进行交互登录 + en: Get auth key from https://login.tailscale.com/admin/settings/keys, leave empty for interactive login via tailscale up + - default: "false" + edit: true + envKey: TS_USERSPACE + labelEn: Userspace Mode + labelZh: 用户空间模式 + required: false + type: select + values: + - label: "false (Kernel Networking)" + value: "false" + - label: "true (Userspace)" + value: "true" + description: + zh: "false 使用内核网络(推荐),true 使用用户空间网络" + en: "false for kernel networking (recommended), true for userspace" + - default: "" + edit: true + envKey: TS_ROUTES + labelEn: Subnet Routes (Optional) + labelZh: 子网路由(可选) + required: false + rule: paramCommon + type: text + description: + zh: 要广播的子网路由,如 192.168.1.0/24,10.0.0.0/8 + en: Subnet routes to advertise, e.g. 192.168.1.0/24,10.0.0.0/8 + - default: "true" + edit: true + envKey: TS_ACCEPT_DNS + labelEn: Accept DNS + labelZh: 接受 DNS + required: false + type: select + values: + - label: "true" + value: "true" + - label: "false" + value: "false" + - default: "" + edit: true + envKey: TS_EXTRA_ARGS + labelEn: Extra Arguments (Optional) + labelZh: 额外参数(可选) + required: false + rule: paramCommon + type: text + description: + zh: tailscale up 命令的额外参数,如 --accept-routes + en: Extra arguments for tailscale up command, e.g. --accept-routes diff --git a/apps/tailscale/latest/docker-compose.yml b/apps/tailscale/latest/docker-compose.yml new file mode 100644 index 0000000..a26edaf --- /dev/null +++ b/apps/tailscale/latest/docker-compose.yml @@ -0,0 +1,28 @@ +services: + tailscaled: + container_name: ${CONTAINER_NAME} + image: tailscale/tailscale:latest + hostname: ${HOSTNAME:-tailscale} + restart: always + volumes: + - ./data/var/lib:/var/lib + - /dev/net/tun:/dev/net/tun + cap_add: + - NET_ADMIN + - SYS_MODULE + network_mode: host + privileged: true + environment: + - TS_AUTHKEY=${TS_AUTHKEY} + - TS_USERSPACE=${TS_USERSPACE:-false} + - TS_STATE_DIR=/var/lib + - TS_ROUTES=${TS_ROUTES:-} + - TS_ACCEPT_DNS=${TS_ACCEPT_DNS:-true} + - TS_EXTRA_ARGS=${TS_EXTRA_ARGS:-} + - TS_AUTH_ONCE=${TS_AUTH_ONCE:-false} + command: containerboot + labels: + createdBy: "Apps" +networks: + 1panel-network: + external: true diff --git a/apps/tailscale/logo.png b/apps/tailscale/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..0b7934fa8492db242943ae9e2cf21f46cd81b3ed GIT binary patch literal 16121 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 X9~AT6WD