Tutorial: Installation of an OpenVPN server on a Windows Server

by Daniel Marschall, 31 January 2022

Example environment

In this example, we are having following environment:

Name of the company Contoso Ltd.
Country and city USA (US), Washington D.C.
Public Hostname/IP vpn.example.com
Company network 192.168.20.0/24
Server IP 192.168.20.10
Router IP 192.168.20.1
DNS Server 192.168.20.10
Windows Domain CONTOSO.local
Network for VPN clients 192.168.30.0/24
Server directory of VPN-stuff C:\Data\VPN
OpenVPN-Port 1194
OpenVPN-Protocol UDP
Location of OpenSSL C:\OpenSSL-Win32\
Location of OpenVPN C:\Program Files\OpenVPN\

Please make sure you adjust all the references to these values (marked yellow) when you follow this tutorial.

This tutorial was tested with a Windows Server 2012 with Windows 7 and macOS clients and a Telekom Digitalisierungsbox as router.

Security

Since many tutorials on the Internet show deprecated and outdated configuration settings, it was important to me to use only new and secure parameters. For example, tls-auth was used previous to OpenVPN 2.4, but tls-crypt is more secure and has more advantages. Also, I am not using compression, since it is also considered insecure today.

If you want even higher security, you could also try to Multi-Factor Authentication plugins that can be added once everything is configured and working properly.

Important: This tutorial was written to my best knowledge and belief, but I am not reliable for any damage that might occour by following this tutorial!

Setup step by step

Overview

Here is a diagram of the desired setup of our example:

1. Opening the port

(1.1) You need to make sure that you register at a dynamic DNS service so that your server can be reached via a permanent host name (e.g. vpn.example.com). Alternatively, you can ask your ISP if you can receive a static IP, so that your system is always reachable via the same IP address.

(1.2) Open the UDP-Port 1194 in the Windows Firewall

(1.3) in case your server is behind a router, then also forward this port in your router to your server.

2. Install software

(2.1) Create the directory C:\Data\VPN on your server.

(2.2) At your server, download and install OpenSSL into directory C:\OpenSSL-Win32\

(2.3) Also, download install OpenVPN Community on your server at C:\Program Files\OpenVPN\ and make sure you select the installation option "OpenVPN Service".

3. Create the scripts to manage everything

C:\Data\VPN\openssl.cnf

[ ca ]
default_ca = CA_vpn

[ CA_vpn ]
dir = ".\\CA"
certs = $dir
crl_dir = $dir
crlnumber = $dir\\crlnumber.txt
database = $dir\\index.txt
new_certs_dir = $certs
certificate = $dir\\CA.cer
serial = $dir\\serial.txt
crl = $dir\\CA.crl
private_key = $dir\\CA.key
RANDFILE = $dir\\.rand
default_days = 3650
default_crl_days = 365
default_md = sha256
policy = vpn_policy

[ vpn_policy ]
countryName = optional
localityName = optional
organizationName = match
commonName = supplied

[ req ]
default_bits = 4096
distinguished_name = req_vpn_dn

[ req_vpn_dn ]
countryName = Country name
localityName = Locality (e.g. city)
organizationName = Organization Name (e.g. company)
commonName = Common Name (eg. Linux machine)
commonName_max = 64

# --- These sections with be called by the batch file:

[ v3_vpn_ca ]
basicConstraints = critical, CA:TRUE
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always, issuer:always
keyUsage = critical, cRLSign, digitalSignature, keyCertSign

[ v3_vpn_server ]
basicConstraints = critical, CA:FALSE
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always, issuer:always
keyUsage = critical, nonRepudiation, digitalSignature, keyEncipherment, keyAgreement
extendedKeyUsage = critical, serverAuth

[ v3_vpn_client ]
basicConstraints = critical, CA:FALSE
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always, issuer:always
keyUsage = critical, nonRepudiation, digitalSignature, keyEncipherment
extendedKeyUsage = critical, clientAuth

C:\Data\VPN\build-ca.bat

@echo off

cd /d %~dp0
call config.bat

if "%COMPANY%" == "Contoso Ltd." (
	echo STOP! Please edit config.bat first!
	pause.
	exit
)

if exist "CA" (
	echo ATTENTION! DO YOU REALLY WANT TO RESET THE WHOLE CERTIFICATION AUTHORITY?
	echo Yes: Press any key, otherwise close the window
	pause.
)

