Openswan L2TP/IPsec VPN 客户端设置

来自 ArchWiki

此条目或章节需要改进语言、wiki 语法或风格。参见 Help:Style 以获取参考。

原因: 此条目需要改进语言,请勿以第一人称写作(在 Talk:Openswan L2TP/IPsec VPN client setup 中讨论)

本文介绍如何在 Arch Linux 上配置和使用 L2TP/IPsec 虚拟专用网络客户端。它涵盖了几个所需软件包的安装和设置。L2TP 指的是 w:Layer 2 Tunneling Protocol,对于 w:IPsec,采用的是 Openswan 实现。

本指南主要针对连接到 Windows Server 机器的客户端,因为它使用了一些特定于 Microsoft L2TP/IPsec 实现的设置。但是,它也适用于任何其他常见的 L2TP/IPsec 设置。Openswan wiki 提供了设置相应 L2TP/IPSec Linux 服务器的说明。

安装

要与 NetworkManager 一起使用,安装 networkmanager-l2tpstrongswan 软件包。

否则,安装 xl2tpdopenswanAUR 软件包。

配置

NetworkManager

打开 NetworkManager UI,然后

  1. 转到 网络 > VPN。点击 “+”
  2. 选择 “Layer 2 Tunneling Protocol (L2TP)”。
  3. 你可以为 VPN 选择一个名称。
  4. 在网关中输入你的 VPN 服务器 IP。
  5. 在用户名中输入你的 VPN 用户名。
  6. 右键单击密码字段中的 ?,选择 “仅为此用户存储密码”。(如果此选项给你带来麻烦,你可能需要使用 “为所有用户存储密码”)
  7. 在密码中输入你的 VPN 密码。
  8. 将 NT 域字段留空。
  9. 点击 “IPsec 设置...” 按钮。
  10. 勾选 “启用 IPsec 隧道到 L2TP 主机” 复选框。
  11. 将网关 ID 字段留空。
  12. 在预共享密钥中输入你的 VPN IPsec PSK。
  13. 点击 “确定”,然后点击 “添加” 以保存 VPN 连接信息。

现在你应该能够通过打开切换按钮来启动 VPN。

OpenSwan

编辑 /etc/ipsec.conf 文件,使其包含以下行

config setup
     virtual_private=%v4:10.0.0.0/8,%v4:192.168.0.0/16,%v4:172.16.0.0/12
     nat_traversal=yes
     protostack=netkey            # default is auto, which will try netkey first
     plutoopts="--interface=eth0" # Replace eth0 with your network interface or use %defaultroute to use default route

conn L2TP-PSK
     authby=secret
     pfs=no
     auto=add
     keyingtries=3
     dpddelay=30
     dpdtimeout=120
     dpdaction=clear
     rekey=yes
     ikelifetime=8h
     keylife=1h
     type=transport
     left=192.168.0.123           # Replace with your local IP address (private, behind NAT IP is okay as well)
     leftprotoport=17/1701
     right=68.68.32.79            # Replace with your VPN server's IP
     rightprotoport=17/1701

此文件包含建立到 VPN 服务器的安全 IPsec 隧道的基本信息。它启用了 NAT 穿透,以防你的机器位于 NAT 路由器之后(大多数人都是如此),以及正确连接到远程 IPsec 服务器所需的各种其他选项。下一个文件包含服务器的预共享密钥 (PSK)。

创建文件 /etc/ipsec.secrets:它应包含以下行

192.168.0.123 68.68.32.79 : PSK "your_pre_shared_key"

记住将本地 (192.168.0.123) 和远程 (68.68.32.79) IP 地址替换为你所在位置的正确数字。预共享密钥将由 VPN 提供商提供,并且需要以明文形式放置在此文件中。你可能会发现此文件已经存在并且已经有一些数据,如果你在下一节中启用连接时看到 Can't authenticate: no preshared key found for ...,请尝试备份它并创建一个仅包含你的 PSK 的新文件。不要忘记为此文件设置正确的权限 (600),否则你会收到错误消息 We cannot identify ourselves with either end of this connection.

添加连接,使其可用

# ipsec auto --add L2TP-PSK

至此,IPsec 配置已完成,我们可以继续进行 L2TP 配置。

在容器中运行 Openswan

不要忘记添加 CAP_SYS_MODULE 功能以及对主机模块树的访问权限。例如 nspawn

--bind=/lib/modules --capability=CAP_SYS_MODULE

