第十三章:虚拟专用网络

你可以将SSH包裹在任意TCP连接周围,为任何协议添加一层加密。但OpenSSH还支持构建通用隧道,可以通过所有流量得所有协议,而不仅仅是TCP。

你可以使用OpenSSH连接到远程办公室,创建一个虚拟专用网络(Virtual Private Networks——VPN),允许一个办公室得用户访问另一个办公室,几乎就像他们在不同楼层,而不是在不同国家一样。

VPN是SSH协议得OpenSSH扩展。PuTTY不包括VPN功能,PuTTY开发人员一再表示他们不打算将其添加到他们的客户端。本书将只检查类Unix系统上的OpenSSH VPN。

SSH不是作为通用VPN协议设置的,TCP内部的隧道协议是一种可怕的做法。当TCP连接丢失数据包时,它必须重新传输这些数据包,直到连接的另一端确认收到为止。通过将TCP连接包裹在另一个TCP连接中,可以放大数据包丢失的影响。基于TCP的VPN在拥塞面前崩溃。我强烈建议你使用OpenVPN而不是OpenSSH作为VPN。

OpenSSH VPN的优点是它只需要在客户端和服务器之间打开一个TCP端口。如果你只有这些连接,那么OpenSSH VPN可能是最不糟糕(least terrible)的选择。

使用OpenSSH,VPN可能是最复杂的事情。本章假设你熟悉前面的章节,包括公钥身份验证、保持SSH会话活动以及限制SSH客户端可用的命令。

示例网络

我们的SSH客户端avarice.mwl.io有两个网络接口。一个是在公共互联网上。虽然我们可以通过IP地址引用该接口,但我们将使用主机名。第二个接口位于专用网络A上,地址为172.16.0.1/24。

SSH服务器gluttony.mwl.io在公共互联网上也有一个接口。我们将通过主机名而不是IP来引用此接口。它的第二张网卡在专用网络B上,IP地址为172.17.0.1/24。

我们将使用SSH在两台主机之间建立点对点隧道。隧道客户端的IP地址为192.168.0.2/30。隧道的服务器端获得192.168.0.1/30。

我们将考虑OpenBSD、FreeBSD、Debian和CentOS。OpenBSD拥有任何操作系统中最好的SSH VPN支持——考虑到OpenSSH起源于OpenBSD,这不应该让任何人感到惊讶。在FreeBSD上运行SSH VPN需要基本的脚本。大多数Linux发行版都会更改OpenSSH以更好地适应其系统,并且他们还弃用了标准的UNIX网络命令,转而使用Linux特定的工具。这意味着每个操作系统都需要不同的方法。在这四种方法中,您应该找到一种可以适应您的操作系统的方法。

创建和管理VPN是OpenSSH中最困难的功能,支持VPN的操作系统会随着时间的推移而变化。如果看到这些VPN说明比本书其他部分更快地过时,我不会感到震惊。如果您在使用这些示例时遇到问题,请参阅操作系统文档以获取更多最新参考。

常见概念

以下OpenSSH VPN的概念(concept)和配置出现在所有操作系统中。无论你运行哪种操作系统,你都必须理解这些材料并遵循这些一般原则。虽然你可以找到旨在简化隧道设置的工具,但一旦你了解了隧道的工作原理,就会发现使用原始SSH非常简单。

隧道接口

SSH VPN使用隧道(或 tun)接口工作。隧道是位于其他网络接口之上的虚拟接口。隧道接口最常见的用途是在两个单独的主机之间创建虚拟链接,例如在VPN中。此隧道被视为点对点连接。创建隧道接口的方法因操作系统而异。

当你使用SSH VPN时,客户端和服务器端都将自己连接到各自机器上的隧道接口。当操作系统向隧道发送数据包时,数据包通过SSH连接中继。当其他机器的SSH进程接收到数据包时,它将其解包并通过本地隧道接口将其发送到操作系统。

就像你想用于IP路由的任何其他接口一样,你的隧道接口需要IP地址。你必须将发送远程网络的流量路由到隧道远程端的IP地址。我们将在每个示例中演示这一点。

