pfSense as a Cisco AnyConnect VPN Client using OpenConnect

pfSense, as of 2016-03-01, does not support OpenConnect out of the box. However, it’s in the FreeBSD repository, and relatively easy to add:

# pkg
# pkg update -f
# pkg install openconnect
# rehash

You can now play around with the openconnect command and test your connection.

Next step: Autostart, and adding the tun interface to the pfSense GUI. The GUI will, by default, ignore any interface named “tun*”, while openconnect will refuse to work with any interface not named “tun*”. Brilliant. The easiest workaround for this special case seems to be renaming the VPN interface after creation.

I made a script that automates checking if the connection is up, and (re-)starting it if it is not.
Replace the options in the “settings” section with appropriate values for your setup, and you should be good to go.

The “test” field should be a command that returns 0 when the connection is up, and anything else when it’s broken. I used netcat’s port testing feature on the remote desktop port of a server I needed to be able to connect to, but you can just as easily use things like ping with a limited count or similar.

#!/bin/sh

# settings
user="vpnuser"
pass="P4ssw0rd"
host="vpn.server.here.com"
test="nc -v -w 10 -z 172.16.0.4 3389"
tmpif="tun69"
iface="ocvpnc1"
pidfile="/tmp/${iface}.pid"
script="/usr/local/sbin/vpnc-script"


# env
openconnect="/usr/local/sbin/openconnect"
ifconfig="/sbin/ifconfig"


# func
ifkill()
{
        $ifconfig "$1" down 2>/dev/null || :
        $ifconfig "$1" destroy 2>/dev/null || :
}


# check if we're already running
if [ -n "$test" ] && $test; then
        echo "Connection is already up"
        exit 0
fi


# clean up previous instance, if any
if [ -e "$pidfile" ]; then
        read pid <"$pidfile"
        echo "Killing previous pid: $pid"
        kill -TERM "$pid"
        rm "$pidfile"
fi
ifkill "$tmpif"
ifkill "$iface"


# open vpn connection
echo "$pass" |\
$openconnect \
        --background \
        --pid-file="$pidfile" \
        --interface="$tmpif" \
        --user="$user" \
        --passwd-on-stdin \
        --script="$script" \
        "$host"


# rename the interface
if [ "$iface" != "$tmpif" ]; then
        echo "Renaming $tmpif to $iface"
        $ifconfig "$tmpif" name "$iface"
fi

Next, use crontab -e and add an entry to run the script regularly.

*/5 * * * * /root/openconnect-vpn >/dev/null 2>&1

Again, replace the path and timing with your own preferred values.

With the connection established, you can now go ahead and add the interface in the “assignment” tab of the GUI and set up appropriate rules for it.

CAUTION: Adding an interface that’s not available at boot time to the GUI will cause pfSense to think something is wrong on subsequent reboots and ask you to configure interfaces. I am not currently aware of a workaround for this, other than to not add the interface, controlling rules directly from the script instead. Please use the workaround below to avoid this issue, and make sure to verify that it works before leaving a pfSense box at a remote site unattended.

Interface boot workaround

The following workaround was offered by “DJC” in the comments section:

  1. Install “Shellcmd” in PfSense WebConfigurator:
    System => Package Manager => Available Packages
    Find Shellcmd and INSTALL
  2. Navigate to Shellcmd:
    Services => Shellcmd
  3. Add the following item in Shellcmd:
    Command: /sbin/ifconfig tun create; /sbin/ifconfig tun0 name ocvpnc1
    Shellcmd Type: earlyshellcmd
    Description: Create tunnel interface for OVPNC1 at boot

