Monthly Archives: July 2014

OpenVPN: Routen aus LDAP

I had an OpenVPN-Server which authenticates users with the help of a slapd / OpenLDAP-Server. But I wanted to have more: Depending on the groups the authenticated user is a member of, firewall and routing rules should be pushed to the client and installed on the server.

OpenVPN has two configuration variables: client-connect and client-disconnect which can be set to scripts.

The connect-script itself searches for groups with ip-ranges and addresses (here, they are parameters of a user defined scheme), sets up the regarding iptables-rules for the Masquerading and pushes the route to the client (with the help of a connection specific configuration file).

Please keep in mind: This is a dirty hack. No exceptions are caught, I even mixed different programming languages. But I didn’t found any other solution – so please use this scripts as a inspiration, implement it how it should be and send me the link 😉

First, we need two wrapper-scripts that are called by openvpn:

From openvpn.conf:


client-connect /usr/local/bin/sudo_connect.sh
client-disconnect /usr/local/bin/sudo_disconnect.sh

These scripts are just calling the *real* scripts and are sorting enviromental and command line arguments.

/usr/local/bin/sudo_connect.sh

#!/bin/bash
exec sudo /usr/local/bin/python-ldap-connect.py $username $ifconfig_pool_remote_ip $1

/usr/local/bin/sudo_disconnect.sh

#!/bin/bash
exec sudo /usr/local/bin/clean_ruleset.sh $ifconfig_pool_remote_ip

(The openvpn-user, in my case nobody, has to be allowed to run this commands using sudo. Please allow it in /etc/sudoers )

The clean_ruleset.sh does one simple thing: Delete every iptables-rule created for a given source address in the chain FORWARD:

#!/bin/bash

IP=$1

for line_num in $(iptables -n --line-numbers --list FORWARD | awk '$5=="'$IP'" {print $1}')
do
# You can't just delete lines here because the line numbers get reordered
# after deletion, which would mean after the first one you're deleting the
# wrong line. Instead put them in a reverse ordered list.
LINES="$line_num $LINES"
done

# Delete the lines, last to first.
for line in $LINES
do
sudo iptables -D FORWARD $line
done

unset LINES

It is called by openvpn after a clients disconnects – and by the connect script, just to be sure.

The connect script does the real work:

#!/usr/bin/python

import sys
import ldap
from subprocess import call

def main(username, remote_ip, configfile):
# First: Delete all existing rules:
call(["/usr/local/bin/clean_ruleset.sh", remote_ip])

#Initialize LDAP Connection
ldapServer = "ldap://[ldap-server]"
ldapBindDN = ""
ldapBindPassword = ""
userDn = "uid=" + username + ",ou=[usual User-DN-Suffix for your users]"

try:
ldapCon = ldap.initialize(ldapServer)
ldapCon.simple_bind_s(ldapBindDN, ldapBindPassword)
except ldap.LDAPError, error_message:
print "Couldn't connect to LDAP. Error: %s" % error_message

#Now: Get list of groups the user is in
ldapFilter = "(&(objectClass=[name of your scheme - alternatively groupOfNames])(member=" + userDn + "))"
ldapBase = "[searchBase for your iptables-Groups]"
scope = ldap.SCOPE_SUBTREE
retrieveAttributes = ["[name of the attribute the IP-Adresses are saved]"]

resultSet = []
timeout = 1

try:
resultId = ldapCon.search(ldapBase, scope, ldapFilter, retrieveAttributes)
while 1:
resultType, resultData = ldapCon.result(resultId,0,1)
if (resultData == []):
break
else:
if resultType == ldap.RES_SEARCH_ENTRY:
for address in resultData[0][1]['[name of the attribute the IP-Adresses are saved]']:
resultSet.append(address)

except ldap.LDAPError, error_message:
print error_message

#Open Connection-specific configure file
configFileHandler = open (configfile, 'w')

#Remove duplicates from address-list
resultSet = list(set(resultSet))

for address in resultSet :
call("iptables" + " -A FORWARD" + " -s " + remote_ip + " -d " + address + " -o eth0" + " -j ACCEPT", shell=True)
configFileHandler.write('push route ' + address + '\n')

configFileHandler.close()

if __name__ == "__main__":
main(sys.argv[1], sys.argv[2], sys.argv[3])

Leave a comment

Filed under Administration