具有高级身份验证和授权的OpenVPN

本文讨论了具有其他功能的OpenVPN的配置:

  • 用于主要身份验证的令牌证书(以Rutoken为例)
  • 用于辅助身份验证的LDAP后端(以ActiveDirectory为例)
  • 过滤用户可用的内部资源(通过iptables)

它还描述了如何为Linux,Windows和MacOS配置客户端。

服务器设置


安装OpenVPN


以脚本Nyr / openvpn-install ,从根目录运行。

git clone https://github.com/Nyr/openvpn-install.git cd openvpn-install 

启动过程将询问一些问题。

  • udp协议
  • 1194端口
  • DNS服务器-本地
  • 外部ip-Internet上的网关地址,通过该网关可以使用vpn服务器

原始脚本还有一个增强安全性的版本-github.com/Angristan/OpenVPN-install 。 它具有更多的加密设置以及原因说明。

用户管理


新增中
如果不使用令牌,则通过相同的脚本添加用户。 该脚本实质上会生成一个自定义的ovpn配置,并在其中插入由根证书签名的证书。

如果使用了令牌(请参阅下面的令牌部分),那么将根据对令牌生成的证书请求,手动将证书写出。 用户配置必须从现有模板(从生成配置脚本的模板)中手动完成。 模板在这里/etc/openvpn/client-common.txt 。 它不包含在openvpn软件包中,而是由脚本在配置过程中生成的。

删掉
删除用户是通过相同的安装脚本完成的。 证书被添加到CRL ,新的CRL被推送到vpn服务器。 服务器认为CRL中的所有证书均无效,并拒绝接受。

如何手动吊销证书:

 cd /etc/openvpn/easyrsa #   ./easyrsa revoke $CLIENT #   crl ./easyrsa gen-crl #   crl rm -rf /etc/openvpn/crl.pem #    cp /etc/openvpn/easy-rsa/pki/crl.pem /etc/openvpn/crl.pem # openvpn    crl,       nobody chown nobody:nobody /etc/openvpn/crl.pem 

筛选客户端可用的主机


客户端需要受它们连接到openvpn时可以在网络上访问的主机的限制。

手动地

这个想法是即使在tun0接口上也要捕获数据包,这些数据包来自客户端,并在进入NAT之前对其进行过滤。 进行NAT之后,将没有理由对其进行过滤-它们在内部网络上都将具有openvpn服务器ip地址。 在进入NAT之前,每个用户的数据包都有自己唯一的ip地址(有关ip地址和用户的对应关系,请参见文件/etc/openvpn/ipp.txt )。

穿过系统的数据包(不是直接来自系统,也不是传入的,也就是说,实际上是由系统路由)由FORWARD表处理。 iptables中的表是从上到下处理的,如果表中的任何规则都没有导致有关数据包命运的决定,那么将触发默认规则。

准备FORWARD表:

 #   iptables -F FORWARD #     FORWARD -    iptables -P FORWARD DROP #     iptables -I FORWARD -m state --state ESTABLISHED,RELATED -j ACCEPT 

特定客户的示例规则。 由于该表的默认规则是DRO​​P,因此仅保留它允许的主机+端口对。 允许访问主机上的端口+ ping主机本身:

 iptables -I FORWARD -s 10.8.0.3 -i tun0 -d 10.0.2.3 -p tcp --dport 443 -j ACCEPT iptables -I FORWARD -s 10.8.0.3 -i tun0 -d 10.0.2.3 -p icmp --icmp-type echo-request -j ACCEPT 

在上面的示例中,主机10.8.0.3被允许访问主机10.0.2.3的端口443。

如何关闭访问权限:

 #         iptables -L FORWARD --line-numbers #     iptables -D FORWARD { } 

然后,您需要找到特定客户端的所有规则并将其删除。

在调试过程中,可以方便地查看哪些规则有效。 每个规则都有一个已处理数据包的计数器。

 #  ,      watch iptables -nvL FORWARD #     iptables -Z FORWARD 

自动地

OpenVPN服务器可以执行某些动作的脚本。 特别是在连接和断开客户端时。 只要脚本是可执行的,脚本就可以写在任何东西上。 在脚本内部,环境变量将各种参数传递给当前连接。 我们对变量感兴趣:

  • common_name (证书所有者的名称;创建证书时驱动到“通用名称”字段的内容)
  • ifconfig_pool_remote_ip (tun0上的客户端IP地址)
  • script_type (发生了哪个事件-连接或断开连接)。

