This discussion has been locked.
You can no longer post new replies to this discussion. If you have a question you can start a new discussion

[DIY] Duo Security 2FA on Sophos UTM

EDIT: It seems that UTM 9.5+ now supports Duo 2FA directly. The following is for posterity only.

====================

I hope this is ok to post on the Sophos board; any modifications done to your UTM via a root ssh console could potentially void your warranty, blow up your UTM device, or set the neighbor's cat on fire. I can't be held liable for anything that comes of this information (unless it's good). 

While the built-in OTP functions work fine on the UTM, I wanted to utilize a pushbased 2FA solution such as Duo Security for my SSL VPN. I saw that there's an outstanding feature request for this functionality, so I got it in my head to go off and try to accomplish it myself. 

I found there's two ways to get DuoSec working with OpenVPN on the UTM. I'll outline both and the differences between the methods. For reference, my UTM is running release 9.315-2 on a 64-bit platform (Core i3-4130). 

Method 1 - OpenVPN plugin
The DuoSec OpenVPN integration is done via a plugin, which is a compiled shared object. Since there's no build tools on the UTM (that's a good thing), the plugin has to be built on a separate machine. It's much easier to do this with an arch and distribution-similar platform, so I stood up an x86_64 SLED 11 SP4 vm. I later discovered that a lot of the binaries on the UTM are 32-bit (including the existing openvpn-plugin-utm.so), so the same could have been achieved using a 32-bit distro. 

Here's an overview of the steps taken to build the plugin:

  • Download DuoSec OpenVPN plugin from github
  • Modify Makefile to include $(CFLAGS) for the shared object compilation, include 32-bit flag in CFLAGS
  • run make && make install
  • Copy /opt/duo to openvpn chroot on UTM (/var/sec/chroot-openvpn)
  • Bind mount the python libraries into openvpn chroot, and copy python binary into chroot
  • Edit openvpn.conf-default so the changes don't disappear when the service is restarted
  • Restart openvpn, test with client


Changes to the Makefile (if building on a 64-bit VM):


Original Makefile:


CFLAGS += -DPREFIX='"$(PREFIX)"'

duo_openvpn.so: duo_openvpn.o
        $(CC) -fPIC -shared -Wl,-soname,duo_openvpn.so -o duo_openvpn.so duo_openvpn.o -lc



Changes:


CFLAGS += -DPREFIX='"$(PREFIX)"' -m32

duo_openvpn.so: duo_openvpn.o
        $(CC) $(CFLAGS) -fPIC -shared -Wl,-soname,duo_openvpn.so -o duo_openvpn.so duo_openvpn.o -lc



This should allow the plugin to compile properly. Tar (not zip; no unzip on the UTM) the files in /opt/duo up and scp it to your UTM. On the UTM, untar the file and move that /opt/duo into the openvpn chroot (it should land at /var/sec/chroot-openvpn/opt/duo). 

Since there's no python within the openvpn chroot, and the duo plugin requires it for the helper script, we need to copy or link in the python libraries and binaries. Since I don't like to waste space by duplicating files, I just used a bind mount to link in the /usr/lib/python2.7 directory like so:


utm:/var/sec/chroot-openvpn/usr/lib # mkdir python2.7
utm:/var/sec/chroot-openvpn/usr/lib # mount --bind /usr/lib/python2.7/ ./python2.7/



Also, you'll probably need /usr/lib/libpython2.7.so.1.0


utm:/var/sec/chroot-openvpn/usr/lib # cp /usr/lib/libpython2.7.so.1.0 .



And finally you'll need the python2.7 binary and symlink:


utm:/var/sec/chroot-openvpn/usr/bin # cp /usr/bin/env .
utm:/var/sec/chroot-openvpn/usr/bin # cp /usr/bin/python2.7 .
utm:/var/sec/chroot-openvpn/usr/bin # ln -s python2.7 ./python



Finally, the openvpn.conf-default needs to be changed. This file is the template that is used to build the openvpn.conf whenever the service is (re)started.

It's located at /var/sec/chroot-openvpn/etc/openvpn/openvpn.conf-default
Original:


auth-user-pass

plugin /usr/lib/openvpn/plugins/openvpn-plugin-utm.so



Modified:


auth-user-pass-optional

#plugin /usr/lib/openvpn/plugins/openvpn-plugin-utm.so
plugin /opt/duo/duo_openvpn.so IKEY SKEY HOST