每个隧道接口都需要一个设备号,就像类Unix系统上的任何其他设备一样。正如你的网络接口可能是eth0或em1一样,隧道设备也可能时tun0或tun1。我们的示例使用设备零,创建像tun0这样的设备名称。如果你有很多隧道设备,我建议你为每种目的分配一个特定的设备,并重新评估你的设计选择。

SSH 服务器设置

sshd_config关键字PermitTunnel指定客户端是否可以建立VPN隧道。PermitTunnel有四个有效选项:yes、no、point-to-point、ethernet。

如果设置为no(默认值),禁止使用隧道。如果设置yes,则允许使用所有隧道。

poin-to-point隧道是一种从一个点到另一个点的虚拟专用线路。点对点隧道需要路由才能使用。这通常是SSH VPN的最佳隧道类型。

ethernet隧道传输第2层流量,允许两个单独的位置共享其本地局域网。如果可能的话,不要通过SSH隧道传输以太网。VPN一侧的本地网络问题可能会在链路上传播,并使你的外部带宽饱和。SSH VPN已经容易受到拥塞的影响,不要再放大这个问题。

要使用SSH VPN,SSH进程必须具有足够的权限来更改客户端和服务器上的隧道设备和路由表。创建SSH VPN需要客户端和服务器上的root权限。你将以root身份运行ssh,并直接以root身份登录。以前讨论过,以root身份登录是一个糟糕的选择。但是,如果你使用SSH VPN,你基本上没有好的选择。

此处,我允许我们的SSH客户端在SSH服务器上进行root登录,但只能通过公钥身份验证。我还允许该IP地址打开隧道:

在生产配置中,使用客户端的IP地址,而不要使用主机名。

非常旧的OpenSSH版本可能不允许你将PermitTunnel语句放入Match语句中。如果你遇到这样的sshd,请立即升级服务器的OpenSSH——在公共互联网上使用它是不安全的。

IP 转发

对于连接两个不同网络的SSH VPN,SSH服务器和客户端都必须将数据包从一个接口转发到另一个接口。这被称为IP转发。在接口之间转发数据包是主机和路由器之间的唯一区别。SSH客户端在其内部以太网接口上接收数据包,并通过VPN将这些数据包传输到远程位置。同样,SSH服务器在其内部接口上接受绑定到其他办公室的数据包,并通过VPN进行拍摄。

VPN 认证密钥

使用VPN的密钥身份验证。如果你只在特殊情况下手动启动VPN,请按照第七章"SSH密钥"中的讨论创建标准用户身份验证密钥。如果自动过程将启动VPN,请按照第十二章"自动化"中的说明创建一个没有密码短语的密钥。将密钥放在一个特殊的文件中,例如客户端上的/root/.ssh/tunnelkey。

将密钥的公钥复制到服务器的/root/.ssh/authorized_keys。此密钥应只能运行VPN命令,即使使用基于密钥的身份验证,你也不希望远程入侵者能够在你的服务器上获得root登录。第十二章讨论了限制密钥权限,但所需的确切命令因操作系统而异。

SSH隧道命令

使用-w标志激活一个OpenSSH隧道:

-i告诉ssh使用哪个私钥文件。-w告诉客户端请求隧道,以及在每一侧请求哪个隧道设备编号。-f将ssh置于后台,这样就不会在远程系统上看到命令提示符。我们运行true,这样就有了一个总是成功运行的命令。

以下示例中,密钥文件是/root/.ssh/tunnelkey,每侧使用隧道设备0,服务器是gluttony.mwl.io:

如果一切正常,这应该会悄无声息地返回到本地命令提示符。

其中一些命令选项可以在ssh_config中设置。我建议将隧道选项放置在/root/.ssh/config中,而不是系统范围的配置中。你不希望一个无特权用户的无辜SSH会话尝试打开隧道并通过它进行路由:

根据你的环境或操作系统的需要,为主机添加其他选项。这会删除激活隧道所需的命令行。

我们的示例假设你已启用root登录,将客户端的公钥复制到服务器,并在/root/.ssh/config中设置主机的密钥和隧道设备。