您需要root特权才能管理iptables。 连接后的Openvpn会将权限重置为无人,并从中运行脚本。 不允许任何人从sudo下做某事是很不好的,最好不要在规则中使用星号,但是您需要以某种方式允许用户控制iptables。

 # /etc/sudoers.d/50_openvpn # #    nobody ALL = NOPASSWD: /sbin/iptables -A FORWARD* #     nobody ALL = NOPASSWD: /sbin/iptables -L FORWARD* #    nobody ALL = NOPASSWD: /sbin/iptables -D FORWARD* 

在服务器配置中,您需要添加权限以执行第三方文件,并启用两个负责连接和断开用户连接的钩子。

 script-security 2 client-connect /etc/openvpn/bin/hosts.rb client-disconnect /etc/openvpn/bin/hosts.rb 

该脚本本身读取配置并为iptables应用规则。 该脚本的工作原理与上一节中所述的相同。

/openvpn/bin/hosts.rb
 #!/usr/bin/ruby # -*- coding: utf-8 -*- require 'pp' def log(string) puts 'hosts.rb: ' + string end def parse_config_file(name) config_path = "hosts/#{name}" unless File.exist?(config_path) puts "There is no specific configuration for #{name}." p name exit 0 end config_source = IO.read(config_path).split("\n") config = config_source.inject([]) do |result,line| ip, port, protocol = line.split(/\s+/) result << { ip: ip, port: port, protocol: protocol || 'tcp' } end end def get_config(name) user_config = parse_config_file(name) if user_config everybody_config = parse_config_file('everybody') end everybody_config + user_config end def apply_rule(rule) command = "sudo iptables #{rule}" log(command) system(command) end def remove_rule(number) command = "sudo iptables -D FORWARD #{number}" log(command) system(command) end def allow_target(source_ip, options) #         . apply_rule("-A FORWARD -s #{source_ip} -i tun0 -d #{options[:ip]} -p #{options[:protocol]} --dport #{options[:port]} -j ACCEPT") #       apply_rule("-A FORWARD -s #{source_ip} -i tun0 -d #{options[:ip]} -p icmp --icmp-type echo-request -j ACCEPT") end def clear_targets(source_ip) #      FORWARD,  source_ip. rules_exist = true while rules_exist table = `sudo iptables -L FORWARD --line-number`.split("\n") the_line = table.find do |line| fields = line.split(/\s+/) ip = fields[4] ip == source_ip end if the_line number = the_line.split(/\s+/)[0] remove_rule(number) else rules_exist = false end end end ################################################################################ script_type = ENV['script_type'] log(script_type) name = ENV['common_name'] source_ip = ENV['ifconfig_pool_remote_ip'] case script_type when 'client-connect' config = get_config(name) config.each{|target| allow_target(source_ip, target)} when 'client-disconnect' clear_targets(source_ip) else puts "Unknown script type #{script_type}." end 

规则存储在与/etc/openvpn/hosts文件夹中的证书的通用名称相对应的文件中。 它们准确地指定了特定客户端可以使用的IP地址。 分隔符-任意数量的空格。 IP地址,端口和协议(tcp或udp)通过分隔符写入。

 10.0.0.24 53 udp 10.0.0.25 53 udp 10.0.2.3 443 tcp 

结果,以下结构应/etc/openvpn/etc/openvpn文件夹中

├──箱
│└──hosts.rb
├──房东
│├──用户1
│├──user2
│└──大家
├──server.conf
└──...

User1user2是上述格式的文件。 它们描述了具有相应公共名称的用户可以访问的主机。

还有另一个附加的everybody文件,它包含适用于所有客户端的规则,前提是这些客户端有单独的配置文件。 也就是说,如果用户有一个可以访问的主机列表,则将应用此列表以及everybody人中列出的那些主机。 如果没有,那么everybody都不适用。 例如,将DNS服务器放置在此文件中非常方便。

记录中

安装脚本仅包括当前连接的日志记录( status)参数status) 。 为了显示常规日志,您需要在服务器配置( /etc/openvpn/server.conf )中添加/etc/openvpn/server.conf
 log-append /var/log/openvpn.log 


LDAP

有一个openvpn-auth-ldap插件,可让您通过LDAP再次验证用户身份。

交付包裹:

 sudo yum install openvpn-auth-ldap 

添加到server.conf:

 plugin /usr/lib64/openvpn/plugin/lib/openvpn-auth-ldap.so "/etc/openvpn/ldap.conf" 

