Client Operations¶
Adding a Client — newClient()¶
Flow¶
graph TD
A[Validate client name] --> B[Set certificate duration]
B --> C{Password?}
C -->|Yes| D[Get password]
C -->|No| E[Continue]
D --> E
E --> F{Client exists?}
F -->|Yes| G[Fatal error]
F -->|No| H{Auth mode?}
H -->|PKI| I[easyrsa build-client-full]
H -->|Fingerprint| J[easyrsa self-sign-client]
I --> K[Generate .ovpn]
J --> L[Register fingerprint in server.conf]
L --> M[Reload OpenVPN]
M --> K Certificate Generation¶
PKI mode:
cd /etc/openvpn/server/easy-rsa/
./easyrsa --batch --days=3650 build-client-full "clientname" nopass
Fingerprint mode:
After generation, the fingerprint is extracted and added to server.conf:
OpenVPN is then reloaded to pick up the new fingerprint.
.ovpn File Generation — generateClientConfig()¶
The function assembles the .ovpn file from:
- Client template (
/etc/openvpn/server/client-template.txt) - CA certificate (PKI mode) or server fingerprint (fingerprint mode)
- Client certificate (from
pki/issued/<name>.crt) - Client private key (from
pki/private/<name>.key) - TLS key:
tls-crypt-v2: Generates a unique per-client key from the server keytls-crypt: Embeds the shared keytls-auth: Embeds the key withkey-direction 1
Everything is embedded inline using <ca>, <cert>, <key>, and <tls-crypt-v2> blocks — producing a single self-contained .ovpn file.
File Permissions¶
The .ovpn file is saved to the client's home directory (if a matching system user exists) or to the home of the SUDO_USER, or /root. Permissions are set to owner-only read/write (chmod go-rw).
Listing Clients — listClients()¶
PKI Mode¶
Parses /etc/openvpn/server/easy-rsa/pki/index.txt:
V 360318120000Z 01 unknown /CN=clientname # V = Valid
R 360318120000Z 260321 02 unknown /CN=revoked # R = Revoked
Filters out server certificates (CN starting with server_).
Fingerprint Mode¶
- Gets valid clients from the
<peer-fingerprint>block inserver.conf - Scans
pki/issued/for all certificate files - Marks certificates present in the fingerprint block as "valid", others as "revoked"
Output¶
Table format:
Name Status Expires Days Left
---- ------ ------- ---------
laptop Valid 2036-03-18 3650
old-device Revoked - -
JSON format:
Revoking a Client — revokeClient()¶
PKI Mode¶
The CRL is copied to /etc/openvpn/server/crl.pem. OpenVPN checks this list on every connection attempt.
Fingerprint Mode¶
Removes the client's comment and fingerprint lines from server.conf, then reloads OpenVPN.
Post-Revocation¶
- Removes
.ovpnfiles from/home/and/root/ - Removes IP assignment from
ipp.txt - Immediately disconnects the client via the management socket:
Renewing a Client — renewClient()¶
Flow¶
graph TD
A[Select client] --> B[Set new duration]
B --> C[Backup old cert]
C --> D{Auth mode?}
D -->|PKI| E[easyrsa renew + revoke-renewed]
D -->|Fingerprint| F[Remove old cert, generate new]
E --> G[Regenerate CRL]
F --> H[Update fingerprint in server.conf]
H --> I[Reload OpenVPN]
G --> J[Generate new .ovpn]
I --> J The old certificate is revoked (PKI mode) or its fingerprint is replaced (fingerprint mode). A new .ovpn file is generated — the client must download it.
Connected Clients — listConnectedClients()¶
Reads /var/log/openvpn/status.log (refreshed every 60 seconds by OpenVPN).
Parses CLIENT_LIST lines:
Extracts: name, real address, VPN IP, bytes received/sent, connection time.
Traffic is formatted using formatBytes() (converts to GB/MB/KB).
Helper Functions¶
| Function | Purpose |
|---|---|
getHomeDir() | Find client's home directory for .ovpn file |
getClientOwner() | Determine file owner for permissions |
setClientConfigPermissions() | Set owner:group and remove world-readable |
selectClient() | Interactive client selector with expiry info |
getCertExpiry() | Extract expiry date and days remaining from a certificate |
extractFingerprint() | Get SHA256 fingerprint from a certificate |
removeCertFiles() | Delete cert, key, and request files |
regenerateCRL() | Regenerate CRL after cert changes (15-year validity) |
disconnectClient() | Kill active connection via management socket |
json_escape() | Escape strings for JSON output |