Removing users
Users leave, laptops get stolen, and configuration files get emailed to the wrong person, so removing access cleanly is as important as granting it. OpenVPN gives you two layers.
tls-crypt: strangers get nothing
Someone with a stolen or old certificate but without the current tls-crypt key gets no response at all. The server discards every packet before TLS even starts (tls-crypt unwrap error: packet authentication failed), and the client just times out. From outside, the server looks like nothing is listening. If the tls-crypt key itself leaks, generate a new one and reissue every user's file, which is why it isn't a substitute for the next layer.
Revoking one user
To remove bob: easyrsa revoke bob, easyrsa gen-crl, and copy the new crl.pem into place. The lab shows three things worth knowing:
- Revoking doesn't disconnect anyone. bob's existing session kept working. OpenVPN checks the CRL when a user connects (and when the session renegotiates, hourly by default), so restart the service, or disconnect the user, to make it take effect now.
- After that, bob is refused: VERIFY ERROR: … certificate revoked.
- The CRL file must be readable by
nobody. OpenVPN read the first CRL as root at start-up. But when the file changed later, OpenVPN, by then running asnobody, couldn't read the root-only copy, and refused every new connection, alice's included: CRL: cannot read: crl.pem. A CRL is public information; make it world-readable (chmod 644).
The CRL expires
The CRL easy-rsa generates is valid for 180 days. When it expires, OpenVPN rejects all new connections, and easy-rsa's own output warns about this. Regenerate and reinstall it on a schedule, well before nextUpdate, for example monthly from cron or a systemd timer, even when nobody has been revoked.
root@bob:~# openvpn --config intruder.ovpn --daemon --log /var/log/intruder.log && sleep 70 && grep -E "TLS|Initialization" /var/log/intruder.log | tail -4
2026-10-21 05:22:14 TLS Error: TLS key negotiation failed to occur within 60 seconds (check your network connectivity)
2026-10-21 05:22:14 TLS Error: TLS handshake failed
root@vpn:~# journalctl -u openvpn-server@server --no-pager -o cat --since "-75s" | grep -iE "tls-crypt|unwrap|198.51.100.22" | head -4
tls-crypt unwrap error: packet authentication failed
TLS Error: tls-crypt unwrapping failed from [AF_INET]198.51.100.22:37107
tls-crypt unwrap error: packet authentication failed
TLS Error: tls-crypt unwrapping failed from [AF_INET]198.51.100.22:37107- Lines 2–3: The client just times out after 60 seconds: the server never answered.
- Lines 6–9: The server discarded every packet before any TLS happened.
root@vpn:~/easy-rsa# ./easyrsa --batch revoke bob
Using Easy-RSA 'vars' configuration:
* /root/easy-rsa/vars
WARNING
=======
This process is destructive!
These files will be MOVED to the 'revoked' sub-directory:
* /root/easy-rsa/pki/issued/bob.crt
These files will be DELETED:
All PKCS files for commonName: bob
The inline credentials files:
* /root/easy-rsa/pki/inline/bob.inline
* /root/easy-rsa/pki/inline/private/bob.inline
Using configuration from /root/easy-rsa/pki/0841c2a6/temp.1.1
Revoking Certificate 2D1B0F7F66B2E9EB7C31D51E2291ADB6.
Database updated
Notice
------
* IMPORTANT *
Revocation was successful. You must run 'gen-crl' and upload
a new CRL to your infrastructure in order to prevent the revoked
certificate from being accepted.
root@vpn:~/easy-rsa# ./easyrsa gen-crl 2>&1 | tail -4 && cp pki/crl.pem /etc/openvpn/server/crl.pem && ls -l /etc/openvpn/server/crl.pem
IMPORTANT: When the CRL expires, an OpenVPN Server which uses a
CRL will reject ALL new connections, until the CRL is replaced.
-rw------- 1 root root 707 Oct 21 05:22 /etc/openvpn/server/crl.pem
root@bob:~# ping -c 2 -W 1 10.10.0.20
PING 10.10.0.20 (10.10.0.20) 56(84) bytes of data.
64 bytes from 10.10.0.20: icmp_seq=1 ttl=62 time=0.208 ms
64 bytes from 10.10.0.20: icmp_seq=2 ttl=62 time=1.54 ms
--- 10.10.0.20 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1057ms
rtt min/avg/max/mdev = 0.208/0.875/1.542/0.667 ms
root@vpn:~# systemctl restart openvpn-server@server
root@vpn:~# grep CLIENT_LIST /run/openvpn-server/status-server.log
HEADER,CLIENT_LIST,Common Name,Real Address,Virtual Address,Virtual IPv6 Address,Bytes Received,Bytes Sent,Connected Since,Connected Since (time_t),Username,Client ID,Peer ID,Data Channel Cipher
CLIENT_LIST,branch,198.51.100.23:39880,10.8.0.20,,4659,4776,2026-10-21 05:22:46,1792560166,UNDEF,1,0,AES-256-GCM
CLIENT_LIST,alice,198.51.100.21:36372,10.8.0.10,,4657,4828,2026-10-21 05:22:46,1792560166,UNDEF,2,1,AES-256-GCM
root@vpn:~# journalctl -u openvpn-server@server --no-pager -o cat --since "-30s" | grep -iE "crl|revoked|VERIFY ERROR|cannot|error" | head -8
CRL: loaded 1 CRLs from file crl.pem
198.51.100.22:51928 VERIFY ERROR: depth=0, error=certificate revoked: CN=bob, serial=59955766155406113166262009914427813302
198.51.100.22:51928 Sent fatal SSL alert: certificate revoked
198.51.100.22:51928 OpenSSL: error:0A000086:SSL routines::certificate verify failed:
198.51.100.22:51928 TLS_ERROR: BIO read tls_read_plaintext error
198.51.100.22:51928 TLS Error: TLS object -> incoming plaintext read error
198.51.100.22:51928 TLS Error: TLS handshake failed
198.51.100.22:51928 SIGUSR1[soft,tls-error] received, client-instance restarting- Lines 1–21: easy-rsa marks bob's certificate revoked in its database.
- Lines 27–29: …but nothing changes until a new CRL is generated and installed.
- Lines 32–37: Generated and copied into place (still root-only).
- Lines 39–46: bob is still connected and working: revocation is checked at connect time.
- Lines 48–53: After a restart, alice and branch are back, and bob isn't.
- Lines 57–58: Why: his certificate is on the CRL.
root@vpn:~/easy-rsa# ./easyrsa gen-crl >/dev/null 2>&1 && cp pki/crl.pem /etc/openvpn/server/crl.pem && ls -l /etc/openvpn/server/crl.pem
-rw------- 1 root root 707 Oct 21 05:23 /etc/openvpn/server/crl.pem
root@vpn:~# journalctl -u openvpn-server@server --no-pager -o cat --since "-15s" | grep -iE "crl|alice|error" | head -6
198.51.100.21:57061 CRL: cannot read: crl.pem
198.51.100.21:57061 VERIFY WARNING: depth=0, unable to get certificate CRL: CN=alice
198.51.100.21:57061 VERIFY WARNING: depth=1, unable to get certificate CRL: CN=TTR Lab VPN CA
198.51.100.21:57061 VERIFY ERROR: CRL not loaded
198.51.100.21:57061 OpenSSL: error:0A000086:SSL routines::certificate verify failed:
198.51.100.21:57061 TLS_ERROR: BIO read tls_read_plaintext error
root@alice:~# tail -4 /var/log/openvpn.log
2026-10-21 05:23:22 Validating certificate extended key usage
2026-10-21 05:23:22 ++ Certificate has EKU (str) TLS Web Server Authentication, expects TLS Web Server Authentication
2026-10-21 05:23:22 VERIFY EKU OK
2026-10-21 05:23:22 VERIFY OK: depth=0, CN=server- Lines 1–2: Another CRL update, root-only like the first.
- Line 5: OpenVPN, now running as
nobody, can't read it… - Line 8: …so it refuses alice, who did nothing wrong. Everyone else who reconnects is refused too.
- Lines 13–16: From alice's side, the server checked out fine; then nothing.
root@vpn:~# chmod 644 /etc/openvpn/server/crl.pem && ls -l /etc/openvpn/server/crl.pem
-rw-r--r-- 1 root root 707 Oct 21 05:23 /etc/openvpn/server/crl.pem
root@alice:~# grep -E "TLS Error|Restart pause|Initialization Sequence" /var/log/openvpn.log | tail -4
2026-10-21 05:24:23 TLS Error: TLS key negotiation failed to occur within 60 seconds (check your network connectivity)
2026-10-21 05:24:23 TLS Error: TLS handshake failed
2026-10-21 05:24:23 Restart pause, 1 second(s)
2026-10-21 05:24:24 Initialization Sequence Completed
root@vpn:~# journalctl -u openvpn-server@server --no-pager -o cat --since "-80s" | grep -E "alice/.*(primary virtual IP|PUSH_REPLY)|CRL" | cut -c1-110 | tail -3
198.51.100.21:32930 CRL: loaded 1 CRLs from file crl.pem
198.51.100.22:52264 CRL: cannot read CRL from file crl.pem
198.51.100.22:52264 CRL: loaded 1 CRLs from file crl.pem- Lines 1–2: A CRL is public: world-readable is correct.
- Lines 5–8: alice's client times out once, retries, and connects.
- Line 11: The CRL loads again.
root@vpn:~# openssl crl -in /etc/openvpn/server/crl.pem -noout -lastupdate -nextupdate
lastUpdate=Oct 21 05:23:20 2026 GMT
nextUpdate=Apr 19 05:23:20 2027 GMT- Line 3: 180 days after it was generated. After that, every new connection is refused until a fresh CRL is installed.
Check your understanding
You revoked a contractor's certificate and installed the new CRL an hour ago, but they're still connected. Why, and what do you do?
- The CRL is checked when users connect or renegotiate; restart the service or disconnect them to enforce it now.
- The CRL takes 24 hours to propagate.
- Revocation doesn't work with tls-crypt.
- The CRL needs to be installed on the client too.