OpenVPN with Extended Authentication and Authorization

 3r33984. 3r3-31. The article discusses the configuration of OpenVPN with additional features:
 3r33984.
 3r33984.
 3r33984.
certificates on tokens for primary authentication (for example, Rutoken) 3r-3243.  3r33984.
LDAP backend for secondary authentication (for example, ActiveDirectory) 3r-3243.  3r33984.
Filtering internal resources available to the userx (via iptables)
 3r33984.
 3r33984. The configuration of clients under Linux, Windows and MacOS is also described.
 3r33984. Nyr /openvpn-install , run as root.
 3r33984.
 3r33984. 3r33966. 3r3393939. git clone https://github.com/Nyr/openvpn-install.git
cd openvpn-install
3r33971. 3r33939.
 3r33984. A few questions will be asked in the startup process.
 3r33984.
 3r33984.
 3r33984.
udp protocol
 3r33984.
port 1194
 3r33984.
DNS servers are local
 3r33984.
external ip - the address of the gateway on the Internet through which the vpn-server
will be available.  3r33984.
 3r33984. There is also an improved version of the original script in terms of security - github.com/Angristan/OpenVPN-install . It has more encryption settings with explanations why this is so.
 3r33984.
 3r33984.

User management


 3r33984. 3r3756. Adding [/b]
 3r33984. If tokens are not used, user addition is done through the same script. The script essentially generates a user ovpn-config and inserts a certificate signed by the root certificate.
 3r33984.
 3r33984. If tokens are used (see the section on tokens below) then the certificate is written out by hand based on a request for a certificate that is generated on the token. The user config needs to be made by hands from the existing template (from the same one from which the config script generates). The template lies here /etc/openvpn/client-common.txt . It is not included in the openvpn distribution and is generated by the script during the configuration process.
 3r33984.
 3r33984. 3r3756. Removal [/b]
 3r33984.
 3r33984. Users are deleted through the same installation script. The certificate is added in r3r399. CRL 3r3393913. A new CRL is being pushed by the vpn server. All certificates that are in the CRL server are considered invalid and refuse to accept.
 3r33984.
 3r33984. How to revoke a certificate manually:
 3r33984.
 3r33984. 3r33966. 3r3393939. cd /etc /openvpn /easyrsa
3r33984. # withdraw certificate
./easyrsa revoke $ CLIENT
# generate a new crl
./easyrsa gen-crl
3r33984. # remove old crl
rm -rf /etc/openvpn/crl.pem
# replace it with a new
cp /etc/openvpn/easy-rsa/pki/crl.pem /etc/openvpn/crl.pem
# openvpn should be able to read crl when it dropped the privilege to nobody
chown nobody: nobody /etc/openvpn/crl.pem
3r33971. 3r33939.
 3r33984.

Filtering available hosts for

clients.
 3r33984. Clients should be limited to those hosts to which they can go inside the network when they connect to openvpn.
 3r33984.
 3r33984. 3r3756. Manually [/b]
 3r33984.
 3r33984. The idea is to catch packages even on the interface tun0 3r33971. in which they come from clients and filter them before they get into NAT. After NAT, filtering them will no longer be why - they all will have the ip-address of the openvpn server in the internal network. Before getting into NAT, the packets for each user have their own unique ip-address (the correspondence of ip-addresses and users can be found in the file 3r33960./etc/openvpn/ipp.txt ).
 3r33984.
 3r33984. Packages that pass through the system (they do not come directly from it and are not incoming, that is, they are essentially routed by the system) are processed by the FORWARD table. Tables in iptables are processed from top to bottom, if none of the rules in the table lead to deciding the fate of the packet, then the default rule is triggered.
 3r33984.
 3r33984. FORWARD table preparation:
 3r33984.
 3r33984. 3r33966. 3r3393939. # reset all
