My battle with Site-to-Site IPSEC (VTI): A tutorial of sorts
-
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 hereYou 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.
-