442 lines
17 KiB
Markdown
442 lines
17 KiB
Markdown
# Tailscale + SSH 隧道访问 TDX 开发手册
|
||
|
||
## 1. 用途与适用边界
|
||
|
||
本手册用于 Mac 本地开发期间,访问家中 Windows 上仅监听回环地址的 TDX HTTP 接口。TDX 是市场概览的正式行情来源;SSH 隧道负责把家庭局域网和外部 Tailscale 两种开发位置统一成同一个 Mac 回环入口。该链路不是生产采集、数据上报或公网部署方案。
|
||
|
||
```text
|
||
Mac 本地程序 / TDX Bridge
|
||
-> 127.0.0.1:17710
|
||
-> SSH 本地端口转发
|
||
-> 家庭局域网或 Tailscale 私有网络
|
||
-> 同一台 Windows OpenSSH
|
||
-> Windows 127.0.0.1:17709
|
||
-> TDX
|
||
```
|
||
|
||
两个场景只切换 SSH 目标别名,TDX Bridge 和上层业务始终访问 `http://127.0.0.1:17710/`。离线样本只允许作为显式开发和自动化测试回退,并必须标记为离线数据;不得伪装为实时 TDX。Windows 离线不得阻塞普通测试或构建。
|
||
|
||
## 2. 安全边界
|
||
|
||
- 不在路由器上开放或转发 SSH、RDP、`17709`、`17710` 等端口。
|
||
- TDX 继续只监听 Windows `127.0.0.1:17709`,不得改为 `0.0.0.0`。
|
||
- SSH 使用独立 Ed25519 密钥,不在脚本、仓库、文档或远程 URL 中保存密码和私钥。
|
||
- Mac 转发端口只绑定 `127.0.0.1:17710`,不得绑定局域网或所有网卡。
|
||
- 真实返回只用于只读验证。日志和测试报告必须脱敏,不保存家庭网络信息、登录信息或无关原始载荷。
|
||
- 开发隧道不连接生产数据库、不承担生产采集,也不证明数据源具备商业展示或再分发授权。
|
||
|
||
文档中的值均为占位符:
|
||
|
||
- `<WINDOWS_TAILSCALE_HOST>`:Windows 的 Tailscale MagicDNS 名称或 Tailscale IP。
|
||
- `<WINDOWS_SSH_USER>`:用于 SSH 登录的 Windows 用户。
|
||
- `<MAC_TAILSCALE_IP>`:Mac 的 Tailscale IP,仅在收紧防火墙来源时使用。
|
||
|
||
## 3. 首次建立 Tailscale 网络
|
||
|
||
### 3.1 Mac
|
||
|
||
1. 从 Tailscale 官方渠道安装客户端。
|
||
2. 登录项目使用的同一个 Tailnet。
|
||
3. 确认菜单栏中的 Tailscale 状态为已连接。
|
||
4. 在终端检查本机和节点列表:
|
||
|
||
```bash
|
||
tailscale ip -4
|
||
tailscale status
|
||
```
|
||
|
||
### 3.2 Windows
|
||
|
||
1. 从 Tailscale 官方渠道安装客户端。
|
||
2. 登录与 Mac 相同的 Tailnet。
|
||
3. 确认 Windows 节点在 Tailscale 管理后台和 Mac 的 `tailscale status` 中均为在线。
|
||
4. 在 Windows PowerShell 中检查地址和状态:
|
||
|
||
```powershell
|
||
tailscale ip -4
|
||
tailscale status
|
||
```
|
||
|
||
### 3.3 私网连通验证
|
||
|
||
在 Mac 执行:
|
||
|
||
```bash
|
||
tailscale ping <WINDOWS_TAILSCALE_HOST>
|
||
```
|
||
|
||
只有 Tailscale ping 成功后再排查 SSH。不要用公网 IP 替换占位符,也不要为排障临时开放路由器端口。
|
||
|
||
## 4. 配置 Windows OpenSSH Server
|
||
|
||
以下命令在 Windows 管理员 PowerShell 中执行。
|
||
|
||
### 4.1 安装并启动服务
|
||
|
||
```powershell
|
||
Get-WindowsCapability -Online | Where-Object Name -Like 'OpenSSH.Server*'
|
||
Add-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0
|
||
Start-Service sshd
|
||
Set-Service -Name sshd -StartupType Automatic
|
||
Get-Service sshd
|
||
```
|
||
|
||
如果 OpenSSH Server 已安装,`Add-WindowsCapability` 可以跳过。`Get-Service sshd` 应显示服务正在运行且启动类型为自动。
|
||
|
||
检查端口监听:
|
||
|
||
```powershell
|
||
Get-NetTCPConnection -LocalPort 22 -State Listen
|
||
```
|
||
|
||
### 4.2 确认防火墙规则
|
||
|
||
OpenSSH 安装通常会创建入站规则。先检查,不要重复创建同名规则:
|
||
|
||
```powershell
|
||
Get-NetFirewallRule -Name 'OpenSSH-Server-In-TCP' -ErrorAction SilentlyContinue
|
||
```
|
||
|
||
首次联调可以保留系统创建的 SSH 规则;链路确认后按第 11 节限制为 Tailscale 来源。不得新增公网端口映射。
|
||
|
||
## 5. 配置 SSH 密钥登录
|
||
|
||
### 5.1 在 Mac 生成项目专用密钥
|
||
|
||
```bash
|
||
ssh-keygen -t ed25519 -a 100 -f ~/.ssh/tdx_windows -C "tdx-windows-development"
|
||
chmod 600 ~/.ssh/tdx_windows
|
||
chmod 644 ~/.ssh/tdx_windows.pub
|
||
pbcopy < ~/.ssh/tdx_windows.pub
|
||
```
|
||
|
||
私钥 `~/.ssh/tdx_windows` 只保留在 Mac,不发送给 Windows,也不提交仓库。建议为私钥设置口令,并由 macOS 钥匙串或 `ssh-agent` 管理。
|
||
|
||
### 5.2 Windows 管理员账户
|
||
|
||
Windows OpenSSH 默认从 `C:\ProgramData\ssh\administrators_authorized_keys` 读取管理员公钥。必须先创建文件,再设置 ACL;否则 `icacls` 会报“系统找不到指定的文件”。
|
||
|
||
在管理员 PowerShell 中执行,按提示粘贴刚复制的单行公钥:
|
||
|
||
```powershell
|
||
$authorizedKeys = "$env:ProgramData\ssh\administrators_authorized_keys"
|
||
New-Item -ItemType Directory -Force -Path (Split-Path $authorizedKeys)
|
||
if (-not (Test-Path $authorizedKeys)) {
|
||
New-Item -ItemType File -Path $authorizedKeys
|
||
}
|
||
$publicKey = Read-Host '粘贴 Mac 的单行 SSH 公钥'
|
||
if (-not (Get-Content $authorizedKeys | Where-Object { $_ -eq $publicKey })) {
|
||
Add-Content -Path $authorizedKeys -Value $publicKey -Encoding ascii
|
||
}
|
||
icacls $authorizedKeys /inheritance:r
|
||
icacls $authorizedKeys /grant "*S-1-5-18:F" "*S-1-5-32-544:F"
|
||
Restart-Service sshd
|
||
```
|
||
|
||
`S-1-5-18` 是 SYSTEM,`S-1-5-32-544` 是本地 Administrators 组。使用 SID 可以避免中文 Windows 的组名本地化问题。
|
||
|
||
### 5.3 Windows 非管理员账户
|
||
|
||
非管理员账户使用用户目录下的 `authorized_keys`。在该登录用户自己的 PowerShell 中执行:
|
||
|
||
```powershell
|
||
$sshDirectory = "$env:USERPROFILE\.ssh"
|
||
$authorizedKeys = "$sshDirectory\authorized_keys"
|
||
New-Item -ItemType Directory -Force -Path $sshDirectory
|
||
if (-not (Test-Path $authorizedKeys)) {
|
||
New-Item -ItemType File -Path $authorizedKeys
|
||
}
|
||
$publicKey = Read-Host '粘贴 Mac 的单行 SSH 公钥'
|
||
if (-not (Get-Content $authorizedKeys | Where-Object { $_ -eq $publicKey })) {
|
||
Add-Content -Path $authorizedKeys -Value $publicKey -Encoding ascii
|
||
}
|
||
icacls $authorizedKeys /inheritance:r
|
||
icacls $authorizedKeys /grant "$($env:USERNAME):F"
|
||
Restart-Service sshd
|
||
```
|
||
|
||
管理员和非管理员两种方式只选与实际账户匹配的一种。若修改了 `C:\ProgramData\ssh\sshd_config`,先执行 `sshd -t` 校验,再重启服务。
|
||
|
||
## 6. 配置 Mac SSH 双场景别名
|
||
|
||
家庭局域网和外部 Tailscale 连接同一台 Windows、同一个账户和同一个 `sshd`,复用同一把项目专用密钥,不创建第二把 SSH 密钥。编辑 Mac 的 `~/.ssh/config`,加入:
|
||
|
||
```sshconfig
|
||
Host tdx-windows-lan tdx-windows-tailnet
|
||
User <WINDOWS_SSH_USER>
|
||
IdentityFile ~/.ssh/tdx_windows
|
||
IdentitiesOnly yes
|
||
UseKeychain yes
|
||
AddKeysToAgent yes
|
||
ServerAliveInterval 30
|
||
ServerAliveCountMax 3
|
||
ExitOnForwardFailure yes
|
||
LocalForward 127.0.0.1:17710 127.0.0.1:17709
|
||
|
||
Host tdx-windows-lan
|
||
HostName <WINDOWS_LAN_HOST>
|
||
|
||
Host tdx-windows-tailnet
|
||
HostName <WINDOWS_TAILSCALE_HOST>
|
||
```
|
||
|
||
真实局域网地址、Tailscale 地址和用户名只保存在本机 SSH 配置,不写入仓库。开发代理需要核对当前配置时使用:
|
||
|
||
```bash
|
||
ssh -G tdx-windows-lan | awk '/^(hostname|user|identityfile|localforward) / {print}'
|
||
ssh -G tdx-windows-tailnet | awk '/^(hostname|user|identityfile|localforward) / {print}'
|
||
```
|
||
|
||
然后修正权限:
|
||
|
||
```bash
|
||
chmod 700 ~/.ssh
|
||
chmod 600 ~/.ssh/config ~/.ssh/tdx_windows
|
||
```
|
||
|
||
首次连接先验证密钥登录:
|
||
|
||
先在 Windows 管理员 PowerShell 查看主机公钥指纹:
|
||
|
||
```powershell
|
||
ssh-keygen -lf "$env:ProgramData\ssh\ssh_host_ed25519_key.pub"
|
||
```
|
||
|
||
再在 Mac 连接,并确认首次连接提示的 Ed25519 指纹与 Windows 输出一致:
|
||
|
||
```bash
|
||
ssh -v tdx-windows-tailnet
|
||
```
|
||
|
||
预期可以直接登录,或只要求输入私钥口令。若仍要求 Windows 账户密码,重点检查公钥文件位置、ACL、登录账户是否属于 Administrators,以及 SSH 调试输出中实际使用的密钥。
|
||
|
||
## 7. 两种场景的选择、启动和停止
|
||
|
||
### 7.1 选择规则
|
||
|
||
| 当前场景 | 使用别名 | 前置条件 |
|
||
|---|---|---|
|
||
| Mac 与 Windows 位于已配置的家庭局域网 | `tdx-windows-lan` | Windows 在线、TDX 已登录、家庭 LAN 的 SSH 22 可达 |
|
||
| Mac 在外部网络,或家庭 LAN SSH 不可达 | `tdx-windows-tailnet` | Mac 与 Windows 的 Tailscale 均在线,Tailnet ping 成功 |
|
||
|
||
两种隧道不能同时启动,因为都会独占 Mac `127.0.0.1:17710`。在家庭网络内优先使用 LAN;若 LAN 前置检查失败,不修改 TDX 监听地址,改用 Tailscale。
|
||
|
||
### 7.2 家庭局域网模式
|
||
|
||
前置检查:
|
||
|
||
```bash
|
||
ssh -o BatchMode=yes -o ClearAllForwardings=yes -T tdx-windows-lan exit
|
||
```
|
||
|
||
仓库文档不保存真实地址,家庭路径直接通过 SSH 别名验证。首次连接必须核对局域网地址与已信任的 Tailscale 地址返回相同 Windows 主机密钥指纹。
|
||
|
||
后台启动和检查:
|
||
|
||
```bash
|
||
ssh -fN -M -S ~/.ssh/tdx-windows-lan-control tdx-windows-lan
|
||
ssh -S ~/.ssh/tdx-windows-lan-control -O check tdx-windows-lan
|
||
```
|
||
|
||
停止:
|
||
|
||
```bash
|
||
ssh -S ~/.ssh/tdx-windows-lan-control -O exit tdx-windows-lan
|
||
```
|
||
|
||
Windows 的家庭 SSH 防火墙规则应使用项目专用名称 `A-Share-TDX-SSH-LAN`,只允许家庭子网访问 TCP 22。系统原有的全范围 OpenSSH 入站规则保持禁用,不为 TDX 开放 `17709`。
|
||
|
||
### 7.3 外部 Tailscale 模式
|
||
|
||
前置检查:
|
||
|
||
```bash
|
||
tailscale up
|
||
tailscale ping <WINDOWS_TAILSCALE_HOST>
|
||
```
|
||
|
||
后台启动和检查:
|
||
|
||
```bash
|
||
ssh -fN -M -S ~/.ssh/tdx-windows-tailnet-control tdx-windows-tailnet
|
||
ssh -S ~/.ssh/tdx-windows-tailnet-control -O check tdx-windows-tailnet
|
||
```
|
||
|
||
停止:
|
||
|
||
```bash
|
||
ssh -S ~/.ssh/tdx-windows-tailnet-control -O exit tdx-windows-tailnet
|
||
```
|
||
|
||
如果控制套接字残留但进程已不存在,先确认 `17710` 没有监听,再删除对应场景的单个控制套接字。不要用宽泛的 `pkill ssh`,以免终止其他 SSH 会话。
|
||
|
||
## 8. 验证 TDX 链路
|
||
|
||
### 8.1 Windows 本机验证
|
||
|
||
先确认 TDX 客户端已启动、已登录,接口监听 `127.0.0.1:17709`:
|
||
|
||
```powershell
|
||
Get-NetTCPConnection -LocalAddress 127.0.0.1 -LocalPort 17709 -State Listen
|
||
```
|
||
|
||
端口没有监听时,应先恢复 TDX 客户端;SSH 和 Tailscale 无法替代 TDX 服务本身。
|
||
|
||
### 8.2 Mac 端口验证
|
||
|
||
隧道启动后在 Mac 执行:
|
||
|
||
```bash
|
||
nc -vz 127.0.0.1 17710
|
||
lsof -nP -iTCP:17710 -sTCP:LISTEN
|
||
```
|
||
|
||
`17710` 的监听进程应为本次 SSH 隧道,不应是其他应用。
|
||
|
||
### 8.3 只读 HTTP 冒烟
|
||
|
||
```bash
|
||
curl --fail-with-body --silent --show-error \
|
||
--connect-timeout 3 \
|
||
--max-time 10 \
|
||
-H 'Content-Type: application/json' \
|
||
-X POST 'http://127.0.0.1:17710/' \
|
||
--data '{"id":1,"method":"get_market_data","params":{"stock_list":["600519.SH"],"count":5,"dividend_type":"none","period":"1d"}}'
|
||
```
|
||
|
||
链路验收条件:HTTP 返回成功,且 TDX 业务响应中的错误码表示成功。只验证少量公开行情,不调用交易、账户、自选、写入或大批量接口。
|
||
|
||
## 9. 外网场景验证
|
||
|
||
在真正离开家庭网络前,使用 Mac 手机热点或其他外部网络完成一次演练:
|
||
|
||
1. 确认 Windows 保持开机、联网,Tailscale 和 `sshd` 在线,TDX 已启动并登录。
|
||
2. Mac 切换到外部网络后重新连接 Tailscale。
|
||
3. 执行 `tailscale ping <WINDOWS_TAILSCALE_HOST>`。
|
||
4. 执行 `ssh -N tdx-windows-tailnet` 并保持终端运行。
|
||
5. 依次执行 `nc` 和只读 `curl` 冒烟。
|
||
6. 停止隧道,确认 `127.0.0.1:17710` 不再监听。
|
||
|
||
如果外网演练失败,开发和自动化测试可显式切换到离线样本,并明确标记数据来源。不要临时开放公网 SSH、RDP 或 TDX 端口。
|
||
|
||
## 10. Windows 无人值守准备
|
||
|
||
当前链路是开发辅助,不承诺生产级 7x24 可用性。为了减少外出后无法联调的概率,可在 Windows 上做以下准备:
|
||
|
||
- 将虚拟机配置为宿主机启动后自动启动,并确认异常关机后的恢复策略。
|
||
- 保持 Tailscale 与 `sshd` 服务自动启动,重启后通过另一台 Tailnet 设备验证在线状态。
|
||
- 接通电源时禁用自动睡眠:
|
||
|
||
```powershell
|
||
powercfg /change standby-timeout-ac 0
|
||
```
|
||
|
||
- 明确 TDX 是否需要交互式桌面登录、验证码或人工确认。需要人工登录时,不得把它描述为无人值守服务。
|
||
- 安排 Windows 更新和虚拟机重启窗口;更新后重新验证 Tailscale、SSH、TDX 监听和只读请求。
|
||
- 不为自动启动把 Windows 密码、TDX 凭据或 SSH 私钥写入仓库和普通脚本。
|
||
|
||
## 11. 收紧访问范围
|
||
|
||
链路验证通过后,在 Tailscale 管理后台将访问规则限制为指定开发身份或 Mac 到 Windows SSH 端口。具体策略应以当前 Tailnet 的身份和标签设计为准,不在仓库保存真实账户和地址。
|
||
|
||
还可在 Windows 中新增一条仅允许 Mac Tailscale IP 访问 SSH 的规则。先确认 Tailscale 网卡名称,再创建规则:
|
||
|
||
```powershell
|
||
Get-NetAdapter | Where-Object InterfaceDescription -Match 'Tailscale'
|
||
New-NetFirewallRule `
|
||
-DisplayName 'OpenSSH Server - Tailscale Mac Only' `
|
||
-Direction Inbound `
|
||
-Action Allow `
|
||
-Protocol TCP `
|
||
-LocalPort 22 `
|
||
-RemoteAddress '<MAC_TAILSCALE_IP>' `
|
||
-InterfaceAlias 'Tailscale'
|
||
```
|
||
|
||
新规则必须在外网场景实测通过后,才能禁用范围更宽的旧 SSH 规则。若实际网卡别名不是 `Tailscale`,使用 `Get-NetAdapter` 显示的名称,不凭猜测修改。
|
||
|
||
## 12. 故障排查
|
||
|
||
### Tailscale ping 不通
|
||
|
||
- 检查两端是否登录同一个 Tailnet、节点是否过期、Windows 是否睡眠或断网。
|
||
- 检查 Tailnet ACL 或设备审批状态。
|
||
- 不要先排查 TDX;此时底层私网尚未连通。
|
||
|
||
### SSH 连接超时或拒绝
|
||
|
||
- Windows 执行 `Get-Service sshd` 和 `Get-NetTCPConnection -LocalPort 22 -State Listen`。
|
||
- 检查 Windows 防火墙和 Tailscale 访问规则。
|
||
- Mac 执行 `ssh -vvv tdx-windows-tailnet`,确认目标是 Tailscale 地址而非家庭公网地址。
|
||
|
||
### SSH 仍要求 Windows 密码
|
||
|
||
- 确认 `IdentityFile` 指向 `~/.ssh/tdx_windows`,并检查 `ssh -vvv` 是否提供了该密钥。
|
||
- 管理员账户确认文件是 `C:\ProgramData\ssh\administrators_authorized_keys`;非管理员账户确认文件是用户目录下的 `.ssh\authorized_keys`。
|
||
- 确认公钥为完整单行文本,文件已先创建,ACL 没有继承无关权限。
|
||
- 执行 `sshd -t` 检查配置,再执行 `Restart-Service sshd`。
|
||
|
||
### Mac 的 `17710` 无法监听
|
||
|
||
- 查看 SSH 前台错误;`ExitOnForwardFailure yes` 会在端口冲突或转发失败时直接退出。
|
||
- 执行 `lsof -nP -iTCP:17710 -sTCP:LISTEN` 查找占用者。
|
||
- 不要把本地端口改成 `0.0.0.0`;需要换端口时同步修改本机开发配置,并保持回环绑定。
|
||
|
||
### 端口可连但 HTTP 失败
|
||
|
||
- 在 Windows 本机确认 TDX `127.0.0.1:17709` 正在监听。
|
||
- 确认 TDX 客户端已登录且当前版本仍支持目标方法。
|
||
- 使用最小只读请求检查 HTTP 状态、TDX 错误码和响应时间,不把协议错误误判为网络错误。
|
||
|
||
### 外出后 Windows 不在线
|
||
|
||
- 无法远程唤醒或恢复交互式 TDX 登录时,显式切回离线样本继续开发,并保持非实时标记。
|
||
- 不通过新增公网暴露、共享密码或关闭防火墙绕过问题。
|
||
- 待公网部署需求明确后,另立功能实现生产级采集器、心跳、补传和远程运维。
|
||
|
||
## 13. 日常操作速查
|
||
|
||
### 13.1 在家开发
|
||
|
||
```bash
|
||
ssh -o BatchMode=yes -o ClearAllForwardings=yes -T tdx-windows-lan exit
|
||
ssh -fN -M -S ~/.ssh/tdx-windows-lan-control tdx-windows-lan
|
||
ssh -S ~/.ssh/tdx-windows-lan-control -O check tdx-windows-lan
|
||
nc -vz 127.0.0.1 17710
|
||
```
|
||
|
||
结束家庭局域网联调:
|
||
|
||
```bash
|
||
ssh -S ~/.ssh/tdx-windows-lan-control -O exit tdx-windows-lan
|
||
lsof -nP -iTCP:17710 -sTCP:LISTEN
|
||
```
|
||
|
||
### 13.2 在外开发
|
||
|
||
```bash
|
||
tailscale up
|
||
tailscale ping <WINDOWS_TAILSCALE_HOST>
|
||
ssh -fN -M -S ~/.ssh/tdx-windows-tailnet-control tdx-windows-tailnet
|
||
ssh -S ~/.ssh/tdx-windows-tailnet-control -O check tdx-windows-tailnet
|
||
nc -vz 127.0.0.1 17710
|
||
```
|
||
|
||
结束外部联调:
|
||
|
||
```bash
|
||
ssh -S ~/.ssh/tdx-windows-tailnet-control -O exit tdx-windows-tailnet
|
||
lsof -nP -iTCP:17710 -sTCP:LISTEN
|
||
```
|
||
|
||
最后一条命令无输出表示本地转发已经停止。切换场景时必须先停止当前隧道,再启动另一场景;不得通过更换 TDX Bridge 地址绕过端口冲突。
|
||
|
||
## 14. 后续 Provider 接入约束
|
||
|
||
- 平台配置只能引用 Mac 回环地址 `127.0.0.1:17710`,不得保存家庭网络或 Tailscale 地址。
|
||
- TDX 是正式行情来源;离线样本只作为显式开发和测试回退,必须展示来源与陈旧状态,生产环境禁止启用。
|
||
- Provider 必须限制请求超时、响应体大小、股票数量和并发,并输出来源、采集时间、交易日期、协议版本、追踪 ID 和陈旧状态。
|
||
- TDX 原始字段仅在 Adapter 内转换,上层市场概览、规则分析和 AI 分析只依赖版本化内部契约。
|
||
- 隧道中断必须返回明确的不可用或陈旧状态,不能把离线样本伪装成实时行情。
|
||
- Windows 采集器、公网接收 API、多目标推送、补传队列和无人值守运维继续延期,实施时单独立项并重新进行安全、性能和合规评估。
|