Making Debian Play Nice with 802.1x and User VLAN Switching

As you could probably guess from previous posts, I’m currently working on a project that will see the deployment of 802.1x on the wired network. The ultimate aim would be to authenticate both the endpoint and user, with the VLAN a machine is dropped in to dependant on user role.
Turns out that’s surprisingly simple to deploy on Windows – just enable 802.1x and tick the right boxes on the configuration screen. Windows will authenticate itself using the computer account in Active Directory and will pass on a user’s credentials via. 802.1x before attempting AD authentication.
On OS X, things are a little more complicated. We can create a configuration profile that will authenticate the machine using a system account and also passes on the credentials obtained at the login screen.
On Linux, things are even more complicated. The Debian systems I’ve been testing this deploy with Network Manager. Don’t get me wrong, it works surprisingly well for deploying a single system-wide 802.1x configuration. No more banging your head off a wall trying to get wpa_supplicant to work.
However, there’s no built-in mechanism for taking the credentials entered at the login screen and passing them through for 802.1x authentication. It would be amazing if GDM had the capability to do this but I can’t see an option for it anywhere.
What GDM does have is login and logout scripts we can hook in to. I’ll warn you, it’s messy but does (kinda) work for being able to flip VLANs depending on the user role.
To make this work, start by creating a folder called /opt/company/ with root only permissions. In here you’ll be creating a few files. Specifically, one for each role your users could have.
Take a copy of /etc/NetworkManager/system-connections/Wired connection 1 as a template for these files. Now add a few lines so it looks like the example below:

[802-3-ethernet]
duplex=full
[connection]
id=Wired connection 1
uuid=.....
type=802-3-ethernet
timestamp=....
[ipv6]
method=auto
ip6-privacy=2
[802-1x]
eap=ttls;
identity=username
password=password
ca-cert=/opt/company/ca.cer
phase2-auth=pap
password-flags=0
[ipv4]
method=auto

There’s a couple of things that should be pointed out in this example. We’re using TTLS for the 802.1x connection but using PAP inside that. I know that could be considered insecure as there’s plain text passwords but it’s done here to keep it simple and make sure it works. Consider MSCHAPv2 or even certificate based authentication for live deployments.
Oh, and yes, the passwords are plain text in these files. While being careful with permissions will help here, it’s another point that should be considered for a live deployment.
Finally, you’ll note that we needed to specify a certificate. That’s the certificate for the RADIUS server the machine will be authenticating against. Without it, or the appropriate CA certificate, authentication may fail for trust reasons.
With these files created for every role you need, copy the file for the default profile back to /etc/NetworkManager/system-connections/Wired connection 1. It’s worth noting that this default profile will need access to your AD or LDAP server. This is because authentication occurs before the login scripts are triggered. If the user can’t authenticate first, they won’t get onto the machine, never mind the network.
You’ll also need to crack out the scripting skills in order to perform the role switching. See below for an example of what you could put into the /etc/gdm3/PostLogin/Default script:

#!/bin/bash
# User role detection - this will be organisation specific
# Remember - this script runs as root but we do have the $HOME and $USER values to work with
if [[ role1 ]]; then
cp /opt/company/role1.conf "/etc/NetworkManager/system-connections/Wired connection 1"
elif [[ role2 ]]; then
cp /opt/company/role2.conf "/etc/NetworkManager/system-connections/Wired connection 1"
fi
# Unmount NFS shares
echo "Unmounting NFS..."
/bin/umount -a -t nfs -l
while [[ `/bin/mount` == *"type nfs"* ]]
do
echo "Waiting for lazy unmount to complete..."
sleep 1
done
# Bounce the NIC
echo "Bouncing the network..."
/etc/init.d/network-manager restart
/usr/bin/mncli dev disconnect iface eth0
/usr/bin/nmcli con up id "Wired connection 1"
# Make sure it's up
while [[ `/bin/ip addr` != *"10."* ]]
do
echo "Waiting for network to return..."
done
# Remount the shares
echo "Mounting NFS..."
/bin/mount -a -v

That’s quite a script but let’s break it down and see what’s going on. Why don’t we start with the first section?

# User role detection - this will be organisation specific
# Remember - this script runs as root but we do have the $HOME and $USER values to work with
if [[ role1 ]]; then
cp /opt/company/role1.conf "/etc/NetworkManager/system-connections/Wired connection 1"
elif [[ role2 ]]; then
cp /opt/company/role2.conf "/etc/NetworkManager/system-connections/Wired connection 1"
fi

This is the bit that’ll be specific to your organisation. You need to figure out what role the user has and copy the correct 802.1x configuration file in place for Network Manager.

# Unmount NFS shares
echo "Unmounting NFS..."
/bin/umount -a -t nfs -l
while [[ `/bin/mount` == *"type nfs"* ]]
do
echo "Waiting for lazy unmount to complete..."
sleep 1
done

Before you do any mucking about with the network interface, you need to disconnect any NFS mounts. They do not take well to the client IP address changing. In fact, I know people that have run into issues with NFS mounts when a DHCP lease runs out and changes.
In this example, we’re using the lazy unmount option. It’s not ideal (we can’t tell 100% when it’s unmounted for one) but does the job in most cases.

# Bounce the NIC
echo "Bouncing the network..."
/etc/init.d/network-manager restart
/usr/bin/mncli dev disconnect iface eth0
/usr/bin/nmcli con up id "Wired connection 1"
# Make sure it's up
while [[ `/bin/ip addr` != *"10."* ]]
do
echo "Waiting for network to return..."
done

Now here’s the bit where the magic really happens. We start by restarting the Network Manager service. This forces it to pick up your new configuration. While yes, you could rely on the monitor-connection-files option in the main config, you can’t guarantee when it’ll pick up the changes.
Restarting the service will trigger the 802.1x authentication to happen with the new credentials. However, it doesn’t trigger a DHCP request. You’ll be on the correct VLAN but the machine will continue to use its old address.
To fix that, we force the network connection down then back up again. There’s also a little logic in there to ensure we see an address allocation before continuing.

# Remount the shares
echo "Mounting NFS..."
/bin/mount -a -v

The final part of the script is re-mounting the NFS shares we disconnected earlier. I would love to say that’s all to need but I’ve been having real problems with the NFS part. Getting it to reliably re-mount on demand has been really problematic and in fact hangs the script. Oops!
This is also incredibly “hacky” approach to take. It would be nice to see proper 802.1x integration into PAM (there is a module but it’s limited and seeing no active development) or even GDM. Admittedly, it’s a bit of a specialist request and not likely to apply to most users. On top of that, it wouldn’t get round the NFS problems we ran in to.
That said, this script should be enough to get you going. Especially if you’re not using NFS for home drives. You will need a similar script in /etc/gdm3/PostSession/Default to handle flipping roles back on logout.
Oh, and if you need to debug any of this script, check out /var/log/gdm3/:0-slave.log. You’ll see any echoed output from the script in real time.

You may also like...