xl2tpd

编辑 /etc/xl2tpd/xl2tpd.conf 文件,使其具有以下内容

[lac vpn-connection]
lns = 68.68.32.79
ppp debug = yes
pppoptfile = /etc/ppp/options.l2tpd.client
length bit = yes

此文件配置 xl2tpd,包含连接名称、服务器 IP 地址(再次提醒,请记住更改为你的服务器地址)以及隧道建立后将传递给 pppd 的各种选项。

现在创建 /etc/ppp/options.l2tpd.client 文件,包含以下内容

ipcp-accept-local
ipcp-accept-remote
refuse-eap
require-mschap-v2
noccp
noauth
idle 1800
mtu 1410
mru 1410
defaultroute
usepeerdns
debug
connect-delay 5000
name your_vpn_username
password your_password

将分配给你的 VPN 服务器的用户名和密码放在此文件中。这些选项中的很多是为了与 Windows Server L2TP 服务器互操作。如果你的 VPN 服务器使用 PAP 身份验证,请将 require-mschap-v2 替换为 require-pap

至此,连接到 L2TP/IPsec 服务器的适用软件包的配置已完成。要启动连接,请执行以下操作

启动 openswan.servicexl2tpd.service

# ipsec auto --up L2TP-PSK
# echo "c vpn-connection" > /var/run/xl2tpd/l2tp-control

此时隧道已建立,如果输入以下命令,你应该能够看到它的接口

$ ip link

你应该看到一个代表隧道的 pppX 设备。目前,没有任何东西会通过它路由。你需要添加一些路由规则才能使其正常工作

路由

通过隧道将流量路由到单个 IP 地址或子网

这就像在你的内核表中添加一个路由规则一样简单

# ip route add xxx.xxx.xxx.xxx via yyy.yyy.yyy.yyy dev pppX

注意 xxx.xxx.xxx.xxx 是你希望通过隧道设备(例如 ppp0)与之通信的特定 IP 地址(例如 192.168.3.10)或子网(例如 192.168.3.0/24)。

注意 yyy.yyy.yyy.yyy 是你的 pppX 设备的 “对等 IP”,用于将流量路由到隧道目标 xxx.xxx.xxx.xxx。


请参阅下面的示例,了解用于识别隧道设备名称和对等 IP,然后添加路由的命令。

$ ip address
4: ppp0: <POINTOPOINT,MULTICAST,NOARP,UP,LOWER_UP> mtu 1400 qdisc fq_codel state UNKNOWN group default qlen 3
    link/ppp 
    inet 10.192.168.40 peer 192.0.2.1/32 scope global ppp0
       valid_lft forever preferred_lft forever
# ip route add 192.168.3.0/24 via 192.0.2.1 dev ppp0

通过隧道路由所有流量

这要复杂得多,但你的所有流量都将通过隧道传输。首先,通过你当前的网关为实际的 VPN 服务器添加一个特殊路由

# ip route add 68.68.32.79 via 192.168.1.1 dev eth0

这将确保一旦默认网关更改为 ppp 接口,你的网络堆栈仍然可以通过绕过隧道路由来找到 VPN 服务器。如果你错过此步骤,你将失去与 Internet 的连接,并且隧道将崩溃。现在添加一个路由到 PPP 远程端的默认路由

# ip route add default via yyy.yyy.yyy.yyy dev pppX

可以通过遵循上一节中的步骤来发现远程 PPP 端。现在,为了确保所有流量都通过隧道路由,请删除原始默认路由

# ip route delete default via 192.168.1.1 dev eth0

要将你的系统恢复到之前的状态,你可以重新启动或撤销上述所有步骤。

路由创建也可以通过将脚本放在 /etc/ppp/ip-up.d 中来实现自动化。

故障排除

问题: journalctl 日志 VPN 连接:连接失败:‘无法重启 ipsec 服务。

解决方案: 确保你已安装 strongswan

注意: 第一步可能是使用 ipsec verify 命令来检查已安装的 IPSEC 的配置。

问题: 我收到来自 pppd 的消息,提示 “Failed to authenticate ourselves to peer”,并且我已经验证我的密码是正确的。可能是什么问题?

解决方案 1: 如果你在你的 /var/log/daemon.log 中看到以下内容