19 Comments

  • Paul says:

    I’ve just tried that, but somehow it doesn’t work. I’m located in China and currently use a patched openvpn with pfsense.

    When I run your script it connects fine and the pfsense can ping the server.
    On the gui I just changed the openvpn interface from ovpnc1 to ocvpnc1 so all the rules should just carry over, but somehow the LAN can’t connect anywhere. What am I doing wrong?

    Thanks a lot

    • bolt says:

      Without knowing how all your rules are set up, that would be very hard for me to diagnose.
      I suggest taking a look at your firewall logs to see which rule is blocking the communication.

      • Paul says:

        I got it working. Was a problem with the server. Thanks a lot. That has really helped me.

        • bolt says:

          Glad it worked for you. Keep in mind the “CAUTION” section above. The GUI will rebel on boot if the interface you add doesn’t exist at boot time.

          You could probably hack that by adding it in a script before that check is made, but I’m uncertain of where that would be.

  • Bob says:

    Hello!

    I would like to set this up to allow my house (and tablet) to have access to my university’s resources.

    Would you be able to tell me where you’ve placed the autostart script? My experience with bsd is limited.

    Also, could you clarify about the CAUTION section?

    Like after I’ve added the connection using the script and added the interface to the GUI and added the routes, will it detonate on reboots or can I remove it from the “assign” interface tab for it to survive reboots and still preserve routes?

    • bolt says:

      I generally add my scripts in cron (crontab -e) using the @reboot timing option. This keeps all my stuff generally out of the way of pfSense, and is very robust when upgrading. It’s also easy to find all my modifications by doing “crontab -l”.

      About the CAUTION: pfSense will indeed detonate, as you say, on reboot. There will be a missing interface, as the VPN software hasn’t created it yet. This will kick pfSense into the interface assignment part of the setup, which you’ll have to skip out of to continue booting. Since this requires access to the console, I could not use this at the remote site I was deploying it at, since accessing the console, even over the network, would of course require the network to be there in the first place.

      Instead, I ended up installing a minimal virtual machine with Linux, installing the VPN client on there and adding a few simple iptables rules, using shorewall as I often do, to tunnel all traffic from LAN through the VPN. Then I added this linux VM as an additional router in pfSense and I control which traffic is routed through it using the pfSense interface. It’s less intrusive, and has worked quite well. If this option interests you, let me know, and I can provide more details.

  • DJC says:

    Here’s the workaround to allow PfSense to boot/reboot and avoid the interface issue. Please update your notes:

    ——- Interface boot workaround ——-

    1. Install “Shellcmd” in PfSense WebConfigurator:
    System => Package Manager => Available Packages
    Find Shellcmd and INSTALL

    2. Navigate to Shellcmd:
    Services => Shellcmd

    3. Add the following item in Shellcmd:
    Command:
    /sbin/ifconfig tun create; /sbin/ifconfig tun0 name ocvpnc1

    Shellcmd Type:
    earlyshellcmd

    Description:
    Create tunnel interface for OVPNC1 at boot

    ——-

    This will create the missing interface at boot; early enough to avoid the interface mismatch, and you won’t have to reconfigure interfaces or hang at boot.

    I used the same setup for VPNC and works great.

    Thanks,

    DJC

  • HW says:

    This post helped me greatly getting started with openconnect! Thank you very much.

    One question: I am trying to connect to a larger VPN server, which assigns a different IP address to the client every time it is connected to. This essentially renders the gateway I had specified for the interface as kaput. Any advice on how that can be fixed?

  • HW says:

    OK, answering my own question here… with a named gateway, the following addition at the end of the script works for me:

    # grab our new IP address, edit the config file, and reload the filters
    ip=`$ifconfig $iface | grep ‘inet ‘ | awk ‘{ print $2 }’`
    xml ed -L -u ‘//gateway_item[name=”VPN_Gateway”]/gateway’ -v $ip /cf/conf/config.xml
    /etc/rc.filter_configure

    this required the xmlstarlet package to be installed.

  • ferret says:

    The install does not work in pfSense 2.3.2+:

    You will get this error: “pkg: No packages available to install matching ‘openconnect’ have been found in the repositories”

    To fix:

    1. Make sure “enabled” key is set to “yes” in /etc/pkg/FreeBSD.conf
    2. Change “enabled: no” to “enabled: yes” in /usr/local/etc/pkg/repos/FreeBSD.conf
    3. Change “enabled: no” to “enabled: yes” in /usr/local/etc/pkg/repos/pfSense.conf for FreeBSD repo
    4. Now you’re able to install packages from FreeBSD official repo

    https://forum.pfsense.org/index.php?PHPSESSID=7gohk3pvkochgitq2bf5r2d4b6&topic=109827.msg644067#msg644067

    • ferret says:

      One other thing… before doing the “pkg update” and “pkg install”, you may need to *temporarily* rename /usr/local/etc/pkg/repos. Once openconnect is installed, then rename it back.

  • Gerhard Recher says:

    Hi Bolt,

    intrestet in your minimal vm solution … which iptabels parameters will you use ?

    regards Gerhard

  • Robert says:

    Is it possible to make pfSense be a VPN Server for AnyConnect clients? I already have AnyConnect installed on many PC’s for work. A formal package would be wonderful!

  • Fred says:

    I was only able to get this partially working. The connection is made and pfsense sees the connection and a valid IP address. However, the gateway never gets assigned. And my subnet is wrong (255.255.255.255 instead of 255.255.255.0) so pfsense doesn’t let me manually assign a gateway. Without that, I haven’t been able to create any routes. Any ideas why that would happen or how to troubleshoot?

Leave a Reply

Your email address will not be published. Required fields are marked *