iptables -F FORWARD
# default rule for FORWARD table is not to miss anything
iptables -P FORWARD DROP
# skip already established connections
iptables -I FORWARD -m state --state ESTABLISHED, RELATED -j ACCEPT
3r33971. 3r33939.
 3r33984. An example of rules for a specific client. Since the default rule for the table is DROP, it only remains to allow those host + port pairs to which you can. Allow access to the port on the host + ping the host itself:
 3r33984.
 3r33984. 3r33966. 3r3393939. iptables -I FORWARD -s ???.3 -i tun0 -d ???.3 -p tcp --dport 443 -j ACCEPT
iptables -I FORWARD -s ???.3 -i tun0 -d ???.3 -p icmp --icmp-type echo-request -j ACCEPT
3r33971. 3r33939.
 3r33984. In the example above, host ???.3 is allowed access to port 443 of host ???.3.
 3r33984.
 3r33984. How to close access:
 3r33984.
 3r33984. 3r33966. 3r3393939. # show rules in the table with their numbers 3r3393984. iptables -L FORWARD --line-numbers
# delete rule by number
iptables -D FORWARD {rule number}
3r33971. 3r33939.
 3r33984. Then you need to find all the rules for a specific client and delete them.
 3r33984.
 3r33984. During debugging it is convenient to look at what rules work. Each rule has a counter for processed packets.
 3r33984.
 3r33984. 3r33966. 3r3393939. # show counters, update every two seconds
watch iptables -nvL FORWARD
# reset counters to zeros
iptables -Z FORWARD
3r33971. 3r33939.
 3r33984. 3r3756. Automatically [/b]
 3r33984.
 3r33984. The openvpn server has the ability to execute scripts for certain actions. In particular, when connecting and disconnecting clients. Scripts can be written on anything, just to be executable. Inside the script, the environment variables are passed all the parameters of the current connection. We are interested in variables:
 3r33984.
 3r33984.
  •  3r33984.
  • common_name 3r3393971. (the name of the owner of the certificate; what is driven into the common name field when creating the certificate)  3r33984.
  • ifconfig_pool_remote_ip (ip-address of the client on tun0)  3r33984.
  • script_type (which event happened - connection or disconnection).  3r33984.

 3r33984. Root privileges are required to manage iptables. After connecting, Openvpn resets permissions to nobody and executes scripts from it. It's bad to allow nobody to do something from under sudo, and it’s better not to use the asterisk in the rules, but somehow you need to allow the user to manage iptables.
 3r33984.
 3r33984. 3r33966. # /etc/sudoers.d/50_openvpn
#
# allow add rules
nobody ALL = NOPASSWD: /sbin /iptables -A FORWARD *
# allow viewing the list of rules
nobody ALL = NOPASSWD: /sbin /iptables -L FORWARD *
# allow delete rules
nobody ALL = NOPASSWD: /sbin /iptables -D FORWARD *
3r33971. 3r33939.
 3r33984. In the server configuration, you need to add permission to execute third-party files and enable two hooks responsible for connecting and disconnecting the user.
 3r33984.
 3r33984. 3r33966. script-security 2
client-connect /etc/openvpn/bin/hosts.rb
client-disconnect /etc/openvpn/bin/hosts.rb
3r33971. 3r33939.
 3r33984. The script itself, which reads configs and applies the rules for iptables. The script works on the same principles as described in the previous section.
 3r33984.
 3r33984. 3r33782. /openvpn/bin/hosts.rb [/b]