/etc/openvpn/ldap.conf为ldap创建一个配置:
 <LDAP> URL ldaps://{LDAP_DOMAIN_HERE} Timeout 15 TLSEnable no FollowReferrals yes BindDN "BIND_DN_HERE" Password "BIND_PASSWORD_HERE" </LDAP> <Authorization> BaseDN "{BASE_DN_HERE}" SearchFilter "(&(sAMAccountName=%u)(objectClass=organizationalPerson)(objectCategory=person)(!(userAccountControl:1.2.840.113556.1.4.803:=2)))" RequireGroup false </Authorization> 

将行添加到自定义ovpn配置中:

 auth-user-pass 

因此,首先将要求用户从域中输入用户名和密码,然后是来自令牌的PIN。 如果这些步骤之一失败,将不会建立连接。

ldap.conf的选项描述在插件存储库中 。 它支持通过组成员身份进行身份验证,但我尚未对其进行测试。

速度


速度的最大提高使udp模式成为可能。 在所有手册中都建议这样做。 关键是,在TCP通道中启动TCP客户端连接没有任何意义。 客户端一个TCP就足以正确发送数据包。 如果数据包在udp通道中丢失,则客户端tcp连接将控制传递调整。

速度至少会提高,因为不需要等待确认通道中每个数据包的传递。 TCP存在第二个问题-一个客户端tcp数据包很可能不适合vpn通道的一个数据包。 MTU相同,但是需要将标头添加到客户端程序包。 结果,您必须在每个用户数据包的vpn通道内发送两个数据包。

如果无法通过其他方式使用TCP,则可以使用。 例如,当vpn通过ssh通道工作时。

完整服务器配置的示例


example-server.conf
 port 1194 proto tcp dev tun sndbuf 0 rcvbuf 0 ca ca.crt cert server.crt key server.key dh dh.pem tls-auth ta.key 0 topology subnet server 10.8.0.0 255.255.255.0 ifconfig-pool-persist ipp.txt push "redirect-gateway def1 bypass-dhcp" push "dhcp-option DNS 10.0.0.25" push "dhcp-option DNS 10.0.0.24" keepalive 10 120 cipher AES-256-CBC comp-lzo user nobody group nobody persist-key persist-tun status openvpn-status.log verb 3 crl-verify crl.pem log-append /var/log/openvpn.log script-security 2 client-connect /etc/openvpn/bin/hosts.rb client-disconnect /etc/openvpn/bin/hosts.rb 


代币设置


PKCS#11库


要使用令牌,您需要一个特殊的库。 创建密钥对和实际连接都需要该库。 通过链接下载所有平台

以后无论在哪里找到librtpkcs11ecp.so,这都是一个需要下载的库,并将其放在方便的地方。

在令牌上创建证书


在令牌上生成密钥对。 这里的id参数是密钥对适合的令牌上插槽的序列号。

 pkcs11-tool --module /usr/lib64/librtpkcs11ecp.so --keypairgen --key-type rsa:2048 -l --id 01 

提出公共密钥证书请求。 在创建证书请求的过程中,将设置证书的生存期和通用名称,该名称和名称用于过滤网络中的可用ip地址。 公用名必须与ActiveDirectory中的登录名匹配,以免造成混淆。

 openssl openssl> engine -t dynamic -pre SO_PATH:/usr/lib64/openssl/engines/pkcs11.so -pre ID:pkcs11 -pre LIST_ADD:1 -pre LOAD -pre MODULE_PATH:/usr/lib64/librtpkcs11ecp.so openssl> req -engine pkcs11 -new -key slot_0-id_01 -keyform engine -out /home/john/good.req 

收到的请求必须移至/etc/openvpn/easy-rsa/pki/reqs/ 。 文件扩展名必须为req
将请求转换为证书:

 cd /etc/openvpn/easy-rsa/ ./easyrsa sign-req client good 

之后,具有相同名称但扩展名为crt的证书将出现在/etc/openvpn/easy-rsa/pki/issued/文件夹中。

在记录之前,必须将证书转换为DER:

 openssl x509 -in /home/user/user-cert.pem -out /home/user/user-cert.crt -outform DER 

为令牌编写证书:

 pkcs11-tool --module /usr/lib/librtpkcs11ecp.so -l -y cert -w /home/user/user-cert.crt --id 45 --label TEST 

它是根据“将Rutoken EDS与OpenSSL(RSA)结合使用”一文编写的。

使用令牌进行身份验证


找到要提供给服务器的证书的ID:

 $ openvpn --show-pkcs11-ids /usr/lib64/librtpkcs11ecp.so The following objects are available for use. Each object shown below may be used as parameter to --pkcs11-id option please remember to use single quote mark. Certificate DN: /CN=User1 Serial: 490B82C4000000000075 Serialized id: aaaa/bbb/41545F5349474E415455524581D2A1A1B23C4AA4CB17FAF7A4600 

