Corporate OpenVPN, or How to Make a Tiger Out of a Cat

They say you can't make a tiger out of a cat, but today we'll try to add some “corporateness” to a very simple and popular VPN solution. OpenVPN Community Edition is an open source solution, very popular in the world and quite safe. In a corporate environment, its use is often problematic – it lacks important functions that allow the solution to be deployed to a large number of users with minimal costs.

I will not put OpenVPN in the place of the ideal VPN solution. In this article, I will analyze how, using certificates and ldap authentication in the Microsoft ecosystem, to organize access of different user groups to certain closed segments of the corporate network (authorization). No Easy-RSA and self-signed certificates – maximum corporateness and automation.

IP pools associated with security groups will allow you to separate different functional users into groups on the firewall. The free version of OpenVPN does not provide groups and pools, so we will use scripts when connecting and disconnecting the client.

In the scheme considered in the article, there are 3 factors checked when the user logs in:

  • User certificate (ownership factor, located in the user's computer certificate store, can be made non-exportable)

  • Username/password (“knowledge factor”, checked in Microsoft Active Directory)

  • Single key (“ownership factor”, part of the client config, mainly DDoS protection)

Thus, with the correct implementation of the certificate distribution system, we can obtain full-fledged multi-factor authentication with good protection against DDoS attacks.

Figure 1 shows the authentication and authorization scheme discussed in the article:

To implement this scheme we will need:

— Microsoft Active Directory Domain (MS AD)

o Users and computers in the domain

o Three security groups OpenVPN_sg1_users, OpenVPN_sg2_users, OpenVPN_sg3_users users

o Service account for reading the list of users

— Microsoft Active Directory Certificate Server (ADCS, PKI)

o PKI system (AD CS) must be implemented in the domain.

— Linux virtual machine for OpenVPN server with Internet access/output

— Network connectivity to the domain controller

— Firewall

Configuring Microsoft Active Directory, firewalls, and PKI is beyond the scope of this article, although I will definitely provide important comments. We will assume that you already have a Microsoft corporate infrastructure with the services from the list above working. This article focuses only on OpenVPN configuration.

Installing Applications and OpenVPN

In general, installing an OpenVPN server on most Linux branches does not cause any particular difficulties. In my case, Ubuntu 22.04 LTS was used.

sudo apt update -y

sudo apt install openvpn openssl ldap-utils openvpn-auth-ldap dos2unix -y

The server is ready for configuration and further operation.

If you are using “hardened” versions of Linux, you may need to add local firewall permissions.

Setting up routing

Let's make a router from a virtual machine:

sudo nano /etc/sysctl.conf

add the line:

net.ipv4.ip_forward=1

Restart the service:

sudo sysctl -p

We are setting up access to a corporate network, so we are not setting up access to the Internet from a virtual machine (and this is SNAT, iptables, etc.). (This is NOT bypassing blockages, etc., this is a corporate VPN)

Don't forget about routing. In this example, we will use the client address pool 10.10.0.0/16. Traffic should be routed through our virtual machine.

Preparing the server certificate

Disclaimer: Configuring PKI is beyond the scope of this article. The following screenshots and comments are only provided for important parameters.

Each client checks the server certificate when connecting. This measure prevents an attacker from using a fake server, for example, to collect corporate passwords. The server certificate is signed by the root certification authority and is thus integrated into the Microsoft PKI system.

So, we issue a certificate for the OpenVPN server manually.

There are no specific requirements for the server, except for the EKU, which we will “force” all clients to check in their configuration file.

Sample

Prepare a template for issuing OpenVPN server certificates if you do not have one. The template can be based on the “Computer” template, or you can use an existing one that is suitable for this purpose.

Determine the validity period of the certificate.

I think it is not safe to set long terms. One year is the recommended option. Keep in mind that a little earlier than a year later, you will have to remember the certificate installation procedure and reboot the OpenVPN server.

Very often, an outdated certificate is remembered after the VPN has stopped working. It is better to set a reminder for yourself in advance or connect certificate monitoring.

The OpenVPN server cannot obtain a certificate automatically, so you will either have to export the key and certificate or create a signing request. In this article, I will request and issue a certificate directly from the PKI server, and then export it and transfer it to the OpenVPN server.

This screenshot shows the field that allows such an export.

In this case, we will have to fill in the “subject” field manually:

For the OpenVPN authentication process, the “subject” field is (unfortunately) irrelevant. Clients will connect to any server listed in the certificate specified in their configuration file. The same goes for the “alt” fields: they are not used. The important parameter that we will check on the client side is:

I will be requesting a certificate from this server, so I set the appropriate permission:

We issue and export a certificate:

Fill in “Subject”:

Request and issue a certificate. If everything went well, you will see the certificate in the computer's storage:

Export the certificate:

Required with a private key.

We turn on the whole chain:

We set the password, path and file name. The server certificate and key are ready.. We copy the received file to the OpenVPN server.

Additionally, prepare the root certificate in PEM format and copy it to the OpenVPN server as well:

If you have a multi-level PKI system, save the chain in one file. Pay attention to the CN (Common Name) field. This field will be useful to us when we configure the client configuration file. Remember it. In my case the name is trivially simple.

The root certificate can be copied, for example, by clicking on the appropriate button:

Thus, we prepared and copied 2 files to the OpenVPN server:

  • vm-a-openvpn-01.pfx (key, certificate in pfx format)

  • ca.crt (Root certificate in PEM format)

Configuring the OpenVPN application

1. Generate the required file for cryptography to work:

sudo openssl dhparam -out /etc/openvpn/server/dh.pem 2048

2. Prepare an additional authentication key:

sudo openvpn –genkey secret /etc/openvpn/server/ta.key

3. Copy the prepared certificate to the OpenVPN server: vm-a-openvpn-01.pfx (name to your taste)

4. We make the pfx file into a format suitable for the OpenVPN server (PEM):

sudo openssl pkcs12 -in vm-a-openvpn-01.pfx -nocerts -nodes -out /etc/openvpn/server/vm-a-openvpn-01.key (Key to certificate, no password, keep secret)

sudo openssl pkcs12 -in ./vm-a-openvpn-01.pfx -clcerts -nokeys -out /etc/openvpn/server/vm-a-openvpn-01.crt (server certificate)

sudo cp ca.cer /etc/openvpn/server/root-ca.crt (just copy the root certificate to a convenient location for the server without any transformations)

OpenVPN server configuration file /etc/openvpn/server/example-simple.conf

1. Create a file:

sudo nano /etc/openvpn/server/example-simple.conf

2. Insert the text:

mode server

tls-server

port 443

proto tcp server

script security 2

dev tun

ca root-ca.crt

cert vm-a-openvpn-01.crt

key vm-a-openvpn-01.key

dh dh.pem

tls-crypt ta.key

verify-client-cert-require

subnet topology

#Our common pool of addresses for ALL pools associated with security groups. All “subpools” should be included here

server 10.10.0.0 255.255.0.0

push “route 192.168.3.0 255.255.255.0”

push “dhcp-option DNS 192.168.3.10”

keepalive 10 120

AES-256-CBC cipher

persist-key

persist-tun

verb 3

client-connect client_connect.sh

client-disconnect client_disconnect.sh

remote-cert-tls-client

plugin /usr/lib/openvpn/openvpn-auth-ldap.so ldap.conf

The file references the following important files (should be in the /etc/openvpn/server folder):

· root-ca.crt (copied above)

· vm-a-openvpn-01.crt (copied above)

· vm-a-openvpn-01.key (copied above)

· dh.pem (generated above)

· ta.key (generated above)

· client_connect.sh (the main address pool and security group mapping file, described below)

· client_disconnect.sh (terminate connection and clear database of used IP addresses)

· ldap.conf (AD authentication config in OpenVPN)

File client_connect.sh

The file contains the main script for mapping AD security groups and subnets. It is the one that will assign the correct address to the client. Before using it, you need to add a list of groups and used address pools to the file in the section at the beginning. In our example, there are three pools: 10.10.10, 10.10.20, and 10.10.30. Remember that the pools must fall within the common range configured in the main OpenVPN server configuration file (server 10.10.0.0 255.255.0.0).

1. Create a file:

sudo nano /etc/openvpn/server/client_connect.sh

2. Insert the contents:

#!/bin/sh

################################################## ####

#Microsoft AD Security Group name|subnet|type

grouplist=”OpenVPN_sg1_users|10.10.10|sg1

OpenVPN_sg2_PowerUsers|10.10.20|sg2

OpenVPN_sg3_admins|10.10.30|sg3″

################################################## ###

The file with occupied addresses is stored here:

reservedipfile=/tmp/reserved_ip.txt

echo “CLIENT CONNECTED”

echo “DEBUG: 0=$0 1=$1 2=$2 common_name=$common_name username=$username TempIP=$ifconfig_pool_remote_ip”

3. We check that the name entered by the user (Login) and the certificate (common name) match.

if [ ! “$username” = “$common_name” ]; then

echo “Access deny. Username (LDAP) is not the same as common name (Cetrificate)”

return 1

fi

grp=`ldapsearch -x -H ldaps://hatter2.demo.land -D “sp_openvpn” -w P@ssword -b “DC=demo,DC=land” “(sAMAccountName=$username)” | grep “memberOf\|pager”`

username=`echo $username | grep ^[a-zA-Z0-9_\.\-]*$`

for a in $grouplist

do

groupname=`echo $a | cut -d '|' -f 1`

groupsubnet=`echo $a | cut -d '|' -f 2`

grouptype=`echo $a | cut -d '|' -f 3`

isingroup=`echo $grp | grep $groupname`

if [ ! -z “$isingroup” ]

then

echo “User is a member of $groupname”

fi

for b in `seq 2 254`

do

if [ -z “`grep ${groupsubnet}.${b} $reservedipfile`” ]

then

oc4=$b

break

fi

done

echo ifconfig-push ${groupsubnet}.${oc4} 255.255.0.0> $1

echo ${groupsubnet}.${oc4} >> $reservedipfile

return 0

done

return 1

4. A file for storing occupied IP addresses must be created:

sudo touch /tmp/reserved_ip.txt

5. It would be a good idea to clear the file when rebooting the OpenVPN server. The config is not provided in this example.

ldapsearch

The ldapsearch utility (installed in the step above) is used to check security group membership in the client_connect.sh file. It will be called from the script, but it is worth checking its operation and configuring LDAPS first.

1. Set the TLS_CACERT parameter in the /etc/ldap/ldap.conf file /etc/openvpn/server/root-ca.crt. This will allow you to connect to the domain controller using the secure ldapS method.

sudo nano /etc/ldap/ldap.conf

#TLS_CACERT /etc/ssl/certs/ca-certificates.crt

TLS_CACERT /etc/openvpn/server/root-ca.crt

2. Make sure the command works and we get a response:

ldapsearch -x -H ldaps://hatter2.demo.land -D “sp_openvpn” -w P@ssword -b “DC=demo,DC=land” “(sAMAccountName=alice)”

In my case:

· hatter2.demo.land – domain controller

· sp_openvpn – service account with rights to read the list of domain users + password.

· DC=demo,DC=land – limitation for searching accounts

· alice – an example of a domain account whose parameters we display.

In the future script (and response) we will be interested in the memberOf parameter (MS AD Security Group).

File client_disconnect.sh

After disconnecting the user, the IP address must be released. This script does this cleaning.

1. Create a file:

sudo nano /etc/openvpn/server/client_disconnect.sh

2. Insert the contents:

#!/bin/sh

#Select a location and create a file for the address database:

reservedipfile=/tmp/reserved_ip.txt

echo “CLIENT DISCONNECTED”

echo “0=$0 1=$1 2=$2 common_name=$common_name username=$username TempIP=$ifconfig_pool_remote_ip”

echo “Removing $ifconfig_pool_remote_ip from $reservedipfile”

sed -i “/${ifconfig_pool_remote_ip}$/d” $reservedipfile

[ -z “`grep $ifconfig_pool_remote_ip $reservedipfile`” ] && echo “Success: User IP was removed from DB” || echo “Something went wrong”

3. Make the files executable:

sudo chmod +x /etc/openvpn/server/client_connect.sh

sudo chmod +x /etc/openvpn/server/client_disconnect.sh

4. Most likely, the format of the copied text copied from this site will not allow the last two scripts to run. Perform the conversion:

sudo dos2unix /etc/openvpn/server/client_disconnect.sh

sudo dos2unix /etc/openvpn/server/client_connect.sh

ldap.conf file

The file contains the openvpn-auth-ldap.so configuration (LDAP authentication). Do not confuse the file with /etc/ldap/ldap.conf – this is another file, it is described above.

sudo nano /etc/openvpn/server/ldap.conf

1. Insert the contents:

#Your domain controller address. When using LDAPS, you must use the name specified in the certificate on the server.:

URL ldap://hatter2.demo.land

#Service account + password with rights to read the list of users.

BindDN sp_openvpn

Password P@ssword

Timeout 5

TLSEnable yes

TLSCACertFile /etc/openvpn/server/root-ca.crt

#Path and filter for searching users in the domain:

BaseDN “DC=demo,DC=land”

SearchFilter “(&(objectClass=user)(sAMAccountName=%u))”

2. When all files are copied and edited, launch the service.

sudo systemctl start openvpn-server@example-simple

3. Make sure the service is running:

sudo systemctl status openvpn-server@example-simple

4. Turn on the service:

sudo systemctl enable openvpn-server@example-simple

Audit

For event auditing, my Linux version uses the /var/log/syslog file. View:

sudo tail -f /var/log/syslog

The audit detail can be increased by changing the value of verb 3 in the main configuration file example-simple.conf, for example, to 9. Perhaps this will help to find the problem.

By the way, you can start the OpenVPN server manually. Go to the OpenVPN config folder, start it and look closely at the errors online:

cd /etc/openvpn/server/

sudo openvpn –config ./example-simple.conf

The server is ready, let's move on to configuring clients. Don't forget to save/copy the contents of the files:

sudo cat /etc/openvpn/server/ta.key

sudo cat /etc/openvpn/server/root-ca.crt

We will need this content when generating the client configuration file (profile).

Client

Download the OpenVPN client:

https://openvpn.net/community-downloads/

In my case OpenVPN 2.6.12 and OpenSSL 3.3.1.

Client configuration:

The following configuration file must be substituted for all domain clients. The file is the same for all, without personal user parameters. The certificate will be taken from the user's certificate store, and the password will be requested upon login.

client

dev tun

proto tcp

# Your server address

remote 51.250.68.123 443

resolve-retry infinite

nobind

remote-cert-tls-server

auth-user-pass

persist-key

persist-tun

# cryptoapicert and ISSUER tell the client program which certificate to choose for authentication. In our case, the root server name (Common Name) is trivially simple.

cryptoapicert “ISSUER:CA”

auth-nocache

AES-256-GCM cipher

auth SHA256

verb 3

—–BEGIN CERTIFICATE—–

there should be a root certificate in this place, we copied it to the server named /etc/openvpn/server/root-ca.crt. Any server signed by this root CA and having the correct EKU will be considered trusted by our clients. —

—–END CERTIFICATE—–

—–BEGIN OpenVPN Static key V1—–

there should be a key in this place, we copied it to the server named /etc/openvpn/server/ta.key. The key is the same for all clients, the secrecy is low. The key is an additional protection, including protection against DDoS attacks. —

—–END OpenVPN Static key V1—–

Client certificate

Hopefully it's obvious: the client certificate must be obtained before connecting to the VPN, while the client is online and “sees” the domain controller and PKI server.

The OpenVPN server does not require any special fields or special preparation of the user certificate. Moreover, the client does not even check the CRL of its certificate and the server certificate. In fact, it only checks that the certificate was issued by the “correct issuer”. In our example, we additionally check that the client certificate has EKU Client Authentication, and the server certificate has EKU Server Authentication (the remote-cert-tls config option). Without this directive, any client with a valid certificate can become a server and perform a Men-in-the-Middle attack. And, of course, remember that it is not worth issuing certificates with EKU Server Authentication to everyone and for a long time.

If the user does not have a suitable certificate, they will see the following message:

In this case, the certificate must be installed, and the Microsoft infrastructure has a convenient solution that allows you to automate this process as much as possible – PKI and AD CS.

Preparing a User Certificate Template

The template can be made based on any user template, for example, “User”.

Determine the validity period of the certificate at your discretion. Do not make long certificates. It is safer to enable automatic reissue and issue certificates as needed.

The certificate must contain this field, otherwise the server will not allow the user to connect:

Please note 2 important parameters:

  • If you allow “key export”, the certificate can be copied by the user to any other computer. Whether or not to use this option depends on your organization's policies. In my opinion, the ban should be set for corporate devices. You can further enhance protection by placing the certificate in the TRM module (this is not described in the article, but is highly recommended). If you need to issue an exportable certificate (for non-domain users), simply create another template that is accessible only to PKI administrators.

  • Make sure that the “subject” field is filled in automatically. You cannot allow the user to fill in this field manually.

Let's not complicate the configuration – in my example All Domain users will be able to request a certificate manually or via “auto-issue” when logging into the domain:

For “autoenrollment” to work, the GPO policy for users “Certificate Services Client – Auto-Enrollment” must be additionally configured. Here describes in detail how to do this. The following figure shows an example of a configuration for forming the “Subject” field in the certificate. The field will be the user identifier – on the server in the logs you will be able to determine which user established the connection.

Only the Common name (CN) is used in OpenVPN server checks, the full name is not required. There is no need to fill in alternative names, moreover, for example, the absence of the e-mail field in the user profile will cause an error in issuing the certificate.

If you have configured your PKI correctly, each user should have a certificate issued using your template:

The image shows the name from the certificate (common name), which must match the domain name. In the client_connect.sh script, we additionally check this condition.

Disclaimer:

The given PKI config is not the only correct and comprehensive one. Usually, there are already enough experts in the field of AD + PKI working in the corporate environment and the given information is enough for them to prepare the correct templates. The public key infrastructure from Microsoft is quite compatible with “non-domain” machines, but these tools are not described in the article. Perhaps it is worth devoting a separate article to this topic if there are enough reviews.

Instead of a conclusion

This article discusses a connection option to VPN for a domain computer and user. The solution in this configuration allows you to reduce the load on VPN support by using the Microsoft ecosystem, without reducing security. Moreover, you can connect non-domain computers. You can organize the issuance and export of a certificate using portals and any other tools available in the Microsoft ecosystem.

The presented configuration is not the only correct one, you can upgrade and mix different parameter options yourself to achieve the goals you set.

Next you will have to fight with MTU and routing, but that's a completely different story.

Whether the tiger turned out or not is up to you to decide, despite all the obvious and non-obvious advantages, the presented solution has its drawbacks. Here I tried to collect them in one list. Your versions can also be included here.

  • For better protection against DDoS, OpenVPN recommends using the UDP protocol. TCP is convenient in case of organizing a fault-tolerant active-active cluster. In addition, TCP/443 is blocked by external providers less often than other protocols.

  • The password for the ldap service account is stored in the configuration files

  • The client can save the ldap password on his computer. It is necessary to solve it by organizational measures.

  • The OpenVPN application runs with super-user (root) rights. It is recommended to run the service as user nobody, group nobody. In this article, root access is provided to simplify the configuration.

The OpenVPN application runs with super-user (root) rights. It is recommended to run the service as user nobody, group nobody. In this article, root access is provided to simplify the configuration.

  • CRL checking is not enabled. The mechanism is implemented inconveniently and is essentially useless in our scheme. The revocation lists must be loaded as an OpenVPN config file and the server must be rebooted. The server itself does not “go” to check whether the certificate has been revoked using the CLR link from the certificate. We can “sacrifice” this function, since in addition to the certificate we have working integration with ldap. If the user is “blocked” in the domain, even despite the valid certificate, he will not get access to the VPN.

  • Address pools associated with security groups must be part of the server's general address pool. This reduces the efficiency of address space usage.

  • There is no possibility to implement conditional access.

  • There is no way to implement an assessment of the user's computer state before logging in and during work.

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *