by Daniel Marschall, 31 January 2022
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.
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!
Here is a diagram of the desired setup of our example:
(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.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".
[ 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
@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.
@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.
@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.
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 ---------------------------------------------
@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.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.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.1) Give the user their .ovpn file.
(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.
(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"
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.
If just an employee client certificate needs to be revoked, then run:
revoke_user.bat
Create a temporary batch file, e.g. create_employee.bat with following contents and then run it.
add_user.bat
If you have any questions or improvement suggestions, please send me an email to info at daniel-marschall dot de