3r33966. #! /usr /bin /ruby ​​
# - * - coding: utf-8 - * -
require 'pp'
3r33984. def log (string)
puts 'hosts.rb:' + string
end
3r33984. def parse_config_file (name)
config_path = "hosts /# {name}"
3r33984. File File.exist? (config_path)
puts "There is no specific configuration for # {name}." 3r33984. p name
exit 0
end
3r33984. config_source = IO.read (config_path) .split ("n")
3r33984. config = config_source.inject ([]) do | result, line | 3r33984. ip, port, protocol = line.split (/s + /)
result {3r33984. ip: ip,
port: port,
protocol: protocol || 'tcp'
}
end
end
3r33984. 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
3r33984. def apply_rule (rule)
command = "sudo iptables # {rule}"
log (command)
system (command)
end
3r33984. def remove_rule (number)
command = "sudo iptables -D FORWARD # {number}"
log (command)
system (command)
end
3r33984. def allow_target (source_ip, options)
# Allow client access to a specific port of a particular host. 3r33984. apply_rule ("- A FORWARD -s # {source_ip} -i tun0 -d # {options[:ip]} -p # {options[:protocol]} --dport # {options[:port]} -j ACCEPT")
# Allow a client to ping a specific host
apply_rule ("- A FORWARD -s # {source_ip} -i tun0 -d # {options[:ip]} -p icmp - icmp-type echo-request -j ACCEPT")
end
3r33984. def clear_targets (source_ip)
# Delete all rules from the FORWARD table containing source_ip. 3r33984. 3r33984. rules_exist = true
3r33984. while rules_exist
3r33984. table = `sudo iptables -L FORWARD --line-number`.split (" n ")
3r33984. the_line = table.find do | line | 3r33984. fields = line.split (/s + /)
ip = fields[4]3r33984. ip == source_ip
end
3r33984. if the the_line
number = the_line.split (/s + /)[0]3r33984. remove_rule (number)
else
rules_exist = false
end
3r33984. end
3r33984. end
3r33984. ################################################## ##############################
3r33984. script_type = ENV['script_type']3r33984. log (script_type)
3r33984. name = ENV['common_name']3r33984. source_ip = ENV['ifconfig_pool_remote_ip']3r33984. 3r33984. 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}." 3r33984. end
3r33971. 3r33939.

 3r33984. The rules are stored in files corresponding to the common name of the certificates in the folder. /etc /openvpn /hosts . They are spelled out what exactly IP-addresses are available for a particular client. Separator - an arbitrary number of spaces. Separator records IP address, port and protocol (tcp or udp).
 3r33984.
 3r33984. 3r33966. ???.??? udp
???.??? udp
???.??? tcp
3r33971. 3r33939.
 3r33984. As a result, in the folder /etc /openvpn The following structure should be
 3r33984.
 3r33984. Bin── bin
 3r33984. Hosts └── hosts.rb
 3r33984. Hosts── hosts
 3r33984. User ├── user1
 3r33984. User ├── user2
 3r33984. Every └── everybody
 3r33984. Server── server.conf
 3r33984. …──

 3r33984.
 3r33984. User1 3r33971. and 3r33960. user2 - These are files in the above format. They describe which hosts the user with the corresponding common name has access to.
 3r33984.
 3r33984. There is one additional file everybody it contains rules that apply to all clients, provided that for these clientsThere is a separate configuration file. That is, if the user is given a list of hosts where he can go, then this list and those hosts that are listed in apply. everybody . If not, then everybody not applicable. In this file it is convenient to take out for example a DNS server.
 3r33984.
 3r33984. 3r3756. Logging 3r3784.
 3r33984.
 3r33984. The installation script only includes logging of current connections (parameter 3r36060. Status) 3r33971. . For the usual log to appear, you need to add a line to the server configuration ( /Etc/openvpn/server.conf ):
 3r33984. 3r33966. log-append /var/log/openvpn.log 3r33939.
 3r33984.
 3r33984. 3r3756. LDAP 3r3784.
 3r33984.
 3r33984. There is a plugin. openvpn-auth-ldap , which allows you to re-authenticate the user via LDAP.
 3r33984.
 3r33984. Put the package:
 3r33984.
 3r33984. 3r33966. 3r3393939. sudo yum install openvpn-auth-ldap
3r33939.
 3r33984. Add to server.conf:
 3r33984.
 3r33984. 3r33966. 3r3393939. plugin /usr/lib64/openvpn/plugin/lib/openvpn-auth-ldap.so "/etc/openvpn/ldap.conf" 3r33939.
 3r33984. Create a config for ldap:
 3r33984. 3r33966. 3r3393939. 3r33984. URL ldaps: //{LDAP_DOMAIN_HERE}
