15.9. IPsec 上的 VPN

撰写者 Nik Clayton.

使用 FreeBSD 网关在两个被 Internet 分开的网络之间架设 VPN。

15.9.1. 理解 IPsec

撰写者 Hiten M. Pandya.

这一节将指导您完成架设 IPsec。为了配置 IPsec, 您应当熟悉如何编译一个定制的内核的一些概念 (参见 第 9 章 配置FreeBSD的内核)。

IPsec 是一种建立在 Internet 协议 (IP) 层之上的协议。 它能够让两个或更多主机以安全的方式来通讯 (并因此而得名)。 FreeBSD IPsec 网络协议栈 基于 KAME 的实现, 它支持两种协议族, IPv4 和 IPv6。

IPsec 包括了两个子协议:

  • Encapsulated Security Payload (ESP), 保护 IP 包数据不被第三方介入, 通过使用对称加密算法 (例如 Blowfish、 3DES)。

  • Authentication Header (AH), 保护 IP 包头不被第三方介入和伪造, 通过计算校验和以及对 IP 包头的字段进行安全散列来实现。 随后是一个包含了散列值的附加头, 以便能够验证包。

ESPAH 可以根据环境的不同, 分别或者一同使用。

IPsec 既可以用来直接加密主机之间的网络通讯 (也就是 传输模式); 也可以用来在两个子网之间建造 虚拟隧道 用于两个网络之间的安全通讯 (也就是 隧道模式)。 后一种更多的被称为是 虚拟专用网 (VPN)ipsec(4) 联机手册提供了关于 FreeBSD 中 IPsec 子系统的详细信息。

要把 IPsec 支持放进内核, 应该在配置文件中加入下面的选项:

options   IPSEC        #IP security
device    crypto
      

如果需要 IPsec 的调试支持, 还应增加:

options   IPSEC_DEBUG  #debug for IP security
      

15.9.2. 问题

由于对如何建立 VPN 并不存在标准, 因此 VPN 可以采用许多种不同的技术来实现, 每种技术都有其强项和弱点。 这篇文章将展现一个具体的应用情景, 并为它设计了适合的 VPN。

15.9.3. 情景: 两个网络,一个家庭的网络和一个公司的网络。 都接入了 Internet,并且通过这条 VPN 就像在同一个网络一样。

现有条件如下:

  • 至少有两个不同的站点

  • 每个站点都使用内部的 IP

  • 两个站点都通过运行 FreeBSD 的网关接入 Internet。

  • 每个网络上的网关至少有一个公网的 IP 地址。

  • 网络的内部地址可以是公网或私有的 IP 地址, 这并不是问题。它们并不冲突,比如它们不同时使用 192.168.1.x 这样的地址。

15.9.4. 在 FreeBSD 上配置 IPsec

作者Tom Rhodes.

开始需先从 Ports Collection 安装 security/ipsec-tools。 这个第三方软件提供了一些能够帮助配置的应用程序。

下一步是创建两个 gif(4) 伪设备用来在两个网络间传输数据包的 隧道。 使用 root 身份运行以下命令, 并用真实的内部外部网关替换命令中的 internalexternal 项:

# ifconfig gif0 create
# ifconfig gif0 internal1 internal2
# ifconfig gif0 tunnel external1 external2

比如,公司 LAN 对外的 IP 地址是 172.16.5.4, 内部的 IP 地址为 10.246.38.1。 家庭 LAN 对外的 IP 地址是 192.168.1.12, 内部的 IP 地址为 10.0.0.5

这看起来可能有些混乱,所以我们通过 ifconfig(8) 命令输出再回顾一下:

Gateway 1:

gif0: flags=8051 mtu 1280
tunnel inet 172.16.5.4 --> 192.168.1.12
inet6 fe80::2e0:81ff:fe02:5881%gif0 prefixlen 64 scopeid 0x6
inet 10.246.38.1 --> 10.0.0.5 netmask 0xffffff00

Gateway 2:

gif0: flags=8051 mtu 1280
tunnel inet 192.168.1.12 --> 172.16.5.4
inet 10.0.0.5 --> 10.246.38.1 netmask 0xffffff00
inet6 fe80::250:bfff:fe3a:c1f%gif0 prefixlen 64 scopeid 0x4

一旦完成以后,两个私有的 IP 地址都应该能像下面 ping(8) 命令输出那样互相访问。

priv-net# ping 10.0.0.5
PING 10.0.0.5 (10.0.0.5): 56 data bytes
64 bytes from 10.0.0.5: icmp_seq=0 ttl=64 time=42.786 ms
64 bytes from 10.0.0.5: icmp_seq=1 ttl=64 time=19.255 ms
64 bytes from 10.0.0.5: icmp_seq=2 ttl=64 time=20.440 ms
64 bytes from 10.0.0.5: icmp_seq=3 ttl=64 time=21.036 ms
--- 10.0.0.5 ping statistics ---
4 packets transmitted, 4 packets received, 0% packet loss
round-trip min/avg/max/stddev = 19.255/25.879/42.786/9.782 ms

