一旦部署,个人或企业VPN在安全性和功能性方面都可以成为一个强大的工具。一个设计良好的VPN将允许用户安全地连接到远程资源。然而,有时仅仅拥有VPN是不够的。给定的应用程序可能要求更严格的安全标准,或者需要更好的监视和控制。
将插件和脚本与OpenVPN集成可以解决许多组织或功能需求。本章将演示如何使用插件来增强身份验证,以及脚本如何跟踪连接、生成路由表等。
脚本可能是OpenVPN管理员可用的最佳工具之一。通过指定客户端和服务器端脚本的能力,OpenVPN可以通过打开防火墙、运行应用程序,甚至向管理员发送消息来启动其他系统响应。
编写脚本时的一个重要警告是脚本完成需要的时间。OpenVPN是一个单线程进程,这意味着当脚本运行时,整个VPN对所有连接或正在连接的客户端都被阻止。缓慢的身份验证脚本可能会削弱运行良好的VPN。插件受此影响较小,因为它们确实在单独的线程中运行。
从2.3.6版开始,OpenVPN支持13个服务器端脚本选项和10个客户端选项。带星号的命令是设置选项,允许后续选项执行特定操作。
服务器端脚本如下(按执行顺序):
--setenv*
--setenv-safe*
--script-security*
--up-restart*
--up
--route-up
--tls-verify
--auth-user-pass-verify
--client-connect
--learn-address
--client-disconnect
--route-pre-down
--down
在客户端侧,以下脚本可用(按执行顺序):
--setenv*
--script-security*
--up-restart*
--tls-verify
--ipchange
--setenv-safe*
--up
--route-up
--route-pre-down
--down
我们现在将简要介绍所有这些选项,解释它们在服务器端和客户端的功能。在本章的后面,我们将提供一个详细的示例,并讨论每个脚本的行为和微妙之处。
让我们来看看服务器端使用的脚本。
setenv
和 setenv-safe
选项用于设置脚本和插件都可以使用的环境变量。
setenv
选项允许我们设置几乎任何环境变量,但此选项不能【推送】到客户端。
setenv-safe
选项在每个环境变量前添加前缀 OPENVPN_
,避免与 PATH
和 LD_LIBRARY_PATH
等系统环境变量冲突。此选项可以推送给客户,从而提供极大的灵活性。
script-security
选项决定了可以从OpenVPN配置执行哪些类型的应用程序或脚本。安全级别有四种选择:
0
:不允许使用外部脚本或程序。
在Linux/Unix机器上,这会导致OpenVPN无法运行。因为OpenVPN总是需要运行一些外部命令来设置IP地址。但是,在Windows客户端上,你可以在这种模式下使用OpenVPN,前提是默认网关没有更改。为此,需要调用外部应用程序。1
:允许使用某些内置的可执行文件,例如 ip
、route
、ifconfig
等。
这个是默认设置。2
:这是最常见的安全级别。不仅允许使用前面提到的内置命令,还允许使用用户自定义的脚本。3
: 允许通过环境变量将密码传递给被调用的脚本。
这可能不安全,但对于某些身份验证脚本甚至密码更改操作很有用。up-restart
只是一个可以设置的标志。如果设置了此标志,则每当OpenVPN重新启动时,都会调用 down
和 up
脚本(按此顺序)。
up
脚本是OpenVPN执行初始化后执行的第一个脚本。
通常,此脚本在OpenVPN将自己绑定到配置的网络端口并打开tap TUN 或 TAP设备后立即运行。在启动过程的这一点上,没有客户端连接到服务器,并且尚未进行授权。有些人使用脚本来初始化代理服务器和/或防火墙规则集。
打开TUN或TAP设备后,执行 route-up
脚本以在服务器端设置任何系统路由。
每当客户端连接到服务器时,将在客户端和服务器上执行的第一个脚本是 tls-verify
脚本。此脚本被调用多次,客户端向服务器提供的每个证书调用一次。此时,远程对等体仍然被认为是不可信的。这可用于在身份验证之前验证客户端或服务器证书信息。如果tls-verify
脚本返回非零的退出代码,则客户端连接将被拒绝。
除了相对简单的SSL证书客户端身份验证外,OpenVPN还支持一套相当强大的用户名和密码身份验证工具。此参数有两个两个参数,命令及其方法。该方法定义了OpenVPN如何将身份验证凭据传递给命令。该方法可以是 via-env
或 via-file
。为了使用 via-env
选项,需要将 script-security
选项设置为3,以支持此功能。如果 auth-user-pass-verify
脚本返回非零的退出码,则客户端连接将被拒绝。
重要的是要知道,每当客户端重新启动或需要与服务器重新协商安全参数时,auth-user-pass-verify
脚本也会被执行。安全密钥的重新协商通常每小时进行一次,但可以使用 reneg-sec
、reneg-pkts
、reneg-bytes
选项进行控制。
一旦客户端通过VPN服务器的身份验证,就会执行此操作。大多数脚本通常在这里运行。向 client-connect
脚本传递一个参数,即临时文件的名称。脚本完成后,文件由OpenVPN处理,所有内容都被解析为额外的配置选项。这允许管理员向特定客户端添加特殊设置,从而比CCD文件具有更大的灵活性。本章中的一个示例利用 client-connect
脚本更新用于跟踪和趋势VPN连接统计的数据库。
learn-address
脚本允许OpenVPN帮助定义防火墙规则和其他特定于地址的选项。每当从OpenVPN的内部地址表中添加、更新或删除新客户端时,它都会执行。更多详细信息请参阅手册页。此选项同时支持IPv4和IPv6。learn-address
脚本实际上是针对IPv4和IPv6地址分别调用的,一次使用IPv4地址作为主要参数,另一次使用IPv6地址。
与前一节中的 client-connect
脚本一样,client-disconnect
脚本是第二个最常用的脚本连接点。如前所述,在本章的一个示例中,将使用 client-disconnect
脚本来更新数据库记录,包括使用统计数据和其他信息。
一旦隧道关闭开始,就会运行 route-pre-down
脚本。这可用于自动关闭远程代理服务器,并关闭防火墙中在 --up
脚本中早期打开/建立的漏洞。
与 up
选项相反, down
选项在TUN/TAP设备关闭后运行命令。此选项将命令或脚本作为参数,并将其他参数传递给脚本或命令。如果需要在TUN/TAP设备关闭之前运行命令,可以使用 down-pre
选项。
注意:在TUN/TAP设备关闭之前和之后都没有运行脚本的方法。
我们来看看客户端侧的脚本。
与前面服务器端脚本部分完全相同。
与前面服务器端脚本部分完全相同。
up-restart
选项只是一个可以设置的标志。如果设置了此标志,则每当OpenVPN重新启动时,都会调用 down
和 up
脚本(按此顺序)。
每当客户端连接到服务器时,将在客户端和服务器上执行的第一个脚本是 tls-verify
脚本。此脚本被调用多次,服务器向客户端提供的每个证书调用一次。此时,远程对等体仍然被认为是不可信的。这可用于在身份验证之前验证客户端或服务器证书信息。如果 tls-verify
脚本返回非零的退出代码,则客户端连接将被拒绝。
在客户端, ipchange
脚本在 tls-verify
后以及远程(也称为受信任的)IP地址更改时执行。这可用于在打开TUN/TAP适配器之前更新防火墙规则或代理服务器。
up
脚本是客户端身份验证完成后执行的第一个脚本。服务器成功验证客户端后,会向客户端发送一组配置参数。这些参数包括要使用的VPN IP地址,以及推送到客户端的任何其他选项。有些人使用up
脚本来初始化代理服务器和/或防火墙规则集。
一旦通过身份验证并建立了路由,就会执行 route-up
脚本。可选地,可以使用 route-delay
选项将此脚本延迟给定的秒数。
一旦隧道关闭开始,就会运行 route-pre-down
脚本。这可用于关闭与远程代理服务器、其他隧道(SSH)的连接或更正DNS服务器条目。
与 up
选项相反,down
选项在TUN/TAP设备关闭后运行命令。此选项将命令或脚本作为参数,并将其他参数传递给脚本或命令。如果需要在TUN/TAP设备关闭之前运行命令,可以使用 down-pre
选项。
注意:在TUN/TAP设备关闭之前和之后都没有运行脚本的方法。
服务器脚本可用于大大增强OpenVPN部署。脚本可用于身份验证、授权、日志记录等。结合 client-config
选项,可以进一步利用脚本来动态生成客户端配置指令。例如,身份验证可以通过LDAP进行,授权规则可以通过同一LDAP目录动态进行。可以生成和应用防火墙规则,并将路由传递给客户端。本节将演示所有这些内容,以帮助您应用这些方法。
最常见的服务器端脚本是 --client connect
和 --client disconnect
脚本。这些脚本可用于许多事情,包括打开防火墙规则集、挂载文件系统,甚至动态构建客户端配置文件。
结合任务调度程序,其他脚本可以在OpenVPN的直接上下文之外运行,但仍然可以通过OpenVPN管理界面对连接的客户端进行操作。例如,管理员可以向最终用户提供分配的时间,并在分配的时间用完后断开用户的连接。
我们现在看一下 client-connect
脚本。
身份验证(authentication)是对谁能够连接的定义。这并没有定义这些用户可以做什么,只是简单地定义是否允许他们连接到VPN。在最基本的层面上,完全有可能允许用户连接,但不允许该用户实际做任何事情。其中一个用途可能是监控脚本,它只是想验证你的VPN服务器是否正在运行并对用户进行身份验证。这个伪用户不应该有路由流量的能力,因为没有必要。
可以使用身份验证脚本检查许多变量和权限,包括智能卡、LDAP或RADIUS服务器、证书信息、证书吊销列表等。如果你从一开始就阅读了这本书,在第五章 Advanced Deployment Scenarios in tun Mode 中,你应该已经构建了一个已经使用LDAP和其他后端的VPN服务器配置。使用脚本,你可以在没有当前插件的情况下扩展对这些后端的支持,也可以查询其他来源。
最常见的服务器端脚本可以说是 --client-connect
脚本。此脚本在所有TLS验证完成后执行。在客户端连接脚本有很多事情要做的情况下,这是防止由你自己的脚本引起的拒绝服务器(Denial of Server——DoS)攻击的理想选择。在运行此脚本之前,已验证客户端可能具有正确的 tls-auth
密钥和有效证书。client-connect
脚本可以用作一种预身份验证,因为它在 --auth-user-pass-verify
脚本之前执行。它还可以用于动态生成客户端配置。
在使用脚本时,请确保将 --script-security
设置伪2或3。如果不显示此选项,将向客户端返回 AUTH_FAIL
消息。例如,我们创建了一个非常简单的脚本,打印shell环境并以零(0)退出,表示OpenVPN守护进程成功。在我们的服务器配置中,我们添加了以下两个指令:
xxxxxxxxxx
script-security 2
client-connect /usr/local/etc/openvpn/cc.sh
注意:有一个环境变量 script_type
,它定义了被调用的脚本类型。使用此变量,可以使用单个单片脚本来处理所有OpenVPN脚本调用。
以下是我们的 client-connect
脚本:
xxxxxxxxxx
printenv > /tmp/movpn
exit 0
退出代码很重要,因为除了零(0)之外的任何其他值都会导致客户端断开连接。使用前面的脚本和配置,当有新的客户端连接时,我们的脚本将被执行。
此脚本允许连接,但会将shell环境打印到临时文件。此文件的内容很有趣,将应用于所有其他脚本:
xxxxxxxxxx
daemon_start_time=1425344172
daemon_pid=4004
local_1=SERVER_IP
trusted_ip=CLIENT_IP
redirect_gateway=0
untrusted_port=1194
tun_mtu=1500
X509_0_ST=Enlightenment
X509_0_CN=client1
X509_0_emailAddress=root@example.org
time_ascii=Mon Mar 2 18:56:17 2015
proto_1=udp
X509_1_emailAddress=root@example.org
tls_id_0=C=ZA, ST=Enlightenment, O=Mastering OpenVPN, CN=client1, emailAddress=root@example.org
tls_id_1=C=ZA, ST=Enlightenment, L=Overall, O=Mastering OpenVPN, CN=Mastering OpenVPN, emailAddress=root@example.org
ifconfig_ipv6_local=2001:db8:100::1
untrusted_ip=CLIENT_IP
daemon=1
tls_serial_hex_0=02
trusted_port=1194
dev_type=tun
tls_serial_hex_1=d2:93:32:f0:8e:bc:58:ee
X509_1_ST=Enlightenment
X509_1_CN=Mastering OpenVPN
script_context=init
tls_serial_0=2
PWD=/usr/local/etc/openvpn
daemon_log_redirect=1
tls_serial_1=15173527578309581038
ifconfig_local=10.200.0.1
dev=tun0
local_port_1=1194
time_unix=1425344177
link_mtu=1541
remote_port_1=1194
X509_0_C=ZA
tls_digest_0=1b:27:a6:b4:5f:7a:9c:3f:17:fb:ff:33:05:61:3f:2a:56:89:16:d3
tls_digest_1=e4:f1:43:37:34:51:de:99:7a:dc:e3:6d:f2:4c:5b:84:34:4b:f3:64
script_type=client-connect
X509_1_C=ZA
ifconfig_broadcast=10.200.0.255
ifconfig_pool_remote_ip=10.200.0.2
ifconfig_ipv6_remote=2001:db8:100::2
ifconfig_ipv6_netbits=64
ifconfig_netmask=255.255.255.0
config=/usr/local/etc/openvpn/openvpn.conf
ifconfig_pool_netmask=255.255.255.0
X509_0_O=Mastering OpenVPN
X509_1_L=Overall
verb=4
common_name=client1
X509_1_O=Mastering OpenVPN
使用这些环境变量,OpenVPN管理员可以在看似简单的服务器设置中自定义配置。
授权(authorization)可以在几个地方进行,包括 client-config-dir
文件,也可以通过 client-connect
脚本进行添加。
身份验证(authentication)证明你是谁。授权(authorization)决定了你被允许做什么。
传递给 client-connect
脚本的第一个参数将是临时文件的路径,脚本可以使用该路径将客户端的配置选项传递给OpenVPN守护进程。此脚本检查 common_name
环境变量,如果它是 client1
,则在客户端配置中设置为禁用:
xxxxxxxxxx
if [ "$common_name" = "client1" ];
then
echo "disable" >> $1
fi
exit 0
当 client1
连接时,禁用选项将传递给服务器,阻止连接继续。可以传递其他选项,例如静态IP地址的 ifconfig
、推送不同路由等。
考虑一个拥有两个数据中心的网络管理员,每个数据中心都有自己的一对OpenVPN服务器。有一次,有必要与一个数据中心合作,同时确保如果另一个无法访问,服务和系统在正常运行的数据中心仍然可用。
为了帮助测试这一点,我们创建了一些脚本( client-connect
和 auth-user-pass-verify
)来选择传递给客户端的路由。
下图应该提供这个概念的大致概念。VPN客户端有三个配置选项可供选择,即完整路由(full)、Data Center 1路由(dc1
)和Data Center 2路由(dc2
)。此外,客户端可以连接到任何一个数据中心,并获得这三个数据中心中任何一个所需的路由。
在我们的方案中,client1
连接到数据中心,系统将提示其输入用户名和密码。实际上,我们忽略密码并读取用户名以确定所需的路由。
为了捕获路由选择,我们使用了 auth-user-pass-verify
脚本。OpenVPN有两个参数,一个是脚本路径,另一个是通过文件或环境来确定如何将凭据传递给脚本。在本例中,我们选择了 via-file
。
该脚本从脚本中读取凭据,并将其重写为client-connect
脚本可访问的文件:
xxxxxxxxxx
echo 'head -n1 $1' > \
/tmp/openvpn-${untrusted_ip}-${untrusted_port}.tmp
exit 0
接下来,我们的 client-connect
脚本将运行,并读取之前创建的 .tmp
文件。根据读取的参数,它将路由选择写入传递给 client-connect
的配置参数文件中:
xxxxxxxxxx
creds="/tmp/openvpn-${untrusted_ip}-${untrusted_port}.tmp"
if [ -f "$creds" ];
then
selected='head -n1 $creds'
if [ "$selected" = "dc1" ];
then
cat >> $1 <<- EOF
push "route 10.10.0.0 255.255.255.0"
push "route 10.10.1.0 255.255.255.0"
EOF
elif [ "$selected" = "dc2" ];
then
cat >> $1 <<- EOF
push "route 10.20.0.0 255.255.255.0"
push "route 10.20.1.0 255.255.255.0"
EOF
else
cat >> $1 <<- EOF
push "route 10.10.0.0 255.255.255.0"
push "route 10.10.1.0 255.255.255.0"
push "route 10.20.0.0 255.255.255.0"
push "route 10.20.1.0 255.255.255.0"
EOF
fi
fi
exit 0
虽然这并不重要,并且可以通过多种方式攻击它,但它允许我们为每个系统管理员设置一个OpenVPN配置,并根据他们的任务为他们提供一定程度的动态路由。
使用 client-connect
和 client-disconnect
脚本,可以将客户端连接统计信息写入数据库或其他位置。对于这个例子,我们只想跟踪用户何时连接以及他们连接到VPN服务器的时间。
我们假设你至少熟悉SQL,因此不会关注语义。在以下示例中,SQLite3用于存储会话信息。示例数据库的模式如下:
xxxxxxxxxx
CREATE TABLE vpn_session (
session_id integer primary key autoincrement not null,
cn test not null,
connect_time timestamp default CURRENT_TIMESTAMP,
disconnect_time timestamp default null,
vpn_ip4 char(15),
vpn_ip6 char(40),
remote_ip4 char(40),
connection_time integer default 0
);
可以使用以下命令将架构加载到数据库中:
xxxxxxxxxx
ecrist@example:~-> sqlite3 movpn.sqlite3 < file.schema
贴士:
你用于SQLite的目录和文件需要是OpenVPN用户可读和可写的。如果你在配置文件中定义了 --user
或 --group
,则该用户将需要此访问权限。没有它,你的 client-connect
和 client-dissconnect
脚本将无法更新数据库。
这一次,我们将创建一个脚本,用于 client-connect
和 client-disconnect
。我们将检测并处理代码中的脚本类型。对于客户端连接类型,我们将为新会话插入一条新记录。对于 client-disconnect
,我们将更新该记录以提供进一步的说明。
代码如下:
xxxxxxxxxx
DBFILE="/var/openvpn/movpn.sqlite3"
DBBUFFER="/var/openvpn/buffer.sql"
db_query (){
SQL="$1"
/usr/local/bin/sqlite3 $DBFILE "$SQL"
if [ $? -ne 0 ];
then
# There was an error, write the SQL out to a buffer file
echo "$SQL" | tr -d "\t" | tr -d "\n" | tee -a $DBBUFFER
echo ";" | tee -a $DBBUFFER
fi
}
logger "OpenVPN Type: $script_type"
case "$script_type" in
client-connect)
# do record insert
logger "OpenVPN: client-connect"
SQL="
INSERT INTO vpn_session (
cn, connect_time, vpn_ip4,
vpn_ip6, remote_ip4
) VALUES (
'$common_name', '$time_unix',
'$ifconfig_pool_remote_ip',
'$ipconfig_ipv6_remote',
'$untrusted_ip'
)"
db_query "$SQL"
;;
client-disconnect)
# update the record, if it's found
logger "OpenVPN: client-disconnect"
SQL="
UPDATE
vpn_session
SET
disconnect_time = '$time_unix'
WHERE
cn = '$common_name'
AND disconnect_time IS NULL
AND session_id = (
SELECT MAX(session_id)
FROM vpn_session
WHERE cn = '$common_name'
)
"
db_query "$SQL"
;;
esac
exit 0
此脚本使用switch case函数来确定脚本类型并响应地执行操作。在新的客户端连接上,它将用连接信息更新数据库表,并在客户端断开连接时更新该数据库记录。我们在这里使用的模式相当简单,可以很容易地扩展到支持跟踪带宽使用情况和多个客户端连接。
使用 sqlite3
命令,我们可以提取三个最新的数据库条目:
xxxxxxxxxx
ecrist@example:/usr/local/etc/openvpn-> sqlite3 /var/openvpn/movpn.sqlite3 "SELECT * FROM vpn_session ORDER BY session_id DESC LIMIT 3"
10|client1|1426430834||10.200.0.2||CLIENT_IP|
9|client1|1426430759|1426430759|10.200.0.2||CLIENT_IP|0
8|client1|1426429888|1426429888|10.200.0.2||CLIENT_IP|0
有很多方法可以处理这种情况。如果你为用户提供短时间的访问权限,比如一次30分钟,一个具有简单睡眠的 client-connect
脚本可以完全断开连接和账户锁定。假设你销售长达30分钟的短期一次性VPN会话。在这种情况下,用户使用30分钟后,cron
作业将断开用户的连接,并使用CCD目录条目锁定他们的账户。
这个例子将在前一个例子的基础上构建一点,使用我们创建的SQLite数据库来跟踪使用的时间。我们的脚本将有几个任务:
为了完成上述任务,我们将编写一个由 cron
守护进程调用的小型shell脚本。在这里,我们将通过查询管理端口和我们在前面的示例中创建的数据库来检查连接信息。查询管理端口的另一种方法是轮询OpenVPN状态日志文件并实时处理该数据。轮询管理接口的一个严重警告是,它是单线程的,一次只允许一个连接。如果脚本挂起或有人连接到界面,连续的轮询也将挂起。代码如下:
xxxxxxxxxx
#
# Determines if user ($1) has been connected more than $2 seconds
if [ $# -lt 2 ];
then
echo "usage: $0 <user> <time_in_seconds>"
exit 1
fi
USER=$1
TO=$2
# DB seconds
SQL="SELECT SUM(connection_time) FROM vpn_session WHERE cn='$USER'"
DBTIME='/usr/local/bin/sqlite3 /var/openvpn/movpn.sqlite3 "$SQL"'
if [ "$DBTIME" = "" ];
then
DBTIME=0
fi
# Check management port
CTIME='echo "status 2" | nc -N localhost 1194 | grep -E "CLIENT_LIST.*$USER" | cut -f8 -d,'
if [ "$CTIME" != "" ];
then
# we have an active connection
D='date +"%s"'
CTIME='expr "$D - $CTIME"'
else
CTIME=0
fi
UTIME='expr $DBTIME + $CTIME'
if [ $UTIME -gt $TO ];
then
logger "Disconnecting $USER, activity time exceeded ($UTIME/$TO)."
echo "disable" >> /usr/local/etc/openvpn/ccd/$USER
cho "kill $USER" | nc localhost 1194
fi
现在我们有了一个脚本,我们可以称之为:
xxxxxxxxxx
root@example:~-> timeout.sh client1 1800
这将计算 client1
连接到VPN的秒数。如果时间超过 1800
(或你在其中放置的任何数字),它通过 client-config-directory
禁用该配置,并使用管理界面终止任何活动会话。
贴士:OpenVPN管理界面一次只允许一个连接。确保你的脚本正确处理了这个限制。
许多第三方OpenVPN客户端包大量使用客户端脚本,以提供与各种操作系统的牢固集成。Tunnelblick最初由Angelo Laub编写,使用客户端脚本将OpenVPN服务器DNS设置与Mac OS X操作系统集成。
客户端脚本的“客户端”绰号可能有点用词不当。在许多情况下,OpenVPN客户端实际上可能是另一台服务器。也许你有多个不同的办公室,并且正在使用OpenVPN连接它们。客户端脚本可用于启动守护进程、备份过程或本地网络依赖OpenVPN会话的其他服务。
客户端脚本的编写方式与服务器端脚本类似,并且具有几乎相同的可用环境变量列表。
一个常见的客户端连接任务是在连接到公司网络后挂载远程共享。让我们考虑一个需要在连接到VPN后自动挂载web目录的web开发人员。
第一个任务是编写客户端的 up
和 donw
脚本。向上脚本将连接网络共享,down
脚本将删除网络共享。在这种情况下,up
脚本非常简单,从10.200.0.53开始通过NFS挂载 webroot
文件夹。此示例是为Mac OS X系统编写的,利用 osascript
提供图形弹出窗口,在NFS共享已挂载时通知用户。代码如下:
xxxxxxxxxx
# make webroot in home if it doesn't exist
mkdir -p ~/remote_shares/webroot
if [ $? -eq 0 ];
then
mount 192.168.19.53:/webroot ~/remote_shares/webroot
if [ $? -eq 0 ];
then
osascript -e 'tell app "System Events" to display dialog "~/remote_shares/webroot is mounted"'
else
osascript -e 'tell app "System Events" to display dialog "Unable to mount webroot directory."'
fi
else
osascript -e 'tell app "System Events" to display dialog "Unable to create remote share path."'
fi
以下脚本相当简单:
xxxxxxxxxx
umount -f ~/remote_shares/webroot
创建脚本后,需要更新客户端配置,通过添加 script-security
指令和 up
指令来允许外部脚本:
xxxxxxxxxx
script-security 2
up /path/to/script/up.sh
小贴士:
Tunnelblick做了很多自己的脚本编写,并将覆盖 --up
和 --down
的脚本调用。要解决此问题,请按照脚本命名和位置的说明进行操作。这些说明可以在以下网址找到https://tunnelblick.net/cUsingScripts.html.
在下面的示例中,我们将同时使用客户端和服务器端的所有脚本。虽然这与现实生活中的情况不同,但它确实为脚本的执行顺序以及每个脚本的参数和环境变量提供了一些很好的见解。
我们从以下服务器配置文件开始:
xxxxxxxxxx
tls-server
proto udp
port 1194
dev tun
server 10.200.0.0 255.255.255.0
server-ipv6 FD00::200:0/112
ca /etc/openvpn/movpn/movpn-ca.crt
cert /etc/openvpn/movpn/server.crt
key /etc/openvpn/movpn/server.key
dh /etc/openvpn/movpn/dh2048.pem
tls-auth /etc/openvpn/movpn/ta.key 0
persist-key
persist-tun
keepalive 10 60
topology subnet
user nobody
group nobody
daemon
log-append /var/log/openvpn.log
route 10.100.0.0 255.255.0.0
route 192.168.0.0 255.255.255.0
ask-pass /etc/openvpn/mopvn/secret
script-security 3
cd /etc/openvpn/movpn
setenv MASTERING_OPENVPN server
push "setenv-safe SPECIAL hack"
up ./movpn-07-01-script.sh
tls-verify ./movpn-07-01-script.sh
auth-user-pass-verify ./movpn-07-01-script.sh via-env
client-connect ./movpn-07-01-script.sh
route-up ./movpn-07-01-script.sh
client-disconnect ./movpn-07-01-script.sh
learn-address ./movpn-07-01-script.sh
route-pre-down ./movpn-07-01-script.sh
down ./movpn-07-01-script.sh
我们将其保存为 movpn-07-01-server.conf
。以下是关于此配置文件的一些注意事项:
服务器配置中列出的路由仅用于演示目的。
为了解决OpenVPN2.3.7中的错误,我们添加了以下行:
xxxxxxxxxx
ask-pass /etc/openvpn/movpn/secret
在此文件中,secret
(解密服务器私钥的密码)以明文形式存储
在此服务器配置中,我们还使用以下选项切换到脚本所在的目录:
xxxxxxxxxx
cd /etc/openvpn/movpn
这使得服务器配置更短、更容易阅读。
注意,使用 cd
选项,可以使用较短的路径指定 ca
、cert
、key
、dh
和 tls-auth
选项,例如:
xxxxxxxxxx
ca ./movpn-ca.crt
但是,建议始终使用绝对路径名或 --cd
选项以及安全相关项的相对路径,以避免任何混淆。
我们还使用以下命令将服务器端环境变量 MASTERING_OPENVPN
设置为 server
值:
xxxxxxxxxx
setenv MASTERING_OPENVPN server
我们使用以下命令向所有客户端推送一个 safe
环境变量:
xxxxxxxxxx
push "setenv-safe SPECIAL hack"
在客户端脚本和插件中,此变量应显示为 OPENVPN_SPECIAL
。
接下来,我们创建以下脚本:
xxxxxxxxxx
exec >> /tmp/movpn-07-01.log 2>&1
date +"%H:%M:%S: START $script_type script ==="
echo "argv = $0 $@"
echo "user = 'id -un'/'id -gn'"
env | sort | sed 's/^/ /'
date +"%H:%M:%S: END $script_type script ==="
将其保存为 movpn-07-01-script.sh
到目录 /etc/openvpn/movpn
中。确保此文件有可执行权限,创建一个空日志文件,然后启动 openvpn
:
xxxxxxxxxx
# chmod 0755 /etc/openvpn/movpn/mopvn-07-01-script.sh
# touch /tmp/movpn-07-01.log
# chown nobody /tmp/movpn-07-01.log
# openvpn --config /etc/openvpn/movpn/movpn-07-01-server.conf
在连接之前,查看一下 /tmp/movpn-07-01.log
文件。当OpenVPN启动时,一些脚本已经运行了:
xxxxxxxxxx
15:46:57: START up script ===
argv = ./movpn-07-01-script.sh tun0 1500 1541 10.200.0.1
255.255.255.0 init
user = root/root
[...]
15:46:57: END up script ===
15:46:57: START route-up script ===
argv = ./movpn-07-01-script.sh
user = root/root
up
和 route-up
脚本已执行。传递给 up
脚本的参数提供了TUN/TAP设备的名称(tun0)、tun-mtu
(1500)、和 link-mtu
(1541)值、VPN IP地址和网络掩码(10.200.0.1/255.255.255.0)以及调用类型(可能的值是 init
或 restart
)。
注意,这两个脚本都是以root权限执行的。我们将暂时浏览每个脚本的日志文件输出。
在客户端,我们设置了类似的配置。首先,创建以下配置文件:
xxxxxxxxxx
client
proto udp
remote openvpnserver.example.com
port 1194
dev tun
nobind
remote-cert-tls server
tls-auth /etc/openvpn/movpn/ta.key 1
ca /etc/openvpn/movpn/movpn-ca.crt
cert /etc/openvpn/movpn/client1.crt
key /etc/openvpn/movpn/client1.key
persist-tun
persist-key
explicit-exit-notify 3
auth-user-pass
script-security 3
cd /etc/openvpn/movpn
setenv MASTERING_OPENVPN client
tls-verify ./movpn-07-01-script.sh
ipchange ./movpn-07-01-script.sh
up ./movpn-07-01-script.sh
up-restart
route-up ./movpn-07-01-script.sh
route-pre-down ./movpn-07-01-script.sh
down ./movpn-07-01-script.sh
将其保存为 movpn-07-01-client.conf
,然后从服务器重新创建或复制 movpn-07-01-script.sh
文件。
再次,我们确保脚本是可执行的,创建一个空日志文件,并启动 openvpn
:
xxxxxxxxxx
# chmod 0755 /etc/openvpn/movpn/mopvn-07-01-script.sh
# openvpn --config /etc/openvpn/movpn/movpn-07-01-client.conf
OpenVPN 2.3.7 x86_64-redhat-linux-gnu [SSL (OpenSSL)] [LZO] [EPOLL] [PKCS11] [MH] [IPv6] built on Jun 9 2015
library versions: OpenSSL 1.0.1e-fips 11 Feb 2013, LZO 2.08
Enter Auth Username: *****
## enter "movpn"
Enter Auth Password: ******
## enter "secret"
NOTE: the current --script-security setting may allow this configuration to call user-defined scripts
Control Channel Authentication: using '/etc/openvpn/movpn/ta.key' as a OpenVPN static key file
UDPv4 link local: [undef]
UDPv4 link remote: [AF_INET]<IP>:1194
WARNING: this configuration may cache passwords in memory -- use the auth-nocache option to prevent this
[Mastering OpenVPN Server] Peer Connection Initiated with [AF_INET]<IP>:1194
TUN/TAP device tun0 opened
do_ifconfig, tt->ipv6=1, tt->did_ifconfig_ipv6_setup=1
/usr/sbin/ip link set dev tun0 up mtu 1500
/usr/sbin/ip addr add dev tun0 10.200.0.2/24 broadcast 10.200.0.255
/usr/sbin/ip -6 addr add fd00::200:1000/112 dev tun0
./movpn-07-01-script.sh tun0 1500 1541 10.200.0.2 255.255.255.0 init
Initialization Sequence Completed
你可以为 Auth username
和 Auth password
填写任何你想要的内容,因为服务器端脚本总是会返回成功。
建立连接后,我们验证VPN客户端和服务器端是否可以使用 ping
和 ping6
相互连接。
接下来,我们通过向OpenVPN客户端发送特殊信号来重新启动VPN连接:
xxxxxxxxxx
# killall -USR1 openvpn
这将触发OpenVPN客户端的“软重置”。连接恢复后,我们再次验证VPN是否完全正常工作。软重置发生在现实生活中,主要是当客户端使用 persist-tun
选项,OpenVPN客户端在漫游的移动网络上,或者客户端和服务器之间的网络不太稳定时。在这种情况下,服务器端脚本的调用参数略有不同,我们稍后会看到。
最后,通过终止客户端来关闭VPN连接。我们现在将浏览服务器和客户端上的脚本日志。
服务器端脚本日志可以很容易地增长到数千行,但幸运的是其中有一些结构。让我们首先检查脚本的调用顺序:
xxxxxxxxxx
15:46:57: START up script ===
argv = ./movpn-07-01-script.sh tun0 1500 1541 10.200.0.1 255.255.255.0 init
15:46:57: START route-up script ===
argv = ./movpn-07-01-script.sh
15:47:15: START tls-verify script ===
argv = ./movpn-07-01-script.sh 1 C=ZA, ST=Enlightenment,
L=Overall, O=Mastering OpenVPN, CN=Mastering OpenVPN,
emailAddress=root@example.org
15:47:15: START tls-verify script ===
argv = ./movpn-07-01-script.sh 0 C=ZA, ST=Enlightenment,
O=Mastering OpenVPN, CN=client1,
emailAddress=root@example.org
15:47:15: START user-pass-verify script ===
argv = ./movpn-07-01-script.sh
15:47:15: START client-connect script ===
argv = ./movpn-07-01-script.sh
/tmp/openvpn_cc_5b1f0d25ac0f71c98c44ec128e5c21d6.tmp
15:47:15: START learn-address script ===
argv = ./movpn-07-01-script.sh add 10.200.0.2 client1
15:47:15: START learn-address script ===
argv = ./movpn-07-01-script.sh add fd00::200:1000 client1
17:37:18: START tls-verify script ===
argv = ./movpn-07-01-script.sh 1 C=ZA, ST=Enlightenment,
L=Overall, O=Mastering OpenVPN, CN=Mastering OpenVPN,
emailAddress=root@example.org
17:37:18: START tls-verify script ===
argv = ./movpn-07-01-script.sh 0 C=ZA, ST=Enlightenment,
O=Mastering OpenVPN, CN=client1,
emailAddress=root@example.org
17:37:18: START user-pass-verify script ===
argv = ./movpn-07-01-script.sh
17:37:18: START client-disconnect script ===
argv = ./movpn-07-01-script.sh
17:37:18: START client-connect script ===
argv = ./movpn-07-01-script.sh
/tmp/openvpn_cc_8528c57f838033a03f38ddb72b57ae30.tmp
17:37:18: START learn-address script ===
argv = ./movpn-07-01-script.sh update 10.200.0.2 client1
17:37:18: START learn-address script ===
argv = ./movpn-07-01-script.sh update fd00::200:1000 client1
17:38:50: START client-disconnect script ===
argv = ./movpn-07-01-script.sh
17:38:50: START learn-address script ===
argv = ./movpn-07-01-script.sh delete fd00::200:1000
17:38:50: START learn-address script ===
argv = ./movpn-07-01-script.sh delete 10.200.0.2
17:39:08: START route-pre-down script ===
argv = ./movpn-07-01-script.sh tun0 1500 1541 10.200.0.1 255.255.255.0 init
17:39:09: START down script ===
argv = ./movpn-07-01-script.sh tun0 1500 1541 10.200.0.1 255.255.255.0 init
首先调用 up
和 route-up
脚本,如我们之前所见。
当第一个客户端连接时,tls-verify
脚本会被调用两次:首先用于签署客户端证书的CA证书,然后用于客户端证书本身。
接下来,执行 auth-user-pass-verify
脚本。当此脚本返回成功(退出代码 0
)时,客户端将通过身份验证并被视为可信。
下一个脚本是 client-connect
脚本,它通过临时文件调用。此脚本通常用于为特定客户端设置特殊选项,或在数据库中记录客户端活动。此脚本仍然可以通过打印临时文件的选项来影响分配给客户端的IP地址,例如:
xxxxxxxxxx
echo "ifconfig-push 10.200.0.88 255.255.255.0" > $1
客户端连接时调用的最后一个脚本是 learn-address
脚本。此脚本会被调用两次,一次使用IPv4地址,另一次使用IPv6地址。此脚本实际上最适合更新防火墙规则,但大多数人倾向于使用 client-connect
脚本。需要 learn-address
脚本,特别是在基于TAP的设置中,并结合外部DHCP服务器。
注:在基于TAP的设置中,learn-address
脚本的第二个参数是客户端TAP适配器的MAC地址。客户端VPN IP地址可作为环境变量使用。
在日志文件中,我们可以看到OpenVPN客户端在17:37:18收到 soft-restart
触发。脚本的执行顺序可能看起来很奇怪,但可以解释:
tls-verify
和 auth-user-pass-verify
脚本以确定它是否是一个有效的客户端。client-disconnect
脚本被调用。client-connect
脚本。请注意,新的客户端实例可能来自新的远程IP地址。learn-address
脚本被调用两次,操作集被设置为 update
,一次用于IPv4地址,另一次用于IPv6地址。如果需要更改任何防火墙规则,那么这将是最好的选择。17:38:50,客户端断开连接。因为我们在客户端配置中指定了 explicit-exit-notify
,所以服务器会立即收到通知,并执行 client-disconnect
连接脚本。
请注意,learn-address
脚本现在是在操作设置为 delete
的情况下执行的。
17:39:08,OpenVPN服务器进程本身停止,并调用 route-pre-down
和 down
脚本。请注意,传递给此脚本的参数与传递给 up
脚本的参数相同。
现在我们已经了解了调用脚本的顺序,是时候仔细研究脚本可用的环境变量了。
up脚本可用的环境变量如下:
xxxxxxxxxx
MASTERING_OPENVPN=server
PWD=/etc/openvpn/movpn
SHLVL=1
_=/bin/env
config=movpn-07-01-server.conf
daemon=1
daemon_log_redirect=1
daemon_pid=8070
daemon_start_time=1437659216
dev=tun0
dev_type=tun
ifconfig_broadcast=10.200.0.255
ifconfig_ipv6_local=fd00::200:1
ifconfig_ipv6_netbits=112
ifconfig_ipv6_remote=fd00::200:2
ifconfig_local=10.200.0.1
ifconfig_netmask=255.255.255.0
link_mtu=1541
local_port_1=1194
proto_1=udp
remote_port_1=1194
route_gateway_1=10.200.0.2
route_gateway_2=10.200.0.2
route_net_gateway=<SERVER-IP>
route_netmask_1=255.255.0.0
route_netmask_2=255.255.255.0
route_network_1=10.100.0.0
route_network_2=192.168.0.0
route_vpn_gateway=10.200.0.2
script_context=init
script_type=up
tun_mtu=1500
verb=1
传递给 up
脚本的大多数参数也作为环境变量存在。服务器端路由信息也已经在这里可用,但最好在 route-up
脚本中处理这些变量。
在 route-up
脚本中,与前面的代码中相同的环境可用,但增加了一个:
xxxxxxxxxx
script_type=route-up
redirect_gateway=0
如果默认网关也需要重定向,则此环境变量设置为 1
。服务器配置中列出的所有 route
语句也作为环境变量显示。对于每条路由,route_network
、 route_netmask
和 route_gateway
都是可用的。此外,应注意,OpenVPN关键字 net_gateway
和 vpn_gateway
在这里表示为 route_net_gateway
和 route_vpn_gateway
。
tls-verify
脚本使用完整的证书名称调用,该名称也称为可分辨名称(Distinguished Name——DN)。更多的证书信息作为环境变量可用:
xxxxxxxxxx
X509_0_C=ZA
X509_0_CN=client1
X509_0_O=Mastering OpenVPN
X509_0_ST=Enlightenment
X509_0_emailAddress=root@example.org
X509_1_C=ZA
X509_1_CN=Mastering OpenVPN
X509_1_L=Overall
X509_1_O=Mastering OpenVPN
X509_1_ST=Enlightenment
X509_1_emailAddress=root@example.org
script_type=tls-verify
tls_digest_0=1b:27:a6:b4:5f:7a:9c:3f:17:fb:ff:33:05:61:3f:2a:56:89:16:d3
tls_digest_1=e4:f1:43:37:34:51:de:99:7a:dc:e3:6d:f2:4c:5b:84:34:4b:f3:64
tls_id_0=C=ZA, ST=Enlightenment, O=Mastering OpenVPN, CN=client1, emailAddress=root@example.org
tls_id_1=C=ZA, ST=Enlightenment, L=Overall, O=Mastering OpenVPN, CN=Mastering OpenVPN, emailAddress=root@example.org
tls_serial_0=2
tls_serial_1=15173527578309581038
tls_serial_hex_0=02
tls_serial_hex_1=d2:93:32:f0:8e:bc:58:ee
untrusted_ip=<CLIENT-IP>
untrusted_port=46171
所有这些都可以用来确定这个特定的客户端证书是否真正可信。请注意,有两个环境变量,unsisted_ip=<CLIENT-ip>
和 unsisted_port=46171
,它们表示截至目前不受信任的客户端地址。
auth-user-pass-verify
脚本的环境与前一个脚本相同,但添加了三个新变量:
xxxxxxxxxx
common_name=client1
username=movpn
password=secret
common_name
是在 tls-verify
脚本成功完成后设置的。username
和 password
作为环境变量传递,因为我们将 script-security
设置为 3
,并将 via-env
参数添加到配置文件中的 auth-user-pass-verify
选项中。
最常用的脚本,client-connect
,具有几乎相同的环境,但 password
变量已被删除。此外,随着身份验证过程的完成,有两个新变量 trusted_ip=<CLIENT-ip>
和 trusted_port=<port>
,它们的值与不受信任的对应变量完全相同。请注意,不受信任的版本仍然可用。
learn-address
脚本的环境与 client-connect
脚本完全相同。它需要一个命令来执行,以及一些可选参数:
在基于TAP的设置中,传递给脚本的第二个参数是客户端TAP适配器的MAC地址。服务器(如果已配置)分配给客户端的VPN IP地址分别在环境变量 ifconfig_pool_remote_ip
和 ifconfig_pool_netmask
中可用,如下所示:
xxxxxxxxxx
ifconfig_pool_remote_ip=10.200.0.2
ifconfig_pool_netmask=255.255.255.0
client-disconnect
脚本的环境与 client-connect
脚本完全相同,但它也会返回一些会计统计数据:
xxxxxxxxxx
bytes_received=7553
bytes_sent=8105
这些信息主要是出于账号目的。
最后,使用与 up script
相同的参数调用 route-pre-down
和 down
脚本。当调用 route-pre-down
脚本时,系统路由仍然存在。当调用 down
脚本时,如果OpenVPN有权限这样做,系统路由将被删除。环境变量 signal=sigint
提供了触发OpenVPN关闭的信号类型的信息。
客户端脚本日志的流程和结构与服务器端日志非常相似。再次,让我们首先检查调用脚本的顺序:
xxxxxxxxxx
15:47:15: START tls-verify script ===
argv = ./movpn-07-01-script.sh 1 C=ZA, ST=Enlightenment,
L=Overall, O=Mastering OpenVPN, CN=Mastering OpenVPN,
emailAddress=root@example.org
15:47:15: START tls-verify script ===
argv = ./movpn-07-01-script.sh 0 C=ZA, ST=Enlightenment,
O=Mastering OpenVPN, CN=Mastering OpenVPN Server,
emailAddress=root@example.org
15:47:15: START ipchange script ===
argv = ./movpn-07-01-script.sh [AF_INET]<SERVER-IP> [AF_INET]1194
15:47:17: START up script ===
argv = ./movpn-07-01-script.sh tun0 1500 1541 10.200.0.2 255.255.255.0 init
15:47:17: START route-up script ===
argv = ./movpn-07-01-script.sh
17:37:16: START down script ===
argv = ./movpn-07-01-script.sh tun0 1500 1541 10.200.0.2 255.255.255.0 restart
17:37:18: START tls-verify script ===
argv = ./movpn-07-01-script.sh 1 C=ZA, ST=Enlightenment,
L=Overall, O=Mastering OpenVPN, CN=Mastering OpenVPN,
emailAddress=root@example.org
17:37:18: START tls-verify script ===
argv = ./movpn-07-01-script.sh 0 C=ZA, ST=Enlightenment,
O=Mastering OpenVPN, CN=Mastering OpenVPN Server,
emailAddress=root@example.org
17:37:18: START ipchange script ===
argv = ./movpn-07-01-script.sh [AF_INET]<SERVER-IP> [AF_INET]1194
17:37:20: START up script ===
argv = ./movpn-07-01-script.sh tun0 1500 1541 10.200.0.2 255.255.255.0 restart
17:38:53: START route-pre-down script ===
argv = ./movpn-07-01-script.sh tun0 1500 1541 10.200.0.2 255.255.255.0 init
17:38:53: START down script ===
argv = ./movpn-07-01-script.sh tun0 1500 1541 10.200.0.2 255.255.255.0 init
当客户端首次连接时,tls-verify
脚本会被调用两次,首先是用于签署服务器证书的CA证书,然后是服务器证书本身。这样,客户端可以验证它是否正在连接到受信任的服务器。
之后,鲜为人知的 ipchange
脚本被调用。此脚本尚不知道将为其分配哪个客户端IP地址。它主要用于调整客户端上的防火墙设置,或通知另一个应用程序正在进行VPN连接设置。
一旦客户端通过服务器的身份验证,一块信息就会从服务器推送到客户端。然后,该块在本地被解析为配置选项,之后调用 up
和 route-up
脚本。
当我们向OpenVPN客户端发送 USR1
信号时,它会导致OpenVPN执行 soft-restart
。这在脚本执行日志中也可以看到:
down
脚本时,将最后一个参数设置为 restart
,而不是 init
。tls-verify
和 ipchange
脚本,因为我们需要再次与服务器重新进行身份验证。up
脚本来设置VPN IP地址。在这里,脚本的最后一个参数也被设置为 restart
,而不是 init
。请注意,在这种情况下不会调用 route-up
脚本。这是因为我们在客户端配置中包含了 persist-tun
。由于TUN/TAP接口没有关闭或关闭,所有客户端路由仍然有效,因此不会执行 route-up
脚本。
当客户端关闭时,再次调用 route-pre-down
和 down
脚本,这次参数设置为 init
。
现在我们已经了解了调用脚本的顺序,是时候再次查看环境变量了。
客户端的大多数环境变量与服务器端的环境变量相似。一些变量是镜像的,例如包含服务器端证书通用名称的 common_name
,可以在 up
和ipchange
脚本环境中找到:
xxxxxxxxxx
common_name=Mastering OpenVPN Server
up script
环境中还存在从服务器推送到客户端的变量:
xxxxxxxxxx
OPENVPN_SPECIAL=hack
OpenVPN客户端收到了 safe
变量 SPECIAL
,并通过在其前面添加 OpenVPN_
为其创建了一个环境变量。
注:没有将信息或变量从客户端发送回服务器的方法。在2.4版本中,一些系统信息将被发回,但无法在客户端上配置。
当OpenVPN客户端使用 USR1
信号重新启动时,会调用 down
和 up
脚本,并将最后一个参数设置为restart
,而不是 init
。这也反映在两个脚本的环境变量中:
xxxxxxxxxx
script_context=restart
script_type=down ## or up
signal=sigusr1
当OpenVPN终止时,这些环境变量包含以下内容:
xxxxxxxxxx
script_context=init
script_type=down
signal=exit-with-notification
由于易于编写脚本,OpenVPN插件接口是OpenVPN服务器管理员相对未充分利用的工具。默认情况下,OpenVPN附带了一对插件,一个用于PAM身份验证,另一个用于以root权限执行 --down
脚本,无论管理员是否降级权限。
在OpenVPN中删除权限是一个好主意,down-root
插件允许您这样做。防火墙等应用程序需要升级权限才能添加和删除防火墙规则。通过使用down-root
插件,管理员可以在客户端连接时提供新的防火墙规则,以及在客户端断开连接后删除这些规则的能力。
使用场景可以是支持整个公司员工的单个OpenVPN实例。行政和办公室工作人员通常不需要访问公司网络上的熄灯管理界面和其他此类系统。通过添加防火墙规则,OpenVPN可以根据CN或其他环境变量为特定客户端连接引入允许访问。一旦该技术人员断开连接,删除防火墙规则将阻止另一名非技术人员获得该访问权限,即使他们与以前连接的工作人员位于同一子网或获得相同的IP。
OpenVPN附带的第二个插件是 auth-pam
插件。这与操作系统的可插拔身份验证模块(Pluggable Authentication Modules——PAM)堆栈接口。通过使用PAM,管理员能够利用任何也可以以这种方式进行接口的后端。LDAP是 auth-pam
的一个示例用例。
许多Unix和Linux系统都能够通过LDAP进行身份验证。在OpenLDAP的情况下,有一些共享对象,如PADL软件的 pam-ldap
和 nss-ldap
。配置这些并将其添加到系统PAM堆栈后,OpenVPN可以通过几个简单的配置参数进行绑定。
小贴士:由于缺乏对pam的支持,auth-pam
插件无法在Windows系统上使用。
在最简单的形式中,可以将以下内容添加到服务器配置中:
xxxxxxxxxx
plugin openvpn-auth-pam.so login
这将启用插件并指示它使用登录PAM服务。请注意,这可以是任何PAM服务,包括管理员定义的OpenVPN特定设置。前面代码中的第三个参数标识服务。更复杂的配置可能包括传递的参数:
xxxxxxxxxx
plugin openvpn-auth-pam.so login login USERNAME password PASSWORD
在这种情况下,我们仍然使用登录PAM服务,但它需要两个参数:登录和密码。OpenVPN可能会传递三个参数,将关键字 PASSWORD
、USERNAME
和 COMMONNAME
替换为明显的对应项。USERNAME
和 PASSWORD
要求在客户端配置中设置 auth-user-pass
。
要确定PAM服务的查询或调试 auth-pam
模块本身,请将OpenVPN日志详细程度设置为7级或更高。运行 auth-pam
和OpenVPN 2.3.6在日志文件中给出以下输出:
xxxxxxxxxx
AUTH-PAM: BACKGROUND: received command code: 0
AUTH-PAM: BACKGROUND: USER: ecrist
AUTH-PAM: BACKGROUND: my_conv[0] query='Login:' style=2
AUTH-PAM: BACKGROUND: name match found, query/match-string ['Login:', 'login'] = 'USERNAME'
AUTH-PAM: BACKGROUND: my_conv[0] query='Password:' style=1
AUTH-PAM: BACKGROUND: name match found, query/match-string ['Password:', 'password'] = 'PASSWORD'
查看 my_conv
的行,我们可以看到两个查询值,Login:
和 Password:
。这些成功地部分匹配了 login
和 password
。为了演示部分匹配,我更改了 config
行如下:
xxxxxxxxxx
plugin openvpn-auth-pam.so login log USERNAME password PASSWORD
从这里的日志中可以明显看出,该模块能够成功地将 log
与 Login:
匹配,将 password
与 Password:
匹配。
xxxxxxxxxx
AUTH-PAM: BACKGROUND: received command code: 0
AUTH-PAM: BACKGROUND: USER: ecrist
AUTH-PAM: BACKGROUND: my_conv[0] query='Login:' style=2
AUTH-PAM: BACKGROUND: name match found, query/match-string ['Login:', 'log'] = 'USERNAME'
AUTH-PAM: BACKGROUND: my_conv[0] query='Password:' style=1
AUTH-PAM: BACKGROUND: name match found, query/match-string ['Password:', 'password'] = 'PASSWORD'
尽管这种松散的匹配很有帮助,但您需要小心,因为根据您使用的PAM插件,可能会发生冲突。
提供各种身份验证插件以及各种前端和证书管理器的项目列表可以在以下网址找到https://community.openvpn.net/openvpn/wiki/RelatedProjects.
脚本和插件是扩展OpenVPN的强大工具。它允许管理员将OpenVPN更好地集成到现有的基础设施中,例如,通过启用针对单独后端系统的身份验证或记录客户端的使用统计数据。
编写OpenVPN脚本可能很棘手,因为需要特别注意脚本的时间安排。当前版本的OpenVPN是单体和单线程的,这意味着冗长或行为不端的服务器端脚本可能会阻止所有用户的整个VPN。
理解脚本调用的流程和顺序也很重要。在本章中,我们探讨了这种顺序是如何工作的,以及每个服务器端和客户端脚本都有哪些环境变量。
在下一章中,我们将看到更多的图形用户界面,因为我们将深入了解OpenVPN在智能手机、平板电脑和其他移动设备上的使用。