rss logo

How to Set Up an OpenVPN Server on Debian 13 Trixie

OpenVPN Logo

With the release of Debian 13 “Trixie”, it’s the perfect time to update my tutorial on setting up an OpenVPN server and discover what’s new in this version.

This is a hands-on tutorial for installing and configuring an OpenVPN server on Debian 13 Trixie.

Our goal is to transform the Debian server into a fully functional VPN gateway, giving remote Windows clients secure access to the entire network.

This setup is my preferred approach, as it provides seamless access to all internal company resources (see the Gateway Mode section for details).

OpenVPN Network Diagram

Windows client connected via OpenVPN to Debian 13 Trixie server, VPN gateway network diagram.
  • OpenVPN Server:
    • OS: Debian GNU/Linux 13 (Trixie)
    • Role: OpenVPN Server + Gateway
    • IP (wan): OPENVPN_IP
    • IP (lan): 192.168.0.254 and 10.50.8.1

OpenVPN Server Setup (Debian 13 Trixie)

Debian Logo

Installation

  • Install the OpenVPN package:
root@host:~# apt update && apt install openvpn
  • Enable automatic start of the OpenVPN service:
root@host:~# sed -i 's/#AUTOSTART="all"/AUTOSTART="all"/' /etc/default/openvpn
root@host:~# systemctl daemon-reload; systemctl restart openvpn

PKI

  • Navigate to the /etc/openvpn/ directory:
root@host:~# cd /etc/openvpn/
  • Initialize the PKI (Public Key Infrastructure):
root@host:~# /usr/share/easy-rsa/easyrsa init-pki
  • Type yes when prompted to initialize the PKI and confirm removal of the existing configuration:
WARNING!!!

You are about to remove the EASYRSA_PKI at:
* /etc/openvpn/pki

and initialize a fresh PKI here.

Type the word 'yes' to continue, or any other input to abort.
  Confirm removal: yes

         ******************************************
         * SECOND WARNING - STOP - SECOND WARNING *
         ******************************************

  To keep your current 'pki/vars' settings use 'init-pki soft'.
  To keep your current Request files use 'init-pki soft'
  The Requests can then be signed by a new CA (Partial CA renewal)
  To keep your current Easy-RSA TLS Key use 'init-pki soft'
  This private key file is in use by your current VPN.

       ** USE OF   'init-pki soft'   IS RECOMMENDED **


Type the word 'yes' to continue, or any other input to abort.
  
  WARNING: COMPLETELY DESTROY current PKI (NOT recommended) ?

    [yes/NO]: yes


Notice
------
'init-pki' complete; you may now create a CA or requests.

Your newly created PKI dir is:
* /etc/openvpn/pki

Using Easy-RSA configuration:
* undefined
  • If, like me, you don’t want to regenerate certificates frequently, create and edit the file /etc/openvpn/pki/vars:
root@host:~# cp -a /usr/share/easy-rsa/vars.example /etc/openvpn/pki/vars
# In how many days should the root CA key expire?
set_var EASYRSA_CA_EXPIRE       3650

# In how many days should certificates expire?
set_var EASYRSA_CERT_EXPIRE     1825

# How many days until the Certificate Revokation List will expire.
#
# IMPORTANT: When the CRL expires, an OpenVPN Server which uses a
# CRL will reject ALL new connections, until the CRL is replaced.
#
set_var EASYRSA_CRL_DAYS        1095

# Choose a size in bits for your keypairs. The recommended value is 2048.
# Using 2048-bit keys is considered more than sufficient for many years into
# the future. Larger keysizes will slow down TLS negotiation and make key/DH
# param generation take much longer. Values up to 4096 should be accepted by
# most software. Only used when the crypto alg is rsa, see below.
set_var EASYRSA_KEY_SIZE        4096
  • Once everything is set, create the Certificate Authority (CA) in /etc/openvpn/pki/ca.crt:
root@host:~# /usr/share/easy-rsa/easyrsa build-ca nopass
  • When prompted, enter a Common Name (CN) for your CA:
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Common Name (eg: your user, host, or server name) [Easy-RSA CA]:openvpn-server

Notice
------
CA creation complete. Your new CA certificate is at:
* /etc/openvpn/pki/ca.crt

Create an OpenVPN TLS-AUTH|TLS-CRYPT-V1 key now: See 'help gen-tls'

Build-ca completed successfully.

Generate the OpenVPN Server Certificate, Private Key, and Diffie-Hellman Parameters

  • Create the OpenVPN server certificate and its private key:
root@host:~# /usr/share/easy-rsa/easyrsa build-server-full server nopass
Using Easy-RSA 'vars' configuration:
* /etc/openvpn/pki/vars
[…]
-----

Notice
------
Private-Key and Public-Certificate-Request files created.
Your files are:
* req: /etc/openvpn/pki/reqs/server.req
* key: /etc/openvpn/pki/private/server.key