corp-net# ping 10.246.38.1
PING 10.246.38.1 (10.246.38.1): 56 data bytes
64 bytes from 10.246.38.1: icmp_seq=0 ttl=64 time=28.106 ms
64 bytes from 10.246.38.1: icmp_seq=1 ttl=64 time=42.917 ms
64 bytes from 10.246.38.1: icmp_seq=2 ttl=64 time=127.525 ms
64 bytes from 10.246.38.1: icmp_seq=3 ttl=64 time=119.896 ms
64 bytes from 10.246.38.1: icmp_seq=4 ttl=64 time=154.524 ms
--- 10.246.38.1 ping statistics ---
5 packets transmitted, 5 packets received, 0% packet loss
round-trip min/avg/max/stddev = 28.106/94.594/154.524/49.814 ms

正如预期的那样,两边都有从私有地址发送和接受 ICMP 数据包的能力。下面, 两个网关都必须配置路由规则以正确传输两边的网络流量。 下面的命令可以实现这个:

# corp-net# route add 10.0.0.0 10.0.0.5 255.255.255.0
# corp-net# route add net 10.0.0.0: gateway 10.0.0.5
# priv-net# route add 10.246.38.0 10.246.38.1 255.255.255.0
# priv-net# route add host 10.246.38.0: gateway 10.246.38.1

此刻,不论从网关还是网关后的机器都能访问内部的网络。 这很容易通过以下的例子确认:

corp-net# ping 10.0.0.8
PING 10.0.0.8 (10.0.0.8): 56 data bytes
64 bytes from 10.0.0.8: icmp_seq=0 ttl=63 time=92.391 ms
64 bytes from 10.0.0.8: icmp_seq=1 ttl=63 time=21.870 ms
64 bytes from 10.0.0.8: icmp_seq=2 ttl=63 time=198.022 ms
64 bytes from 10.0.0.8: icmp_seq=3 ttl=63 time=22.241 ms
64 bytes from 10.0.0.8: icmp_seq=4 ttl=63 time=174.705 ms
--- 10.0.0.8 ping statistics ---
5 packets transmitted, 5 packets received, 0% packet loss
round-trip min/avg/max/stddev = 21.870/101.846/198.022/74.001 ms

priv-net# ping 10.246.38.107
PING 10.246.38.1 (10.246.38.107): 56 data bytes
64 bytes from 10.246.38.107: icmp_seq=0 ttl=64 time=53.491 ms
64 bytes from 10.246.38.107: icmp_seq=1 ttl=64 time=23.395 ms
64 bytes from 10.246.38.107: icmp_seq=2 ttl=64 time=23.865 ms
64 bytes from 10.246.38.107: icmp_seq=3 ttl=64 time=21.145 ms
64 bytes from 10.246.38.107: icmp_seq=4 ttl=64 time=36.708 ms
--- 10.246.38.107 ping statistics ---
5 packets transmitted, 5 packets received, 0% packet loss
round-trip min/avg/max/stddev = 21.145/31.721/53.491/12.179 ms

