很久以前就有听说 Cloudflare Teams 这一零信任网络服务,且其免费套餐足以个人用户使用,不过因为各种原因一直觉得必要性不大,没有进行配置;在2021年底和2022年初接连爆出 Grafana 目录遍历、Log4j2 RCE、PwnKit 提权 等一系列惊人漏洞后,感觉可能还是有必要提高一下自己手头基础设施的安全等级。考虑到手头的服务中,有一个直接暴露在公网的 Grafana 和一个 Minecraft 服务器,决定先从访问鉴权做起,防止把此类服务暴露在公网。
Disclaimer: Cloudflare Teams 的服务器位于新加坡和美国洛杉矶,从国内访问可能较慢。一般来说,用于给托管在海外的服务器提供安全访问通道并无明显性能问题,但对于国内的服务器则可能有较大延迟。这一情况下,可以考虑采用 n2n 等点对点方案代替,或者采用其他加速方案,但请考虑可能带来的合规问题。
配置 Cloudflare Teams
正如其名,如果想要使用这一服务,你需要有一个解析托管在 Cloudflare 的域名;幸运的是,我手头的两个域名都恰好托管在 Cloudflare 上。如果你的域名仍然在使用其他解析服务,需要先切换到使用 Cloudflare 再继续。
在 Cloudflare Dashboard 点击 域名 > 左侧边栏 Access,首先添加一种 One-Time Pin 以外的登录方法,我图省事就选择了 GitHub。用这个凭据即可认证受到 Teams 保护的服务,所以务必注意其安全性。
Cloudflare Teams 大致支持3类服务:
- 基于 Cloudflare Edge Network 的网站代理,官方称之为 Application,通过 Cloudflare 的网络对访问者鉴权后,把流量转发给后端服务,一切都在浏览器中进行。
- 基于 Cloudflare Warp 的 Warp Network,类似传统的虚拟专用网业务,需要用户运行单独的 Warp 应用程序。
- 基于 DNS over HTTPS / DNS over TLS 的网络级代理,类似带有虚拟专用网功能的路由器。
而将应用程序连接到 Cloudflare Edge Network 又有下列方法
- 采用
cloudflared
守护程序,主动建立到 Cloudflare Edge Network 的隧道(即 Argo Tunnel)。 - 暴露在公网,但对传入连接进行 JWT 验证以确保其来自 Cloudflare。
理论上,使用 cloudflared
可以把任何防火墙后的服务暴露到公网,类似于让 Cloudflare 的网络替你做了端口转发、HTTPS 解密等一系列操作。
实例1:配置 Grafana
之前 Grafana 是通过一层反向代理暴露在公网的,由反向代理处理 HTTPS 流量;现在只需要把反向代理放在 Cloudflare 上即可。
具体做法是,创建一个 Argo Tunnel,然后设置 Tunnel 的 Ingress 服务为本地的反向代理目标端口(如 localhost:8080
),最后在 Teams Dashboard 配置一个 Application.
参考文档,在服务器上安装 cloudflared
软件,配置一个 Tunnel:
$ sudo cloudflared tunnel login # 登录 Cloudflare 账户
$ sudo cloudflared tunnel create grafana # 创建 tunnel,请记录输出的UUID,此后记为 <隧道UUID>
$ sudo mkdir -p /root/.cloudflared
$ sudo vim "/root/.cloudflared/config.yml"
在上述 VIM 打开的文件中,如下配置 Grafana:
tunnel: <隧道UUID>
credentials-file: /root/.cloudflared/<隧道UUID>.json
ingress:
- hostname: grafana.example.site # 你的服务域名,之后会用到
service: http://localhost:8080 # 8080为本机Grafana端口
- service: http_status:404 # 不符合上述要求的,返回 404
再到 Cloudflare Dashboard 中修改 grafana.example.site
的 DNS 记录为 CNAME,值为 <隧道UUID>.cfargotunnel.com
。(也可以使用 cloudflared tunnel route dns <隧道UUID> <域名>
这一指令)
启动 Tunnel 服务:
$ sudo cloudflared service install
$ sudo systemctl enable --now cloudflared
最后在 Teams Dashboard 选择添加 Self-hosted Application,选择刚才的域名,并进行适当的权限设置即可。此时访问你指定的域名,则会出现如下窗口:
在完成认证后,才可以进入 Grafana 的登录页,最大限度保证了应用程序的安全。
实例2:配置内网访问
现有一个 192.168.1.0/24
的局域网,需要允许通过 Warp Client 的方式进行访问。为此,我们在局域网内的一台设备安装 cloudflared
,并且配置好 warp routing,即可使用户得以访问内网内的资源。
内网通常有自己的 DNS 服务器,为此,除了运行 Warp Client 外,还可能需要在联网的设备进行额外的设置。目前可以参考这份文档;之后也可以采用 Cloudflare 提供的 DNS 服务(尚未发布)。
首先,需要在 Teams Dashboard 进行 Split Tunnels 的配置;Settings > Network > Firewall > Split Tunnels 选择 Include IPs and Domains,并且点击 Manage 设置仅仅包括需要代理的内网 IP.
然后类似上文安装 cloudflared
,但如下配置 config.yml
:
tunnel: <隧道UUID>
credentials-file: /root/.cloudflared/<隧道UUID>.json
warp-routing:
enabled: true
再配置 cloudflared
的路由表:
$ sudo cloudflared tunnel route ip add <CIDR网段> <隧道UUID>
最后启动隧道即可。在自己的设备上安装启用 Cloudflare Warp,即可正常上网。
由于国内网络环境众所周知的原因,Cloudflare Warp 经常无法和其 API 通信完成注册设备;遇到此类情况请自行解决。
实例3:配置 SSH 远程登录
由于使用 Cloudflare 内嵌在浏览器的 SSH 客户端,SSH远程登录的情景和实例1类似,只是反向代理端口从 HTTP 服务变成了一个一般的 TCP 端口 (即 22)。默认配置下,完成 Cloudflare Teams 鉴权后还需要采用 SSH Key 进行鉴权,为了减少一次鉴权,可以考虑配置 Short-lived certificate,这样在连接后,即可通过 cloudflared
自动签发的证书完成鉴权。
首先修改实例1中的 config.yml 如下:
tunnel: <隧道UUID>
credentials-file: /root/.cloudflared/<隧道UUID>.json
ingress:
- hostname: server.example.site # 你的服务域名
service: ssh://localhost:22 #
- service: http_status:404 # 不符合上述要求的,返回 404
并且在 Teams Dashboard 创建 Application 时,在 Cloudflared settings 内勾选 “enable automatic cloudflared authentication”,选择 Browser rendering 为 SSH.
接着,参考文档完成 ssh 配置即可。
文档中记录的步骤设置 SSH 为 PubkeyAuthentication 认证方式,并且通过设置 TrustedUserCAKeys,让 Cloudflare 作为 CA 来签发用户登录所用 Key。这样,即可把 Cloudflare Teams SSO 的认证凭据对接给 OpenSSH,从而允许用户成功登录。这里 sshd 对 CA Key 的检查是经典的公私钥体系操作:只需要用户出示的 Public key 由指定的 CA 签发即可。
其实,ssh 不仅支持以这种方式进行用户鉴权,还支持对服务器的公钥进行签名,从而实现可信的服务器鉴权。在签名服务器的 Host Key 后,需要配置客户端信任签发所用 CA,此后客户端连接服务器就不会遇到 Unknown Host 的提示了。因为服务端的真实性,已经由 CA 的签名所证明。
这一套机制非常像 TLS 中的服务端证书和客户端证书,不过有趣的是,大多数 TLS 流量只有服务端证书签名,而不需要客户端证书,而 SSH 的公钥体系中,常常是对客户端证书进行签名,服务端真实性则采用所谓
known_hosts
文件进行记录管理。
参考资料
- https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/install-and-setup
- https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/routing-to-tunnel/dns
- https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/routing-to-tunnel/dns
- https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/configuration/configuration-file/ingress
- https://developers.cloudflare.com/cloudflare-one/connections/connect-devices/warp
- https://developers.cloudflare.com/cloudflare-one/applications/configure-apps/self-hosted-apps
- https://developers.cloudflare.com/cloudflare-one/tutorials/ssh-browser#recommended-enable-short-lived-certificates
- https://developers.cloudflare.com/cloudflare-one/identity/users/short-lived-certificates
- https://betterprogramming.pub/how-to-use-ssh-certificates-for-scalable-secure-and-more-transparent-server-access-720a87af6617