Jacky's blog
首页
  • 学习笔记

    • web
    • android
    • iOS
    • vue
  • 分类
  • 标签
  • 归档
收藏
  • tool
  • algo
  • python
  • java
  • server
  • growth
  • frida
  • blog
  • SP
  • more
GitHub (opens new window)

Jack Yang

编程; 随笔
首页
  • 学习笔记

    • web
    • android
    • iOS
    • vue
  • 分类
  • 标签
  • 归档
收藏
  • tool
  • algo
  • python
  • java
  • server
  • growth
  • frida
  • blog
  • SP
  • more
GitHub (opens new window)
  • 服务器tutorial
  • spring

  • 数据库

  • 运维

    • SSH 进行本地文件的交互
    • 查询本地电脑外网 IP
    • DNS 查询
    • 内网穿透
      • 🌐 核心原理详解
        • 1. 为什么无法直接访问?
        • 2. 隧道建立的三步曲
      • 🛠️ 主流内网穿透方案对比
      • 🚀 进阶实践:基于 SSH 的远程端口转发
        • 1. 执行命令
        • 1.1. 服务端操作(在内网服务器上执行)
        • 1.2 客户端操作 (Client-side Access)
        • 2. 部署建议与安全优化
        • 3. 执行中遇到的问题记录
        • 3.1 实现绑定非回环接口(GatewayPorts yes)
        • 3.2 保持隧道稳定
      • ✅ 总结
    • macOS 定时任务实现:Git 仓库状态自动同步方案
  • 运营

  • other

  • 《server》
  • 运维
Jacky
2024-09-18
目录

内网穿透

# 内网穿透技术指南:原理、方案与实践建议

内网穿透(NAT Traversal)是一种允许外部互联网用户访问位于私有网络(NAT/防火墙后)内设备或服务的技术。在家庭办公、远程运维以及分布式系统联调中,它是解决“无公网 IP”痛点的核心方案。


# 🌐 核心原理详解

# 1. 为什么无法直接访问?

  • 私有地址空间: 家庭或公司内部设备通常分配的是非公网路由地址(如 192.168.x.x),这些地址在公共互联网中不可识别。
  • NAT 的保护/阻隔: 路由器通过网络地址转换(NAT)技术允许内部设备共享一个公网出口,但它会拦截所有来自外部、未经请求的主动连接。

# 2. 隧道建立的三步曲

  • 主动出击: 内网设备首先向具有固定公网 IP 的中转服务器发起连接,并建立一个持久的通信隧道。
  • 流量监听: 公网服务器在特定端口上监听外部请求。
  • 反向转发: 当外部请求到达服务器时,服务器通过已建立的隧道将请求“推送”给内网设备,从而绕过防火墙的入站限制。

# 🛠️ 主流内网穿透方案对比

根据不同的使用场景和技术需求,可以参考下表选择最合适的工具:

方案 技术核心 典型工具 适用场景
SSH 隧道转发 SSH 协议加密隧道 原生 OpenSSH 开发者临时调试、单端口加密传输。
应用层穿透 TCP/UDP 流量转发 frp, nps, ngrok 长期挂载 Web 服务、远程桌面、游戏服务器。
虚拟局域网 P2P 组网 / 虚拟网卡 ZeroTier, Tailscale 跨地域设备互联、构建私有办公内网。
P2P 打洞 UDP 状态检测 WebRTC 协议等 高带宽、低延迟实时通信(如视频通话)。

# 🚀 进阶实践:基于 SSH 的远程端口转发

对于拥有云服务器(VPS)的用户,使用 SSH 远程转发(Remote Forwarding)是最安全、快捷的实现方式。

# 1. 执行命令

# 1.1. 服务端操作(在内网服务器上执行)

# -N: 不执行远程命令,只做端口转发
# -R: 远程端口转发(反向)
ssh -N -R <remote_port>:localhost:<local_port> <user>@<server_ip>
1
2
3

