环境声明
以下内容都是建立在2个前提
- 免费版CF大善人域名托管
- 运行私有化服务的公网IP主机(eg: 甲骨文 ARM 424主机)
这是弄什么
结合域名DNS解析的通配符机制,使用一个Nginx配置的HTTPS反向代理通配多个服务,实现接近于一次配置,始终有效,不用每次部署新服务都手动设置反代。
例如,当我访问 https://10000.kr.xxx.com 时,反向代理这台主机的10000端口的http服务,即约等于 https://kr.xxx.com:10000,不同在于原本的10000端口可能是HTTP,而我希望它是HTTPS。
PS:尝试使用kr-10000.xxx.com或10000-kr.xxx.com这种通配方式,在CF上都解析不了,不得已采用2级子域名方案。
感兴趣的话,先看完,再动手,确保已经知悉并评估有哪些风险。
为什么要弄
- Docker部署项目后,每次都要去修改Nginx配置HTTPS反向代理,想偷懒。
- 由于懒,有一些服务在HTTP上运行而不是HTTPS。
- 由于HSTS和Strict-Transport-Security的存在,导致偷懒的HTTP的服务有时可能无法访问,不得不
- 手动删除浏览器中的HSTS缓存
- 或让强制HTTPS的服务在另一个浏览器中使用
- 或找到这个默认开启HSTS的服务并修改它的Nginx设置,并承担可能导致服务工作异常的风险。
- 在同一个域名下使用端口号访问服务,如 kr.xxx.com:10086 ,多个服务在同一域下是有安全性风险的。同时也可能出现一些麻烦,例如ope-api和new-api会互相踢下线,因为使用的cookies key冲突了。
- 使用HTTPS是个好习惯,明知不该偷懒确还是忍不住偷懒,那就换一种偷懒的方式,于是形成这篇折腾笔记。
怎么弄
可选步骤
如果你只有一台主机(VPS/NAS),那么可以:
- 跳过域名通配符的设置(如果你已经设置),因为你不需要用到2级子域名。
- 跳过申请SSL证书的步骤(如果你已经拥有),因为你可以直接使用*.xxx.com的证书。
设置域名通配符
在DNS解析服务中,将*.kr.xxx.com解析到位于韩国的主机上,A或CNAME均可,如果你的域名托管在CF,不要开启Proxied;
如果你购买了CF的高级证书,那么你可以在后面的步骤中开启Proxied。如果你是白嫖大善人,那么保持一直关闭Proxied。如果你既要又要,那我建议你用*.woqinshihuang.xxx.com之类的只有你知道的子域稍微来点安慰剂。
申请SSL证书
由于*.xxx.com的证书是不能用于*.kr.xxx.com的,所以需要签个新证书。
此处使用acme.sh生成证书(推荐)
# 如果你的服务器支持IPV6
sudo CF_Token="此处填写CF_API_TOKEN" "$HOME/.acme.sh/acme.sh" --issue -d "*.kr.xxx.com" --dns dns_cf -k ec-256 --server "letsencrypt" --listen-v6
# 如果你的服务器不支持IPV6
sudo CF_Token="此处填写CF_API_TOKEN" "$HOME/.acme.sh/acme.sh" --issue -d "*.kr.xxx.com" --dns dns_cf -k ec-256 --server "letsencrypt"
设置证书自动更新(推荐)
crontab -e
12 * * * * "/root/.acme.sh"/acme.sh --cron --home "/root/.acme.sh" > /dev/null
设置CF中的边缘证书(可选)
如果你没有开启2级子域名的Proxied(小黄云),你可以跳过此步骤,并且以后也不要开启2级子域名的Proxied。
CF免费版不提供2级子域的通配证书,这一节的内容不适合CF免费托管用户,只需要保持*.kr.xx.com没有开启Proxied即可,否则你将遇到ERR_SSL_VERSION_OR_CIPHER_MISMATCH错误。
具体操作路径为
你的域名 -> SSL/TLS -> 边缘证书 -> 上传自定义SSL证书
你的域名 -> SSL/TLS -> Edge Certificates -> Upload Custom SSL Certificate
但是话说回来,付了钱也没必要上传,窗口往下滑动,直接开启Total TLS。(理论,我也没给大善人上贡)
添加Nginx配置
server {
listen 443 ssl;
listen [::]:443 ssl;
server_name *.kr.xxx.com;
ssl_certificate /root/.acme.sh/*.kr.xxx.com_ecc/fullchain.cer;
ssl_certificate_key /root/.acme.sh/*.kr.xxx.com_ecc/*.kr.xxx.com.key;
location / {
# 如果你出于安全考虑,想屏蔽例如10086和10010端口被反代,可以尝试使用下面一行写法代替,理论写法,未测试
# if ($host ~* "^(\d+)\.kr\.xxx\.com$" && $1 !~* ^(10086|10010)$) {
if ($host ~* "^(\d+)\.kr\.xxx\.com$") {
proxy_pass http://127.0.0.1:$1;
}
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
验证设置
使用Ping woqinshihuang.kr.xxx.com -4
验证解析的IP地址是否正确。
访问https://80.kr.xxx.com
验证SSL证书和Nginx是否配置正确,访问这个URL应该返回的是这个主机的80端口的信息,如果你没有使用80端口,就调整为你正在使用的其他端口。
结束语
也曾想过将docker容器服务名作为域名前缀,而不是纯端口号,但有很多容器需要暴露出多个端口,还是需要手动维护,这不符合偷懒的初衷,遂放弃。
你需要知道的是,这种方式会绕过iptables的保护,让内网的HTTP(S)服务都通过443端口暴露出来,如果有人知道这个规则,就可以尝试每一个端口来发现你的内网HTTP(S)服务。
我认为我也没这么大影响力,也没有被人针对性渗透的价值,此处使用的域名我从未公开过(小尾巴的域名是单独注册的),并且我有良好的密码习惯,我不在乎。但我仍建议你开启fail2ban等工具保护服务安全,ban掉HTTP服务中大量错误的IP。
手动添加到类似于/etc/fail2ban/jail.local
的配置中
[DEFAULT]
ignoreip = 127.0.0.1/8 192.168.0.0/16 172.16.0.0/12 10.0.0.0/8 169.254.0.0/16 ::1 你信任的公网IP.....
# 其他你的配置
[nginx-404]
enabled = true
port = http,https,10000:65535
logpath = /var/log/nginx/access.log
filter = nginx-404
[nginx-5xx]
enabled = true
port = http,https,10000:65535
logpath = /var/log/nginx/access.log
filter = nginx-5xx
添加规则文件
# ban掉扫ip的
sudo bash -c 'cat <<EOF > /etc/fail2ban/filter.d/nginx-404.conf
[Definition]
failregex = ^<HOST> -.*"(GET|POST|HEAD|PUT|DELETE|CONNECT|OPTIONS|TRACE|PATCH) .*" 404 .*$
ignoreregex = 域名甲\.com|域名乙\.com|域名丙\.com
EOF'
# ban掉所有5xx状态码的(nginx反代到一个不存在的端口是502错误)
sudo bash -c 'cat <<EOF > /etc/fail2ban/filter.d/nginx-5xx.conf
[Definition]
failregex = ^<HOST> -.*"(GET|POST|HEAD|PUT|DELETE|CONNECT|OPTIONS|TRACE|PATCH) .*" 5[0-9]{2} .*
EOF'
# 重启服务生效
systemctl restart fail2ban
你还需要知道的是,这不是万金油,充其量是9k金油,可以在很多情况下偷懒,但不能百分百在所有情况下有效,某些情况下你还是需要手动配置反代,具体来说:
这只是反代http服务,因此如果你的服务本身已经开启了https,访问会出错,例如Portainer就会返回Client sent an HTTP request to an HTTPS server。例如JumpServer会在域名发生变化后要求你更改配置文件,这都是项目的特性,并不是我造成的。
使用landscape批量推送配置到所有服务器,5分到手美滋滋