Fill in your IKEY, SKEY, and HOST from your Duo Admin panel for your Openvpn application. The auth-user-pass-optional directive is used to automatically use a push-based authentication when connecting. See more info here. One thing to note is that while OpenVPN plugins can be chained (much like PAM modules), the openvpn-plugin-utm plugin does not allow you to enter in any secondary authentication info, so auth will always fail. The downside of removing that plugin is that you will no longer see any connected client status in the UTM interface. 

Once you're done making changes, disable/enable your SSL VPN profile and tail the log file at /var/log/openvpn.log to make sure everything starts up fine. A successful integration should look something like this:


: OpenVPN 2.3.0 i686-suse-linux-gnu [SSL (OpenSSL)] [LZO] [EPOLL] [MH] [IPv6] built on Jul  8 2015
: MANAGEMENT: client_uid=0
: MANAGEMENT: client_gid=0
: MANAGEMENT: unix domain socket listening on /var/run/openvpn_mgmt
: NOTE: OpenVPN 2.1 requires '--script-security 2' or higher to call user-defined scripts or executables
: PLUGIN_INIT: POST /opt/duo/duo_openvpn.so '[/opt/duo/duo_openvpn.so] [IKEY] [SKEY] [HOST]
' intercepted=PLUGIN_AUTH_USER_PASS_VERIFY
: Diffie-Hellman initialized with 2048 bit key
: WARNING: experimental option --capath /etc/openvpn/ca.d
: Socket Buffers: R=[212992->131072] S=[212992->131072]
: ROUTE_GATEWAY 255.255.255.1/255.255.248.0 IFACE=eth0 HWADDR=30:00:aa:bb:cc[:D]d
: TUN/TAP device tun0 opened
: TUN/TAP TX queue length set to 100
: do_ifconfig, tt->ipv6=0, tt->did_ifconfig_ipv6_setup=0
: /bin/ip link set dev tun0 up mtu 1500



Test it with a client (no username/password needed if you used the auth-user-pass-optional directive) and verify that your 2FA device with Duo Mobile on it receives the push notification. If it does, go ahead and allow it and check your openvpn client to see if it completed the connection. You should be good to go at this point.

If there are issues, a few things to check:

  • Verify your UTM can reach *.duosecurity.com (might need to alter firewall or web filter rules)
  • Verify you can run the python helper script from within the chroot:

    utm:/var/sec/chroot-openvpn # chroot .
    utm:/ # cd /opt/duo
    utm:/opt/duo # ./duo_openvpn.py
    utm:/opt/duo # exit
    utm:/var/sec/chroot-openvpn #

If you plan on using this method, you'll probably want to edit your /etc/fstab to include the bind mount upon boot. 

Edit /etc/fstab and add:


/usr/lib/python2.7         /var/sec/chroot-openvpn/usr/lib/python2.7   none   bind  0 0



Using the OpenVPN plugin is an ok implementation, but since it can't be chained with the stock utm plugin, there's no primary username/password authentication prior to the second factor Duo auth. It really becomes 2 things you have (the client certificate and the Duo authenticator) and nothing you know. Not the greatest for security in my opinion, which is why I moved on to the second method.

Method 2 - RADIUS plugin + Duo Authentication Proxy
DuoSec's RADIUS integration is much better suited to the UTM, as it allows for primary AD authentication, in addition to the secondary Duo auth. This means building an OpenVPN radius plugin. All the major linux distros have this available as openvpn-auth-radius (or some variation thereof), but sadly the UTM does not come with this. Here's the overview of how I got this method working:

  • Download Duo Authentication Proxy for Linux and build on separate server
  • Configure auth proxy to talk to AD
  • Download openvpn-auth-radius from github
  • Install all necessary 32-bit libraries and devel packages, compile radiusplugin
  • Copy resulting radiusplugin.so and radiusplugin.cnf to openvpn chroot on UTM
  • Copy all shared libraries into chroot
  • Edit radiusplugin.cnf to talk to my DuoSec RADIUS Auth Proxy running on a separate server
  • Edit openvpn.conf-default to enable radiusplugin plugin, and comment out duo_openvpn plugin
  • Test, verify 2FA working, profit


Building the Duo Authentication Proxy is out of scope for this post, but it's fairly straight-forward and easy to do on any modern linux system. I built mine on a Ubuntu 14.04LTS server. Follow the documentation on Duo's site for more info

The RADIUS plugin is somewhat of a bear to compile on SLED, as it needs libraries and headers that aren't easily obtained if you don't have a SUSE subscription. I used the Open Build Service on openSUSE's site to find the packages needed. The idea is to keep the library dependencies as close to the versions as the UTM comes with. 