@echo on
echo Preparing...
if exist "CA" (
	del /q CA\*.*
) else (
	mkdir CA
)
echo | set /p dummyName= > CA\index.txt
echo unique_subject = no> CA\index.txt.attr
echo 01> CA\serial.txt
echo 1> CA\crlnumber.txt

echo Create CA Cert...
if "%CRYPTO:~0,3%" == "rsa" (
	"%OpenSSLPath%openssl.exe" genrsa -out CA\CA.key %CRYPTO:~3%"
) else (
	"%OpenSSLPath%openssl.exe" ecparam -name %CRYPTO% -genkey -noout -out CA\CA.key
)
"%OpenSSLPath%openssl.exe" req -new -x509 -nodes -key CA\CA.key -out CA\CA.cer -days 3650 -batch -subj "/C=%COUNTRY%/L=%LOCALITY%/O=%COMPANY%/CN=VPN CA" -config openssl.cnf -extensions v3_vpn_ca

echo Create Server Cert...
if "%CRYPTO:~0,3%" == "rsa" (
	"%OpenSSLPath%openssl.exe" genrsa -out CA\Server.key %CRYPTO:~3%"
) else (
	"%OpenSSLPath%openssl.exe" ecparam -name %CRYPTO% -genkey -noout -out CA\Server.key
)
"%OpenSSLPath%openssl.exe" req -new -nodes -key CA\Server.key -out CA\Server.req -batch -subj "/C=%COUNTRY%/L=%LOCALITY%/O=%COMPANY%/CN=VPN Server" -config openssl.cnf -extensions v3_vpn_server
"%OpenSSLPath%openssl.exe" ca -in CA\Server.req -batch -out CA\Server.cer -config openssl.cnf -extensions v3_vpn_server
del /q CA\Server.req

echo Generate CRL...
"%OpenSSLPath%openssl.exe" ca -gencrl -out CA\CA.crl -config openssl.cnf

echo Create DH params...
"%OpenSSLPath%openssl.exe" dhparam -out "CA\dh_params.pem" %DH_PARAMS_BITS%

echo Create OpenVPN shared secret...
"%OpenVPNPath%openvpn.exe" --genkey secret "CA\OpenVPN_Shared_Secret.key"

echo All done! Please check the output.
pause.

C:\Data\VPN\add_user.bat

@echo off

cd /d %~dp0
call config.bat

if "%COMPANY%" == "Contoso Ltd." (
	echo STOP! Please edit config.bat first!
	pause.
	exit
)

if not exist "CA" (
	echo CA is not yet created. Please create it first.
	pause.
	exit
)

:entername
set /p EMPLOYEE=Enter the name of the new employee/user: 
if "%EMPLOYEE%" == "" goto entername

if exist "CA\%EMPLOYEE%Client.key" (
	echo ERROR: User already has a certificate.
	echo If you want to give the user a new certificate, please
	echo revoke the previous first.
	pause.
	exit
)

echo Create %EMPLOYEE% Client Cert...
@echo on
if "%CRYPTO:~0,3%" == "rsa" (
	"%OpenSSLPath%openssl.exe" genrsa -out "CA\%EMPLOYEE%Client.key" %CRYPTO:~3%"
) else (
	"%OpenSSLPath%openssl.exe" ecparam -name %CRYPTO% -genkey -noout -out "CA\%EMPLOYEE%Client.key"
)
"%OpenSSLPath%openssl.exe" req -new -nodes -key "CA\%EMPLOYEE%Client.key" -out "CA\%EMPLOYEE%Client.req" -batch -subj "/C=%COUNTRY%/L=%LOCALITY%/O=%COMPANY%/CN=%EMPLOYEE% VPN Client" -config openssl.cnf -extensions v3_vpn_client
"%OpenSSLPath%openssl.exe" ca -in "CA\%EMPLOYEE%Client.req" -batch -out "CA\%EMPLOYEE%Client.cer" -config openssl.cnf -extensions v3_vpn_client
del /q "CA\%EMPLOYEE%Client.req"
@echo off