Dec 20 15:14:03 myhost pppd[26529]: rcvd [CHAP Challenge id=0x1 <some_or_another_hash>, name = "SonicWALL"]
Dec 20 15:14:03 myhost pppd[26529]: sent [CHAP Response id=0x1 <some_or_another_hash>, name = "your_vpn_username"]
Dec 20 15:14:03 myhost pppd[26529]: rcvd [LCP EchoRep id=0x0 magic=0x45c269c6]
Dec 20 15:14:03 myhost pppd[26529]: rcvd [CHAP Failure id=0x1 ""]
Dec 20 15:14:03 myhost pppd[26529]: CHAP authentication failed
Dec 20 15:14:03 myhost pppd[26529]: CHAP authentication failed
Dec 20 15:14:03 myhost pppd[26529]: sent [LCP TermReq id=0x3 "Failed to authenticate ourselves to peer"]
Dec 20 15:14:03 myhost pppd[26529]: rcvd [LCP TermReq id=0x2]
Dec 20 15:14:03 myhost pppd[26529]: sent [LCP TermAck id=0x2]
Dec 20 15:14:03 myhost pppd[26529]: rcvd [LCP TermAck id=0x3]

那么你正在针对 SonicWALL LNS 进行身份验证,它不知道如何正确处理 CHAP 风格的身份验证。

对此的解决方案是将以下内容添加到你的 options.l2tp.client 文件中

   refuse-chap

这将导致 SonicWALL 默认使用下一个身份验证机制,即 MSCHAP-v2。这应该会成功进行身份验证,并且从那时起,xl2tpd 应该能够成功地在你和远程 L2TP 服务器之间构建隧道。

解决方案 2: 如果你在以 root 身份运行 journalctl -ru xl2tpd 后在你的日志中看到以下内容

vas. 03 12:31:21 myhost pppd[8922]: rcvd [LCP EchoRep id=0x0 magic=<some_or_another_hash>]
vas. 03 12:31:21 myhost pppd[8922]: rcvd [CHAP Failure id=0x1 "E=691 R=0 C=<some_or_another_hash> V=3 M=bad username or password"]
vas. 03 12:31:21 myhost pppd[8922]: MS-CHAP authentication failed: bad username or password
vas. 03 12:31:21 myhost pppd[8922]: CHAP authentication failed

尝试在你的 options.l2tpd.client 文件中的用户名之前添加域名(注意双反斜杠),即

…
name DOMAIN\\your_vpn_username
password your_password

问题: 在使用 Openswan 3.0.0 运行 ipsec auto --ad L2TP-PSK 后,出现 cannot initiate connection with ID wildcards (kind=CK_TEMPLATE)

确定 VPN 服务器在 VPN 后面的目标网络中的私有 IP,并将相应的行添加到 /etc/ipsec.conf

rightid = private IP of VPN server

技巧与提示

脚本启动和关闭

你可以在你的主目录或其他地方(记住你放置它们的位置)创建一些脚本,以启动隧道,然后再将其关闭。

首先,一个实用程序脚本,用于自动发现 PPP 远端

getip.sh
#!/bin/bash

ifconfig $1 | grep "P-t-P" | gawk -F: '{print $2}' | gawk '{print $1}'

接下来,是启动隧道的脚本。这将替换默认路由,因此所有流量都将通过隧道

startvpn.sh
#!/bin/bash

systemctl start openswan
sleep 2                                                   #delay to ensure that IPsec is started before overlaying L2TP
systemctl start xl2tpd
ipsec auto --up L2TP-PSK                        
echo "c vpn-connection" > /var/run/xl2tpd/l2tp-control     
sleep 2                                                   #delay again to make that the PPP connection is up.
PPP_GW_ADD=`./getip.sh ppp0`

ip route add 68.68.32.79 via 192.168.1.1 dev eth0
ip route add default via $PPP_GW_ADD
ip route del default via 192.168.1.1

最后,是关闭脚本,它只是反转该过程

stopvpn.sh
#!/bin/bash

ipsec auto --down L2TP-PSK
echo "d vpn-connection" > /var/run/xl2tpd/l2tp-control
systemctl stop xl2tpd
systemctl stop openswan

ip route del 68.68.32.79 via 192.168.1.1 dev eth0
ip route add default via 192.168.1.1

更进一步的脚本

上面的脚本真的对我的工作很有帮助。并注意到脚本使用固定 IP,像我这样的人可能会更改网络 VPN 地址,我想把我的进一步的脚本放在下面(不确定如何添加附件,所以只是原始文本)