此命令会将公网服务器端口接收到的流量,通过 SSH 安全加密隧道转发至本地指定端口。

# 1.2 客户端操作 (Client-side Access)

当反向隧道建立后,根据 VPS 的安全配置,客户端有以下两种访问方式:


1.2.1 情况 A:通过 VPS 公网直接访问 (最常用)

若 VPS 防火墙已放行 <remote_port>,且 SSH 配置(sshd_config)已开启 GatewayPorts yes (安全风险):

  • 前置条件:VPS 防火墙允许外部访问指定端口,且 SSH 允许绑定非回环接口。
  • 访问方式:直接在浏览器或工具中输入:

    http://<server_ip>:<remote_port>


1.2.2 情况 B:通过“隧道中的隧道”访问 (更安全)

若 VPS 安全级别较高(默认仅监听 127.0.0.1),则需要在客户端执行 本地端口转发 (-L) 来二次对接隧道:

  • 执行命令:
    # 将本地 client_port 流量通过 SSH 转发至 VPS 的 remote_port
    ssh -L <client_port>:localhost:<remote_port> <user>@<server_ip>
    
    1
    2
  • 访问方式:在客户端本地浏览器输入:

    http://localhost:<client_port>

# 2. 部署建议与安全优化

  • 持久化连接: 使用 autossh 或系统的 systemd 服务来管理隧道,确保在网络波动(如运营商重新分配动态 IP)导致断开后能自动重连。
  • 最小暴露原则: 仅映射业务所需的特定端口,严禁将 SSH 或数据库管理端口直接暴露在公网。
  • 防火墙策略: 确保公网服务器的防火墙(如 UFW 或 iptables)仅放行必要的转发端口,并配置强口令或 SSH 密钥认证。
  • IPv6 备选方案: 在运营商支持 IPv6 的环境下,可以尝试利用 IPv6 的公网寻址能力结合 DDNS 实现更直接的访问,作为穿透方案的有效补充。

# 3. 执行中遇到的问题记录

# 3.1 实现绑定非回环接口(GatewayPorts yes)

要让你的 SSH 转发真正“对外生效”,你需要配置 VPS 的 SSH 守护进程(sshd)来打破回环接口的限制:

  1. 修改OpenSSH 守护进程配置:打开 /etc/ssh/sshd_config文件。
  2. 开启选项:将 GatewayPorts 设置为 yes。
    # 开启所有网络接口监听
    GatewayPorts yes
    
    1
    2
  3. 重启服务:sudo systemctl restart ssh
  4. 注意:配置修改后,需要重新创建新的ssh隧道才能起作用,以前的隧道还是旧的配置,不能立即生效。 销毁先前的进程如下:
    # 查看进程占用
    sudo netstat -tlnp | grep <port>
    
    # 强制结束该进程:
    sudo kill -9 <pid>
    
    # 确认端口已释放. 预期无任何输出
    sudo netstat -tlnp | grep <port>
    
    1
    2
    3
    4
    5
    6
    7
    8

# 3.2 保持隧道稳定

在linux上

  1. SSH 连接容易因超时断开,生产环境推荐用 autossh 自动重连:

    # 安装
    apt install autossh
    
    # 自动重连的反向隧道
    autossh -M 9090 -N -R 8080:localhost:80 user@公网服务器IP
    
    1
    2
    3
    4
    5
  2. 写成 systemd(linux) 服务开机自启

    # /etc/systemd/system/ssh-tunnel.service
    [Unit]
    Description=SSH Reverse Tunnel
    After=network.target
    
    [Service]
    ExecStart=autossh -M 9090 -N -R 8080:localhost:80 user@公网服务器IP
    Restart=always
    RestartSec=10
    
    [Install]
    WantedBy=multi-user.target
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12

    然后使用 systemctl enable --now ssh-tunnel


在mac上

Mac 上推荐用 launchd(macOS 原生服务管理)来代替 Linux 的 systemd。


