字数:10439
引言
这篇文章主要介绍在需要跳板的网络环境中配置https方式访问web程序,以及利用nginx stream的方式转发TCP请求,牵扯到的内容就比较多了:
(1)无密ssh登录
(2)如何访问内网云主机,内网云主机又如何访问外网
(3)nginx如何转发tcp连接和请求
(4)如何实现内网云主机的程序的https化访问
1 ssh无密登录
相关内容见独立文章,ssh免密登录。
2 内网云主机的互通
2.1 如何访问内网云主机
在windows下可以很方便的实现,有两种方式:
(1)通过xshell跑板机,如图:
(2)通过xshell的xagent
详情可以参考这里,具体做法是在tools菜单项,
启用xagent,然后在host里配置公网主机的连接信息,在login scripts里新增一条命令,类似:ssh root@172.26.3.142
,
其中172.26.3.142,是远程内网主机的内网IP:
跳板的本质应该就是借已建立的公网主机的ssh连接通道来发请求,不管sftp还是scp都可以通过tcp22搞定
(3)其它
xshell里的login scripts功能挺好用,证书登录之后是普通用户,每次连接时如何自动切换成为root用户?一般情况,
需要连接后输入su root
,然后再输入密码,现在可以这样,新增两脚本,第一条:expect: <空> send: <su root>
,
第二条:expect: <Password:> send: <密码>
,这样每次连接时就自动为root了。
2.2 内网云主机如何访问外网
使用squid代理上网,在代理服务器上安装和配置squid,在客户机上配置使用代理即可。
(1)代理服务器上安装和配置squid
查看squid是否安装:rpm -qa|grep squid
yum方式安装squid:yum -y insatll squid
设置开机启动:chkconfig --level 35 squid on
配置squid:vim /etc/squid/squid.conf
添加或改写内容如下:
http_port 3128 # 默认此值
cache_mem 64 MB # 默认256MB
maximum_object_size 4 MB # 默认此值
cache_dir ufs /var/spool/squid 100 16 256 # 默认此值
access_log /var/log/squid/access.log # ubuntu 20191228 默认:access_log daemon:/var/log/squid/access.log squid
acl localnet src 10.0.0.0/8 #改成自己的
http_access allow localnet
http_access deny all
初始化squid:squid –z
启动squid:squid start
,ubuntu上是:/etc/init.d/squid restart
参考自这里
如果还要代理不同的端口呢?而不是80或443呢?squid是默认允许了这些,比如以下配置:
acl Safe_ports port 1025-65535 # unregistered ports
http_access deny !Safe_ports
可以扩大范围,或者直接加http_port ,见这里, 使用netstat -plant查看是否加入了端口。
测试:
curl http://xx.76.60.135:15606/v1/cdc/acs/api -X POST -H "Content-Type:application/json"
-d "{\"Data\":{\"LibraryCode\":\"CN-410000-CSLIB\",\"Mac\":\"F4-8E-38-88-E1-B3\",\"PatronSn\":\"xxx\"
,\"Password\":\"111111\"},\"Id\":\"2019112615394672998335\",\"RequestObject\":\"cdc.acs\"
,\"Operation\":\"QueryPatronInfoByPatronSn\"}"
走正向代理的内部机器是无法通过以下代码直连外部主机的:
Socket socket = new Socket();
socket.connect(new InetSocketAddress(external_host, external_port), 3000);
更多连接代理的示例,见这里
(2)客户机上配置使用代理
全局代理方法,修改环境变量:vim /etc/profile
增加内容:
export http_proxy=http://username:password@yourproxy:3128/
export https_proxy=http://username:password@yourproxy:3128/
使配置生效:source /etc/profile
- 使用数据库账户来验证squid的使用,可以参考这里
- squid无法代理mysql的访问?讨论见这里
3 nginx如何转发tcp连接和请求
下面假设三台centOS云主机,只有一台有公网IP,其它两台是内网机器,一台安装MQTT消息服务,一台部署WEB程序,域名解析到公网主机,然后根据不同的端口映射到不同的内网机器。
还有几个情况:
(1)公网主机的nginx在docker容器内;
(2)MQTT的部分服务是TCP直连的形式
3.1 配置带stream模块的nginx
了解到的是TCP转发需要nginx的stream模块,而这个编译参数–with-stream一般的nginx版本中并没有,要么自己编译,要么使用nginx的加强收费版 ,相关链接1、链接2。 不过网上有一个docker镜像,是人家编译好的,可以拿来一用,主要参考这里。
按照文档里的说明,进入目录/opt/nginx,新建两个配置文件目录:http.conf.d和stream.conf.d
在http.conf.d添加文件,比如如下配置:
upstream qingdu {
server 172.26.3.142;
}
# xx小程序的服务接口
server {
listen 80;
server_name xx;
location / {
#有下面三行,目标才能解释到server_name
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_pass http://qingdu;
}
}
# mqtt服务器后台管理
server {
listen 80;
server_name mqttconsole.xx.com;
location / {
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_pass http://mqtt_console;
}
}
又比如在stream.conf.d中的mqtt的TCP直连配置,文件名mqtt.conf,内容比如:
#程序直连
upstream mqtt_tcp_1883 {
server 172.26.205.107:1883;
}
#小程序web socket直连
upstream mqtt_ws_8083 {
server 172.26.205.107:8083;
}
server {
listen 1883;
proxy_pass mqtt_tcp_1883;
}
server {
listen 8083;
proxy_pass mqtt_ws_8083;
}
# 使用nginx反向代理加密websocket: https://blog.csdn.net/zouyongjin/article/details/78465755
3.2 docker容器等相关
安装容器、映射目录、启动容器:
docker run -d -p 80:80 -p 1883:1883 -p 8083:8083 -p 8883:8883 -p 8084:8084 -p 18083:18083 -v `pwd`\http.conf.d:/opt/nginx/http.conf.d -v `pwd`\stream.conf.d:/opt/nginx/stream.conf.d -v `pwd`\logs:/opt/nginx/logs --name nginx tekn0ir/nginx-stream
停止容器:docker stop nginx
启动容器:docker start nginx
进入docker命令行:docker exec -it nginx bash
使配置生效(目前看没有作用):
docker exec -ti nginx bash -c '/opt/nginx/sbin/zero_downtime_reload.sh'
4 如何实现内网云主机的程序的https化访问
4.1 用certbot安装ca证书
https化首先需要ca证书,之前一直用的Certbot,但是在这种跳板环境下,有一些新情况,由于个云主机只有一台有外网访问权限,
程序部署在内网,通过squid代理上网的,一开始在内网主机上安装certbot,出现两个矛盾的问题:
(1)开启nginx,80端口被占用,certbot报不能bind 80端口
(2)不开启nginx,certbot报不能访问到qingdu.newread.com/.well-know/xxx,域名后面那些东西是Certbot自己写的
第一点看,只能关nginx了,第二点,没有写权限吗?开启了全部目录权限,无效,剩下就是dns了,域名明明可以访问,但是!!! ping域名绝对不会是内网主机,也正是因为这个原因,certbot会校验这两个东东,一是域名能ping通,二是ping出来的ip是本机
果然,在公网主机上安装证书没有问题了
4.2 尝试配置nginx
基于第3节的内容,添加https解释:
upstream qingdu_443 {
server 172.26.3.142:443;
}
server {
listen 443;
server_name qingdu.xx.com;
ssl on;
ssl_certificate /opt/nginx/http.conf.d/qingdu.xx.com/fullchain1.pem;
ssl_certificate_key /opt/nginx/http.conf.d/qingdu.xx.com/privkey1.pem;
root /opt/nginx/http.conf.d;
index index.html;
# location / {
# #有下面三行,目标才能解释到server_name
# 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_pass http://qingdu_443;
# }
}
用着用着这个配置出现了一个问题:SSL received a record that exceeded the maximum permissible length
,解决办法,可[参考这里](https://blog.csdn
.net/dppass/article/details/82455343)
,使用如下配置:
listen 443 default; #1.15上可以将default用ssl代替
ssl on; #1.15上可以不配,然后listen上加ssl
server_name qingdu.xx.com;
ssl_certificate /etc/letsencrypt/live/qingdu.xx.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/qingdu.xx.com/privkey.pem;
ssl_session_cache shared:SSL:1m;
ssl_session_timeout 5m;
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
docker容器重建,并加443映射:
docker run -d -p 80:80 -p 443:443 -p 1883:1883 -p 8083:8083 -p 8883:8883 -p 8084:8084 -v `pwd`/http.conf.d:/opt/nginx/http.conf.d -v `pwd`/stream.conf.d:/opt/nginx/stream.conf.d -v `pwd`/logs:/opt/nginx/logs --name nginx tekn0ir/nginx-stream
无效!原来在本地telnet qingdu.xx.com 443都无法成功
4.3 解决centos防火墙的问题
主要参考自这里
关闭防火墙:systemctl stop firewalld.service
关闭开机启动:systemctl disable firewalld.service
开启iptables
有的版本已经装过可以跳过:yum install iptables
安装服务:yum install iptables-services
启动:service iptables restart
开机启动:chkconfig iptables
或 systemctl enable iptables.servcie
查看iptables:chkconfig iptables on
显示内容:
Note: Forwarding request to 'systemctl enable iptables.service'.
Created symlink from /etc/systemd/system/basic.target.wants/iptables.service to /usr/lib/systemd/system/iptables.service.
/usr/lib/systemd/system/iptables.service这个文件的内容如下:
[Unit]
Description=IPv4 firewall with iptables
Before=ip6tables.service
After=syslog.target
AssertPathExists=/etc/sysconfig/iptables
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/usr/libexec/iptables/iptables.init start
ExecReload=/usr/libexec/iptables/iptables.init reload
ExecStop=/usr/libexec/iptables/iptables.init stop
Environment=BOOTUP=serial
Environment=CONSOLETYPE=serial
StandardOutput=syslog
StandardError=syslog
[Install]
WantedBy=basic.target
查看vim /etc/sysconfig/iptables
,内容如下:
查看iptables的设置情况:iptables -L -n
或 iptables -t nat -nL --line-number
,内容如下:
和之前的有区别,之前可能是未开启iptables:
如果在主机上直接启nginx是可以telnet了,并且https站点可正常访问,于是添加一堆,注意要运行命令service iptables restart
才能使规则生效:
再重装docker的容器,出错误:
docker: Error response from daemon: driver failed programming external connectivity on endpoint nginx (ce22e5d894bea1eaae45a6aadd34d6bb86b2660a1a473a62d3d54f97323b211b): (iptables failed: iptables --wait -t nat -A DOCKER -p tcp -d 0/0 --dport 8883 -j DNAT --to-destination 172.17.0.2:8883 ! -i docker0: iptables: No chain/target/match by that name.
(exit status 1)).
原因?docker服务启动时定义的自定义链DOCKER由于某种原因被清掉。估计是之前 想动态加了一条443的端口映射:iptables -t nat -A DOCKER -p tcp --dport 8001 -j DNAT --to-destination 172.17.0.19:8000
解决办法?重启docker服务:systemctl restart docker
,参考这里
。有了iptables,可以如这里所说动态添加映射了吗?
之前试的telnet 443是可以了,但是在docker内的nginx,还是不行,具体情况是:
-
ip转发也开启了呀:
cat /proc/sys/net/ipv4/ip_forward
-
运行
iptables-save | grep 443
看到有一些不该有的配置,这个命令在iptables上追加了很多乱乱的内容,重新清理了一下iptables,像安装之初 ,仅保留我手动添加的配置,现在docker中的443也可以telnet了!
又有问题,访问网页显示无法提供https服务,原因暂时不明,是因为放在docker内吗?从nginx的访问日志来看,出问题的https访问和http略有不同:
183.17.229.193 - - [05/Dec/2018:08:12:56 +0000] "GET /swagger-resources/configuration/security HTTP/1.1" 200 81 "http://qingdu.newread.com/swagger-ui.html" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36"
183.17.229.193 - - [05/Dec/2018:08:13:10 +0000] "\x16\x03\x01\x02\x00\x01\x00\x01\xFC\x03\x03\xB7g\xB9\xD6R\xE8oA\xBA\x02\xE6\xF5\x8Af\x05\x9E\xFB^5N\xF8W3\xE1\x1E'\xC9\xFE\x1F\xD2D\xDA 12\xDE\x1B+\x9BW%\xA2\x02=\x9EC\xCEF\xCE\xEF\x99\xBDM\xF9\xB4v}\xC7\x94\xD5\xD8\x0ExC4\x00\x22jj\x13\x01\x13\x02\x13\x03\xC0+\xC0/\xC0,\xC00\xCC\xA9\xCC\xA8\xC0\x13\xC0\x14\x00\x9C\x00\x9D\x00/\x005\x00" 400 173 "-" "-"
正常情况https请求应该是这样的:
183.17.229.193 - - [05/Dec/2018:16:27:29 +0800] "GET /index.html HTTP/1.1" 200 4 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36" "-"
183.17.229.193 - - [05/Dec/2018:16:27:30 +0800] "GET /favicon.ico HTTP/1.1" 404 571 "https://qingdu.newread.com/index.html" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36" "-"
莫不certbot证书认mac?需要在docker内部安装certbot证书吗?或者需要这个docker版的certbot方案
另外还有一个诡异的问题,将包含有同样的server conf的文件用到宿主的nginx即不行,唯一就是另外配置了upstream配置(并没有使用),可能是宿主的nginx不支持,nginx -t没有检查出错误。
4.4 怎么解决https的问题
暂时,启两个nginx,https走宿主nginx,tcp走docker中的nginx,未来的wss还未考虑,补充,后来wss用的某端口(443之外的,用443转发没配成功),但是开启了sll并加了证书, 然后内部走的是普通ws端口,其实内网走wss端口需要在mqtt服务器端做一些适配。