#!/bin/bash
if [ $# != 1 ] ; then
	echo "Usage: (sudo) sh $0 {init|start|stop}" 
	exit 1;
fi

VPN_ADDR=XXX
IFACE=wlan0

function getIP(){
	ip addr show $1 | grep "inet " | awk '{print $2}' | sed 's:/.*::'       
}

function getGateWay(){
	ip route show default | awk '/default/ {print $3}'
}
function getVPNGateWay(){
	ip route | grep -m 1 "$VPN_ADDR" | awk '{print $3}'
}

GW_ADDR=$(getGateWay)  

function init(){
	cp ./options.l2tpd.client /etc/ppp/
	cp ./ipsec.conf /etc/
	cp ./ipsec.secrets /etc/
	cp ./xl2tpd.conf /etc/xl2tpd/
}

function start(){
	sed -i "s/^lns =.*/lns = $VPN_ADDR/g" /etc/xl2tpd/xl2tpd.conf
	sed -i "s/plutoopts=.*/plutoopts=\"--interface=$IFACE\"/g" /etc/ipsec.conf
	sed -i "s/left=.*$/left=$(getIP $IFACE)/g" /etc/ipsec.conf
	sed -i "s/right=.*$/right=$VPN_ADDR/g" /etc/ipsec.conf
	sed -i "s/^.*: PSK/$(getIP $IFACE) $VPN_ADDR : PSK/g" /etc/ipsec.secrets
	systemctl start openswan
	sleep 2    #delay to ensure that IPsec is started before overlaying L2TP

	systemctl start xl2tpd
	ipsec auto --up L2TP-PSK                        
	echo "c vpn-connection" > /var/run/xl2tpd/l2tp-control     
	sleep 2    #delay again to make that the PPP connection is up.

        ip route add $VPN_ADDR via $GW_ADDR dev $IFACE
        ip route add default via $(getIP ppp0)
        ip route del default via $GW_ADDR
}

function stop(){
	ipsec auto --down L2TP-PSK
	echo "d vpn-connection" > /var/run/xl2tpd/l2tp-control
	systemctl stop xl2tpd
	systemctl stop openswan
	
	VPN_GW=$(getVPNGateWay)
        ip route del $VPN_ADDR via $VPN_GW dev $IFACE
        ip route add default via $VPN_GW
}

$1
exit 0

解析 DNS 名称并连接的脚本

此条目或章节需要改进语言、wiki 语法或风格。参见 Help:Style 以获取参考。

原因: Wiki 不应托管脚本。(在 Talk:Openswan L2TP/IPsec VPN client setup 中讨论)

如果你的服务器使用动态 IP,这将非常有用。

#!/bin/python

from os import system
from socket import gethostbyname
from netifaces import ifaddresses, AF_INET
from time import sleep

# netifaces is a library installed with pip, not part of default insatllation of python
# The script is useful if you have dynamic IP, or need to use a domain for the vpn server
# gist: https://gist.github.com/physicalit/bf9e27c7ecbc12843cd68e442358616c
# The template files are identical to the examples from the link above, except they use the sign `<` as placeholder for the server ip
# can be added to cron, do not forghet to modify your domain and the ip/subnet from the `ip add route ...`

ip = gethostbyname('your.domain.tld')

file_list = ['/etc/xl2tpd/xl2tpd.conf_tmp', '/etc/ipsec.secrets_tmp', '/etc/ipsec.conf_tmp']

def read_file(file):
    with open(file, 'r') as f:
        result = f.readlines()
        #result = [l.rstrip('\n') for l in result]  # l.split('_')[0] 
        return result

def write_ip(ip):
    for l in file_list:
        result = [ip.join(e.split('<')) if "<" in e else e for e in read_file(l)]
        with open(l.split('_')[0], 'w') as f:
            for e in result:
                f.write(e)

if __name__ == "__main__":
    write_ip(ip)
    [ system('systemctl restart {}'.format(l)) for l in ['openswan', 'xl2tpd']]
    vpn = system('ipsec auto --up L2TP-PSK')
    system('echo "c vpn-connection" > /var/run/xl2tpd/l2tp-control')
    sleep(2) # very important or is not going to see ppp0 interface
    if not vpn:
        peer = ifaddresses('ppp0')[AF_INET][0]['peer']
        route = system('ip route add 192.168.88.0/24 via {0} dev ppp0'.format(peer))
        if not route:
            print("VPN sucesfully connected. Route created.")
        else:
            print("VPN KO")

参见