ssh妙用

虽然SSH代理本身是高度加密的,但是防火墙能识别出协议是ssh,如果你用它来浏览网页或看视频,会产生长时间、高…

ssh端口转发(用于设置代理)

ssh -D [本地监听IP]:[本地端口] [用户名]@[远程服务器IP]

作用: 在本地建立一个 SOCKS 代理服务器

动态转发: 与固定端口转发(只能访问特定 IP 和端口)不同,“动态”意味着你可以通过这个隧道访问远程服务器能触达的任何地址。

虽然SSH代理本身是高度加密的,但是防火墙能识别出协议是ssh,如果你用它来浏览网页或看视频,会产生长时间、高带宽的 SSH 流量。这种“不正常”的行为很容易触发防火墙的封锁。所以如果为了安全需要,最好配合stunnel 进行的加密隧道传输。

严格来说,这通常被归类为 加密代理(Encrypted Proxy) 而非传统意义上的 VPN,但在广义的“科学上网”语境下,它们常被当作 VPN 的替代方案。

SSH 单独使用: 流量特征 = [TCP] -> [SSH 加密数据](容易被识别)。

SSH + Stunnel: 流量特征 = [TCP] -> [TLS/SSL 握手] -> [看似 HTTPS 的加密数据]

Windows 批处理脚本 (Auto-Reconnect)

你可以创建一个 .bat 文件(例如 ssh_proxy.bat),将以下代码复制进去。即使连接断开,它也会每隔 30 秒自动尝试重新连接(使用 SSH 密钥对登录!):

@echo off
:loop
echo [%date% %time%] 正在尝试建立 SSH 隧道...

:: 如果你已经配置了密钥登录(推荐):
ssh -D 127.0.0.1:18080 -N -o ServerAliveInterval=30 -o ServerAliveCountMax=3 root@google.cogrise.top
(如果在config文件配置了其他的参数)
ssh -N google.cogrise.top

:: 如果连接断开,程序会执行到这里
echo [%date% %time%] 连接已断开或失败,30秒后重试...
timeout /t 30
goto loop

-o ServerAliveInterval=30: 每隔 30 秒向服务器发送一个“心跳”包。

-o ServerAliveCountMax=3: 如果连续 3 次没收到回应,才认为连接断开。

可以设置在本机config文件里面

Host google.cogrise.top
    User root
    Port 22
    ServerAliveInterval 30
    ServerAliveCountMax 3
    DynamicForward 127.0.0.1:18080

1. 默认行为:静默等待

ServerAliveInterval: 默认值是 0

  • 这意味着客户端永远不会主动向服务器发送“心跳”包。

ServerAliveCountMax: 默认值通常是 3

  • 但因为 Interval 是 0,这个计数器实际上处于失效状态。

不设置会导致什么后果?

A. 被防火墙/路由器踢掉(死连接)

这是最常见的情况。很多网络设备(如家用路由器、公司防火墙或云服务商的负载均衡器)为了节省资源,会自动切断长时间没有数据传输的 TCP 连接。

B. 无法及时察觉断线

如果远程服务器意外重启或网络瞬间中断:

现象: SSH 客户端会卡在那里,既不报错也不退出,可能要等几十分钟(触发 TCP 自身的 KeepAlive 超时)才会提示 Connection timed out

附上配置密钥对登录的全过程:

1. 在本地 Windows 生成密钥对

在你的 C:\Users\maluyao 路径下的命令行执行以下命令:

ssh-keygen -t ed25519

-t ed25519: 这是目前最推荐的算法,比传统的 RSA 更快且更安全。

按回车确认: 提示保存路径和密码(Passphrase)时,全部直接按回车(不设置密码,这样重连时才不需要人工干预)。

生成结果: 你会在 C:\Users\maluyao\.ssh\ 文件夹下看到两个文件:

  • id_ed25519: 你的私钥(绝对不能给别人)。
  • id_ed25519.pub: 你的公钥

2. 将公钥上传到远程服务器

你需要把公钥的内容告诉服务器,以便它能识别你的电脑。

复制公钥内容: 用记事本打开 C:\Users\maluyao\.ssh\id_ed25519.pub,复制里面那串以 ssh-ed25519 开头的长字符串。

登录服务器并写入: 先最后一次手动输入密码登录到你的 Ubuntu 服务器,然后执行以下命令:

# 创建 .ssh 目录(如果不存在)
mkdir -p ~/.ssh
# 将公钥写入授权文件
echo "这里粘贴你刚才复制的公钥内容" >> ~/.ssh/authorized_keys
# 设置权限(非常重要,权限不对会导致无法登陆)
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys

3. 修改本地 config 文件(终极优化)

现在你可以把所有的连接参数固化到 C:\Users\maluyao\.ssh\config 文件中:

Host myproxy
    HostName google.cogrise.top
    User root
    Port 22
    # 自动重连心跳设置
    ServerAliveInterval 30
    ServerAliveCountMax 3
    # 密钥路径
    IdentityFile ~/.ssh/id_ed25519
    # 自动开启动态代理
    DynamicForward 127.0.0.1:18080

4. 验证效果

现在,你在命令行只需要输入:

ssh -N myproxy

它将不再询问密码,直接建立隧道。

Stunnel (TLS/SSL Tunnel)

用它来配合 SSH,主要是为了解决 SSH 流量特征过于明显的问题:

