Skip to main content

Linux StrongSwan Cisco IPsec Configuration

Estimated reading time: 7 minutes (1384 words)

A quick post regarding setting up StrongSwan to connect to a Cisco IPsec endpoint, as the Cisco configuration makes a number of unnamed assumptions and it took a few hours to figure it out. This is specifically for configuring authentication with a shared key on ikev1

Configured on Ubuntu 24.04 LTS

1. Install packages

Just need to install strongswan

    ShellScript
    sudo apt install strongswan

    2. Get your variables prepared.

    In order to make sense of the config, it’s easier if you get all your variables you’ll need to replace ready ahead of time. You’ll need the following variables, with some examples provided.

    Python
    user_name     = "client1" # Your client connections user name
    user_password = "0xDADBEEF" # Your client connections password
    
    local_id      = "remote-site-1" # The unqiue name of the clients local id, this can be whatever you want.
    
    remote_id            = "outpost-x" # Your made up name for this configuration, it's the same name you use when you bring it up later.
    remote_shared_secret = "LetMeInnnn" # Secure Pre-Sharked IKE secret key, also called a PSK
    remote_endpoint      = "192.168.9.12" # IP or DNS name of the IPsec server
    right_subnet         = "192.168.10.0/24" # The configured server subnet to route traffic to

    3. Configure your secrets

    This should be located at /etc/ipsec.secrets configured with the owner as root and permissions as 0400.

    Jinja HTML
    "{{ remote_id }}" %any : PSK "{{ remote_shared_secret }}"
    {{ user_name }}} %any : XAUTH "{{ user_password }}"

    4. Configure your IPsec config

    This should be located at /etc/ipsec.conf with the owner as root and permissions more lax at 0644.

    Things to know about the IPsec config:

    • IPsec for some awful reason refers to all of it’s config as a left and right nomenclature where left is your local client connection (your local host) and right is the remote server.
    • The config makes use of a %var syntax to make sure of specific macros. These are documented upstream https://wiki.strongswan.org/projects/strongswan/wiki/ConnSection .
    Jinja HTML
    # ipsec.conf - strongSwan IPsec configuration file
    
    # Basic config
    config setup
            charondebug="ike 2, knl 2, cfg 2, net 2, esp 2, dmn 2, mgr 2"
    
    conn {{ local_id }}
            # "Cisco/IKEv1" as it's often referred to
            # isn't really a thing, it's just "ikev1"
            # with some preconfigured params.
            keyexchange=ikev1
            authby=secret
    
            # local (client) side
            ## Can define custom routes if needed by defining the src ip
            left=%defaultroute
            # What the IP you'll use in the remote network is.
            # by defining "%config" the upstream can provide you with what IP it sees fit.
            leftsourceip=%config
            leftauth=psk
            leftauth2=xauth
            
            # This should match the id of your PSK from /etc/ipsec.secrets
            # Confusingly it uses the "right" (remote) information. 
            # Many Cisco devices will reject your connection if your "leftid" and "rightid" don't match.
            leftid="{{ remote_id }}"
            # This should match the id of your XAUTH user password entry from /etc/ipsec.secrets
            xauth_identity={{ user_name }}
    
            # remote (gateway) side
            right={{ remote_endpoint }}
            rightid="{{ remote_id }}"
            # Subnet to route packets through (generally  needs to match what the IPsec server has configured)
            rightsubnet={{ right_subnet }}
            rightauth=psk
    
            # Try Aggressive Mode - many Cisco PSK+XAuth setups use this
            # If you don't enable this, the IPsec server may just never respond to you.
            aggressive=yes
    
            # Broader proposals to match average Cisco defaults
        ike=aes256-sha1-modp1024,aes128-sha1-modp1024,3des-sha1-modp1024!
            esp=aes256-sha1,aes128-sha1,3des-sha1!
            ikelifetime=24h
            lifetime=8h
            
            # Most Cisco gateways don't use PFS in Phase 
            pfs=no
            dpdaction=restart
            dpddelay=30s
            dpdtimeout=120s
            auto=add
    

    5. Start the Server

    For first time setup, you’ll need restart the Charon (in strongswan-start) and then bring up your IPSec connection. (On many systems the “ipsec” and “strongswan-starter” are the same service unit.

    Bash
    # Restart the Charon
    user@host:~# sudo systemctl restart strongswan-starter.service
    # Bring up the network
    user@host:~# sudo ipsec up {{ local_id }}
    ...
    XAuth authentication of 'user_name' (myself) successful
    IKE_SA {{ local_id }}[1] established between {{ local_ip }}}[{{ remote_id }}]...{{ remote_ip }}[{{ remote_id }}]
    ...
    installing new virtual IP 192.168.10.100
    ...
    received packet: from {{ remote server ip }}[4500] to {{ local_ip }}[4500] (156 bytes)
    connection '{{ local_id }}' established successfully

    Hopefully at this point things should just work for you (note in the current format you still need to run sudo ipsec up {{ local_id }} again after a reboot).

    6. Debugging Tips

    I spent a few too many hours debugging this, so let’s start walking through some things you can do to debug:

    1. sudo ipsec status
      • A general status command, a good starting place for “am I connected already or not?”
    2. Charon log levels
      • Can be fairly useful, just add the block log_level = X to the charon section of /etc/strongswan.d/charon.conf (or as a drop-in to /etc/strongswan.d/charon/).
      • Log levels are -1 (no logging) to 4 (everything including sensitive info), usually log_level = 2 will cover most of your cases.
    3. tcpdump
      • Old faithful, just run a tcpdump -i any -n "host {{ remote_server_ip }} and (udp port 500 or udp port 4500)"
      • Genuinely, I spent most of the time figuring out to set this up by doing TCP Dumps, and seeing what the remote actually responded to and didn’t.
      • Wireshark also works fine for this.
    4. Don’t look at ip route !
      • Don’t look at ip route or iptable-save for the routes to your remote! Most of the time StrongSwan will configure it as an XFRM policy base. Instead try looking at sudo ipsec xfrm policy
    5. System logs (or journald)
      • The usual place may often have some useful info journalctl --unit strongswan-start (if you try to specify the unit as ipsec, you won’t get results, surprisingly against how earlier aliasing works for status/start/stop/etc).
    6. Percussive Maintenance
      • Sometimes it helps, or at least makes you feel better.
      • It’s a Cisco switch, it could use reminder who’s boss anyways.