方案一: launchd(推荐)

  1. 创建配置文件:

    nano ~/Library/LaunchAgents/com.ssh.tunnel.plist
    
    1
  2. 写入内容:

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
      "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
    <plist version="1.0">
    <dict>
        <key>Label</key>
        <string>com.ssh.tunnel</string>
    
        <key>ProgramArguments</key>
        <array>
            <string>/usr/bin/ssh</string>
            <!-- 不执行任何远程命令,仅建立隧道连接,纯转发模式 -->
            <string>-N</string>
            <!-- 每隔 60 秒向服务器发送一次心跳包,防止因长时间无数据传输被防火墙/路由器断开。 -->
            <string>-o</string><string>ServerAliveInterval=60</string>
            <!-- 心跳包连续发送 3 次没有收到响应,则判定连接已死,主动断开。(即最多等 60×3=180 秒) -->
            <string>-o</string><string>ServerAliveCountMax=3</string>
            <!-- 如果端口转发建立失败(比如服务器端口被占用),ssh 进程直接退出。这样 launchd 检测到进程退出后会自动重启,避免僵死。 -->
            <string>-o</string><string>ExitOnForwardFailure=yes</string>
            <string>-R</string>
            <string>8080:localhost:80</string>
            <string>user@公网服务器IP</string>
        </array>
    
        <!-- 断开后自动重启 -->
        <key>KeepAlive</key>
        <true/>
    
        <!-- 开机自启 -->
        <key>RunAtLoad</key>
        <true/>
    
        <!-- 日志 -->
        <key>StandardOutPath</key>
        <string>/tmp/ssh-tunnel.log</string>
        <key>StandardErrorPath</key>
        <string>/tmp/ssh-tunnel.err</string>
    </dict>
    </plist>
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
  3. 加载并启动:

    # 加载服务
    launchctl load ~/Library/LaunchAgents/com.ssh.tunnel.plist
    
    # 查看状态
    launchctl list | grep ssh.tunnel
    
    # 停止
    launchctl unload ~/Library/LaunchAgents/com.ssh.tunnel.plist
    
    1
    2
    3
    4
    5
    6
    7
    8

方案二:autossh + launchd

  1. 先安装 autossh:

    brew install autossh
    
    1
  2. plist 改为调用 autossh:

    <!-- autossh 的 -M 9090 会额外开一个监控端口,检测隧道是否存活,比原生 ssh 重连更可靠。 -->
    <key>ProgramArguments</key>
    <array>
        <string>/opt/homebrew/bin/autossh</string>
        <string>-M</string><string>9090</string>
        <string>-N</string>
        <string>-R</string>
        <string>8080:localhost:80</string>
        <string>user@公网服务器IP</string>
    </array>
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
  3. 必要前提:免密登录 launchd 运行时没有交互界面,必须配置 SSH 密钥登录,否则无法自动重连:

    # 生成密钥(如果还没有)
    ssh-keygen -t ed25519
    
    # 上传公钥到服务器
    ssh-copy-id user@公网服务器IP
    
    # 测试免密登录
    ssh -N user@公网服务器IP
    
    1
    2
    3
    4
    5
    6
    7
    8

# ✅ 总结

内网穿透的本质是通过**“内网主动出击”来换取“外部受控进入”**。选择方案时应平衡安全性、稳定性和操作复杂度:临时需求选 SSH,长期服务选 frp,多设备互联选虚拟网卡组网

#运维
上次更新: 2026/05/07, 22:02:37
DNS 查询
macOS 定时任务实现:Git 仓库状态自动同步方案

← DNS 查询 macOS 定时任务实现:Git 仓库状态自动同步方案→

最近更新
01
Android 端口转发(Forward & Reverse) 的实操与避坑全指南
04-29
02
VisiData 终极生存指南(vd)
04-27
03
macOS 定时任务实现:Git 仓库状态自动同步方案
04-26
更多文章>
Theme by Vdoing | Copyright © 2019-2026 Jacky | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式