配置 隧道 是比较容易的部分。 配置一条安全链接则是个更加深入的过程。 下面的配置是使用 pre-shared (PSKRSA 密钥。除了 IP 地址外,两边的 /usr/local/etc/racoon/racoon.conf 也几乎相同。

path    pre_shared_key  "/usr/local/etc/racoon/psk.txt"; #location of pre-shared key file
log     debug;	#log verbosity setting: set to 'notify' when testing and debugging is complete

padding	# options are not to be changed
{
        maximum_length  20;
        randomize       off;
        strict_check    off;
        exclusive_tail  off;
}

timer	# timing options. change as needed
{
        counter         5;
        interval        20 sec;
        persend         1;
#       natt_keepalive  15 sec;
        phase1          30 sec;
        phase2          15 sec;
}

listen	# address [port] that racoon will listening on
{
        isakmp          172.16.5.4 [500];
        isakmp_natt     172.16.5.4 [4500];
}

remote  192.168.1.12 [500]
{
        exchange_mode   main,aggressive;
        doi             ipsec_doi;
        situation       identity_only;
        my_identifier   address 172.16.5.4;
        peers_identifier        address 192.168.1.12;
        lifetime        time 8 hour;
        passive         off;
        proposal_check  obey;
#       nat_traversal   off;
        generate_policy off;

                        proposal {
                                encryption_algorithm    blowfish;
                                hash_algorithm          md5;
                                authentication_method   pre_shared_key;
                                lifetime time           30 sec;
                                dh_group                1;
                        }
}

sainfo  (address 10.246.38.0/24 any address 10.0.0.0/24 any)	# address $network/$netmask $type address $network/$netmask $type ( $type being any or esp)
{								# $network must be the two internal networks you are joining.
        pfs_group       1;
        lifetime        time    36000 sec;
        encryption_algorithm    blowfish,3des,des;
        authentication_algorithm        hmac_md5,hmac_sha1;
        compression_algorithm   deflate;
}

解释所有可用的选项, 连同这些例子里列出的都超越了这份文档的范围。 在 racoon 配置手册页中有着丰富的相关信息。

SPD 策略也需要配置一下, 这样 FreeBSD 和 racoon 就能够加密和解密主机间的网络流量了。

这可以通过在公司的网关上运行一个类似下面简单的 shell 脚本实现。保存到 /usr/local/etc/racoon/setkey.conf, 这个文件会被在系统初始化的时候用到。

flush;
spdflush;
# To the home network
spdadd 10.246.38.0/24 10.0.0.0/24 any -P out ipsec esp/tunnel/172.16.5.4-192.168.1.12/use;
spdadd 10.0.0.0/24 10.246.38.0/24 any -P in ipsec esp/tunnel/192.168.1.12-172.16.5.4/use;

一旦完成后,便使用下面的命令在两边的网关上都启动 racoon

# /usr/local/sbin/racoon -F -f /usr/local/etc/racoon/racoon.conf -l /var/log/racoon.log

输出将会类似这样的:

corp-net# /usr/local/sbin/racoon -F -f /usr/local/etc/racoon/racoon.conf
Foreground mode.
2006-01-30 01:35:47: INFO: begin Identity Protection mode.
2006-01-30 01:35:48: INFO: received Vendor ID: KAME/racoon
2006-01-30 01:35:55: INFO: received Vendor ID: KAME/racoon
2006-01-30 01:36:04: INFO: ISAKMP-SA established 172.16.5.4[500]-192.168.1.12[500] spi:623b9b3bd2492452:7deab82d54ff704a
2006-01-30 01:36:05: INFO: initiate new phase 2 negotiation: 172.16.5.4[0]192.168.1.12[0]
2006-01-30 01:36:09: INFO: IPsec-SA established: ESP/Tunnel 192.168.1.12[0]->172.16.5.4[0] spi=28496098(0x1b2d0e2)
2006-01-30 01:36:09: INFO: IPsec-SA established: ESP/Tunnel 172.16.5.4[0]->192.168.1.12[0] spi=47784998(0x2d92426)
2006-01-30 01:36:13: INFO: respond new phase 2 negotiation: 172.16.5.4[0]192.168.1.12[0]
2006-01-30 01:36:18: INFO: IPsec-SA established: ESP/Tunnel 192.168.1.12[0]->172.16.5.4[0] spi=124397467(0x76a279b)
2006-01-30 01:36:18: INFO: IPsec-SA established: ESP/Tunnel 172.16.5.4[0]->192.168.1.12[0] spi=175852902(0xa7b4d66)

确认一下 隧道 能正常工作, 切换到另外一个控制台用如下的 tcpdump(1) 命令查看网络流量。根据需要替换掉下面的 em0 网卡界面。

# tcpdump -i em0 host 172.16.5.4 and dst 192.168.1.12

控制台上能看到如下类似的输出。如果不是这样的话, 可能就有些问题了,调试的话需要用到返回的数据。

01:47:32.021683 IP corporatenetwork.com > 192.168.1.12.privatenetwork.com: ESP(spi=0x02acbf9f,seq=0xa)
01:47:33.022442 IP corporatenetwork.com > 192.168.1.12.privatenetwork.com: ESP(spi=0x02acbf9f,seq=0xb)
01:47:34.024218 IP corporatenetwork.com > 192.168.1.12.privatenetwork.com: ESP(spi=0x02acbf9f,seq=0xc)

此刻,两个网络就好像是同一个网络的一部分一样。 而且这两个网络很可能也应该有防火墙的保护。 要使得这两个网络能互相访问,就需要添加一些进出包的规则。 就 ipfw(8) 来说,加入下面的几行进配置文件:

ipfw add 00201 allow log esp from any to any
ipfw add 00202 allow log ah from any to any
ipfw add 00203 allow log ipencap from any to any
ipfw add 00204 allow log udp from any 500 to any

注意:

规则号可能需要根据现有机器上的配置做相应的修改。

对于 pf(4) 或者 ipf(8) 的用户, 下面的几行规则应该可行:

pass in quick proto esp from any to any
pass in quick proto ah from any to any
pass in quick proto ipencap from any to any
pass in quick proto udp from any port = 500 to any port = 500
pass in quick on gif0 from any to any
pass out quick proto esp from any to any
pass out quick proto ah from any to any
pass out quick proto ipencap from any to any
pass out quick proto udp from any port = 500 to any port = 500
pass out quick on gif0 from any to any

最后,要允许机器初始化的时候开始 VPN 支持,在 /etc/rc.conf 中加入以下的几行:

ipsec_enable="YES"
ipsec_program="/usr/local/sbin/setkey"
ipsec_file="/usr/local/etc/racoon/setkey.conf" # allows setting up spd policies on boot
racoon_enable="yes"

本文档和其它文档可从这里下载: ftp://ftp.FreeBSD.org/pub/FreeBSD/doc/.

如果对于FreeBSD有问题,请先阅读 文档,如不能解决再联系 <questions@FreeBSD.org>.

关于本文档的问题请发信联系 <doc@FreeBSD.org>.