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