我想在远程客户端(RC)上运行 SOCKS 服务器,它将从某些云虚拟服务器(VS)接收请求(反向隧道),而这些云虚拟服务器将从我自己的本地客户端(LC)接收请求。
换句话说,
- VS 将某个本地端口 A 重定向到本地端口 B
- LC 连接到 VS 的端口 A(VS 上的端口 A 暴露在互联网上)
- RC 从 VS 端口 B 反向隧道到自身
但是,我最终还是想确保所有 LC<->RC 连接都经过身份验证和加密。换句话说,
- 对比仅有的接受(并验证 + 加密)SSH来自客户端 LC 和 RC 的连接。任何“未经身份验证”的连接(甚至任何“非 SSH”连接!)都不会被接受(更不用说转发到 RC)——它们都会被丢弃/忽略。 所以,仅限 LC可以连接到 VS 端口 A,并且仅限 RC可以连接到 VS 端口 B。
- 因此,RC 仅接受最终由 LC 发起的连接
理想情况下,这可以通过在所有设备上生成密钥对并共享所有公钥(到所有设备)来实现。但是,SSH 真的能做到这一切吗?SSH 反向隧道/端口转发真的能保护(验证和加密)所有这些连接吗?即使我只是用它来隧道袜子?(即,不创建任何 shell 或“终端”,或执行任何远程命令等......)
*编辑:我不需要 RC 上的 socks 服务器自己进行任何“socks 身份验证”(就此而言,RC socks 服务器可以绑定本地主机)。对于 RC,我只需要命令ssh -R
(从 RC 执行)来确保 RC 接收的任何内容(并发送到其本地 socks 服务器)都是通过与 VS 的经过身份验证/加密的连接进行的。
*编辑#2:是否可以将其扩展到 UDP 连接?(例如,也用于隧道 DNS 请求)
答案1
初步说明
此答案以 Unix 为中心。
ssh
可以在 Windows 中使用。在 Windows 中,此答案中提到的某些概念可能不适用。示例命令使用来自 Unix 世界的引号和路径名。我使用 OpenSSH。其他实现可能会缺少此答案使用的某些功能。
关于 SSH 隧道
其他答案从技术上来说是正确的,但它并没有真正解决您的问题。SSH 协议确实提供了加密和身份验证。当 SSH 连接启动时,客户端和服务器会相互进行身份验证。通过连接传输的所有数据都经过加密,包括隧道数据;但……
SSH 不会以任何特定于 SSH 的方式限制对隧道的访问。如果监听端是 TCP 端口,则将应用特定于网络的限制:接口(例如,本地接口无法从外部使用)、防火墙等。如果监听端是 Unix 套接字,则将应用特定于文件访问的限制(因为套接字是文件): 模式、所有权等(注:手册警告并非所有操作系统都遵守 Unix 域套接字文件的文件模式;仍然在访问受限的目录中创建套接字应限制访问在任何系统中)。
如果可以打开监听端,则可以使用隧道。不会出现任何提示或任何由 SSH 注入的内容。根据设计,使用 或 创建的隧道会按ssh -L
原样ssh -R
将数据从一端转发到另一端(双向);它应该是透明的。这意味着当您连接到 时,some_address:port
无论服务器是在具有 的计算机上some_address
,还是在另一台计算机上的一个或多个隧道后面,都没有区别;服务器会获取您发送的内容,您也会获取服务器发送的内容。
如果您的 RC 使用ssh -R
隧道将 VS 的 B 端口传输到任何address:port
RC 可以访问的地方(一般来说,address:port
可能属于 RC,也可能不属于 RC),那么连接到 VS 的 B 端口就像address:port
从 RC 连接到一样。如果可以从 Internet 访问 VS 的监听端口 B,那么任何人都可以address:port
像在 RC 上一样访问。隧道的透明度允许这样做。如果隧道协议允许身份验证,可以使用它。一般来说,可能没有协议,您可能想要发送任意流,隧道将按原样中继它。
在您的情况下,可能无法从 Internet 访问 VS 的端口 B,但可以访问端口 A,并且将其重定向到端口 B。无论如何,都有一些暴露的端口允许任何人连接,address:port
就像他们在 RC 上一样。
提高一般情况下的安全性
有几种方法可以改善设置并使其对公众不那么开放:
VS 上的防火墙可以配置为拒绝 LC 以外的任何地方访问公开端口。这要求 LC 使用静态 IP 地址,以便其通信到达 VS。如果 LC 位于 NAT 后面,则 VS 将无法可靠地区分 LC 与位于同一 NAT 后面的另一台主机。在 VS 上设置防火墙规则需要对 VS 具有管理访问权限。您可能不想(或不能)使用此方法。
RC 在与 连接时
ssh -R
,可以让 VS 上的 SSH 服务器绑定到 VS 的环回接口。假设所选端口为 5555,它将类似于ssh -R 127.0.0.1:5555:address:port …
。然后 VS 不应将任何暴露的端口重定向到隧道的末端。要使用隧道,需要连接到127.0.0.1:5555
(或类似地址) 在 VS 内部。要从 LC 连接,您需要找到一种到达
127.0.0.1:port
VS 的方法,就像您在 VS 上一样。这很简单,只要您有 SSH 访问权限即可。ssh -L 127.0.0.1:7777:127.0.0.1:5555 …
在 LC 上使用隧道将 LC 的端口 7777 连接到 VS 上的正确端口。然后在 LC 上连接到127.0.0.1:7777
。此连接将使用两个链接的隧道,最终address:port
将从 RC 到达,就像您在 RC 上一样。该方法需要从 LC 通过 SSH 访问 VS。互联网上的随机人员将无法使用隧道。如果 LC 用户连接到 ,则可以使用
127.0.0.1:7777
;这包括您以外的用户。在私人计算机上,这通常不是问题(请参阅下面的注释部分)。但是,如果 VS 用户连接到 VS 内部,则可以使用隧道。127.0.0.1:5555
在多用户环境中,这可能是一个问题。为了保护您的隧道免受 VS 其他用户的攻击,请修改以前的方法,因此它不使用 VS 上的 TCP 端口,而是使用 Unix 套接字。在 RC 上,您需要使用 连接到 VS
ssh -R /path/to/socket:address:port …
;在 LC 上,您需要使用 连接到 VS。使用用户(我的意思是用户 RC 连接的身份,即用户 RC 在连接到 VS 时指定的用户)可以在 VS 中创建文件的ssh -L 7777:/path/to/socket …
任意目录。至少有两个问题:/path/to/
ssh
ssh -R
来自 RC 的将默认创建具有此类所有权和模式的套接字,因此只有用户 RC 才能以(或 VS 的根) 可以使用它。在某些系统中,套接字权限会被忽略 (限制访问/path/to/
) 但在 Linux 中它们可以工作 (参见路径名套接字所有权和权限在man 7 unix
)。这将保护您的隧道免受 VS 的其他用户的攻击,这正是我们想要的。如果 LC 以与 RC 相同的用户身份连接到 VS,那么就完美了。如果您从 LC 以 VS 上的另一个用户身份运行,那么套接字必须对您可用。setfacl
创建套接字后,RC 需要在 VS 中运行。这可以通过 RC 执行时提供的 shell 代码来完成ssh -R
(并且代码应该以 结尾,以exec sleep 2147483647
防止连接终止,此数字应该是安全且足够)。如果
/path/to/socket
VS 上已经存在则必须先将其删除才能ssh -R
工作。存在自动解决方案,它要求StreamLocalBindUnlink
位于yes
服务器上,在您的情况下是在 VS 上。如果您无法通过这种方式配置 VS,则应ssh … 'rm /path/to/socket'
先使用 RC,然后再使用ssh -R
。StreamLocalBindUnlink
存在作为选项,ssh
但我认为它适用于在客户端创建的套接字(我们这里不使用此类套接字);有人说这是一个 bug。
使用套接字而不是 TCP 端口不仅可以让 VS 上的其他用户远离您的隧道;还可以防止他们诱骗您使用错误的隧道。想象一下,RC 用来
ssh -R
创建隧道,却发现它想要在 VS 上使用的端口已被占用。其他用户正在使用它,可能用作隧道。也许它会导致您期望从 RC 获得的服务类型相同。您不知道 RC 失败了,您像往常一样从 LC 连接并发送数据。从其他用户的角度来看,您是一个未经邀请使用其设置的随机人;或者是一个向他们发送敏感数据的受害者。如果您正确使用 Unix 套接字,就不会发生这种情况。
请注意,在上述情况下,您实际上并不需要 SSH 访问 RC。我的意思是,无论 RC 应该运行什么代码,它都可以由 RC 的管理员(可能不是您)设置一次并自动执行。RC 甚至可能不运行sshd
(服务器);ssh
(客户端应用程序)就足够了。另一方面,要链接隧道,您需要从 LC 到 VS 的 SSH 访问权限。
通过 SSH 访问 RC 可以为您提供更多选项。
具体案例:通过 SSH 隧道传输 SSH
我假设您无法从 LC 直接到达 RC(否则首先就不需要 VS)。如果这是阻止您从 LC 到达 RC 的唯一原因ssh
,即如果 SSH 服务器在 RC 上运行并且您有密钥或密码来连接,那么您可以通过另一种方式建立隧道。
首先,您需要能够访问 RC 的 SSH 服务器。这很容易:将 的一般情况(上面已详细说明)address:port
应用于127.0.0.1:22
。
即使 RCssh -R '*:5555:127.0.0.1:22' …
确实需要 VS 监听所有接口(包括暴露的接口),或者 VS 将一些暴露的端口重定向到隧道,该设置仍然非常安全。这是因为任何从 Internet 连接到隧道监听端的人都会到达 RC 的 SSH 服务器,而 RC 的 SSH 服务器无论如何都需要身份验证。
我知道隧道 SSH 不是你的目标。重点是现在你可以通过ssh
在 LC 上调用来在 LC 和 RC 之间创建任何你想要的 TCP 隧道。你可以创建许多隧道。例如,要从 LC 到达任意位置,address:port
就像你在 RC 上一样,在 LC 上调用:
ssh -p 5555 -NL 127.0.0.1:1234:address:port userRC@VS
127.0.0.1:1234
并在您想要连接的任何程序中使用 LC address:port
。
笔记:
传输数据的 SSH 连接本身是在 VS 和 RC 之间建立的。因此,这是隧道中的隧道。
userRC@VS
意味着您需要使用 VS 的地址,但需要使用 RC 的用户名(和凭据)。我们只使用 VS 的地址,因为它是通向 RC 的隧道的监听端。您不需要 SSH 访问 VS。RC 可能无法创建隧道,因为其他人已占用 VS 上选定的端口。我们注意到这种可能性是一般情况下存在的,它不安全。现在情况没那么糟糕。如果发生这种情况,并且您(从 LC)尝试连接到您认为是 RC 的 SSH 服务器,你将能够分辨出它不是 RC,只要你注意指纹。
ssh
它的known_hosts
文件会尝试提供帮助,尽管它可能无法VS:22
区分VS:5555
,因此你可能会收到错误警告。无论如何原则你就能知道。这意味着,如果您小心谨慎,劫持者就无法欺骗您。但是,他或她仍然可以阻止您访问 RC。使用 Unix 套接字创建链式隧道的方法会很方便(请注意,它需要从 LC 到 VS 的 SSH 访问)。再次,采用的一般情况(上面详细说明)
address:port
并将其应用于127.0.0.1:22
。该方法涉及ssh -R
RC 和ssh -L
LC。然后,您可以通过连接到127.0.0.1:7777
LC 来访问 RC 的 SSH 服务器。最后,LC 上的附加ssh
命令采用以下形式:ssh -p 7777 -NL 127.0.0.1:1234:address:port [email protected]
将允许您创建一个隧道
address:port
(从 RC 中看到),本地可用127.0.0.1:1234
。它将是链接隧道中的一条隧道。
具体案例:袜子
你写了:
我想在远程客户端上运行 SOCKS 服务器(RC)[…]
如果可以通过 RC 访问 SOCKS 服务器,address:port
那么要从 LC 连接,您可以使用上述任何一种非特定于 SOCKS 的方法。
但是,如果您可以从 LC 访问 RC 的 SSH 服务器,则不需要独立的 SOCKS 服务器。有一个ssh -D
,它有点类似于ssh -L
,它侦听本地端口。它不会将端口转发到address:port
远程端的任意位置,而是ssh -D
为您创建一个 SOCKS 服务器,就好像端口被隧道传输到远程计算机上 SSH 服务器旁边运行的某个 SOCKS 服务器一样。它是一个内置的 SOCKS 服务器,可与一起使用ssh
。
要像在 RC 上运行独立 SOCKS 服务器一样使用它,您需要通过 SSH 访问 RC。这就是为什么本答案的上一节首先集中于从 LC 访问 RC 的 SSH 服务器。
如果你可以直接到达 RC(无需 VS),你可以ssh -D
在 LC 上这样使用:
ssh -ND 127.0.0.1:8888 userRC@RC
并且您需要将浏览器(或其他任何浏览器)配置127.0.0.1:8888
为 SOCKS 代理。通过 VS 建立隧道时的情况与上一节类似,只是您需要的是-D 127.0.0.1:8888
而不是-L 127.0.0.1:1234:address:port
。示例:
ssh -p 5555 -ND 127.0.0.1:8888 userRC@VS
一般来说,如果我是你,我会这样做。我会设置从 LC 到 RC 的 SSH 访问,然后ssh -D
在 LC 上使用。
鲜为人知的是它ssh -R
可以充当 SOCKS 代理。如果 RC 创建的隧道是在未指定的情况下构建的address:port
,即如果命令类似于ssh -R 127.0.0.1:5555 …
,那么您可以使用127.0.0.1:5555
VS 到达在 RC 上运行的 SOCKS 代理(就好像address:port
那里有一个独立的 SOCKS 代理并且隧道通向它一样)。因此,RC 的管理员可以为您提供 SOCKS 代理,而无需运行独立的 SOCKS 服务器甚至 SSH 服务器,这就ssh
足够了。从 LC,您可以像在一般情况下一样使用它。不同之处在于,ssh -R
充当 SOCKS 代理不能绑定到 Unix 套接字(至少在我写这篇文章时不能),它必须是 TCP 端口。因此,通过使用 Unix 套接字来严格限制访问是不可能的。
笔记
ssh
并且sshd
高度可配置。有很多选项(参见man 5 ssh_config
和man 5 sshd_config
)。ssh
根据文件中的选项,相同的简单命令可能会以不同的方式工作。例如,当我说“ssh -R
默认情况下,RC 将创建具有所有权和模式的套接字,因此只有以 […] 身份连接的用户 RC 可以使用它”时,请不要将“默认情况下”读作“当命令行中没有其他选项时ssh
”。我认为,相关选项StreamLocalBindMask
在服务器上(在sshd_config
),如果指定了它,则“默认情况下”不适用。还有更多选项可能会干扰您想要执行的操作,例如AllowTcpForwarding
。ssh
我的这个答案解释了如何使用 SOCKS 代理创建:如何使用 ssh 创建 SOCKS 代理?在链接答案的上下文中,您的 LC 是A
和B
(A
=B
情况),您的 RC 是C
。您的 VS 位于 和 之间B
,C
链接答案不考虑此类节点。一旦您通过使从 LC 到 RC 成为可能将 VS 移出问题ssh
,链接答案就会变得有用。即使我们在本答案中考虑的最安全的设置也允许其他 LC 用户使用终极隧道。如果这是一个问题,并且如果要使用隧道的程序可以使用 Unix 套接字而不是端口,请考虑在 LC 中使用套接字。如果程序必须使用 TCP 端口,我猜单独的网络命名空间会有所帮助;但设置它并不简单,我不会详细说明。如果你想要不费吹灰之力就能获得安全性,那么成为 LC 的唯一用户会有所帮助。
成为 VS 的唯一用户也有帮助。毕竟我写了这么多,应该很清楚为什么会这样。
UDP
是否可以将其扩展到 UDP 连接?
当您可以通过 LC 到达 RC 时ssh
,您可以创建多个隧道。但 UDP 则不那么容易。不支持 (为什么?)。读取 UDP、写入 TCP、隧道 TCP 并“转换”回 UDP 的技巧(例子) 是有缺陷的,不要相信它们。UDP 保留消息边界,而 TCP 不保留,这就是缺陷的来源。这个答案提到ssh -w
看起来不错(至少理论上没有缺陷),但我还没有测试过。使用 TCP 隧道连接的全尺寸 VPN 是可能的,尽管速度可能很慢。