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

28 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?

  • psychozoic says:

    thank you!
    nice article!

    also i would like to add how to create NAT, cause GUI doesn’t know what is tun69.

    so, first you need to save all NAT rules:
    pfctl -sn > nat.pf

    than edit nat.pf and add needed rule. in my case that would be:
    nat on tun69 inet from 192.168.0.0/22 to any -> 10.12.43.180 port 1024:65535

    and i’ve put it before
    “no rdr proto carp all”

    after that you can reload NAT rules:
    pfctl -N -f nat.pf

    and it should work.

  • Charles Wise says:

    I’m banging my head against the wall on this one. I’ve got it to the point where the connection is established and the routes are created. I can ping servers on the remote network from the pfSense command-line. But nothing is getting routed from the outside network to the VPN.

    Any suggestions for where to look? I’ve got ocvpnc1 assigned to OPT2 and I’ve setup the firewall with allow everything for IPv4 and IPv6 – not other rules. IPv4 – allow all protocols, all sources, all destinations. Save for IPv6.

    Not sure how to diagnose this.

    Thanks in advance.

  • Charles Wise says:

    Never mind, I didn’t add NAT entries for the OPT2 interface. that fixed it.

  • Andriy Kolpakov says:

    I have set up everything as it was described and VPN was working very well for 2 months. Now there was some update on a server side and now when I log in the server prompts me the same password twice:
    First, it prompts for a username, then password and then: second password.

    After this second password was introduced I’m getting the below error:

    Connected to 194.xxx.xxx.xxx:443
    SSL negotiation with vpn.xxx.net
    Server certificate verify failed: unable to get local issuer certificate
    Connected to HTTPS on vpn.xxx.net
    XML POST enabled
    Please enter your username.
    POST https://vpn.xxx.net/auth
    Please enter your password.
    POST https://vpn.xxx.net/auth
    Please enter your password.
    Password1:
    fgets (stdin): Inappropriate ioctl for device
    Renaming tun69 to ocvpnc1
    ifconfig: interface tun69 does not exist

    Do you know how I should edit the script to support this second password, which is static and exactly the same as the first one?

    Thanks

    • bolt says:

      Hey. Unfortunately, I no longer have a need for this kind of setup.
      Since it grabs the password from stdin, it may be enough to do something like echo -e “$pass\n$pass” instead of the single one?
      If you figure it out, feel free to leave a follow-up comment so others can benefit 🙂

      • Chris says:

        It looks like your AnyConnect provider is requiring 2 factor auth and the client is parsing a form. You can double check this by running the anyconnect client from the command line with a dump of the responses. For example:

        openconnect –protocol=anyconnect vpn.server.com –dump-http-traffic -v

        You’ll probably see an HTML response in there with form field names like “username”, “password”, and “secondary_password”. Also, look for the form name, usually in a container tag like

        You can provide values for these form elements by passing them on the command line to the client:

        openconnect –protocol=anyconnect vpn.server.com –form-entry=main:username=myuser –form-entry=main:password=mypass –form-entry=main:secondary_password=push

        In my case “push” is the secondary password requirement that indicates the server should push a request to my mobile device for me to authorize the connection request (2 factor). It could also be a code provided by a key generator that has to be input within a certain amount of time. So if you’re seeing this “secondary_password” then it means you need to input the correct 2 factor mechanism or code.

        On the other hand it could be your provider gives you the option and there are multiple forms now–one that requires two factor and and another form that doesn’t. If that’s the case you can just pick the right form and pass the relevant values.

        Hope this helps someone.

  • Saju says:

    Policy route with openconnect client on pfsense 2.5.2 not working!

    I followed the procedure from https://forum.netgate.com/topic/103215/pfsense-with-openconnect

    The tunnel is up and I can ping the gateway on remote server. If I add a static route to send the traffic through tunnel, it work. But I can not send entire traffic from one IP in LAN through the tunnel. I have multiple openvpn connections which works perfectly with policy route in pfsense.

    Any help in this regard is appreciated

    John

Leave a Reply

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