Timeout 15
TLSEnable no
FollowReferrals yes
3r33984. BindDN "BIND_DN_HERE"
Password "BIND_PASSWORD_HERE"
3r? 3516. 3r33984. 3r33984. 3r? 3519. 3r33984. BaseDN "{BIND_DN_HERE}"
SearchFilter "(& (sAMAccountName =% u) (objectClass = organizationalPerson) (objectCategory = person) (! (UserAccountControl: ???.???.803: = 2)))" 3r3393984. RequireGroup false
3r33524. 3r33984. 3r33971. 3r33939.
 3r33984. Add line to user ovpn-config:
 3r33984.
 3r33984. 3r33966. 3r3393939. auth-user-pass 3r33939.
 3r33984. Thus, the user will first be asked for the login and password from the domain, then the PIN code from the token. If one of these steps fails, the connection will not be established.
 3r33984.
 3r33984. Description of options for ldap.conf there is in the plugin repository . It supports group membership authentication, but I have not tested it.
 3r33984.
 3r33984.

The speed of


 3r33984. The greatest increase in speed gives the inclusion of udp mode. It is advised in all manuals. The point is that there is no point in launching a client tcp connection in a tcp channel. One tcp at the client is enough to make the correct delivery of packets. If packets disappear in the udp channel, the delivery adjustment will be controlled by the client tcp connection.
 3r33984.
 3r33984. The speed will increase at least because you do not have to wait for the confirmation of the delivery of each packet in the channel. There is a second problem with tcp - one client tcp package most likely does not fit into one vpn channel packet. The MTU is the same, but you still need to add headers to the client package. As a result, one user packet has to send two packets inside the vpn channel.
 3r33984.
 3r33984. TCP makes sense when used differently. For example, when vpn works through ssh channel.
 3r33984.
 3r33984.

An example of a complete server configuration


 3r33984. 3r33782. example-server.conf [/b]
3r33966. 3r3393939. 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 ???.???.???r3r3984. ifconfig-pool-persist ipp.txt
push "redirect-gateway def1 bypass-dhcp"
push "dhcp-option DNS ???.25"
push "dhcp-option DNS ???.24"
keepalive ???r3r3984. cipher AES-256-CBC
comp-lzo
user nobody
group nobody
persist-key
persist-tun
status openvpn-status.log
verb 3
crl-verify crl.pem
3r33984. log-append /var/log/openvpn.log
3r33984. script-security 2
client-connect /etc/openvpn/bin/hosts.rb
client-disconnect /etc/openvpn/bin/hosts.rb
3r33971. 3r33939.
 3r33984.

 3r33984. 3r33866. Setting tokens 3r33867.
 3r33984.

PKCS # 11 library


 3r33984. To work with tokens need a special library. The library is needed both for creating key pairs and for the connection itself. 3r32424. Download under all platforms can be link .
 3r33984.
 3r33984. Everywhere, where librtpkcs11ecp.so occurs further, this is the very library that needs to be downloaded and put somewhere in a convenient place.
 3r33984.
 3r33984.

Creating a certificate on the token


 3r33984. Generate a key pair on the token. The id parameter here is the ordinal number of the slot on the token where the key pair is placed.
 3r33984.
 3r33984. 3r33966. 3r3393939. pkcs11-tool - module /usr/lib64/librtpkcs11ecp.so - keypairgen - key-type rsa: 2048 -l - id 01
3r33971. 3r33939.
 3r33984. Make a certificate request for the public key. In the process of creating a certificate request, the certificate’s lifetime and the common name are set, which is used to filter available ip-addresses within the network. Common name must match the login in ActiveDirectory so that there is no confusion.
 3r33984.
 3r33984. 3r33966. 3r3393939. 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
3r33971. 3r33939.
 3r33984. The received request must be transferred to the folder. /etc /openvpn /easy-rsa /pki /reqs / . The file extension must be req 3r33939. .
 3r33984. Convert request to certificate:
 3r33984.
 3r33984. 3r33966. 3r3393939. cd /etc /openvpn /easy-rsa /
