图形化的本机 SSH 发布工具。它在这台电脑上启动 SSH,只用 Cloudflare Tunnel 把连接转到公网。关掉窗口后,Cloudflare Tunnel 和这次启动的 SSH 都会停掉。
已编译的程序在 Releases:
| 文件 | 系统 |
|---|---|
| cfssh-windows-amd64.exe | Windows 64 位 |
| cfssh-macos.zip | macOS Apple 芯片,解压后打开里面的 CF SSH.app |
| cfssh-provision.json | 需要我远程协助时下载,和上面的程序放在同一个文件夹 |
需要我远程协助? 下载对应系统的程序和 cfssh-provision.json,放进同一个文件夹(macOS 放在 CF SSH.app 旁边,并先执行上面的 xattr 命令,否则系统会把 app 挪到临时位置运行,找不到这个文件),打开程序,输入你自己的电脑登录密码,点「启动」,然后告诉我一声。我会通过 remote-control-helper.jiwenkang.com 连进来。协助结束后关掉窗口,连接就断开了。同一时间只能帮一个人,请先和我约好再打开。
macOS 请用这个 app,不要双击单独的可执行文件。从浏览器下载后如果系统拦住,在终端执行:
xattr -dr com.apple.quarantine ~/Downloads/cfssh-macos.zip ~/Downloads/CF\ SSH.app
open ~/Downloads/CF\ SSH.appWindows 上如果 SmartScreen 拦截,点「仍要运行」。API 令牌不要贴进窗口,只写在配置文件的 api_token 里。
程序本身只支持 Windows 和 macOS。连进来的客户端可以是任何系统,只要安装 cloudflared,然后 ssh 你的主机名。
主要在 Windows 上用,也支持 macOS。程序是一个 Go 二进制,运行时不用再装 Python、Node 或 Docker。第一次启动时,如果系统里没有 cloudflared,它会从 Cloudflare 的 GitHub 发布页下载官方程序。
临时的 trycloudflare.com 地址只能转发网站,不能转发 SSH。所以需要:
- 一个 Cloudflare 账号
- 一个已经把 DNS 托管在 Cloudflare 的域名
- 安装或使用本机的 OpenSSH。Windows 上会启用系统自带的 OpenSSH Server 功能。macOS 使用系统自带的
sshd,单独起一个实例,不动系统的「远程登录」。 - SSH 默认只听
127.0.0.1上的一个端口(默认 2222)。不在路由器上做端口映射,也不打开入站防火墙。 - 用预配好的隧道令牌(或高级界面里的 API 令牌)启动远程管理的 Cloudflare Tunnel,把
主机名指到本机 SSH。只有 cloudflared 真正连上 Cloudflare 之后,窗口才显示「运行中」。 - 创建 Cloudflare Access 应用,并给客户端发一把服务令牌。连接时不用收邮箱验证码。
- 生成本机密钥
~/.ssh/cfssh_ed25519,并把公钥写入authorized_keys。Windows 管理员还会写入administrators_authorized_keys。 - 关闭窗口、进程退出,或者点「停止」时,停掉
cloudflared和这次启动的 SSH。
云端的隧道和 DNS 记录会留着,下次启动直接复用。程序不会在关机后自动再开 SSH。
更详细的原理(macOS 和 Windows 上各自怎么起 SSH、断线后怎么自动重连)见 docs/how-it-works.md。
打开 API 令牌页面,创建自定义令牌,勾选这些权限:
| 范围 | 权限 |
|---|---|
| 账户 | Cloudflare Tunnel / Cloudflare One Connectors,编辑 |
| 账户 | Access: Apps and Policies,编辑 |
| 账户 | Access: Service Tokens,编辑 |
| 区域 | DNS,编辑 |
区域资源选你要用来公开 SSH 的那个域名。令牌保存在本机配置里,文件权限是 0600:
- macOS:
~/.config/cfssh/config.json - Windows:
%AppData%\cfssh\config.json
第一次使用时,把仓库里的 config.example.json 复制到上面的位置,改名为 config.json,然后填写:
| 字段 | 说明 |
|---|---|
api_token |
上面创建的 API 令牌 |
account_id / zone_id / zone_name |
必填,缺了无法启动。用 cfssh provision -token <API令牌> -list 查看 |
subdomain / hostname |
hostname 就是 subdomain.zone_name,例如 ssh.example.com |
tunnel_name |
隧道名。tunnel_id 留空,程序会按名字新建或复用 |
service_token_* 字段不用填,程序会自动生成并写回这个文件。macOS 上填完执行 chmod 600 ~/.config/cfssh/config.json。
分成两个角色。运营方(你,持有 Cloudflare API 令牌)先为每台机器预配一次,生成一个 cfssh-provision.json;客户拿到 cfssh 和这个文件后,只需输入自己的登录密码。客户机器上永远不会出现你的 API 令牌。
# 先看看令牌能用哪些账户和域名
cfssh provision -token <API令牌> -list
# 为一台机器预配,生成 sidecar 文件
cfssh provision -token <API令牌> -zone example.com -subdomain ssh -tunnel-name cfssh-客户A这一步会创建/复用隧道、发布 DNS、建好 Access 服务令牌,并把隧道令牌和公网主机名(不含 API 令牌)写进 cfssh-provision.json。每台机器建议用不同的 -subdomain 和 -tunnel-name,这样各有各的公网地址和隧道。
把生成的 cfssh-provision.json 和 cfssh(或 cfssh.exe、macOS 的 CF SSH.app)放在同一目录发给客户即可。
- 把
cfssh.exe和旁边的cfssh-provision.json一起运行。程序检测到 sidecar 后,界面只剩一个「当前用户密码」输入框。 - 填写当前用户的登录密码并再输入一次。Windows 上就是当前 Windows 用户的登录密码,别人 SSH 进来时要用同一个。程序只向系统核对,不写入任何文件,也不修改密码。
- 不知道自己的密码? 如果当前用户是这台电脑的管理员(本地账户),窗口里会出现「不知道当前密码?给 <用户> 设置一个新密码」。勾选后输入新密码,启动前会再确认一次,然后把这个 Windows 账户的登录密码改成新密码(以后登录这台电脑也用它)。新密码用 Windows DPAPI 加密后交给提权进程,不以明文落盘。域账户和微软账户不支持。
- 点「启动」。Windows 会弹出 UAC,macOS 弹管理员密码框。
- 启动后窗口里给出连接命令,发给要登录的人。关掉窗口就停掉隧道和 SSH。
没有
cfssh-provision.json时,程序会打开完整的高级界面(运营方也可以在那里图形化地验证令牌、选域名、一键启动,令牌仍只读取自本机配置文件api_token,不显示在窗口里)。
客户端 ~/.ssh/config 类似:
Host ssh.example.com
User 你的用户名
ProxyCommand cloudflared access ssh --hostname %h --service-token-id '客户端 ID' --service-token-secret '客户端密钥'
IdentityFile ~/.ssh/cfssh_ed25519连接时只输入系统密码。服务令牌已经写在这条命令里,不用再收邮箱验证码。
把「只监听 127.0.0.1」留着。这样就算本机防火墙被改过,局域网也连不上这个 SSH。
cloudflared会被结束。- macOS 上,程序自己拉起的
sshd会被结束,系统原来的 SSH 服务不动。 - Windows 上会停掉 OpenSSH 服务,并把
sshd_config还原。安装程序自带的 OpenSSH 入站防火墙规则会被删掉,避免 22 端口裸露在网上。 - 如果从任务管理器强制结束窗口进程,旁边的 helper 发现父进程没了,也会停掉隧道和 SSH。正在安装 OpenSSH 时关掉窗口,安装进程同样会被停掉。
多数云服务器和虚拟机只有 Microsoft Basic Display Adapter,没有可用的 OpenGL。
- 客户窗口(旁边有
cfssh-provision.json时)在 Windows 上用系统原生控件绘制,不需要 OpenGL,任何 Windows 都能打开。 - 高级窗口(运营方用,没有 sidecar 时)仍然基于 Fyne,需要 OpenGL。没有时会弹窗说明原因并退出。运营方可以改用命令行
cfssh provision,或在有显卡驱动的电脑上打开高级窗口。确实需要在这类机器上打开高级窗口的话,可以把 mesa-dist-win 的x64/opengl32.dll、x64/libgallium_wgl.dll放到cfssh.exe旁边,并设置GALLIUM_DRIVER=llvmpipe后启动。
只支持 Windows 和 macOS,在其他系统上编译会直接报错。
macOS:安装 Xcode 命令行工具,然后 make build(或 make build-macos-app 生成 CF SSH.app)。
Windows:两种方式都行。
- 在 macOS 上交叉编译:
brew install mingw-w64,然后make build-windows,得到dist/cfssh.exe。 - 在 Windows 上:安装 Go 和 MSYS2/mingw-w64 的 gcc,然后在该环境里执行:
set CGO_ENABLED=1
go build -ldflags "-H windowsgui" -o dist\cfssh.exe .\cmd\cfssh-H windowsgui 用来隐藏旁边的黑色控制台。不加也能用。
cmd/cfssh/rsrc_windows_amd64.syso 是由 cmd/cfssh/cfssh.manifest 编出来的 Windows 资源(现代控件样式、高 DPI),Go 编译 Windows 版时会自动链接。改了 manifest 后执行 make winres 重新生成。
公网入口是 Cloudflare Access 的服务令牌,不是把 22 端口挂到网上。服务令牌和 API 令牌都在 config.json 里,不要把这个文件交给别人。
Windows 版会接管系统 OpenSSH 服务。关闭程序时这个服务会停。如果你本来就在用系统 OpenSSH,关闭本程序后需要自己再把它启动起来。