流量伪装: Stunnel 能够将任何 TCP 流量(比如 SSH 流量)封装在 标准的 TLS/SSL 隧道 中。

混淆: 这样在网络防火墙看来,你的流量就像是在访问一个普通的 HTTPS 网站,从而躲避对 SSH 协议的深度数据包检测(DPI)。

要使用 Stunnel 来给 SSH 隧道穿上“HTTPS 伪装衣”,你需要分别在服务端(VPS)和客户端(你的电脑)上进行配置。

第一步:服务端配置

1、安装 Stunnel:

bash
# Ubuntu/Debian
sudo apt install stunnel4
# CentOS
sudo yum install stunnel

2、生成 SSL 证书: Stunnel 需要证书来加密。为了方便,可以生成自签名证书:

openssl req -new -x509 -days 3650 -nodes -out /etc/stunnel/stunnel.pem -keyout /etc/stunnel/stunnel.pem

3、创建配置文件: 新建并编辑 /etc/stunnel/stunnel.conf

ini
    cert = /etc/stunnel/stunnel.pem
    setuid = stunnel4
    setgid = stunnel4
    pid = /var/run/stunnel4/stunnel.pid

    [ssh]
    accept = 443
    connect = 127.0.0.1:22
*   accept = 443: Stunnel 监听的对外端口(伪装成 HTTPS)。
*   connect = 127.0.0.1:22: 收到流量后转发给本地的 SSH 服务。

4、启动服务:

bash
sudo systemctl start stunnel4
第二步:客户端配置

如果你用的是 Windows,可以去官网下载 Stunnel 安装包;macOS 可以使用 brew install stunnel

1、编辑客户端配置文件 (stunnel.conf):

ini
    client = yes
    [ssh]
    accept = 127.0.0.1:10000
    connect = [你的服务器IP]:443
*   accept = 127.0.0.1:10000: 在你本地机器上开一个“入口”端口。
*   connect = [你的服务器IP]:443: 连接到服务器上 Stunnel 监听的 443 端口。

2、启动客户端 Stunnel。

第三步:通过“隧道中的隧道”建立 SSH 代理

现在,你的本地端口 10000 已经通过 TLS 加密直连到了服务器的 SSH 服务。你只需要把之前的 SSH 命令目标改为本地的这个“入口”即可:

bash
ssh -D 1080 -N -f -p 10000 user@127.0.0.1

为什么是 127.0.0.1 因为此时本地的 10000 端口就等同于服务器的 SSH 端口。流量路径如下:

  1. 你的浏览器 -> SOCKS5: 1080
  2. SSH 客户端 -> 转发到本地 10000
  3. Stunnel 客户端 -> 将数据包装成 TLS (HTTPS) -> 发往服务器 443
  4. Stunnel 服务端 -> 解开 TLS -> 发往服务器本地 22 (SSH)

ssh本地转发

ssh -L [本地端口]:[目标IP]:[目标端口] user@server

作用:远程服务器上的某个端口,“映射”到你本地电脑的某个端口

场景: 比如远程服务器(内网)里跑着一个数据库(端口 3306),你的电脑连不上。你可以通过 SSH 把它映射到你本地的 3306。

原理: 访问本地 localhost:3306 = 访问远程 server:3306

ssh -L [本地绑定地址:]本地端口:目标地址:目标端口 用户名@SSH服务器
当你使用SSH密钥来连接远程服务器时,使用-i参数指定
-i /path/to/key.pem

只想做端口转发,不需要进入vps,在后面加上-N参数

SSH 远程转发

ssh -R [服务器端口]:[目标IP]:[目标端口] user@server

作用:你本地的某个端口,“反向映射”到远程服务器的某个端口

场景: 内网穿透。比如你在寝室(内网)电脑跑了一个网站,想让身在外地的朋友通过你的服务器 IP 访问到你这台电脑。

必须在 VPS 的 /etc/ssh/sshd_config 中控制哪些用户允许反向隧道:

# 确保以下配置开启
GatewayPorts yes
AllowTcpForwarding yes

重新启动 SSH 服务:

sudo systemctl restart sshd

调试

检查端口+kill进程

image-20250808003218685

SSH 长时间空闲被踢

ssh -N -R 8868:localhost:8868 -o ServerAliveInterval=60 root@aliyun.maluyao.top

如果本机没有开ipv6会出现问题,因为Windows主机优先把localhost解析为ipv6地址

Windows 上 localhost 默认优先解析为 IPv6(::1), 而你的 Nginx 只监听了 IPv4(127.0.0.1), SSH 反向隧道实际连到了 ::1:443,结果直接被系统关闭。

C:\Users\maluyao>ssh -N -T -R 0.0.0.0:8867:localhost:443 root@aliyun.maluyao.top
失败!!!

C:\Users\maluyao>ssh -N -T -R 8867:127.0.0.1:443 root@aliyun.maluyao.top
成功!!!

这里的 localhost 在 Windows 上经常会优先解析到 IPv6 的 ::1(而不是 127.0.0.1)。 你的本机 Nginx 虽然能用 https://127.0.0.1:443 访问,但很可能 没有在 ::1:443 上监听(没开 IPv6 listen)。

真正的问题是:Windows 上用 localhost 做转发目标时,实际走到 IPv6 ::1,而本机服务只在 IPv4 上可用

评论

评论加载中……