语境
我有一个用于网络的个人服务器。有时我需要通过 SSH/SFTP 连接到它。
免责声明:我对内部事务的经验很少nginx
。
问题
今天早上,我发现一家知名咖啡连锁店的免费 wifi 屏蔽了 SSH(实际上,他们屏蔽了 80/443 以外的所有东西)。但是当我需要 SSH 时,我就需要它,所以我寻找在同一端口上共享 SSH 和 HTTPS 的方法。
我看了什么
我研究了一些可以在端口 443 上运行的可能的解决方案:
SSHL
:SSH/OpenVPN/HTTPS 多路复用器;OpenVPN
:VPN 解决方案具有用于 OpenVPN 和 HTTPS 的内置多路复用器;HAProxy
:网络服务器/负载均衡器也可以多路复用所有内容。
所有这些看起来都很简单但我不太喜欢增加层次和复杂性,可能当我需要在 443 上进行 SSH 时,速度就会变慢。
将 Nginx 纳入其中
我知道nginx
已经支持原始 TCP 流处理。所以我想知道我是否也可以在 中直接在端口 443 上使用它nginx
。这个想法是,如果它识别 HTTP(S) 或其他所有内容,nginx
则可以选择使用该模块。http
stream
问题
因此,我有两个问题:
- 甚至能够做到这样的区分吗?(我甚至不确定我是否能够同时在和块
nginx
中监听端口 443。 )http
stream
- 如果是这样,那么该设置是否存在明显的性能问题?(例如,我考虑的是 SFTP 的传输速度,而不是 SSH 本身。)
答案1
自 nginx 1.15.2 版本开始,添加了新变量$ssl_preread_protocol
。并在官方博客中添加了关于如何使用此变量在同一端口上多路复用 HTTPS 和 SSH 的帖子
https://www.nginx.com/blog/running-non-ssl-protocols-over-ssl-port-nginx-1-15-2/
配置 SSH(默认)和 HTTPS 的示例:
stream {
upstream ssh {
server 192.0.2.1:22;
}
upstream web {
server 192.0.2.2:443;
}
map $ssl_preread_protocol $upstream {
default ssh;
"TLSv1.2" web;
}
# SSH and SSL on the same port
server {
listen 443;
proxy_pass $upstream;
ssl_preread on;
}
}
答案2
我有一个工作配置,使用 nginx 流模块在端口 443 上通过 tls 隧道传输 ssh。我还在同一端口上通过 TLS 和普通 HTTP 进行 XMPP。我通过 ALPN 进行多路复用。
(您需要 nginx > 1.13.10 才能将 ssl_preread 模块与 alpn 一起使用 http://nginx.org/en/docs/stream/ngx_stream_ssl_preread_module.html#ssl_preread)
我的配置使用了docker版本的nginx,但是没有docker也应该可以工作。
stream {
# check ALPN for xmpp client or server and redirect to local ssl termination endpoints
map $ssl_preread_alpn_protocols $ssl_multiplexer {
"xmpp-client" 127.0.0.1:5422;
"xmpp-server" 127.0.0.1:5469;
"identifyssh" 127.0.0.1:8822;
default 127.0.0.1:8443;
}
server {
listen 443;
ssl_preread on;
proxy_pass $ssl_multiplexer;
proxy_protocol on;
set_real_ip_from 172.18.0.0/32;
}
# ssl termination for c2s connections
server {
listen 5422 ssl proxy_protocol;
# ... <- tls keys and options here
proxy_ssl off;
proxy_pass ejabberd:5222;
}
# ssl termination for s2s connections
server {
listen 5469 ssl proxy_protocol;
# ... <- tls keys and options here
proxy_ssl off;
proxy_pass ejabberd:5269;
}
# ssl termination for ssh connections
server {
listen 8822 ssl proxy_protocol;
# ... <- tls keys and options here
proxy_ssl off;
proxy_pass yourserver:22;
}
}
如果你想使用 XMPP 的东西,你必须添加一些 SRV 记录来指向你的服务器 443 端口,请参阅https://xmpp.org/extensions/xep-0368.html
如果要连接到 ssh 服务器,则必须将 ssh 会话包装在 ssl 会话中,该会话发送您在流配置中定义的 ALPN 字符串。我在上面的示例中使用了“identifyssh”。您可以使用任何名称,但尽量不要与官方定义的名称冲突: https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml#alpn-protocol-ids
要从客户端启动到准备好的服务器的 ssh 会话,请使用:
ssh you@yourserver -o "ProxyCommand openssl s_client -alpn identifyssh -ign_eof -connect yourserver:443"
你应该已经连接上了。
我还应该注意,我使用 proxy_protocol 来保留客户端标头和 IP 地址,同时将其传递到我的后端。
您在 http {} 部分中配置的常规 http 服务器应该处理以下问题:
server {
listen 8443 ssl proxy_protocol;
# ...
}
最好的事情是,您不需要 sslh、stunnel、proxytunnel 或其他工具来实现这一点。您只需要一个较新的 nginx 和 openssl。希望这对某些人有帮助。它有助于我深入研究这些东西。
答案3
或者,你可以通过 HTTP/S 连接通过 nginx 进行代理隧道 ssh
看一看:代理隧道
答案4
您可以设置一个 iptables 规则,将您知名的咖啡馆的 443 端口连接转发到 22 端口。或者,将流量从互联网上其他地方的端口中继反弹,从而更改 pirt 号码。
仅仅尝试用 nginx 来解决问题是行不通的。