Nginx可通过启用stream模块实现SSH流量的四层代理。需确认编译时含--with-stream选项,配置load_module加载模块,stream块置于http外层,监听指定端口(如2222)并proxy_pass至内网Git服务器,支持超时、连接限制及IP白名单控制。

Nginx 本身不能直接代理 SSH 流量(因为 SSH 是 TCP 协议,而传统 http 模块只处理 HTTP/HTTPS 应用层流量),但通过启用 Nginx 的 stream 模块(四层 TCP/UDP 代理),完全可以安全、平滑地将公网 SSH 请求转发到内网 Git 服务器,并叠加访问控制、端口隐藏与黑白名单等能力。
关键不是“绕过 SSH”,而是“在 SSH 连接建立前加一层可控的入口门卫”。
确认并启用 Nginx stream 模块
这是整个方案的前提:
- 运行
nginx -V 2>&1 | grep -o with-stream,确认输出含with-stream;若无,需重新编译 Nginx 并添加--with-stream参数 - 在主配置文件
/etc/nginx/nginx.conf的顶层(http块之外)加入:load_module /usr/lib/nginx/modules/ngx_stream_module.so;(路径依系统而异,常见于/usr/lib64/nginx/modules/或通过nginx -V查看模块路径)
配置 stream 块代理 Git SSH 流量
新建或编辑 /etc/nginx/stream.conf(并在 nginx.conf 中 include stream.conf;):
stream {
# 定义上游 Git 服务器(内网地址 + 实际 SSH 端口,如 22)
upstream git_ssh_backend {
server 192.168.10.5:22; # 替换为你的内网 Git 服务器 IP 和端口
}
<pre class="brush:php;toolbar:false;"># 公网监听:例如用 2222 替代默认 22,降低暴力扫描风险
server {
listen 2222;
proxy_pass git_ssh_backend;
proxy_timeout 1s;
proxy_responses 1;
# 可选:限制单 IP 连接数,防爆破
limit_conn addr 3;
# 可选:基于 realip 获取真实客户端 IP(需配合前端防火墙或云厂商透传)
# 注意:stream 模块不支持 X-Forwarded-For,仅支持 proxy_protocol(需客户端/前置设备支持)
}}
✅ 效果:用户执行 git clone ssh://git@your-domain.com:2222/xxx.git,流量经 Nginx 转发至内网 192.168.10.5:22,对 Git 服务零侵入。
强化安全:IP 白名单 + 动态封禁
利用 `geo` + `map` + `limit_conn` 实现轻量级访问控制:
stream {
# 定义可信 IP 段(支持 CIDR)
geo $real_ip_whitelist {
default 0;
203.0.113.0/24 1; # 示例:运维办公网段
2001:db8::/32 1; # IPv6 支持
123.45.67.89 1; # 个别固定运维 IP
}
<pre class="brush:php;toolbar:false;">map $real_ip_whitelist $limit_key {
0 $binary_remote_addr;
1 "";
}
upstream git_ssh_backend { ... }
server {
listen 2222;
proxy_pass git_ssh_backend;
# 仅放行白名单,其余 IP 拒绝连接(返回 connection refused)
if ($real_ip_whitelist = 0) {
return 503;
}
# 或更柔性:对非白名单 IP 限速(每分钟最多 2 次新连接)
limit_conn addr 2;
limit_conn_zone $limit_key zone=git_ssh_limit:10m;
}}
⚠️ 注意:if 在 stream 块中仅支持简单比较(=/!=),不可用于复杂逻辑;生产环境建议搭配 fail2ban 监控 Nginx error log 自动封禁异常 IP。
适配 Git 客户端克隆地址(关键细节)
用户看到的克隆 URL 必须指向你暴露的公网地址和端口,否则会连错。以 GitLab 为例(其他自建 Git 如 Gitea、Gogs 同理):
- 修改 GitLab 配置
/etc/gitlab/gitlab.rb:
# 指向你配置的公网域名和 Nginx stream 监听端口 gitlab_rails['gitlab_ssh_host'] = 'git.example.com' gitlab_rails['gitlab_shell_ssh_port'] = 2222 <h1>关闭 GitLab 自带 Nginx(避免端口冲突)</h1><p>nginx['enable'] = false</p><h1>执行重载</h1><p>sudo gitlab-ctl reconfigure</p>
这样 Web 页面显示的克隆命令就是:git@git.example.com:group/repo.git(自动补上 :2222),无需用户手动写端口。
验证与调试要点
配置完成后务必验证:
- 重启 Nginx:
sudo nginx -t && sudo systemctl reload nginx - 测试 TCP 连通性:
telnet git.example.com 2222(应能连上,出现 SSH banner) - 检查日志:
tail -f /var/log/nginx/error.log,关注 stream 错误(如 upstream unreachable、connection refused) - 确认内网 Git 服务器防火墙放行来自 Nginx 服务器的 22 端口连接
不依赖 HTTPS,不修改 Git 协议,不暴露内网拓扑——这就是 stream 代理 SSH 的简洁力量。