echo Generate OVPN client configuration file...
>"%EMPLOYEE%.ovpn" (
	echo # Connect to the server %VPN_SERVER%
	echo client
	echo dev tun
	echo proto %VPN_PROTOCOL%
	echo remote %VPN_SERVER% %VPN_PORT%
	echo resolv-retry infinite
	echo nobind
	echo persist-key
	echo persist-tun
	echo.
	echo # For TUN only:
	echo windows-driver wintun
	echo topology subnet
	echo.
	echo # Crypto-stuff
	echo #ca "CA.cer"
	echo ^<ca^>
	type "CA\CA.cer"
	echo ^</ca^>
	echo #cert "%EMPLOYEE%Client.cer"
	echo ^<cert^>
	type "CA\%EMPLOYEE%Client.cer"
	echo ^</cert^>
	echo #key "%EMPLOYEE%Client.key"
	echo ^<key^>
	type "CA\%EMPLOYEE%Client.key"
	echo ^</key^>
	echo cipher AES-256-GCM
	echo.
	echo # Additional security settings
	echo remote-cert-tls server
	echo tls-version-min 1.2
	echo auth SHA256
	echo auth-nocache
	echo #tls-auth "OpenVPN_Shared_Secret.key" 1
	echo #tls-crypt "OpenVPN_Shared_Secret.key"
	echo ^<tls-crypt^>
	type "CA\OpenVPN_Shared_Secret.key"
	echo ^</tls-crypt^>
	echo verify-x509-name "C=%COUNTRY%, L=%LOCALITY%, O=%COMPANY%, CN=VPN Server" subject
	echo #tls-cipher TLS-ECDHE-ECDSA-WITH-AES-256-GCM-SHA384
	echo.
	echo # Logging
	echo verb 3
)

echo.
echo All done! Please check the output.
echo.
echo Please send following file to the user:
echo %EMPLOYEE%.ovpn
pause.

C:\Data\VPN\revoke_user.bat

@echo off

cd /d %~dp0
call config.bat

if "%COMPANY%" == "Contoso Ltd." (
	echo STOP! Please edit config.bat first!
	pause.
	exit
)

if not exist "CA" (
	echo CA is not yet created. Please create it first.
	pause.
	exit
)

:entername
set /p EMPLOYEE=Enter the name of the employee/user to be deleted: 
if "%EMPLOYEE%" == "" goto entername

if not exist "CA\%EMPLOYEE%Client.key" (
	echo ERROR: User has no certificate. Cannot revoke.
	pause.
	exit
)

echo Revoke %EMPLOYEE% Client Cert...
@echo on
"%OpenSSLPath%openssl.exe" ca -config openssl.cnf -revoke "CA\%EMPLOYEE%Client.cer"
if errorlevel 1 goto fail
del /q "CA\%EMPLOYEE%Client.*"
del /q "%EMPLOYEE%.ovpn"
"%OpenSSLPath%openssl.exe" ca -config openssl.cnf -gencrl -out "CA\CA.crl"
if errorlevel 1 goto fail
@echo off

echo All done!
pause.
exit

:fail
echo Something failed! Please check the output above.
pause.

C:\Data\VPN\config.bat

rem ---------------------------------------------
set OpenSSLPath=C:\OpenSSL-Win32\bin\
set OpenVPNPath=C:\Program Files\OpenVPN\bin\
set COUNTRY=US
set LOCALITY=Washington D.C.
set COMPANY=Contoso Ltd.
set VPN_SERVER=vpn.example.com
set VPN_PROTOCOL=udp
set VPN_PORT=1194
rem Examples: CRYPTO=rsa4096 (for RSA) or CRYPTO=secp521r1 (for ECC)
set CRYPTO=secp521r1
set DH_PARAMS_BITS=2048
rem ---------------------------------------------

C:\Data\VPN\renew_crl.bat

@echo off

cd /d %~dp0
call config.bat

if not exist "CA" (
	echo CA is not yet created. Please create it first.
	pause.
	exit
)

echo Renew CRL...
@echo on
"%OpenSSLPath%openssl.exe" ca -config openssl.cnf -gencrl -out "CA\CA.crl"
if errorlevel 1 goto fail
@echo off

echo All done!
rem Commented out "pause" so that it does work with Task Scheduler
rem pause.
exit

:fail
echo Something failed! Please check the output above.
pause.

Using the Windows Task Scheduler, run renew_ctl.bat every month (because the CRL expires after 1 year).

4. Setup the Certification Authority (CA) and the certificates

(4.1) Run build_ca.bat.

A directory called "CA" will be created with certificates and keys. Please note that the .key files are top secret. Each client may only receive their own key and the shared secret key (they are included in the .ovpn file). Nobody may receive the CA.key file, as this is the "master key" to generate any client certificate!

(4.2) For each user you want to add, run add_user.bat.

5. Configure and start OpenVPN at the server

(5.1) Create the file C:\Program Data\OpenVPN\config\server.ovpn with following contents:

