ASPONE.CN
苦逼

[原] - 用 SSH 转发和 Nginx 反向代理

先说说 SSH 的本地转发和远程转发,也叫作正向转发和反向转发。

  • 本地转发:内网机器(Client)SSH连接到外网机器(Server),内网机器监听本地指定端口,并将该端口的所有数据转发到外网机器的指定端口。方向是本地到远端。SSH 使用 -L 参数。

    命令格式:

    ssh -L <local port>:<remote host>:<remote port> <SSH hostname>
  • 远程转发:内网机器(Client)SSH连接到外网机器(Server),内网机器要求外网机器监听指定端口,并将该端口的所有数据转发到内网机器的指定端口。方向是远端到本地。SSH 使用 -R 参数。

    命令格式:

    ssh -R <local port>:<remote host>:<remote port> <SSH hostname>


注意:上边虽然以内网机器和外网机器来区分 Client 端和 Server 端,是方便在最容易的情况下理解其工作方式。并不一定是内网的机器或外网的机器,也可能全是外网机器,但是由于策略或者防火墙因素导致无法访问其中某一个,而需要另外一个来作为跳板转发。


下来就是实战。

安装 sshpass 免密码登录 ssh,或者使用证书免密码登录。

apt install sshpass

实现后台连接 ssh,开启远程转发

sshpass -p "112233aabbcc" ssh -CNfg -R 8080:127.0.0.1:80 -R 4433:127.0.0.1:443 -R 2211:127.0.0.01:22 root@1.1.1.1

一次开启了3个转发端口

本地端口(Client)

远程端口(Server)
127.0.0.1:80←→1.1.1.1:8080
127.0.0.1:443←→1.1.1.1:4433
127.0.0.1:22←→1.1.1.1:2211


打开 SSH 转发之前,在 1.1.1.1 (Server) 上查看端口,可以看见只监听了 22 端口

# netstat -ntpl
激活Internet连接 (仅服务器)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN      983/sshd
tcp6       0      0 :::22                   :::*                    LISTEN      983/sshd

打开 SSH 转发之后,再查看 1.1.1.1 (Server)  上的端口,可以看见多了3个监听端口

# netstat -ntpl
激活Internet连接 (仅服务器)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name
tcp        0      0 0.0.0.0:8080            0.0.0.0:*               LISTEN      1244/sshd: root
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN      983/sshd
tcp        0      0 0.0.0.0:4433            0.0.0.0:*               LISTEN      1244/sshd: root
tcp        0      0 0.0.0.0:2211            0.0.0.0:*               LISTEN      1244/sshd: root
tcp6       0      0 :::8080                 :::*                    LISTEN      1244/sshd: root
tcp6       0      0 :::22                   :::*                    LISTEN      983/sshd
tcp6       0      0 :::4433                 :::*                    LISTEN      1244/sshd: root
tcp6       0      0 :::2211                 :::*                    LISTEN      1244/sshd: root


注意:远程转发的 Server 端默认只监听 127.0.0.1 地址上的端口,如果要监听在 0.0.0.0 上,需要在 Server 端需要修改 /etc/ssh/sshd_config 文件,添加一行配置:

GatewayPorts yes
因为 SSH 默认配置出于安全考虑,远程转发是只用于 Server 端自身发起访问请求的,如果跟本例一样,前边还有一个 Nginx 做反向代理的话,就不用修改 SSH 配置了。如果是反向转发之后直接使用的,就需要修改配置文件,监听在 0.0.0.0 上了。

说明端口转发已经开始工作了,我们可以通过 1.1.1.1:222 这个Server的地址 SSH 登录到 Client 端了。

同时可以从 http://1.1.1.1:8080 和 https://1.1.1.1:4433 访问到 Client 的 Web 服务。


为什么反向转发要监听 8080 和 4433 呢,直接监听 80 和 443 不就可以用了,何必多此一举,再开一个 Nginx 反向代理 80 到 8080 ,4433 到 443 呢,因为如果没有 Nginx 的话,在 Client 上获取客户端 IP 永远是 127.0.0.1,有需要 Client IP 的应用就无法拿到正确的 IP 了。


下面就是安装 Nginx

sh -c "echo 'deb http://nginx.org/packages/ubuntu/ xenial nginx' > /etc/apt/sources.list.d/nginx.list"
wget -O - http://nginx.org/keys/nginx_signing.key | apt-key add -
apt update
apt install nginx


注意源地址中的 Ubuntu Code Name,我用的 16.04,所以是 xenial,下面给出常用的 Ubuntu Code Name
Version
Codename
12.04precise
14.04trusty
16.04xenial
16.10yakkety

Nginx 反向代理配置

user  nginx;
worker_processes  1;

error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;

events {
    worker_connections  1024;
}


http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;

    sendfile        on;
    #tcp_nopush     on;

    keepalive_timeout  65;

    gzip  on;

    #include /etc/nginx/conf.d/*.conf;

    server {
        listen 443 ssl;
        ssl on;
        ssl_certificate     server.pem;
        ssl_certificate_key server.key;
        location / {
                proxy_pass https://127.0.0.1:4433;
                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;
                proxy_redirect     off;
        }
    }

    server {
        listen 80;
        location / {
                proxy_pass http://127.0.0.1:8080;
                proxy_set_header Host $host;
                proxy_set_header X-Real-IP $remote_addr;
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        }
    }
}

stream {
    server {
        listen 222;
        proxy_connect_timeout 30s;
        proxy_timeout 600s;
        proxy_pass 127.0.0.1:2211;
    }
}

反向代理 Web 服务的 0.0.0.0:80 到 127.0.0.1:8080,0.0.0.0:443 到 127.0.0.1:4433

反向代理 TCP 的 0.0.0.0:222 到 127.0.0.1:2211


OK,大功告成,内网机器上的 Web 可以从外网访问了,同时也能获取正确的 Client IP 了,通过外网也能 SSH 登录内网机器了。


最后,再写个脚本,如果 SSH 断开了就自动重连,嗯,6 秒检查一次。

#cat /root/SSHPortforwarding.sh
#!/bin/bash
while [ true ]
do
	vpn=`ps aux|grep -E 'ssh \-' |wc -l`
	if [ $vpn -lt 1 ]; then
		sshpass -p "112233aabbcc" ssh -CNfg -R 8080:127.0.0.1:80 -R 4433:127.0.0.1:443 -R 2211:127.0.0.01:22 root@1.1.1.1
	fi
	sleep 6
done

再把这个脚本放到 rc.local 里,开机自动启动。

nohup bash /root/SSHPortforwarding.sh > /var/log/SSHPortforwarding.log 2>&1 &


Copyright © 2016 ASPONE.CN. All Rights Reserved. 京ICP备18038662号