Netgate Discussion Forum
    • Categories
    • Recent
    • Tags
    • Popular
    • Users
    • Search
    • Register
    • Login

    My battle with Site-to-Site IPSEC (VTI): A tutorial of sorts

    Scheduled Pinned Locked Moved IPsec
    1 Posts 1 Posters 1.3k Views
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • R
      rmccall2k16
      last edited by rmccall2k16

      I was extremely bored today. Decided to setup an IPv4/v6 VTI to a VPS I purchased. Please see below and ask questions as needed. I have not cleaned up anything or did any proofreading, so if you see something off let me know.

      For informational purposes:
      
      PFSENSE HOST FQDN: fw.foundry.cx
      PFSENSE WAN IP: 77.77.77.77
      PFSENSE WAN NETWORK: 77.77.77.0/24
      PFSENSE LOCAL MANAGEMENT SUBNET: 172.16.0.0/29
      PFSENSE LAN SUBNET: 172.16.0.0/12
      PFSENSE VTI IP: 192.168.254.2
      
      VPS HOST FQDN = sentinel.foundry.cx
      VPS WAN IP    : 88.88.88.88
      PFSENSE VTI IP: 192.168.254.1
      
      

      Create a CA on PFSENSE HOST. I called mine "FOUNDRY-Server-CA"
      Once done, create 2 certificates (I named mine the following):

      • FOUNDRY-Server-LOCALCERT : This will be the PFSENSE IPSEC TUNNEL CERT

      • FOUNDRY-Server-REMOTECERT : This will be the REMOTE VIRTUAL PRIVATE SERVER (VPS) host. I use CentOS 7/8 on any VPS that I run fyi.

      • Once you create the certs above, export the "FOUNDRY-Server-REMOTECERT" certificate, private key, and the "Foundry-Server-CA" certificate and save them on your computer for later.

      • Next, create the following rules to accept incoming IPSEC connections on the PFSENSE host.

      Permit source 88.88.88.88 from any source port to destination 77.77.77.77 on UDP port 500
      Permit source 88.88.88.88 from any source port to destination 77.77.77.77 on UDP port 4500 (<-May not be needed)

      If your PFSENSE host is only acting as an initiator, then no inbound firewall rules need to be configured! This is common.

      • Next up, go into VPN > IPSEC on the PFSENSE host.
      • Click "Add P1" and use the following settings:
      Key Exchange version = IKEv2
      Internet Protocol  = Dual Stack
      Interface = WAN (Or whatever interface you use for a WAN)
      Remote Gateway = 88.88.88.88
      Description = <Whatever you want to call it>
      
      Authentication method = mutual rsa
      My identifier = distinguished name = fw.foundry.cx
      Peer identifier = distinguished name = sentinel.foundry.cx
      My certificate = FOUNDRY-Server-LOCALCERT
      Peer Certificate Authority = FOUNDRY-Server-CA
      
      Encryption Algorith
       - AES256-GCM	=	128 bits	=	SHA384	= Group 16
       - AES192-GCM	=	128 bits	=	SHA384	= Group 16
      
      Lifetime = 28800
      
      Under advanced options, leave everything as is. Instead of SHA384, you can use AES-XCBC. I'm testing it now.
      

      Under advanced options, leave everything as is. Go back to the previous page;
      Under VPN > IPsec > Tunnels, click "Show phase 2 entries", then click " ADD P2" and input the following settings:

      Mode = Routed (VTI)
      Local Network = 192.168.254.2/30
      Remote Network = 192.168.254.1/0
      
      Protocol = ESP
      Encryption Algorithms = 
        AES192-GCM - 128 bits
        AES256-GCM - 128 bits
      Hash Algortihms =
        SHA384
      PFS Key Group = 16
      Lifetime =3600
      
      Automatically Ping Host = 192.168.254.1
      

      Click save and Lets move to the CentOS7 VPS!

      Once you are logged in as root (and you've ran yum -y update [and dnf update if using centos 8]), apply the following commands

      (Install epel)

      yum -y install epel-release
      

      (Upgrade the kernel to latest)

      rpm --import https://www.elrepo.org/RPM-GPG-KEY-elrepo.org
      rpm -Uvh http://www.elrepo.org/elrepo-release-7.0-3.el7.elrepo.noarch.rpm
      yum --enablerepo=elrepo-kernel install -y kernel-ml
      sed -i "s:GRUB_DEFAULT=saved:GRUB_DEFAULT=0:g" /etc/default/grub
      grub2-mkconfig -o /boot/grub2/grub.cfg
      

      (Install strongswan)

      yum --enablerepo=epel -y install  strongswan
      

      (Erase cert files that are present if any)

      echo "" > /etc/strongswan/ipsec.d/cacerts/cacert.pem
      echo "" > /etc/strongswan/ipsec.d/certs/ipsechost-cert.pem
      echo "" > /etc/strongswan/ipsec.d/private/ipsechost-key.pem
      

      For the file "cacert.pem", paste the contents of the FOUNDRY-Server-CA crt in here.
      For the file "ipsechost-cert.pem", paste the contents of the FOUNDRY-Server-REMOTECERT crt file in here
      For the file "ipsechost-key.pem", paste the contents of the FOUNDRY-Server-REMOTECERT key file in here

      You can use vi or nano to accomplish that, or use the commands below:

      rm -rf /etc/strongswan/ipsec.d/cacerts/cacert.pem
      touch /etc/strongswan/ipsec.d/cacerts/cacert.pem
      echo "
      Please paste the CA CERTIFICATE below :
      "
      python -c 'import sys;print("Type Ctrl + D when finished");f=open("/etc/strongswan/ipsec.d/cacerts/cacert.pem","w");[f.write(l) for l in sys.stdin.readlines()];f.close()'
      
      rm -rf /etc/strongswan/ipsec.d/certs/ipsechost-cert.pem
      touch /etc/strongswan/ipsec.d/certs/ipsechost-cert.pem
      echo "
      Please paste the HOST CERTIFICATE below :
      "
      python -c 'import sys;print("Type Ctrl + D when finished");f=open("/etc/strongswan/ipsec.d/certs/ipsechost-cert.pem","w");[f.write(l) for l in sys.stdin.readlines()];f.close()'
      
      rm -rf /etc/strongswan/ipsec.d/private/ipsechost-key.pem
      touch /etc/strongswan/ipsec.d/private/ipsechost-key.pem
      echo "
      Please paste the PRIVATE KEY CERTIFICATE below :
      "
      python -c 'import sys;print("Type Ctrl + D when finished");f=open("/etc/strongswan/ipsec.d/private/ipsechost-key.pem","w");[f.write(l) for l in sys.stdin.readlines()];f.close()'
      

      (Configure the connection)

      echo "" > /etc/strongswan/ipsec.conf
      cat <<EOF | tee /etc/strongswan/ipsec.conf
      conn A-B
        left=88.88.88.88
        right=77.77.77.77
        leftsubnet = ::/0,0.0.0.0/0
        rightsubnet = ::/0,0.0.0.0/0
        ike=aes256gcm128-sha384-modp4096,aes192gcm128-sha384-modp4096!
        esp=aes192gcm128-sha384-modp4096,aes256gcm128-sha384-modp4096!
        ikelifetime=28800s
        lifetime=14400s
        dpdaction=restart
        dpddelay=10s
        dpdtimeout=60s
        rekey=yes
        keyexchange=ikev2
        reauth=yes
        auto=start
        fragmentation=yes
        leftcert=ipsechost-cert.pem
        leftid=fqdn:sentinel.foundry.cx
        rightauth=pubkey
        leftauth=pubkey
        leftsendcert=always
        leftca="/CN=CN=FOUNDRY-Server-CA/C=US/ST=new jersey/L=newark/O=The Foundry/"
        rightid=fqdn:fw.foundry.cx
        mark=12
      EOF
      

      (Define private key)

      cat <<EOF | tee /etc/strongswan/ipsec.secrets
      : RSA ipsechost-key.pem
      EOF
      

      (Configure CHARON)

      echo "" > /etc/strongswan/strongswan.conf
      cat <<EOF | tee /etc/strongswan/strongswan.conf
      # strongswan.conf - strongSwan configuration file
      #
      # Refer to the strongswan.conf(5) manpage for details
      #
      # Configuration changes should be made in the included files
      
      charon {
              install_routes = 0
              load_modular = yes
              plugins {
                      include strongswan.d/charon/*.conf
              }
      }
      
      include strongswan.d/*.conf
      EOF
      

      STOP the firewall by issuing "systemctl firewalld stop"
      DISABLE the firewall by issuing "systemctl disable firewalld"

      I use the following script to configure my firewall:

      touch /etc/init.d/vpsfw
      cat <<EOF | tee /etc/init.d/vpsfw
      #!/bin/bash
      ### BEGIN INIT INFO
      
      # Provides: vpsfw
      # Required-Start: mountkernfs \$local_fs
      # Required-Stop: \$local_fs
      # Default-Start: 2 3 4 5
      # Default-Stop: 0 1 6
      # Short-Description: Set up iptables rules and read a list of blocked ips
      ### END INIT INFO ###
      
      SS5=/usr/sbin/ss5
      IPTABLES=/sbin/iptables
      SPAMLIST="blockedip"
      SPAMDROPMSG="BLOCKED IP DROP"
      SYSCTL="/sbin/sysctl"
      BLOCKEDIPS="/opt/vdot/blocked.ips.txt"
      
      # LOCAL INTERFACES
      INIF=ens192
      FWDIF=vti0
      REMOTE_HOST_NET=172.16.0.0
      
      # HOMENET1 for SSH ENABLE
      HOMENET1=77.77.77.0/24
      
      # HOMENET2 for SSH ENABLE
      HOMENET2=172.16.0.0/29
      
      # LOCAL GATEWAY IP (ON THIS HOST)
      CURRENTGWIP=
      
      test -x \$IPTABLES || exit 5
      
      case "\$1" in
      start)
      echo -n " Loading packet filters"
      
      #SETUP
      
      #load kernel modules first
      modprobe ip_tables
      
      
      [ -f "\$BLOCKEDIPS" ] && BADIPS=\$(egrep -v -E "^#|^\$" "\${BLOCKEDIPS}")
      
      #Flush old rules
      \$IPTABLES -t nat -F
      \$IPTABLES -t mangle -F
      \$IPTABLES --flush
      \$IPTABLES --delete-chain
      
      #set default-deny policies for all three chains
      \$IPTABLES -P INPUT DROP
      \$IPTABLES -P FORWARD DROP
      \$IPTABLES -P OUTPUT DROP
      
      if [ -f "\${BLOCKEDIPS}" ];
      then
      # create a new iptables list
      \$IPTABLES -N \$SPAMLIST
      
      for ipblock in \$BADIPS
      do
         \$IPTABLES -A \$SPAMLIST -s \$ipblock -j LOG --log-prefix "\$SPAMDROPMSG "
         \$IPTABLES -A \$SPAMLIST -s \$ipblock -j DROP
      done
      
      \$IPTABLES -I INPUT -j \$SPAMLIST
      \$IPTABLES -I OUTPUT -j \$SPAMLIST
      \$IPTABLES -I FORWARD -j \$SPAMLIST
      fi
      
      # Stop certain attacks
      echo "Setting sysctl IPv4 settings..."
      \$SYSCTL net.ipv4.ip_forward=1
      \$SYSCTL net.ipv4.conf.all.send_redirects=0
      \$SYSCTL net.ipv4.conf.default.send_redirects=0
      \$SYSCTL net.ipv4.conf.all.accept_source_route=0
      \$SYSCTL net.ipv4.conf.all.accept_redirects=0
      \$SYSCTL net.ipv4.conf.all.secure_redirects=0
      \$SYSCTL net.ipv4.conf.all.log_martians=1
      \$SYSCTL net.ipv4.conf.default.accept_source_route=0
      \$SYSCTL net.ipv4.conf.default.accept_redirects=0
      \$SYSCTL net.ipv4.conf.default.secure_redirects=0
      \$SYSCTL net.ipv4.icmp_echo_ignore_broadcasts=1
      \$SYSCTL net.ipv4.tcp_syncookies=1
      \$SYSCTL net.ipv4.conf.all.rp_filter=0
      \$SYSCTL net.ipv4.conf.default.rp_filter=0
      \$SYSCTL kernel.randomize_va_space=1
      
      
      #for loopback interfaces
      \$IPTABLES -A INPUT -i lo -j ACCEPT
      \$IPTABLES -A OUTPUT -o lo -j ACCEPT
      
      #anti-IP-spoofing here
      #if the host is in one of these ranges either modify or comment the appropriate range
      \$IPTABLES -A INPUT -s 255.0.0.0/8 -j LOG --log-prefix "Spoofed source IP!"
      \$IPTABLES -A INPUT -s 255.0.0.0/8 -j DROP
      \$IPTABLES -A INPUT -s 0.0.0.0/8 -j LOG --log-prefix "Spoofed source IP!"
      \$IPTABLES -A INPUT -s 0.0.0.0/8 -j DROP
      \$IPTABLES -A INPUT -s 127.0.0.0/8 -j LOG --log-prefix "Spoofed source IP!"
      \$IPTABLES -A INPUT -s 127.0.0.0/8 -j DROP
      
      #inbound policies
      #accept inbound packets that are previously ok'ed
      \$IPTABLES -A INPUT -j ACCEPT -m state --state RELATED,ESTABLISHED
      
      #accept SSH
      \$IPTABLES -A INPUT -p tcp -i \$INIF -s \$HOMENET1 --dport 22 -m state --state NEW -j ACCEPT
      \$IPTABLES -A INPUT -p tcp -i \$INIF -s \$HOMENET2 --dport 22 -m state --state NEW -j ACCEPT
      
      #handle ICMP
      \$IPTABLES -A INPUT -p icmp --icmp-type echo-request -j ACCEPT
      \$IPTABLES -A OUTPUT -p icmp --icmp-type echo-reply -j ACCEPT
      \$IPTABLES -A OUTPUT -p icmp --icmp-type echo-request -j ACCEPT
      \$IPTABLES -A INPUT -p icmp --icmp-type echo-reply -j ACCEPT
      
      #handle NTP
      \$IPTABLES -A INPUT -p udp --sport 123 -j ACCEPT
      \$IPTABLES -A INPUT -m state --state NEW -m udp -p udp --dport 123 -j ACCEPT
      
      #VPN INTERFACE PORTS
      \$IPTABLES -A INPUT -p udp --dport 4500 -s \$HOMENET1 -j ACCEPT
      \$IPTABLES -A INPUT -p udp --dport 500 -s \$HOMENET1 -j ACCEPT
      
      #logging all other inbound traffic
      \$IPTABLES -A INPUT -j LOG --log-prefix "Dropped by default (INPUT):"
      
      #outbound policies
      #logging all other outbound traffic
      \$IPTABLES -A OUTPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
      \$IPTABLES -A OUTPUT -o lo -j ACCEPT
      \$IPTABLES -A OUTPUT -m limit --limit 6/hour -j LOG --log-level 4 --log-prefix 'OutAllow6/h '
      \$IPTABLES -A OUTPUT -j ACCEPT
      
      #forward policies
      #####FORWARDING HAS BEEN DISABLED IN THIS SCRIPT####
      \$IPTABLES -A FORWARD -m state --state ESTABLISHED,RELATED -j ACCEPT
      \$IPTABLES -A FORWARD -i \$FWDIF -o \$INIF -j ACCEPT
      
      #mangle policies
      \$IPTABLES -t mangle -A FORWARD -i \$INIF -p tcp -m tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu
      \$IPTABLES -t mangle -A FORWARD -o \$INIF -p tcp -m tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu
      \$IPTABLES -t mangle -A FORWARD -i \$INIF -p tcp -m tcp --tcp-flags SYN,RST SYN -j TCPMSS --set-mss 1386
      \$IPTABLES -t mangle -A FORWARD -o \$INIF -p tcp -m tcp --tcp-flags SYN,RST SYN -j TCPMSS --set-mss 1386
      
      #prerouting policies
      #PORT FORWARDING TO HOST (DNAT) - EXAMPLE
      \$IPTABLES -t nat -A PREROUTING -i \$INIF -p udp --dport 51413 -j DNAT --to 172.16.200.140:51413
      \$IPTABLES -t nat -A PREROUTING -i \$INIF -p tcp --dport 51413 -j DNAT --to 172.16.200.140:51413
      
      #postrouting policies
      \$IPTABLES -t nat -A POSTROUTING -o \$INIF -j MASQUERADE
      
      #Log any packets which don't fit the rules above...
      #(optional but useful)
      \$IPTABLES -A INPUT -m limit --limit 3/min -j LOG --log-prefix "\$IPTABLES_INPUT_denied: " --log-level 4
      \$IPTABLES -A FORWARD -m limit --limit 3/min -j LOG --log-prefix "\$IPTABLES_FORWARD_denied: " --log-level 4
      \$IPTABLES -A OUTPUT -m limit --limit 3/min -j LOG --log-prefix "\$IPTABLES_OUTPUT_denied: " --log-level 4
      
      ;;
      
      wide_open)
      echo -n "Danger allowing all traffic everywhere"
      \$IPTABLES --flush
      \$IPTABLES -P INPUT ACCEPT
      \$IPTABLES -P FORWARD ACCEPT
      \$IPTABLES -P OUTPUT ACCEPT
      ;;
      
      stop)
      echo -n "stopping ....."
      \$IPTABLES --flush
      ;;
      
      status)
      echo "querying iptables status ..."
      \$IPTABLES --line-number -v --list
      ;;
      
      *)
      echo "Usage: \$0 (start|stop|wide_open|status)"
      exit 0
      ;;
      esac
      EOF
      

      ENSURE YOUR HOMENET ADDRESSES ARE SET PROPERLY OR YOU'LL LOCK YOURSELF OUT OF YOUR VPS!

      (Allow execution)

      chmod +x /etc/init.d/vpsfw
      

      (Execute it)

      /etc/init.d/vpsfw start
      

      (Start strongswan and enable it on boot)

      systemctl enable strongswan
      systemctl start strongswan
      

      (Configure SYSCTL now and permenantly)

      sysctl -w net.ipv4.ip_forward=1
      sysctl -w net.ipv4.conf.ens192.disable_xfrm=1
      sysctl -w net.ipv4.conf.ens192.disable_policy=1
      sysctl -w net.ipv4.conf.vti0.disable_policy=1
      sysctl -w net.ipv4.conf.vti0.rp_filter=0
      
      echo "net.ipv4.ip_forward=1" >> /etc/sysctl.conf
      echo "net.ipv4.conf.ens192.disable_xfrm=1" >> /etc/sysctl.conf
      echo "net.ipv4.conf.ens192.disable_policy=1" >> /etc/sysctl.conf
      echo "net.ipv4.conf.vti0.disable_policy=1" >> /etc/sysctl.conf
      echo "net.ipv4.conf.vti0.rp_filter=0" >> /etc/sysctl.conf
      

      (Configure IPv4/IPv6 VTI interface)

      ip link del vti0
      ip link add vti0 type vti local 88.88.88.88 remote 77.77.77.77 ikey 12 okey 12
      ip addr add 192.168.254.1/30 remote 192.168.254.2/30 dev vti0
      ip route add 172.16.0.0/12 via 192.168.254.2 dev vti0
      ip link set dev vti0 mtu 1400
      ip link set up dev vti0
      

      Go back to the pfsense box. Look at the Status of IPSEC. You should see a connection established message by now.
      Go to Interfaces > Assignments.
      Under "Available network ports", select the new ipsec interface (ipsec1xxx) and click "add"
      Go to Interfaces and select the new ipsec interface you just added and make sure its ENABLED.

      Now, go to Firewall > Rules > "IPSEC" and configure the following rule:

      • Permit source network <LAN/MANAGEMENT/ETC. net> from any source port to any destination on any port.

      Now, go to System > Routing. You should see your VTI interface there somewhere (ends in VTIV4). Click edit.
      Make sure that the "Monitor IP" field has the remote ipsec host VTI point-to-point IP input here (192.168.254.1 in this case)
      Save and exit.

      Now, check on your tunnel on both ends and see if you can pass traffic. TCPDump is very useful here. Also note that your VTI should display on the main dashboard the reachability of VTI addresses.

      1 Reply Last reply Reply Quote 3
      • First post
        Last post
      Copyright 2025 Rubicon Communications LLC (Netgate). All rights reserved.