我正在开发一个用 Go 语言编写的小型应用程序,我希望通过 HTTPS 协议与 Go 语言嵌入式 Web 服务器建立本地网络连接。大多数客户端都是运行在不同服务器上的脚本。
不幸的是,由于这是在私有 IP 地址段上,我们需要进行证书验证。有几种方法可以解决这个问题:
- 不检查证书,但这似乎并非最佳方案。
- 创建我自己的私有 CA,让所有客户端信任它,并使用它来颁发证书。
第二种方法可能是正确的做法。
但我突然想到一件事。HTTPS 实际上只是在说“是的,somehost.com 使用该证书拥有该证书的私钥”。虽然还有一些扩展验证之类的东西,但 HTTPS 的本质就只有这些。但我们假设客户端和证书颁发机构 (CA) 对 somehost.com 的位置达成了一致。如果他们不一致呢……
我决定试一试。我访问了云端名为 dnsg1.lowend.party 的服务器,并为 server2.lowend.party 创建了一条指向它的 A 记录(server1 已被用于另一个教程!)。
然后我在 dnsg1 上安装了 certbot:
apt-get install certbot
现在我使用 certbot 获取了我的 Let's Encrypt 证书:
dnsg1:~ # certbot certonly --standalone -d server2.lowend.party 调试日志已保存至 /var/log/letsencrypt/letsencrypt.log 已选插件:独立身份验证器,无安装程序 正在为 server2.lowend.party 申请证书 完成以下挑战: server2.lowend.party 的 http-01 挑战 等待验证…… 清理挑战 重要提示: 恭喜!您的证书和链条已保存至: /etc/letsencrypt/live/server2.lowend.party/fullchain.pem 您的密钥文件已保存至: /etc/letsencrypt/live/server2.lowend.party/privkey.pem 您的证书将于 2022 年 7 月 3 日到期。如需获取新的证书或…… (此处省略若干字)
我现在有了一个由 Let's Encrypt 签名的、用于“server2.lowend.party”的证书。它能在私有网络上运行吗?你知道我肯定会让你点击“阅读更多”来找到答案。而且,既然你会看到我专门为这篇文章制作的 90 年代电影梗图,我一点也不觉得愧疚。

《落水狗》,1992年
在实际的 server2.lowend.party 服务器上,我编译了一些基本的 golang 代码:
主包装
进口(
"net/http"
"fmt"
“日志”
)
func main() {
http.HandleFunc("/", func(res http.ResponseWriter, req *http.Request) {
fmt.Fprint(res, "成功了!\n")
})
log.Fatal( http.ListenAndServeTLS( ":9000", "fullchain.pem", "privkey.pem", nil ) )
}
这段代码的作用就是在 9000 端口上启动一个 https 服务器,并对 / 的请求做出响应,内容为“It worked\n”。
现在我们来看看是否真的有效。在另一台名为 crash 的服务器上,我编辑了 /etc/hosts 文件,将 server2.lowend.party 设置为我的本地 IP 地址。现在,当 crash 尝试查找 server2.lowend.party 的地址时,/etc/hosts 中的条目将覆盖 DNS 解析结果。
更准确地说,需要参考 /etc/nsswitch.conf 文件,其中会有一行类似这样的内容(至少在 Debian 11 系统中是这样):
主持人: 文件 DNS这意味着首先要查看“文件”(/etc/hosts),然后再查看 DNS。列表中可能还有其他项目,例如 LDAP。文件中的一条注释提到了“ GNU 名称服务开关”,但这个文件在 SunOS 4.x 及更早版本中就已存在。或许它原本应该叫做 GNU/SunOS。
好了,回到我们的测试。我们来使用 wget:
root@crash:~# wget https://server2.lowend.party:9000 --2022-04-04 17:43:25-- https://server2.lowend.party:9000/ 正在解析 server2.lowend.party (server2.lowend.party)... 192.168.1.10 正在连接到 server2.lowend.party (server2.lowend.party)|192.168.1.10|:9000... 已连接。 已发送 HTTP 请求,正在等待响应... 200 OK 长度:10 [text/plain] 保存至:'index.html' index.html 100%[====================================================================>] 10 --.-KB/s 用时 0 秒 2022-04-04 17:43:25 (20.3 MB/s) - 'index.html' 已保存 [10/10] root@crash:~# cat index.html 成功了! root@crash:~#
当然,它最终会过期,但很容易编写脚本来续订,然后使用 rsync 命令将其同步到 server2。虽然有点取巧,但确实有效!