我们对这里的序列化ID感兴趣。

必须在ovpn配置中输入的选项,以便令牌可以使用:

 pkcs11-providers /usr/lib64/librtpkcs11ecp.so pkcs11-id 'aaaa/bbb/41545F5349474E415455524581D2A1A1B23C4AA4CB17FAF7A4600' 

pkcs11-id选项必须用单引号引起来。

本手册适用于所有平台。 您需要在标记上指定库的路径和证书ID。 该库的名称可能有所不同,即.dll而不是.so ,但含义相同。

在这种情况下,您需要从ovpn文件中删除certkey部分,因为证书和私钥将从令牌中获取。

完整的客户端配置(对于Windows)如下所示:

客户程序
client
dev tun
proto tcp
sndbuf 0
rcvbuf 0
remote 78.47.37.247 22222
resolv-retry infinite
nobind
persist-key
persist-tun
remote-cert-tls server
cipher AES-256-CBC
comp-lzo
setenv opt block-outside-dns
key-direction 1
verb 3

pkcs11-providers "c://Windows//System32//rtPKCS11ECP.dll"
pkcs11-id 'Aktiv\x20Co\x2E/Rutoken\x20ECP/342b871d/Rutoken/01'

-----BEGIN CERTIFICATE-----
{CERT_HERE}
-----END CERTIFICATE-----


<tls-auth>
#
# 2048 bit OpenVPN static key
#
-----BEGIN OpenVPN Static key V1-----
{KEY_HERE}
-----END OpenVPN Static key V1-----
</tls-auth>


基于“如何使用客户端智能卡向OpenVPN配置添加双因素身份验证”撰写。

客户设置


的Linux


Openvpn有一个错误,如果该程序包是在systemd支持下构建的,则阻止用户从令牌中输入PIN码。 由于systemd最近无处不在,因此存储库中所有可用的软件包都将在其支持下进行编译。 Linux上的客户端需要自行收集软件包。 这是在Arch Linux上为我工作的示例配置:

 ./configure \ --prefix=/usr \ --sbindir=/usr/bin \ --enable-iproute2 \ --enable-pkcs11 \ --enable-plugins \ --enable-x509-alt-username 

您可以使用以下命令验证openvpn是使用systemd或不使用systemd构建的:

 openvpn --version | grep --color enable_systemd 

马索斯


在Mac OS下,只有一个免费客户端-Tunnelblink

他不知道如何从gui令牌输入密码。 该错误的描述示例如下: https: //groups.google.com/forum/#! topic/ tunnelblick-discuss/f_Rp_2nV-x8从控制台启动openvpn可以绕过此错误。 鉴于Windows的官方客户端也不知道这一点,因此这并不奇怪。

同样在Mac OS(不同于Windows)下,还需要其他脚本来配置网络。 如果仅从控制台运行openvpn,则DNS将不起作用(也许其他原因,仅DNS将出现)。

TunnelBlick具有这些网络配置脚本,仅在建立和断开连接时才需要调用它们。 您需要添加到ovpn配置的内容:

 script-security 2 up "/Applications/Tunnelblick.app/Contents/Resources/client.up.tunnelblick.sh -9 -d -f -m -w -ptADGNWradsgnw" down "/Applications/Tunnelblick.app/Contents/Resources/client.down.tunnelblick.sh -9 -d -f -m -w -ptADGNWradsgnw" 

用于启动openvpn连接的示例脚本,可以将其放在桌面上并用鼠标戳一下:

 #!/bin/bash tunnelblick=/Applications/Tunnelblick.app/Contents/Resources/openvpn/openvpn-2.4.2-openssl-1.0.2k sudo $tunnelblick/openvpn --config $tunnelblick/user.ovpn 

窗户


在Windows下,一切似乎正常。 官方客户端不知道如何从令牌输入PIN码,而是设法从控制台手动打开vpn。

最重要的事情是从管理员那里做所有事情。 以管理员身份运行客户端安装程序。 启动同时具有管理员权限的openvpn启动终端,否则它将无法控制网络接口。

在Windows下,用于处理令牌的库的路径应通过双斜杠记录。 这适用于命令行上的ovpn配置和--show-pkcs11-ids选项。

 pkcs11-providers "c://Windows//System32//rtPKCS11ECP.dll" pkcs11-id 'Aktiv\x20Co\x2E/Rutoken\x20ECP/342b871d/Rutoken/01' 

Source: https://habr.com/ru/post/zh-CN433250/


All Articles