1 需求背景
有点奇葩的一个需求,出现的原因是平台很久以前上线的时候使用的是“http://ip:port/”,系统运行了两年多,培训材料中写的登录地址、用户保存的书签地址都是这种形式。
我接手项目时,为了强化安全(过密评)着手启用https+域名,同时为了保障业务正常不受影响、还得确保市县各部门的老用户正常使用之前收藏的地址打开也不报错。
2 解决方案
2.1 可选思路-重定向
最简单的方法就是直接根据 HTTP 请求进行判断重定向,接收到http的请求直接301到新地址。
server {
listen 8088;
listen 8088 ssl; # 同时监听 ssl
server_name xxx.com;
ssl_certificate xxx.pem;
ssl_certificate_key xxx.key;
# 如果是 http 请求,则重定向到 https
if ($scheme = http) {
return 301 https://$host$request_uri;
}
# ... 其他 location 配置
location / {
proxy_pass http://gateway_service;
}
}2.2 捕获错误码重定向
即利用http请求https端口时的错误码497,将请求重定向到https,和上面的差不多。
server {
listen 8088 ssl;
server_name xxx.com;
ssl_certificate xxx.pem;
ssl_certificate_key xxx.key;
# 捕获 497 错误并进行重定向
error_page 497 301 =301 https://$host:$server_port$request_uri;
location / {
proxy_pass http://gateway_service;
}
}2.3 实际使用
上面两种方法简单又优雅,但是比较坑的地方在于它只对用户使用浏览器访问有效,我们平台的很多业务接口提供出去的时候也是http://ip:port,调用方很多时候没有配置什么针对重定向的处理,导致接口调用失败。
最终还是采用在四层(传输层)对流量进行识别的方式处理了。
利用 Nginx 的 stream 模块和 ssl_preread 功能,在 TCP 层面对流量进行识别和转发,根据是否为 TLS/SSL 流量,将其分别代理到处理 HTTP 和 HTTPS 的不同 server 块。。
./configure \
--add-module=./modules/ngx_http_upstream_vnswrr_module \
--add-module=./modules/ngx_http_proxy_connect_module \
--without-http_rewrite_module \
--with-openssl=/root/openssl-1.0.1j \
--with-pcre \
--without-http_gzip_module \
--with-stream \
--with-stream_ssl_preread_module \
--with-stream_ssl_module \
--with-stream_sni重点参数 --with-stream
--with-stream_ssl_preread_module
user root;
worker_processes 2;
worker_rlimit_nofile 100000;
stream {
upstream http_gateway {
server 127.0.0.1:20036;
}
upstream https_gateway {
server 127.0.0.1:20037;
}
map $ssl_preread_protocol $upstream{
default http_gateway;
"TLSv1.0" https_gateway;
"TLSv1.1" https_gateway;
"TLSv1.2" https_gateway;
"TLSv1.3" https_gateway;
}
server {
listen 12345;
ssl_preread on;
proxy_pass $upstream;
}
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
upstream gateway_service{
server 127.0.0.1:8080 weight=1;
server 127.0.0.1:8081 weight=2;
}
server {
listen 20036;
listen 20037 ssl;
server_name xxx.com;
ssl_certificate xxx.pem;
ssl_certificate_key xxx.key;
location / {
proxy_pass http://gateway_service;
}
}
}3 后记
这种方案虽然兼容性好,但并没有将用户引导到 HTTPS 上,该明文传输的还是明文,虽然过了密评,但总有些自欺欺人的感觉。
后来还是把问题反馈给业主,以一次年度培训为契机,通知所有用户修改访问地址,正式迁移到https://域名:端口,停止对http请求的支持。
还是不能太钻牛角尖,不是所有的问题都要通过技术手段解决,不是所有的需求都要满足,该申请业主协助的还是多走业务手段。
评论区