openvpn mac whitelisting with python
by emre - 11.01.2026
quick walkthrough of implementing mac whitelisting for openvpn with python.
first, create openvpn service user, openvpn, with adduser --system --no-create-home --group --disabled-login openvpn command.
after than that, edit your openvpn server config. you can find my server config below, also i bolded important lines.
local 159.195.71.89
port 1194
proto udp
dev tun
ca ca.crt
cert server.crt
key server.key
dh dh.pem
auth SHA512
tls-crypt tc.key
topology subnet
server 10.8.0.0 255.255.255.0
server-ipv6 fddd:1194:1194:1194::/64
push "redirect-gateway def1 ipv6 bypass-dhcp"
ifconfig-pool-persist ipp.txt
push "dhcp-option DNS 1.1.1.1"
push "dhcp-option DNS 8.8.8.8"
push "block-outside-dns"
keepalive 10 120
cipher AES-256-CBC
user openvpn
group openvpn
persist-key
persist-tun
verb 3
crl-verify crl.pem
explicit-exit-notify
log-append /var/log/openvpn/log.log
script-security 2
client-connect /etc/openvpn/maccheck.py
duplicate-cn
♦
you can find the python script below. i store it under /etc/openvpn, named as maccheck.py.
note: i don't own the python script. i just don't remember where i got it from. did some small changes to use commented lines.
#!/usr/bin/python3
import re
import sys
db_file = '/etc/openvpn/db.txt'
log_file = '/var/log/openvpn/log.log'
regex_mac = 'IV_HWADDR=(.*)'
regex_username = 'depth=0, CN=(.*)'
login = False
logs_username_mac_list = []
with open(log_file, 'r') as log:
lines = log.readlines()
# read only latest 100 lines
last_50_lines = lines[-50:]
# iterate latest 50 lines
for line in last_50_lines:
match_mac = re.search(regex_mac, line)
if match_mac:
log_mac = match_mac.group(1)
print(log_mac)
# on match, add it to list
logs_username_mac_list.append(log_mac)
match_username = re.search(regex_username, line)
if match_username:
log_username = match_username.group(1)
print(log_username)
logs_username_mac_list.append(log_username)
# fetch username and mac address from database
with open(db_file, 'r') as db:
for line in db.readlines():
# Ignore lines that are comments or empty
if line.startswith('#') or not line.strip():
continue
splitter = line.split('-', 1)
# 0 index is username, removing newline
db_username = (splitter[0]).rstrip("\n")
print(db_username)
# 1 index is mac, removing newline
db_mac = (splitter[1]).rstrip("\n")
print(db_mac)
if db_username in logs_username_mac_list:
user_index = logs_username_mac_list.index(db_username) - 1
print(user_index)
mac_index = logs_username_mac_list[user_index]
print(user_index)
# if log mac matches db_mac
if mac_index == db_mac:
print("true")
login = True
print(login)
if login:
sys.exit(0)
else:
sys.exit(1)
♦
python script checks db.txt, so creating db.txt under /etc/openvpn would be a good idea. usage is so easy. you can use commented lines. allowed mac addresses should be written with <profilename>-<mac> scheme. example below.
#allowed remote users
robertjohnson-b7:0d:b6:e4:bc:98
clapton-00:0c:29:9d:72:5d
#server admins
erdalkomurcu-fc:aa:f2:5b:bc:de
♦
for getting mac address from client, we should add push-peer-info setting to profile.ovpn. example below.
client
dev tun
proto udp
remote 159.195.71.89 1194
resolv-retry infinite
nobind
persist-key
persist-tun
remote-cert-tls server
auth SHA512
cipher AES-256-CBC
ignore-unknown-option block-outside-dns
verb 3
push-peer-info
<ca>
-----BEGIN CERTIFICATE-----
redacted
♦
last thing is, don't forget to change ownership of these files below. the openvpn user, in our case it's openvpn, should own them. 700 permission is fair.
/var/log/openvpn
/var/log/openvpn/log.log
/etc/openvpn/maccheck.py
/etc/openvpn/db.txt
you don't need to do daemon-reload and restart openvpn service after adding entries to db.txt.
let's roll!
if you have questions: contact