You are about to sign the following certificate:

  Requested CN:     'server'
  Requested type:   'server'
  Valid for:        '1825' days


subject=
    commonName                = server

Type the word 'yes' to continue, or any other input to abort.
  Confirm requested details: yes

Using configuration from /etc/openvpn/pki/9934d973/temp.6.1
Check that the request matches the signature
Signature ok
The Subject's Distinguished Name is as follows
commonName            :ASN.1 12:'server'
Certificate is to be certified until Aug 14 13:00:21 2030 GMT (1825 days)

Write out database with 1 new entries
Database updated

Notice
------
Inline file created:
* /etc/openvpn/pki/inline/private/server.inline


Notice
------
Certificate created at:
* /etc/openvpn/pki/issued/server.crt

💡 Note: The line Valid for: '1825' days confirms that the modification made in the /etc/openvpn/pki/vars file has been applied.

  • Generate the Diffie–Hellman (DH) parameters and save them to /etc/openvpn/pki/dh.pem:
root@host:~# /usr/share/easy-rsa/easyrsa gen-dh
Using Easy-RSA 'vars' configuration:
* /etc/openvpn/pki/vars
Generating DH parameters, 4096 bit long safe prime
[…]
DH parameters appear to be ok.

Notice
------

DH parameters of size 4096 created at:
* /etc/openvpn/pki/dh.pem

Generate OpenVPN Client Certificates and Keys

💡 Note: Clients' private keys are stored in /etc/openvpn/pki/private/ and issued certificates in /etc/openvpn/pki/issued/.

  • Generate the client01 certificate and private key:
root@host:~# /usr/share/easy-rsa/easyrsa build-client-full client01 nopass
Using Easy-RSA 'vars' configuration:
* /etc/openvpn/pki/vars
[…]
-----

Notice
------
Private-Key and Public-Certificate-Request files created.
Your files are:
* req: /etc/openvpn/pki/reqs/client01.req
* key: /etc/openvpn/pki/private/client01.key

You are about to sign the following certificate:

  Requested CN:     'client01'
  Requested type:   'client'
  Valid for:        '1825' days


subject=
    commonName                = client01

Type the word 'yes' to continue, or any other input to abort.
  Confirm requested details: yes

Using configuration from /etc/openvpn/pki/7e41ac10/temp.6.1
Check that the request matches the signature
Signature ok
The Subject's Distinguished Name is as follows
commonName            :ASN.1 12:'client01'
Certificate is to be certified until Aug 14 13:09:38 2030 GMT (1825 days)

Write out database with 1 new entries
Database updated

Notice
------
Inline file created:
* /etc/openvpn/pki/inline/private/client01.inline


Notice
------
Certificate created at:
* /etc/openvpn/pki/issued/client01.crt

After running the above command, the client01 certificate and private key will be generated in the following locations:

Client Certificate Path Private Key Path
client01 /etc/openvpn/pki/issued/client01.crt /etc/openvpn/pki/private/client01.key
  • To generate 10 client certificates and private keys (client01 to client10) in a single command:
root@host:~# for i in $(seq -w 1 10);do /usr/share/easy-rsa/easyrsa build-client-full client"$i" nopass; done

OpenVPN Server Configuration – server.conf

  • Create and edit the OpenVPN server configuration file at /etc/openvpn/server.conf:
port 1194
proto udp
dev tun

ca /etc/openvpn/pki/ca.crt
cert /etc/openvpn/pki/issued/server.crt
key /etc/openvpn/pki/private/server.key # the server.key private key must be kept secret
dh /etc/openvpn/pki/dh.pem

# internal tun0 connection IP
server 10.50.8.0 255.255.255.0

ifconfig-pool-persist ipp.txt

keepalive 10 120

# Compression - must now be turned off for security reasonfor security reasons.
# Use compress stub-v2 if needed in place
#comp-lzo

persist-key
persist-tun

# parameters to be adjusted according to your network configuration
push "dhcp-option DNS 192.168.0.200"
push "dhcp-option DOMAIN std.local"
push "route 192.168.0.0 255.255.255.0"

status /var/log/openvpn-status.log

# verbose mode
verb 3

Manage the OpenVPN Server Service with Systemd

  • Enable the OpenVPN server service so that it starts automatically at boot:
root@host:~# systemctl enable openvpn@server.service
  • Restart the OpenVPN server service to apply the changes:
root@host:~# systemctl restart openvpn@server.service

Gateway Mode

Gateway mode allows VPN clients to access the 192.168.0.0/24 internal network from the client side.

Configuring nftables

A few netfilter rules will allow VPN clients to access the entire network through the VPN tunnel.

Identify Network Interfaces

  • List all network interfaces on the server and identify the one connected to the local network (LAN):
