字数: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: xagent1
xagent2

跳板的本质应该就是借已建立的公网主机的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

参考网页1网页2

  1. 使用数据库账户来验证squid的使用,可以参考这里
  1. 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 iptablessystemctl 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.jpg

查看iptables的设置情况:iptables -L -niptables -t nat -nL --line-number,内容如下:
iptables.jpg

和之前的有区别,之前可能是未开启iptables:
compare.jpg

如果在主机上直接启nginx是可以telnet了,并且https站点可正常访问,于是添加一堆,注意要运行命令service iptables restart才能使规则生效:
more.jpg

再重装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服务器端做一些适配。