保护 SSH 端口转发?

保护 SSH 端口转发?

我想在远程客户端(RC)上运行 SOCKS 服务器,它将从某些云虚拟服务器(VS)接收请求(反向隧道),而这些云虚拟服务器将从我自己的本地客户端(LC)接收请求。

换句话说,

  1. VS 将某个本地端口 A 重定向到本地端口 B
  2. LC 连接到 VS 的端口 A(VS 上的端口 A 暴露在互联网上)
  3. RC 从 VS 端口 B 反向隧道到自身

但是,我最终还是想确保所有 LC<->RC 连接都经过身份验证和加密。换句话说,

  1. 对比仅有的接受(并验证 + 加密)SSH来自客户端 LC 和 RC 的连接。任何“未经身份验证”的连接(甚至任何“非 SSH”连接!)都不会被接受(更不用说转发到 RC)——它们都会被丢弃/忽略。 所以,仅限 LC可以连接到 VS 端口 A,并且仅限 RC可以连接到 VS 端口 B。
  2. 因此,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:portRC 可以访问的地方(一般来说,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:portVS 的方法,就像您在 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/socketVS 上已经存在则必须先将其删除才能ssh -R工作。存在自动解决方案,它要求StreamLocalBindUnlink位于yes服务器上,在您的情况下是在 VS 上。如果您无法通过这种方式配置 VS,则应ssh … 'rm /path/to/socket'先使用 RC,然后再使用ssh -RStreamLocalBindUnlink存在作为选项,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 -RRC 和ssh -LLC。然后,您可以通过连接到127.0.0.1:7777LC 来访问 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:5555VS 到达在 RC 上运行的 SOCKS 代理(就好像address:port那里有一个独立的 SOCKS 代理并且隧道通向它一样)。因此,RC 的管理员可以为您提供 SOCKS 代理,而无需运行独立的 SOCKS 服务器甚至 SSH 服务器,这就ssh足够了。从 LC,您可以像在一般情况下一样使用它。不同之处在于,ssh -R充当 SOCKS 代理不能绑定到 Unix 套接字(至少在我写这篇文章时不能),它必须是 TCP 端口。因此,通过使用 Unix 套接字来严格限制访问是不可能的。


笔记

  • ssh并且sshd高度可配置。有很多选项(参见man 5 ssh_configman 5 sshd_config)。ssh根据文件中的选项,相同的简单命令可能会以不同的方式工作。例如,当我说“ssh -R默认情况下,RC 将创建具有所有权和模式的套接字,因此只有以 […] 身份连接的用户 RC 可以使用它”时,请不要将“默认情况下”读作“当命令行中没有其他选项时ssh”。我认为,相关选项StreamLocalBindMask在服务器上(在sshd_config),如果指定了它,则“默认情况下”不适用。还有更多选项可能会干扰您想要执行的操作,例如AllowTcpForwarding

  • ssh我的这个答案解释了如何使用 SOCKS 代理创建:如何使用 ssh 创建 SOCKS 代理?在链接答案的上下文中,您的 LC 是AB( A=B情况),您的 RC 是C。您的 VS 位于 和 之间BC链接答案不考虑此类节点。一旦您通过使从 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 是可能的,尽管速度可能很慢。

答案2

SSH 为使用渠道,其中封装了 SOCKS 功能(以及 shell、端口转发或 SFTP)。SSH 连接由多个层组成,这些层在整个协议中都具有特定的功能。此示例的简化视图:

  • 传输层(RFC 4253)提供加密
  • 用户认证层(RFC 4252)通过各种方式提供身份验证
  • 连接层(RFC 4254) 提供上述频道

相关内容