Small edit to the Makefile:
Original:


LIBS=-lgcrypt -lpthread
CFLAGS=-Wall -shared -fPIC -DPIC 



Modified:


LIBS=-lgcrypt -lpthread
CFLAGS=-Wall -shared -fPIC -DPIC -m32



Packages:
You'll need the library and devel packages for libgcrypt11-1.5.0*; SLED comes with libgcrypt11 and libgcrypt11-32bit but no devel packages. We need the 32-bit devel package, so I used the libgcrypt-devel-1.5.0-6.12.1.i586.rpm package found on OBS under the SLE 11 SP1 distro. 

You should be able to run make without errors, and end up with a 32-bit radiusplugin.so:


linux-j9hj:~/openvpn-auth-radius # make
OBJ:  RadiusClass/RadiusAttribute.o 
OBJ:  RadiusClass/RadiusPacket.o 
OBJ:  RadiusClass/RadiusConfig.o 
OBJ:  RadiusClass/RadiusServer.o 
OBJ:  RadiusClass/RadiusVendorSpecificAttribute.o 
OBJ:  Exception.o 
OBJ:  PluginContext.o 
OBJ:  UserAuth.o 
OBJ:  IpcSocket.o 
OBJ:  radiusplugin.o 
OBJ:  User.o 
OBJ:  AuthenticationProcess.o 
OBJ:  main.o 
OBJ:  UserAcct.o 
OBJ:  UserPlugin.o 
OBJ:  Config.o 
BIN:  radiusplugin.so 
linux-j9hj:~/openvpn-auth-radius # file radiusplugin.so
radiusplugin.so: ELF 32-bit LSB shared object, Intel 80386, version 1 (SYSV), dynamically linked, not stripped



Copy the plugin and the config file (radiusplugin.cnf) to the openvpn chroot on the UTM. I chose to put the plugin under the previously-created /opt directory, and the config file in the chroot's /etc dir:


utm:/var/sec/chroot-openvpn/opt # mkdir radius
utm:/var/sec/chroot-openvpn/opt # mv /home/login/radiusplugin.so radius/
utm:/var/sec/chroot-openvpn/opt # cd ../etc
utm:/var/sec/chroot-openvpn/etc # mv /home/login/radiusplugin.cnf .



You'll need to link or copy in all of the shared libraries that the radiusplugin depends on. The easiest way to do this is by using ldd


utm:/var/sec/chroot-openvpn/opt/radius # ldd radiusplugin.so
        linux-gate.so.1 =>  (0x55576000)
        libgcrypt.so.11 => /lib/libgcrypt.so.11 (0x555c7000)
        libpthread.so.0 => /lib/libpthread.so.0 (0x5564d000)
        libstdc++.so.6 => /usr/lib/libstdc++.so.6 (0x55668000)
        libm.so.6 => /lib/libm.so.6 (0x55752000)
        libgcc_s.so.1 => /lib/libgcc_s.so.1 (0x5577c000)
        libc.so.6 => /lib/libc.so.6 (0x5578b000)
        libgpg-error.so.0 => /lib/libgpg-error.so.0 (0x558fc000)
        libdl.so.2 => /lib/libdl.so.2 (0x55901000)
        /lib/ld-linux.so.2 (0x55555000)



For every library, copy it (not its symlink) from the root filesystem to the chroot environment, making sure to keep the file structure the same:


utm:/var/sec/chroot-openvpn/lib # cp /lib/libgcrypt.so.11.7.0 .
utm:/var/sec/chroot-openvpn/lib # ln -s libgcrypt.so.11.7.0 libgcrypt.so.11
utm:/var/sec/chroot-openvpn/lib # cp /lib/libpthread-2.11.3.so .
utm:/var/sec/chroot-openvpn/lib # ln -s libpthread-2.11.3.so libpthread.so.0
utm:/var/sec/chroot-openvpn/lib # cd ../usr/lib
utm:/var/sec/chroot-openvpn/usr/lib # cp /usr/lib/libstdc++.so.6.0.10 .
utm:/var/sec/chroot-openvpn/usr/lib # ln -s libstdc++.so.6.0.10 libstdc++.so.6



Make the edits to /var/sec/chroot-openvpn/etc/openvpn/openvpn.conf-default:
Original:


auth-user-pass

plugin /usr/lib/openvpn/plugins/openvpn-plugin-utm.so



Modified:


auth-user-pass-optional