./easyrsa sign-req client good
3r33971. 3r33939.
 3r33984. After that, in the folder /etc /openvpn /easy-rsa /pki /issued / A certificate will appear with the same name, but extension crt .
 3r33984.
 3r33984. Before recording, the certificate must be converted to DER: 3r3393964.  3r33984.
 3r33984. 3r33966. 3r3393939. openssl x509 -in /home/user/user-cert.pem-out /home/user/user-cert.crt -outform DER
3r33971. 3r33939.
 3r33984. Record certificate token:
 3r33984.
 3r33984. 3r33966. 3r3393939. pkcs11-tool - module /usr/lib/librtpkcs11ecp.so -l -y cert -w /home/user/user-cert.crt --id 45 --label TEST
3r33939.
 3r33984. Written on the basis of article 3r3709. "Using Rutoken ESP with OpenSSL (RSA)" 3r3-3913. .
 3r33984.
 3r33984.

Using a token for authentication


 3r33984. Find the id of the certificate to be presented to the server:
 3r33984.
 3r33984. 3r33966. 3r3393939. $ openvpn - show-pkcs11-ids /usr/lib64/librtpkcs11ecp.so
3r33984. The following objects are available for use. 3r33984. Each object shown below may be used as parameter to
--pkcs11-id option mark 3r33984. 3r33984. Certificate
DN: /CN = User1
Serial: 490B82C4000000000075
Serialized id: aaaa /bbb /41545F5349474E415455524581D2A1A1B23C4AA4CB17FAF7A4600
3r33971. 3r33939.
 3r33984. We are interested in serialized id here.
 3r33984.
 3r33984. Options that need to be entered in the ovpn-config so that the tokens are picked up:
 3r33984.
 3r33984. 3r33966. 3r3393939. pkcs11-providers /usr/lib64/librtpkcs11ecp.so
pkcs11-id 'aaaa /bbb /41545F5349474E415455524581D2A1A1B23C4AA4CB17FAF7A4600' 3r33939.
 3r33984. Optional pkcs11-id 3r3756. must be enclosed in single quotes. [/b]
 3r33984.
 3r33984. This instruction makes sense on all platforms. You need to specify the path to the library and the id of the certificate on the token. The library may be called a little differently, be .dll 3r3393971. and not .so 3r3393971. but the meaning is the same.
 3r33984.
 3r33984. From the ovpn file, you need to delete sections cert and 3r33960. key
, because the certificate and private key will be taken from the token.
 3r33984.
 3r33984. Fully client config (for windows) looks like this:
 3r33984.
 3r33984. 3r33782. client.ovpn [/b]
client
 3r33984. dev tun
 3r33984. proto tcp
 3r33984. sndbuf 0
 3r33984. rcvbuf 0
 3r33984. ???.???r3r3964.  3r33984. resolv-retry infinite
 3r33984. nobind
 3r33984. persist-key
 3r33984. persist tun
 3r33984. remote-cert-tls server
 3r33984. cipher AES-256-CBC
 3r33984. comp-lzo
 3r33984. setenv opt block-outside-dns
 3r33984. key-direction 1
 3r33984. verb 3
 3r33984.
 3r33984. pkcs11-providers "c: //Windows //System32//rtPKCS11ECP.dll"
 3r33984. pkcs11-id 'Aktivx20Cox2E /Rutokenx20ECP /342b871d /Rutoken /01'
 3r33984.
 3r33984. ----- BEGIN CERTIFICATE -----
 3r33984. {CERT_HERE}
 3r33984. ----- END CERTIFICATE ----- 3r3393964.  3r33984.
 3r33984.
 3r33984. 3r33837.
 3r33984. #
 3r33984. # 2048 bit OpenVPN static key
 3r33984. #
 3r33984. ----- BEGIN OpenVPN Static key V1 -----
 3r33984. {KEY_HERE}
 3r33984. ----- END OpenVPN Static key V1 -----
 3r33984. 3r33852. 3r33971.
 3r33984.

 3r33984. Written on the basis of r3r3860. "How to add dual-factor authentication to an OpenVPN configuration using client-side smart cards" .
 3r33984.
 3r33984. 3r33866. Setting up clients 3r36767.
 3r33984.