调试

如果你按照操作系统的步骤操作,但隧道没有启动,请在详细(verbose)模式下运行ssh。你将看到错误的详细信息。如果这没有帮助,请在调试模式下运行sshd。在互联网上搜索错误消息的确切文本。你一定会找到那些经历过并解决过你问题的人。

现在让我们配置一些VPN。

OpenBSD

OpenSSH是在OpenBSD内部开发的,OpenBSD团队创建了OpenSSH VPN功能,因此OpenBSD对OpenSSH VPN有很好的支持。首先,通过在/root/.ssh/authorized_keys中添加控件,收紧客户端可以使用此密钥访问的内容。

我已经锁定了所有基于密钥的选项,然后添加了访问特定隧道设备并运行配置该隧道的命令的能力。即使客户端受到攻击并以root身份登录服务器,也不会造成太大损害。

通过将sysctl net.inet.ip.rewarding设置为1,在OpenBSD上启用数据包转发:

要使此更改在更新启动后永久生效,请在/etc/sysctl.conf中创建一个匹配的条目:

现在配置你的隧道设备。客户端和服务器上都需要/etc/hostname.tun0。每个包含两行。这是客户端:

服务器端的hostname.tun0看起来像这样:

IP地址颠倒了。当隧道启动时,客户端后面的网络会通过它进行路由。

SSH从客户端到服务器。隧道应该启动并自行配置。

FreeBSD

FreeBSD没有开箱即用的OpenSSH VPN,但它们真的很容易设置。最简单的方法是在隧道出现时调用shell脚本。你可以通过狡猾和聪明来避免这种需求,但狡猾聪明有一种在停机期间咬你的神奇能力。此外,我将使用这些脚本来说明OpenSSH的几个功能。

首先,在客户端和服务器上启用数据包转发。使用OpenBSD中的sysctl net.inet.ip.forwarding,或在/etc/rc.conf中设置GATEWAY_ENABLE=YES。

现在让我们准备好脚本。服务器将使用脚本/usr/local/scripts/tunnelserver.sh。我将在authorized_keys中锁定客户端的条目,以允许它只运行该脚本:

当使用此密钥登录时,sshd都会运行配置的脚本。让我们看看服务器端的脚本:

该脚本将IP地址添加到隧道接口,并配置到远程网络的路由。

我们将在客户端上使用类似的脚本/usr/local/scripts/tunnelclient.sh,将IP地址和路由添加到隧道的这一侧。:

SSH进入服务器会激活隧道并配置其服务器端。你需要运行客户端脚本来配置客户端。幸运的是,ssh具有LocalCommand关键字,可以在连接到主机时自动运行命令。

在这里,我使用PermitLocalCommand表示“是的,连接时可以在本地运行命令”,并使用LocalCommand定义命令。

当你通过SSH从客户端连接到服务器时,隧道应该会自动打开。

CentOS 和 Debian

在这两个流行的Linux发行版上配置SSH VPN的方式与FreeBSD非常相似。然而,这两个发行版都不再使用一些标准的Unix工具,如ifconfig和route,因此我们必须使用Linux特定的ip命令。

创建隧道密钥,启用SSH隧道,并允许使用密钥进行root登录,如本章“常见概念”中所述。然后客户端需要一个/root/.ssh/config,就像FreeBSD中使用的那样。

在Linux上启用IP转发,在/etc/sysctl.conf中将sysctl net.ipv4.ip_forward设置为1。Debian已经有了这个条目,但注释掉了。

锁定服务器的/root/.ssh/authorized_keys,这样此密钥就只能打开隧道设备。

现在,你所需要的就是Linux脚本。这是一个/usr/local/scripts/tunnelserver.sh :

下面是客户端脚本:

现在从你的客户端运行 ssh -f gluttony,你的隧道就会打开。

通过这三个示例,你应该能够在任何类Unix操作系统上运行SSH VPN。

记住,SSH VPN并不是一个很好的解决方案。你在遇到问题或遇到拥堵之前,请调查OpenVPN等真正的VPN软件。