#plugin /usr/lib/openvpn/plugins/openvpn-plugin-utm.so
plugin /opt/radius/radiusplugin.so /etc/radiusplugin.cnf



Edit the /var/sec/chroot-openvpn/etc/radiusplugin.cnf to communicate with your Duo Auth Proxy. Don't forget you might need a firewall rule to allow udp/1812 from your Internal interface to your auth proxy server.

Restart the openvpn server from the web UI by disabling/enabling the switch. Watch the /var/log/openvpn.log file to verify everything started up properly:


: OpenVPN 2.3.0 i686-suse-linux-gnu [SSL (OpenSSL)] [LZO] [EPOLL] [MH] [IPv6] built on Jul  8 2015
: MANAGEMENT: client_uid=0
: MANAGEMENT: client_gid=0
: MANAGEMENT: unix domain socket listening on /var/run/openvpn_mgmt
: NOTE: OpenVPN 2.1 requires '--script-security 2' or higher to call user-defined scripts or executables
: PLUGIN_INIT: POST /opt/radius/radiusplugin.so '[/opt/radius/radiusplugin.so] [/etc/radiusplugin.cnf]' intercepted=PLUGIN_AUTH_USER_PASS_VERIFY
: Diffie-Hellman initialized with 2048 bit key
: WARNING: experimental option --capath /etc/openvpn/ca.d
: Socket Buffers: R=[212992->131072] S=[212992->131072]



If you see any errors relating to shared object files not being found, you've missed some libraries and will need to go back and figure out which ones you need within your chroot.

Example (I forgot to copy libgcc_s.so to the chroot):


openvpn[12969]: PLUGIN_INIT: could not load plugin shared object /opt/radius/radiusplugin.so: libgcc_s.so.1: cannot open shared object file: No such file or directory
openvpn[12969]: Exiting due to fatal error



Login to your auth proxy server and tail the Duo auth proxy log file (default is in /opt/duoauthproxy/log). Test it out with a client and enter your username and password. You should see the RADIUS request come over to the auth proxy, and then attempt to authenticate the username against AD (assuming you configured AD as a primary auth source). If this succeeds, it should then push a Duo 2FA request to your mobile device:


[-] Duo Security Authentication Proxy 2.4.12 - Init Complete
[-] DuoForwardServer starting on 1812
[-] Starting protocol 
[DuoForwardServer (UDP)] Sending request from 10.11.12.1 to radius_server_auto
[DuoForwardServer (UDP)] Received new request id 65 from ('10.11.12.1', 48744)
[DuoForwardServer (UDP)] (('10.11.12.1', 48744), 65): login attempt for username u'testuser'
[DuoForwardServer (UDP)] Sending AD authentication request for 'testuser' to '10.11.12.100'
[DuoForwardServer (UDP)] Starting factory 
[_ADAuthClientProtocol,client] http POST to api-secretstuff.duosecurity.com:443/.../preauth



I'm very satisfied with how it all works together and would only be made better if Sophos offered the radiusplugin themselves and integrated the openvpn-plugin-utm functionality as well (session logging, etc). Maybe a future release? 

For anyone that wants to give this a try and not have to setup a build environment, I've attached the compiled plugins to this post. If you're like me though, you won't just blindly trust a compiled binary that you find on a forum, and you'll want to build it yourself from source, thus the instructions above.

EDIT: The SSL VPN (Openvpn) service on the UTM does support using the built-in RADIUS authentication (under Definitions & Users -> Authentication Services -> Servers) and it does work with the Duo Auth Proxy as well, so if you'd rather not use the OpenVPN RADIUS plugin, that's a viable alternative. One caveat is the default RADIUS timeout value; it's set at 3 seconds, and that's not usually enough time to receive your Duo 2FA push notification and respond (so you'll constantly get auth failed messages). If you plan on utilizing the built-in RADIUS auth for Duo 2FA, you'll want to edit the timeout value in /var/aua/AuaConfig.pm:


# our $radius_timeout          = 3;
our $radius_timeout          = 30;



The other issue is the AUA daemon caches authentication details. I don't know what the default length of this cache is, but if you're trying to enforce 2FA on every login attempt, that might be an issue. The cache can be dumped from the web UI, but preferably there should be a setting to determine how long the cache persists.

A positive of using the built-in RADIUS auth backend is that you can then utilize the default openvpn-plugin-utm.so plugin, which will give you client session information in the logs, and the web UI.



This thread was automatically locked due to age.
duosec2utm.zip
Parents Reply Children
No Data