Linux


 3r33984. In openvpn, there is a bug that prevents the user from entering a PIN from a token if the package is built with systemd support. Since recently systemd is everywhere, all packages that are already available in the repositories are built with its support. Linux customers need to build the package themselves. Here is an example of the configuration that earned me on Arch Linux:
 3r33984.
 3r33984. 3r33966. 3r3393939. ./configure
--prefix = /usr
--sbindir = /usr /bin
--enable-iproute2
--enable-pkcs11
--enable-plugins
--enable-x509-alt-username
3r33971. 3r33939.
 3r33984. You can check whether openvpn is built with or without systemd using the following command:
 3r33984.
 3r33984. 3r33966. 3r3393939. openvpn --version | grep --color enable_systemd
3r33971. 3r33939.
 3r33984.

Mas OS


 3r33984. Under Mac OS, there is only one free client - 3r3906. Tunnelblink .
 3r33984.
 3r33984. He does not know how to enter a pin code from a token from gui The bug is described for example here - 3r33912. https://groups.google.com/forum/#!topic/tunnelblick-discuss/f_Rp_2nV-x8 Costing running openvpn fromconsole. This is not surprising, given that the official client under windows is also not able to.
 3r33984.
 3r33984. Also under Mac OS (unlike windows) additional scripts are needed to configure the network. If you just run openvpn from the console, then DNS will not work (maybe something else, only DNS is manifested).
 3r33984.
 3r33984. TunnelBlick has these network configuration scripts, they only need to be called when a connection is established and disconnected. What you need to add in the ovpn-config:
 3r33984.
 3r33984. 3r33966. 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"
3r33939.
 3r33984. An example of a script to run openvpn-connection, which can be put on the desktop and poke the mouse:
 3r33984.
 3r33984. 3r33966. 3r3393939. #! /bin /bash
3r33984. tunnelblick = /Applications /Tunnelblick.app /Contents /Resources /openvpn /openvpn-???-openssl-???k
sudo $ tunnelblick /openvpn --config $ tunnelblick /user.ovpn
3r33971. 3r33939.
 3r33984.

Windows


 3r33984. Under windows everything seems to work. The official client does not know how to enter the pin-code from the token, it costs by starting openvpn with the hands from the console.
 3r33984.
 3r33984. The most important thing is to do everything from an administrator. Run from the administrator install the client. To start the terminal in which openvpn starts, also with the admin rights, otherwise it will not be able to control the network interface.
 3r33984.
 3r33984. Under Windows, the path to the library to work with tokens should be recorded through double slashes. This applies to both the ovpn-config and options --show-pkcs11-ids on the command line.
 3r33984.
 3r33984. 3r33966. 3r33984. pkcs11-providers "c: //Windows//System32//rtPKCS11ECP.dll"
pkcs11-id 'Aktivx20Cox2E /Rutokenx20ECP /342b871d /Rutoken /01'
3r33971. 3r33939.
3r33984. 3r33984. 3r33984.
! function (e) {function t (t, n) {if (! (n in e)) {for (var r, a = e.document, i = a.scripts, o = i.length; o-- ;) if (-1! == i[o].src.indexOf (t)) {r = i[o]; break} if (! r) {r = a.createElement ("script"), r.type = "text /jаvascript", r.async =! ? r.defer =! ? r.src = t, r.charset = "UTF-8"; var d = function () {var e = a.getElementsByTagName ("script")[0]; e.parentNode.insertBefore (r, e)}; "[object Opera]" == e.opera? a.addEventListener? a.addEventListener ("DOMContentLoaded", d ): d ()}}} t ("//mediator.mail.ru/script/2820404/"""_mediator") () (); 3r33978. 3r33984.
3r33984. 3r33984. 3r33984. 3r33984.
+ 0 -

Add comment