大宋汽车 發表於 2025-3-23 16:50:00

如何不购买域名在云服务器上搭建HTTPS服务

<h1 id="step-1-事前准备">step 1: 事前准备</h1>
<h2 id="step-11-云服务器">step 1.1: 云服务器</h2>
<p>购买一台云服务器(带有弹性公网IP),阿里云,腾讯云,华为云什么的都可以。<br>
选择ubuntu系统</p>
<p>开放安全组策略(把你需要的协议/端口暴露出来):</p>
<ul>
<li>TCP:22:ssh</li>
<li>TCP:80:HTTP</li>
<li>TCP:443:HTTPS</li>
<li>ICMP:ping</li>
</ul>
<p>这里我们强烈不推荐暴露所有的端口,根据权限最小化原则,仅应该暴露你需要的端口</p>
<p><img src="https://img2024.cnblogs.com/blog/3061928/202503/3061928-20250323164000819-845727057.png"></p>
<h2 id="step-12-caddy">step 1.2: Caddy</h2>
<p>官方文档:https://caddy2.dengxiaolong.com/docs/install<br>
仓库地址:https://github.com/caddyserver/caddy</p>
<p>Caddy是一个强大的反向代理工具,当然也可以被用作站点服务器。本文使用Caddy作为主要配置工具。</p>
<p>安装脚本:</p>
<pre><code>sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.list
sudo apt update
sudo apt install caddy
</code></pre>
<p>安装完成后,应当显示:</p>
<pre><code>~# caddy --version
v2.9.1
</code></pre>
<h2 id="step-13-apifox">step 1.3: Apifox</h2>
<p>这里安利一波Apifox,可以非常方便的进行接口管理,构造自动化测试等工作,支持RESTful风格的API等。</p>
<p>界面简洁优雅,使用非常方便,并且对于个人开发者来说完全免费,符合我个人的审美观念。</p>
<p><img src="https://img2024.cnblogs.com/blog/3061928/202503/3061928-20250323164143424-922878657.png"></p>
<hr>
<h1 id="step-2获取证书">step 2:获取证书</h1>
<p>众所周知,HTTPS相比于HTTP,其最大的特性就是使用了SSL/TLS对数据进行加密。<br>
我们依赖证书链使用TLS在客户端和服务器之间建立可信连接,具体原理比较复杂这里不多解释。</p>
<p>一般来说,证书是要由CA来进行签发,如果你购买了域名,自然会获取对应的证书。<br>
但是,此处我们没有购买形似<code>example.com</code>的域名,而是类似<code>149.33.138.14</code>这样的裸露公网IP,这就需要我们提前获取对于裸IP的证书支持。<br>
但遗憾的是,绝大部分针对裸IP的证书都需要收费。</p>
<p>毕竟,<strong>奇迹和魔法可不是免费的</strong>,https和域名当然也不是,大家都是穷人,尽量还是少花点钱。</p>
<h2 id="step-21-获取免费证书">step 2.1 获取免费证书</h2>
<p>所幸,这里有一个方法,可以对裸IP获取90天的免费证书,到期相同方法续期即可</p>
<p>该服务由zeroSSL提供,我们使用的Caddy也是zeroSSL下的项目之一</p>
<p>这个教程写的还挺详细的,这里就不抄过来了:https://www.landiannews.com/archives/93605.html</p>
<p>但是,这里我们需要保证<code>IP地址+文件夹+文件</code>能够访问,这里我们需要使用Caddy先建立一个最简单的HTTP server,从服务器上获取静态文件</p>
<p>首先查看80端口(http),确保未被监听:</p>
<pre><code>root@hcss-ecs-0ef3:~# sudo lsof -i :80
root@hcss-ecs-0ef3:~#
</code></pre>
<p>在当前路径下创建Caddyfile(可以理解为配置文件),需要设定为http,<code>file_server</code>支持静态文件访问,<code>/var/www/html</code>为server的根文件目录</p>
<pre><code>http://149.33.138.14 {
    root * /var/www/html
    file_server
}
</code></pre>
<p>创建<code>.well-known/pki-validation/</code>路径并且将文件拷贝到路径下:</p>
<pre><code>sudo mkdir -p /var/www/html/.well-known/pki-validation
sudo cp /path/to/{filename}.txt /var/www/html/.well-known/pki-validation/
</code></pre>
<p>caddy的管理端口默认为<code>localhost:2019</code>,如果发现端口被占用,可以通过<code>sudo lsof -i :2019</code>查看是谁占用了<br>
有可能是自动启动的caddy systemd service和手动启动的caddy发生冲突了<br>
这里我们选择手动启动,将service disable掉:</p>
<pre><code>sudo systemctl status caddy
sudo systemctl stop caddy
sudo systemctl status caddy
</code></pre>
<p>此时应当观察到service状态为<code>Active: inactive (dead)</code>,确定port 2019无人监听后可以重新手动启动caddy:</p>
<pre><code>sudo caddy stop
caddy fmt --overwrite
sudo caddy start
</code></pre>
<p>如果想要更改默认2019端口,可以配置<code>CADDY_ADMIN</code>环境变量,此处不再赘述。<br>
此时再次查看80端口(http),应当已经被监听:</p>
<pre><code>root@hcss-ecs-0ef3:~# sudo lsof -i :80
COMMAND   PID USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
caddy   65370 root    9uIPv6 562057      0t0TCP *:http (LISTEN)
</code></pre>
<p>此时可以开始验证http server是否能够被访问,查看是否有文件内容:<br>
方法一:<code>curl -X GET http://149.33.138.14/.well-known/pki-validation/{filename}.txt</code></p>
<p><img src="https://img2024.cnblogs.com/blog/3061928/202503/3061928-20250323164228100-168299595.png"></p>
<p>方法二:使用Apifox,设置GET方法和request:</p>
<p><img src="https://img2024.cnblogs.com/blog/3061928/202503/3061928-20250323164555486-529534350.png"></p>
<p>至此,HTTP server设置并验证完毕,返回zeroSSL,点击验证并签发证书,下载文件列表如下:</p>
<ul>
<li>certificate.crt:根节点证书</li>
<li>ca_bundle.crt:中间节点证书</li>
<li>private.key:密钥</li>
</ul>
<p><img src="https://img2024.cnblogs.com/blog/3061928/202503/3061928-20250323164254403-1668777374.png"></p>
<p>将以上证书文件上传到服务器中。</p>
<h2 id="step-22-搭建https-server">step 2.2 搭建https server</h2>
<h3 id="step-221-验证证书合法性">step 2.2.1 验证证书合法性</h3>
<p>我们将证书存储在<code>/etc/caddy/ssl</code>路径下,统一管理:</p>
<pre><code>sudo mkdir -p /etc/caddy/ssl
sudo cp certificate.crt /etc/caddy/ssl/
sudo cp ca_bundle.crt /etc/caddy/ssl/
sudo cp your_private.key /etc/caddy/ssl/
</code></pre>
<p>设置权限:</p>
<pre><code>sudo chmod 600 /etc/caddy/ssl/*
sudo chown -R root:root /etc/caddy/ssl/
</code></pre>
<p>验证证书链是否完成,应当输出OK:</p>
<pre><code>openssl verify -CAfile /etc/caddy/ssl/ca_bundle.crt /etc/caddy/ssl/certificate.crt
</code></pre>
<p>验证私钥和证书是否匹配,两者输出应当相同:</p>
<pre><code>openssl x509 -noout -modulus -in /etc/caddy/ssl/certificate.crt | openssl md5
openssl rsa -noout -modulus -in /etc/caddy/ssl/private.key | openssl md5
</code></pre>
<p>出于使用简单的考虑,我们首先拼接证书链:</p>
<pre><code>cat /etc/caddy/ssl/certificate.crt /etc/caddy/ssl/ca_bundle.crt &gt; /etc/caddy/ssl/fullchain.crt
</code></pre>
<p>再次验证证书内容:</p>
<pre><code>openssl x509 -in /etc/caddy/ssl/fullchain.crt -text -noout
openssl rsa -in /etc/caddy/ssl/private.key -check
</code></pre>
<h3 id="step-222-搭建">step 2.2.2 搭建</h3>
<p>首先查看443端口(https),确保未被监听:</p>
<pre><code>root@hcss-ecs-0ef3:~# sudo lsof -i :443
root@hcss-ecs-0ef3:~#
</code></pre>
<p>修改Caddyfile,这是一个最简单的server配置:</p>
<pre><code>{
        default_sni 149.33.138.14
}
https://149.33.138.14 {
        tls /etc/caddy/ssl/fullchain.crt /etc/caddy/ssl/private.key
        respond "Hello, world!" 200
}
</code></pre>
<p>重新启动caddy服务:</p>
<pre><code>sudo caddy stop
caddy fmt --overwrite
sudo caddy start
</code></pre>
<p>此时在另一台服务器上运行:</p>
<pre><code>openssl s_client -connect 149.33.138.14:443 -servername 149.33.138.14
</code></pre>
<p>Verify return code应当返回:0 (ok)</p>
<p><img src="https://img2024.cnblogs.com/blog/3061928/202503/3061928-20250323164715952-508851006.png"></p>
<p>在另一台服务器上执行<code>curl -kv https://149.33.138.14/</code>,可以查看到连接全过程:</p>
<pre><code>xiao@DESKTOP-S896N2C:~$ curl -kv https://149.33.138.14/
*   Trying 149.33.138.14:443...
* Connected to 149.33.138.14 (149.33.138.14) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* TLSv1.0 (OUT), TLS header, Certificate Status (22):
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.2 (IN), TLS header, Certificate Status (22):
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.2 (IN), TLS header, Finished (20):
* TLSv1.2 (IN), TLS header, Supplemental data (23):
* TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8):
* TLSv1.2 (IN), TLS header, Supplemental data (23):
* TLSv1.3 (IN), TLS handshake, Certificate (11):
* TLSv1.2 (IN), TLS header, Supplemental data (23):
* TLSv1.3 (IN), TLS handshake, CERT verify (15):
* TLSv1.2 (IN), TLS header, Supplemental data (23):
* TLSv1.3 (IN), TLS handshake, Finished (20):
* TLSv1.2 (OUT), TLS header, Finished (20):
* TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.2 (OUT), TLS header, Supplemental data (23):
* TLSv1.3 (OUT), TLS handshake, Finished (20):
* SSL connection using TLSv1.3 / TLS_AES_128_GCM_SHA256
* ALPN, server accepted to use h2
* Server certificate:
*subject: CN=119.3.178.14
*start date: Mar 22 00:00:00 2025 GMT
*expire date: Jun 20 23:59:59 2025 GMT
*issuer: C=AT; O=ZeroSSL; CN=ZeroSSL RSA Domain Secure Site CA
*SSL certificate verify result: unable to get local issuer certificate (20), continuing anyway.
* Using HTTP2, server supports multiplexing
* Connection state changed (HTTP/2 confirmed)
* Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0
* TLSv1.2 (OUT), TLS header, Supplemental data (23):
* TLSv1.2 (OUT), TLS header, Supplemental data (23):
* TLSv1.2 (OUT), TLS header, Supplemental data (23):
* Using Stream ID: 1 (easy handle 0x559918bdf9f0)
* TLSv1.2 (OUT), TLS header, Supplemental data (23):
&gt; GET / HTTP/2
&gt; Host: 149.33.138.14
&gt; user-agent: curl/7.81.0
&gt; accept: */*
&gt;
* TLSv1.2 (IN), TLS header, Supplemental data (23):
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
* TLSv1.2 (IN), TLS header, Supplemental data (23):
* Connection state changed (MAX_CONCURRENT_STREAMS == 250)!
* TLSv1.2 (OUT), TLS header, Supplemental data (23):
* TLSv1.2 (IN), TLS header, Supplemental data (23):
* TLSv1.2 (IN), TLS header, Supplemental data (23):
* TLSv1.2 (IN), TLS header, Supplemental data (23):
&lt; HTTP/2 200
&lt; alt-svc: h3=":443"; ma=2592000
&lt; content-type: text/plain; charset=utf-8
&lt; server: Caddy
&lt; content-length: 13
&lt; date: Sun, 23 Mar 2025 08:07:31 GMT
&lt;
* TLSv1.2 (IN), TLS header, Supplemental data (23):
* Connection #0 to host 149.33.138.14 left intact
</code></pre>
<p>通过Apifox访问,能够正确响应输出: Hello, world!:</p>
<p><img src="https://img2024.cnblogs.com/blog/3061928/202503/3061928-20250323164510518-1780344134.png"></p>
<p>在浏览器中也能够正常访问,且没有任何安全问题:</p>
<p><img src="https://img2024.cnblogs.com/blog/3061928/202503/3061928-20250323164655226-704732100.png"></p>
<p>至此,一个最简单的HTTPS server搭建完成。</p>
<hr>
<h1 id="过程中会踩的坑和可能遇到的问题">过程中会踩的坑和可能遇到的问题</h1>
<h2 id="搭建http-server时无法访问">搭建http server时无法访问</h2>
<p>表现:</p>
<pre><code>2025/03/22 18:52:20.394 INFO    http.auto_https server is listening only on the HTTPS port but has no TLS connection policies; adding one to enable TLS{"server_name": "srv0", "https_port": 443}
2025/03/22 18:52:20.394 INFO    http.auto_https enabling automatic HTTP-&gt;HTTPS redirects      {"server_name": "srv0"}
</code></pre>
<p>原因:http请求被重定向,可能是由于Caddyfile写成了这样:</p>
<pre><code>119.3.178.14 {
    root * /var/www/html
    file_server
}
</code></pre>
<p>解决方案:单纯一个caddy已经可以作为http server了,但是会caddy自动重定向http到https,所以需要显式指定http</p>
<h2 id="启动caddy失败">启动caddy失败</h2>
<p>首先看日志,<code>lsof</code>监测对应端口是否被占用。<br>
手动启动的和自动启动的systemd.service是有冲突的,只能启动一个,使用的Caddyfile也不同</p>
<h2 id="https无法访问-verify-return-code-21-unable-to-verify-the-first-certificate">https无法访问 Verify return code: 21 (unable to verify the first certificate)</h2>
<p>openssl验证出现:<code>Verify return code: 21 (unable to verify the first certificate)</code></p>
<p>https://github.com/caddyserver/caddy/issues/6344中提出了该问题,是由于client找不到<strong>裸ip server</strong>的server name(域名则无事)</p>
<p>所以在Caddyfile中一定需要:</p>
<pre><code>{
        default_sni 119.3.178.14
}
</code></pre>
<h2 id="请求返回-405-the-method-is-not-allowed-for-the-requested-url">请求返回 405 The method is not allowed for the requested URL</h2>
<p>这个问题大概率是由于混合使用了<code>GET</code>和<code>POST</code>方法,比如获取文件的方式是<code>GET</code></p>
<h2 id="curl--v-报错-tlsv13-in-tls-alert-internal-error-592">curl -v 报错 TLSv1.3 (IN), TLS alert, internal error (592):</h2>
<p>如果证书链分开,caddy似乎无法以这种方式建立TLS可信连接:</p>
<pre><code>    tls /etc/caddy/ssl/certificate.crt /etc/caddy/ssl/your_private.key {
      ca_root /etc/caddy/ssl/ca_bundle.crt
    }
</code></pre><br><br>
来源:https://www.cnblogs.com/kazusarua/p/18788019
頁: [1]
查看完整版本: 如何不购买域名在云服务器上搭建HTTPS服务