root@host:~# ip addr sh
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
2: ens192: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
    link/ether 01:02:a0:21:fd:54 brd ff:ff:ff:ff:ff:ff
    inet OPENVPN_IP brd X.X.X.X scope global wan
       valid_lft forever preferred_lft forever
    inet6 fe80::ff:fe5d:f333/64 scope link 
       valid_lft forever preferred_lft forever
3: enp2s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
    link/ether 11:a2:a9:21:fd:54 brd ff:ff:ff:ff:ff:ff
    inet 192.168.0.254 brd X.X.X.X scope global wan
       valid_lft forever preferred_lft forever
    inet6 fe80::6a05:caff:fe39:c153/64 scope link 
       valid_lft forever preferred_lft forever
4: tun0: <POINTOPOINT,MULTICAST,NOARP,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UNKNOWN group default qlen 500
    link/none 
    inet 10.50.8.1 peer 10.50.8.2/32 scope global tun0
       valid_lft forever preferred_lft forever
    inet6 fe80::7ea2:577f:e834:7a20/64 scope link stable-privacy 
       valid_lft forever preferred_lft forever

Temporary Rules

  • Add masquerade rules to make the internal network accessible to Windows VPN clients:
root@host:~# nft add table ip NAT
root@host:~# nft add chain ip NAT my_masquerade '{ type nat hook postrouting priority 100; }'
root@host:~# nft add rule NAT my_masquerade ip saddr { 10.50.8.0/24 } oifname enp2s0 masquerade

Persistent Rules

  • To make the nat configuration persistent, edit the /etc/nftables.conf file:
#!/usr/sbin/nft -f

flush ruleset

table inet filter {
        chain input {
                type filter hook input priority 0;
        }
        chain forward {
                type filter hook forward priority 0;
        }
        chain output {
                type filter hook output priority 0;
        }
}
table ip NAT {
        chain my_masquerade {
                type nat hook postrouting priority 100; policy accept;
                ip saddr { 10.50.8.0/24 } oifname "enp2s0" masquerade comment "outgoing NAT"
        }
}
  • Enable the nftables service so that it starts automatically at boot:
root@host:~# systemctl enable nftables.service

Enable IP Forwarding for Gateway Mode

  • Edit the /etc/sysctl.conf file and add the following line:
net.ipv4.ip_forward=1
  • Apply the change immediately with the following command:
root@host:~# sysctl -p /etc/sysctl.conf

OpenVPN with a Single Network Interface (Not Default Gateway)

OpenVPN setup with a single interface, not the default gateway.

Sometimes it’s not possible to install OpenVPN directly on the router. In such cases, you can set up the OpenVPN server as a virtual machine or deploy it on a dedicated server within the internal LAN.

Create a port forwarding rule on the WAN router to redirect OpenVPN traffic (step 1 in the diagram) to the OpenVPN server (step 2).

The rest of the configuration remains the same: enable gateway mode and create a NAT rule so that the client can reach the entire LAN 192.168.0.0/24 (step 3).

In short, apply the same rules as described earlier.

Windows Client Configuration

Microsoft Logo

First, download and install the OpenVPN Community Edition for Windows from the official website: OpenVPN Community Edition for Windows.

  • Copy the following files from the Debian server to the Windows client:
    • ca.crt/etc/openvpn/pki/ca.crt
    • client01.crt/etc/openvpn/pki/issued/client01.crt
    • client01.key/etc/openvpn/pki/private/client01.key
    Place them in one of these directories:
    • C:\Program Files\OpenVPN\config
    • C:\Users\user\OpenVPN\config\ (recommended)
  • After copying, you should see the following client files on your Windows machine:
Windows OpenVPN config folder showing ca.crt, client01.crt, and client01.key files.
  • Create the file client.ovpn in the same directory as the above files:
client

dev tun

proto udp

remote OPENVPN_IP 1194

resolv-retry infinite
nobind
persist-key
persist-tun

ca ca.crt
cert client01.crt
key client01.key

# disabled for security, use compress stub-v2 if needed in place
#comp-lzo

verb 3
  • Open OpenVPN and click on connect:
OpenVPN Windows client system tray menu showing the Connect option.
  • If OpenVPN is launched for the first time by a user without administrator rights, this pop-up window will appear. Click Yes and enter the administrator credentials to add the current user to the OpenVPN Administrators group:
OpenVPN Windows prompt asking to join the OpenVPN Administrators group before connecting.
  • This confirmation pop-up should appear, and the OpenVPN icon will turn green, indicating that you are successfully connected to the server.
OpenVPN Windows notification showing the client is connected with assigned IP 10.50.8.6.

💡 Note: Now that you have a fully functional VPN, I recommend improving its security by adding an HMAC ta.key. See OpenVPN Security Improvements for details.