# Port 1194/UDP needs to be forwarded in the router and the Windows Firewall
dev tun
port 1194
proto udp
keepalive 10 120
persist-key
persist-tun

# For TUN only:
windows-driver wintun
topology subnet

# Our local network is 192.168.20.0/24, and the desired pool for VPN clients is 192.168.30.0/24
local 192.168.20.10
server 192.168.30.0 255.255.255.0
route 192.168.20.0 255.255.255.0
route-gateway 192.168.20.1
push "route 192.168.20.0 255.255.255.0"

# Redirect DNS
push "dhcp-option DOMAIN CONTOSO.local"
push "dhcp-option DNS 192.168.20.10"

# Optional: Route all traffic through VPN
#setenv opt block-outside-dns
#push "redirect-gateway def1 bypass-dhcp"

# Optional: Should the clients "see" each other?
#client-to-client

# Crypto-stuff
ca "C:\\Data\\VPN\\CA\\CA.cer"
cert "C:\\Data\\VPN\\CA\\Server.cer"
key "C:\\Data\\VPN\\CA\\Server.key"
dh "C:\\Data\\VPN\\CA\\dh_params.pem"
cipher AES-256-GCM

# Additional security settings
crl-verify "C:\\Data\\VPN\\CA\\CA.crl"
remote-cert-tls client
tls-version-min 1.2
auth SHA256
#tls-auth "C:\\Data\\VPN\\CA\\OpenVPN_Shared_Secret.key" 0
tls-crypt "C:\\Data\\VPN\\CA\\OpenVPN_Shared_Secret.key"
#ecdh-curve secp521r1
#tls-groups X25519:secp256r1:X448:secp521r1:secp384r1
#tls-cipher TLS-ECDHE-ECDSA-WITH-AES-256-GCM-SHA384
#ncp-ciphers AES-256-GCM:AES-256-CBC

# Logging
status "C:\\Program Files\\OpenVPN\\log\\openvpn-status.log"
verb 3

(5.2) Create C:\Data\VPN\enable_ip_forwarding.reg with following content:

Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters]
"IPEnableRouter"=dword:00000001

Then, run/import this REG file to enable IP forwarding.

(5.3) Reboot the server so that the IP forwarding gets activated.

(5.4) Enable Internet Connection Sharing (ICS): At the local network adapter (which can also be a NIC team or bridge), right click and select "Properties", then go to the tab "Sharing" and enable "Allow other network users to connect through this computer's Internet connection". Below, select the name of the virtual OpenVPN network adapter, e.g. OpenVPN TAP-Windows6

(5.5) Start the OpenVPN GUI and establish the connection.

6. Configure and start OpenVPN at the client

(6.1) Give the user their .ovpn file.

Windows clients

(6.2) Download and install OpenVPN Community.

(6.3) Store the received file in C:\Program Data\OpenVPN\config\

(6.4) Start the OpenVPN GUI and establish the connection.

macOS clients

(6.2) Download and install Tunnelblick.

(6.3) Store the received file somewhere and then drag'n'drop the .ovpn file in the left list of Tunnelblick so that it gets imported.

(6.4) At the icons at the right top (besides the clock), click the Tunnelblick icon, then click "connect"

Things to do after the system is running

What to do if the CA is compromised?

If the whole CA is compromised, e.g. if the server was hacked, if someone accidentally got access to the CA.key file, or if a distrusted administrator has left the company, then you need to do a "hard reset" of the CA. Therefore, run:

build-ca.bat

After this, you need to re-create all users by calling add_user.bat for each user.

You then need to give the updated .ovpn files to all other employees.

Revoke a single certificate / remove access for an employee

If just an employee client certificate needs to be revoked, then run:

revoke_user.bat

Add a new employee

Create a temporary batch file, e.g. create_employee.bat with following contents and then run it.

add_user.bat

Open questions and problems

  1. At the server, OpenVPN doesn't seem to start automatically. It only starts after you open the OpenVPN GUI.

  2. At the server there is following error message: Could not determine IPv4/IPv6 protocol. Using AF_INET . Does this mean dual-stack is not possible?

  3. If you use TAP to connect two subnets, the connection is very unstable and often fails. Clients cannot access devices on the other side, other than the server... I couldn't solve the issue, so I am just using TUN to connect to the server, and from there, I am using RDP to go to the other clients...

If you have any questions or improvement suggestions, please send me an email to info at daniel-marschall dot de