如何给自己 host 的网站设置 subdomain 并加上 SSL
这是一篇对于自己建站的总结,鉴于目前正规网站都需要 HTTPS,所以写下相关部署的知识,以供需要的人参考。我自己买的是一个小的服务器,并且买了一个域名,相关域名已经设置好了,如果你需要设置 DNS,请参考你购买域名的网站,会有相关文章。另外,我的网站架构是一个 nginx 在前,docker 部署的各种服务在后。nginx 用来设置 subdomain,然后把进来的流量分流给相关的服务。docker 来集成所需要的服务,这样封装性比较好,需要新服务,直接跑一个 docker 命令就好。
前提条件
- 有一个自己的 server,可以是 VPS,只要有公网的 IP 以及 root 的权限就行。
- 有一个购买好的域名,并且这个域名的 DNS record 已经设置好,指向你的 server。
- docker,nginx 已经安装好在 server 上,docker 已经有服务在跑,比如一个指向
3000,一个指向3180。
步骤
设置 subdomain
如果你的网站设置 DNS record 的时候就已经写了一个 wildcard record,比如一个A记录:* A 85.219.30.129(那个* 就是 wildcard,匹配所有字符)那么所有的 subdomain 都会先指向的固定 IP(85…)。所以我的 domian 是 paomian.de,那么api.paomian.de 也会先指向我设置的 IP,然后具体 subdomain 想指向哪儿我服务器上的 nginx 会接管并告知。
想要设置 subdomain,需要配置 nginx ,在他的官方文档可以获取如何安装并开启服务。完成后主要文件都在/etc/nginx下面。默认/etc/nginx/sites-available 会有一个default文件,是设置默认网页的(同时/etc/nginx/sites-enabled 是一个它的软链接,用来告诉nginx启动哪些配置)。为了方便看到所有设置,我就不在给每一个网站一个单独的文件配置,而是直接用默认的配置修改,并全部放在一起。
下图就是 /etc/nginx/sites-availalbe/default的配置:
server {
listen 80;
listen [::]:80;
server_name blog.paomian.de;
location / {
proxy_pass http://127.0.0.1:3180;
proxy_set_header Host $host;
proxy_redirect off;
}
}
server {
listen 80;
listen [::]:80;
server_name chat.paomian.de;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_set_header Host $host;
proxy_redirect off;
}
}
- 两片
server的配置,分别用来指定两个 subdomain:blog.paomian.de,chat.paomian.de。 - 两个
listen分别是指示nginx来监听IP4和IP6的通用 HTTP80端口。 server.name用来指示nginx所属的subdomain,也就是说只要你在浏览器里输入相同的域名,你的浏览器就会先被 DNS 服务器指示来到你的这个 server 上(因为你的主域名),然后你的nginx就会看它的请求是哪个subdomain,然后分配到相关的server配置上。location就是用来path的pattern matching,/可以匹配所有的域名后面的path,也就是说,所有subdomain的请求都会进入这个配置,你也可以在另开一个location比如location /api/,那么所有$server_name/api/的请求都会由他来负责,然后其他的请求还是回归到/的配置。proxy_pass http://127.0.0.1:3000用来指示你的请求通过nginx这个 proxy 去向何方,后面的http://127.0.0.1:3000就是指示它到我的目前server的3000端口,而我的docker的一个服务就跑在这个3000的端口。这里就完成了一个收到请求,然后转向我docker跑的服务的配置。proxy_set_header Host $host: 用来保留原请求header里的Host,在经过proxy后,依然是原来的 host, 也就是你的subdomainproxy_redirect off: 告诉nginx不去更改responseheader里的Location
配置好后,可以重启nginx服务,在Debian下的命令是:
sudo systemctl restart nginx
这时你在浏览器里输入你的subdomain,你的docker下跑的服务就会返回相应的数据。至此,subdomain的设置就完成了,不过这时你的所有服务仍然是通过80端口,就是HTTP服务,想要转变为HTTPS,就需要给你的主机加上SSL certificate。如果需要,请继续看下去。
给网站加上 SSL Certificate
这里就不赘述SSL Certificate 是什么,简而言之,如果你的网站需要开启 HTTPS,你就需要这个。你需要跟相关的Certificate Authority (CA)申请一个,取决于CA,有的需要付费,有的不需要。我这里用的是Let’s Encrypt,一个免费开放的CA。下面是如何通过它来获取你的认证以及如何安装给你的网站。
申请获得 SSL Certificate
既然你要申请,那么你就得符合对方提出的条件。而SSL Certificate的核心要求就是你得证明你有相关域名的控制权。通过证明的方式就是你要通过相应的挑战 - challenge,最常见的两种 challenge 一个是HTTP-01, 一个是DNS-01。HTTP-01的核心思想是你有了域名对应server的根控制权,那么你在里面放上一个我生成的文件,如果我去检查,它在,那你就通过了,我可以给你一个认证。DNS-01的核心思想是你既然控制了域名,那么你肯定也能在域名商那里写一个我指定的额外的 DNS record,我只需要检测下这个 record 跟我想的一样,那么我就承认你的控制权。
一般来说DNS-01比较方便一点儿,因为它可以设置Wildcard certificate,也就是说设置一个有 wildcard 的域名的认证,比如给*.paomian.de设置认证,那么它的所有subdomain就都有了这个认证,不需要再为下面的subdomain分别设置。
以下是如何给自己的域名申请Wildcard certificate,因为我们一次就要用两个subdomain,分别来完成HTTP-01挑战比较麻烦,一梭子搞完才是正道的光。
我用的CA是Let’s Encrypt,用的协议 client 是acme.sh,在你的server里直接安装acme.sh:
curl https://get.acme.sh | sh -s email=my@example.com
email 是你证书过期想被通知到的邮箱。
然后找到你的 dns 商对应的 API:https://github.com/acmesh-official/acme.sh/wiki/dnsapi, 前面说过,DNS-01的挑战主要是在DNS record上搞事情~ 那么你的dns供应商就需要有 API 来让来写文件,验证文件。在上面的网站里会有大部分域名供应商适配这个acme协议的 API 的用法,比如我的供应商是 netcup,我需要在我的控制面板里找到以下三个密码对应我的账户:
export NC_Apikey="<Apikey>"
export NC_Apipw="<Apipassword>"
export NC_CID="<Customernumber>"
然后在运行acme.sh时加上--dns dns_netcup,它就会自动引用这三个变量来向netcup来完成相应的认证。具体申请命令:./acme.sh --issue --dns dns_netcup -d paomian.de -d *.paomian.de,这里我一次申请两个的认证,包括了那个wildcard certificate。
acme.sh现在用的是zeroSSL作为默认CA,但是我用这个会有错误,所以我还是用let's encrypt完成的,加参数即可:./acme.sh --issue --dns dns_netcup -d "paomian.de" -d "*.paomian.de" --server letsencrypt。
它还会自动重复,每次等 10 秒左右,因为毕竟DNS record没有那么快就部署,我记得我第一次部署花了 4-5 分钟左右,一直在等待,最后终于全部通过。
题外话,关于 acme 协议的 client 之争,现在很多是用certbot,我是在读了下面两篇文章后选择用acme.sh的,毕竟依赖少,麻烦小。第一篇文章也有大量自己的实践步骤,我也学了很多,可以参考。
安装 SSL certificate 给网站
如果认证成功,你的acmefolder 里面应该会有你的域名的一个 folder,比如我的~/.acme.sh下就多了一个paomian.de_ecc的文件夹,里面有各种需要的认证,这些就是nginx需要用的:
➜ .acme.sh ls
account.conf acme.sh acme.sh.env ca deploy dnsapi http.header notify paomian.de_ecc
➜ .acme.sh cd paomian.de_ecc
➜ paomian.de_ecc ls
backup fullchain.cer paomian.de.conf paomian.de.csr.conf
ca.cer paomian.de.cer paomian.de.csr paomian.de.key
但是官方强烈不建议直接链接这个里面的文件给nginx用,而是应该用--install来拷贝至你需要的地方。
下面我做的就是设置文件夹,把认证转移过去,并且这个文件夹需要对nginx用户组开放,以便nginx后面使用。因为nginx用的用户组/用户名是www-data:www-data,所以首先要把当前用户放进这个组里,这样就可以在当前用户下在里面写文件,在终端中输入:
sudo usermod -a -G www-data <你的当前用户名>
这样你的用户就属于www-data这个组里的一员了。记得登出,然后登入一下,因为你新设置的组,当前用户只有在进入新的 shell session 里后才会继承。
然后在/home下设置一个相应的文件夹,给www-data组相关的权限,并把相关认证拷贝/安装过去:
sudo mkdir -p /home/www-data/certs/domain.dev
sudo chown -R www-data:www-data /home/www-data
sudo chmod -R g+rw /home/www-data/certs
./acme.sh --install-cert -d paomian.de \
--key-file /home/www-data/certs/paomian.de/key.pem \
--fullchain-file /home/www-data/certs/paomian.de/fullchain.pem \
--reloadcmd "sudo systemctl restart nginx"
sudo chown -R www-data:www-data /home/www-data/certs
sudo chmod -R g+rw /home/www-data/certs
最后那个 --reloadcmd "sudo systemctl restart nginx"是用来指示acme.sh如果安装好自动重启nginx的。如果希望它能自动完成,你还需要给www-data一个sudo systemctl restart 的权利,在/etc/sudoers.d/www-data里写入:
%www-data ALL= NOPASSWD: /bin/systemctl restart nginx.service
这时你就配置好了HTTPS网站所需要的一切认证了,现在只需要你把这个新拷贝的认证链接到你的nginx配置上就好了。跟前面[[如何给自己 host 的网站设置 subdomain 并加上 SSL#设置 subdomain]]里一样,只需要在/etc/nginx/sites-available/default里修改即可:
server {
# SSL configuration
listen 443 ssl;
listen [::]:443 ssl;
ssl_certificate /home/www-data/certs/paomian.de/fullchain.pem;
ssl_certificate_key /home/www-data/certs/paomian.de/key.pem;
server_name blog.paomian.de;
location / {
proxy_pass http://127.0.0.1:3180;
proxy_set_header Host $host;
proxy_redirect off;
}
}
server {
listen 443 ssl;
listen [::]:443 ssl;
ssl_certificate /home/www-data/certs/paomian.de/fullchain.pem;
ssl_certificate_key /home/www-data/certs/paomian.de/key.pem;
server_name chat.paomian.de;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_set_header Host $host;
proxy_redirect off;
}
}
因为我们一开始申请的就是wildcard certificate所以这里两个subdomain用的都是相同的认证。
现在就可以用https://开头来进入你的网站了。
renew 更新 ssl 证书
# renew command
./acme.sh --renew --dns dns_netcup -d "paomian.de" -d "*.paomian.de" --server letsencrypt
# install the certificate and restart the nginx
./acme.sh --install-cert -d paomian.de \
--key-file /home/www-data/certs/paomian.de/key.pem \
--fullchain-file /home/www-data/certs/paomian.de/fullchain.pem \
--reloadcmd "sudo systemctl restart nginx"
查看到期时间
echo | openssl s_client -connect paomian.de:443 2>&1 | openssl x509 -noout -dates
痛点
- 怎么去用 nginx 分流各个 subdomain
- subdomain 直接用自己的服务器上的 nginx 就可以设置,不需要额外的 dns (如果你的 dns record 上已经有一个 wildcard 记录的话,因为这个 wildcard 相当于把所有的 subdomain 都包含了,然后指向你 record 里的 ip)
- 怎么去加 SSL
- chown 给自己的用户后记得登出一下再进去,不然当前用户可能没有获得刚给的权限
id和id lvsun就能看出区别 - 当前用户是 lvsun 的话。 - certbot 和 acme 之争
- acme 是协议,而 certbot 只是一个客户端
- two articles:
- 为了获得 certificate,怎么去做认证
- 需要通过一个 challenge,一个是 http 的,需要你证明你可以在主机上可以创建文件;
- 一个是 dns 的,需要你的网址注册商提供相应的 API 来证明你可以在 DNS 的 record 上加上一段文字。
- www-data 是 nginx 的用户群/名
- 用 acme issue 认证的时候,可以不用默认的 zeroSSL,而是指定为 let’s encypt
acme.sh --issue -d domain.dev -d *.domain.dev --dns dns_porkbun --server letsencrypt - 不要直接 copy 认证,而是用 acme.sh 自带的安装命令来完成证书的复制。
- wildcard 和 直接域名是有区别的:
*.paomian.devspaomian.de, 但是认证的时候可以直接一起认证了,这样,每个 subdomain 就不需要单独再来一次认证了。这个好像只支持 dns challenge
- chown 给自己的用户后记得登出一下再进去,不然当前用户可能没有获得刚给的权限
reference
- acme 的执行文件: https://github.com/acmesh-official/acme.sh/wiki/说明
- acme.sh 用法指导: https://decovar.dev/blog/2021/04/05/acme-sh-instead-of-certbot/
- netcup 用 acme: https://github.com/acmesh-official/acme.sh/wiki/dnsapi#51-use-netcup-dns-api-to-automatically-issue-cert
- netcup API documentation, 如何生成 API key: https://helpcenter.netcup.com/de/wiki/general/unsere-api/
- netcup 用户论坛帖子,前几个的来源: https://forum.netcup.de/netcup-intern/operations/11841-let-s-encrypt-wildcard-zertifikate-via-certbot/