-1

Here's the rough picture of what I'm trying to do:

client -> |VPN| -> server A -> |VPN| -> server B -> Internet.

Server A and Server B are both on public internet with public IPs, also I have root access to both servers. Client is behind firewall but can reach server A. Client CANNOT reach server B directly. Server A can reach server B. Server B is where I want packets to exit to internet from. I need to encrypt both communications from client to A, and from A to B, since A and B aren't in a common LAN so traffic between A and B travel via public infrastructure.

I have been able to setup a IPSec VPN server on A and are able to connect to it. But I can't figure out the correct way to establish another VPN from A to B and reroute traffic coming to A from client to B.

Tips?

p.s. originally I thought of using SSH tunnels to connect A and B then route relevant packet from client arriving at A to B via such tunnel. But as people pointed out in comments that this isn't such a good idea. So I'm open to any suggestions. Thanks!

EDIT #1

VPN service on server A is setup with strongswan, which handles any connection between clients and server A. I was done via ipsec.conf:

# ipsec.conf - strongSwan IPsec configuration file

# basic configuration
config setup
        charondebug="ike 2, knl 2, cfg 2, net 2, esp 2, dmn 2, mgr 2"
        strictcrlpolicy=no
        uniqueids=yes
        cachecrls=no
conn ipsec-ikev2-vpn
      auto=add
      compress=no
      type=tunnel  # defines the type of connection, tunnel.
      keyexchange=ikev2
      fragmentation=yes
      forceencaps=yes
      dpdaction=clear
      dpddelay=300s
      rekey=no
      left=%any
      leftid=47.112.200.xxx    # if using IP, define it without the @ sign
      leftcert=vpn-server.cert.pem  # reads the VPN server cert in /etc/ipsec.d/certs
      leftsendcert=always
      leftsubnet=0.0.0.0/0
      right=%any
      rightid=%any
      rightauth=eap-mschapv2
      rightsourceip=10.10.10.0/24  # IP address Pool to be assigned to the clients
      rightdns=1.1.1.1,8.8.8.8  # DNS to be assigned to clients
      rightsendcert=never
      eap_identity=%identity  # defines the identity the client uses to reply to an EAP Identity request.

With ufw (the tutorial I followed used ufw but I can work with basic iptables rules as well) settings in /etc/ufw/before.rules; I omitted portion auto-generated by ufw, also I've manually allowed udp on port 500/4500 and tcp on port 22:

#
# rules.before
#
# Rules that should be run before the ufw command line added rules. Custom
# rules should be added to one of these chains:
#   ufw-before-input
#   ufw-before-output
#   ufw-before-forward
#

# Don't delete these required lines, otherwise there will be errors
*nat
-A POSTROUTING -s 10.10.10.0/24 -o eth0 -m policy --pol ipsec --dir out -j ACCEPT
-A POSTROUTING -s 10.10.10.0/24 -o eth0 -j MASQUERADE
COMMIT

*mangle
-A FORWARD --match policy --pol ipsec --dir in -s 10.10.10.0/24 -o eth0 -p tcp -m tcp --tcp-flags SYN,RST SYN -m tcpmss --mss 1361:1536 -j TCPMSS --set-mss 1360
COMMIT

*filter
:ufw-before-input - [0:0]
:ufw-before-output - [0:0]
:ufw-before-forward - [0:0]
:ufw-not-local - [0:0]
# End required lines


-A ufw-before-forward --match policy --pol ipsec --dir in --proto esp -s 10.10.10.0/24 -j ACCEPT
-A ufw-before-forward --match policy --pol ipsec --dir out --proto esp -d 10.10.10.0/24 -j ACCEPT

This is how far I've got. Separate attempt to setup site-to-site VPN via strongswan IPSec between A and B has failed due to port 500 and 4500 being blocked/tempered with: client can reach A via 500/4500, A cannot reach B via 500/4500, and I've exposed all ports of B to open internet for testing.

EDIT 2

Currently the only tunnel I've managed to make between A and B is a SSH socks (ssh -N -f -4 -D 1080 [server B ip] from server A). I'm thinking about just redirecting traffic exiting A, whose destination isn't my client device, to B via that SSH tunnel.

Something like:-A OUTPUT -p tcp --dport 443 -j REDIRECT --to-ports 1080 in ufw/iptables.(this doesn't actually work, server A's ip still shows up in ip checker and blocked websites are still not accessible)

allen
  • 1
  • 2
  • I would advise against using ssh for the VPN tunnel, since it is TCP based (so a packet loss causes retransmissions in both the ssh connection and tunneled traffic). If you must do it, use [ssh with tun devices](https://serverfault.com/a/985889/530633). – Piotr P. Karwasz Jan 11 '20 at 00:02
  • @PiotrP.Karwasz Thank you for your input. I've edited the question so I'm open to any means to achieve my goal now. Any further suggestions? – allen Jan 11 '20 at 08:03
  • 1
    If you know how to create a tunnel fra A to B, you could use source based routing to send data from the original client to the Internet via A and B. On server A the rule would be: traffic originating tunnel from client has to be sent through tunnel to server B. On server B you can use normal masquerade techniques. – Lasse Michael Mølgaard Jan 11 '20 at 08:50
  • @allen What are you using to configure IPSec? [ipsec.conf](https://wiki.strongswan.org/projects/strongswan/wiki/IpsecConf) or [swanctl.conf](https://wiki.strongswan.org/projects/strongswan/wiki/Swanctlconf)? – Piotr P. Karwasz Jan 11 '20 at 17:05
  • @PiotrP.Karwasz ipsec.conf for I was following a tutorual – allen Jan 13 '20 at 09:16
  • @LasseMichaelMølgaard Which chain should I work with? nat or filter? And for SOCKs tunnel bind with local port 1080, should I just redirect any tcp packet to port 1080 on server A then? Thanks. – allen Jan 13 '20 at 10:04
  • Well my solution works better when using OpenVPN all the way. Though you need to add static routes for client subnet on both `Server A` and `Server B`. On `Server B` I would setup iptables with masquerade rules, since you can't use RFC1918 IP-adresses beyond your own network. All the Internet will see is traffic came from the public ip address of `Server B`. The rules are much easier for IPv6, due to basically all IPv6 adresses are public ip addresses. However: If you are most familiar with IPSec then @piotr solution will be closer to the end result of what you want. – Lasse Michael Mølgaard Jan 13 '20 at 12:13
  • @LasseMichaelMølgaard I don't personally have anything against ovpn but due to IOS devices won't play nice with ovpn, I'm left with not much choice but to go with IPSec. But thank you for your input, I enjoyed learning the new stuff. – allen Jan 13 '20 at 13:05
  • @allen, I added an `ipsec.conf` configuration example. – Piotr P. Karwasz Jan 13 '20 at 16:18

2 Answers2

1

Expanding on my hint in the comment.

enter image description here

I know how to make tunnels in OpenVPN, since I use it to get connected to IPv6 network, but the idea should be the same for IPSec.

I'll start with what I am familiar with (which is IPv6) and show how it can be translated to IPv4.

Though a friendly disclaimer:

I haven't experiemented with multihome routing on IPv4, so your milage may vary.


Server A - IPv6 version

On my server the OpenVPN tunnel network interface is called tun0.

On Server A edit /etc/iproute2/rt_tables, so you have the following entry:

200     openvpn

This will create a dedicated routing table that we can use for VPN routing. The default routing table is called main.

The table openvpn will handle all packages that are sent upstream (you -> the Internet), while the routing table main will handle all traffic going downstream (the Internet -> you).

ip -6 rule add priority 32000 iif tun0 to 2000::/3 table openvpn
ip -6 route add default via <ipv6 address> dev tun1 table openvpn

Note you will have to add rules for any ip range that belongs to you.

Assuming the IPv6 range 2001:db8:cafe::/48 belongs to you and you have the follwing setup:

  • client vpn link is residing on 2001:db8:cafe:1::/64 with the client having the ip address 2001:db8:cafe:1::100
  • client local net is 2001:db8:cafe:100::/64 that would give the rules:
ip -6 route add unreachable 2001:db8:cafe::/48 table main
ip -6 route add 2001:db8:cafe:100::/64 via 2001:db8:cafe:1::100 dev tun0

Server A - IPv4 version

The IPv4 version would be:

We still need a dedicated routing tabel due we need to route traffic based on origin, hence /etc/iproute2/rt_tables still need the entry:

200     openvpn

Assuming IPv4 rules is pretty much analogue to IPv6 gives os the follwing commands for the openvpn routing table:

ip rule add priority 32000 iif tun0 to 0.0.0.0/0 table openvpn
ip route add default via <ipv4 address> dev tun1 table openvpn

Now I am not sure if there are any IPv4 ranges that needs to been caught at this point (like for instance RFC1918 addresses), since the general idea is basically forward everything that comes in on interface tun0 and send it out on tun1 in the diagram above.


Server B - IPv6 version

Now here are two approaces to how IPv6 traffic is forwarded to the Internet:

  • If you have gotten a routeable IPv6 pool from the ISP connected to Server B, then good for you. The default routing table main can problably do the whole thing for you.

  • If you have a IPv6 block from say Hurricane Electric, then you basically need the same setup as on Server A with a dedicated routing table for upstream and using the default routing table for downstream.

Server B - IPv4 version

Here the easiest solution is problably treat Server B as being a NAT, so you need to masquerade all packages comming from you net, before sending them out on the Internet.

1

If you are using IPSec on Linux (StrongSwan) policy routing is performed using ip xfrm policy: policies with dir out are used to decide which tunnel to forward packets to, while policies with dir in and dir fwd are used to decide whether allow or drop the packet.

Assume serverA has public IP 192.0.2.1 and private IP 10.0.10.1 (you can attach it to the loopback device), serverB has public IP 198.51.100.1 and private IP 10.0.20.1, while the VPN clients get an IP in the network 10.0.30.0/24. You can establish a tunnel using a swanctl.conf on serverA like this:

connections {
  serverB {
    version = 2
    local_addrs=192.0.2.1
    remote_addrs=198.51.100.1
    local {
        # serverA auth parameters
    }
        # serverB auth parameters
    }
    children {
      tunnel {
        local_ts = 10.0.10.0/24
        remote_ts = 10.0.20.0/24
        start_action = start
        reqid = 10
        priority = 1000
      }
    }
  }
}

and on serverB a similar configuration with local* and remote* inversed.

Remark that the reqid and priority parameters are fixed, so they can be used in policies. This can't be done in ipsec.conf.

To configure policy routing you need on serverA to:

  1. Allow encrypted traffic from IPs in 10.0.30.0/16 to any IP. This is done automatically if you set local_ts = 0.0.0.0/0 in the connection configuration of the VPN clients.
  2. Allow encrypted traffic to 10.0.30.0/24 from the tunnel with serverB:

    ip xfrm policy add src 0/0 dst 10.0.30/24 \
        dir fwd priority 2000 \
        tmpl src 198.51.100.1 dst 192.0.2.1 \
        proto esp reqid 10 mode tunnel
    

    This works similarly to an ACCEPT rule in the FORWARD ip chain.

  3. Forward traffic from 10.0.30.0/24 to serverB:

    ip xfrm policy add src 10.0.30/24 dst 0/0 \
        dir out priority 2000 \
        tmpl src 192.0.2.1 dst 198.51.100.1 \
        proto esp reqid 10 mode tunnel
    

The priorities are higher than those automatically installed by StrongSwan, so if these policies are installed forwarding is allowed. If you delete these policies, those installed by StrongSwan will allow only traffic between 10.0.10.0/24 and 10.0.20.0/24. On serverB you can use the same policies, just exchange dir out with dir fwd.

PS: Of course you need also some iptables rules so that serverA and serverB allow ESP and IKE traffic:

iptables -I INPUT -p esp -j ACCEPT
iptables -I INPUT -p udp -m multiport --dports 500,4500 -m comment --comment "IKE" -j ACCEPT

and some drop rules so that packets don't exit unencrypted if the tunnel goes down:

iptables -A OUTPUT -o <external_interface> -s 10.0.0.0/16 -m policy --dir out --policy ipsec -j ACCEPT
iptables -A OUTPUT -o <external_interface> -s 10.0.0.0/16 -j DROP

Edit: Since you are using ipsec.conf and equivalent configuration to the aforementioned swanctl.conf is:

conn servers {
    left=192.0.2.1
    right=198.51.100.1
    # The appropriate authentication leftauth, leftid, ...
    leftsubnet = 10.0.10.0/24
    rightsubnet = 10.0.20.0/24
    auto = start
    reqid = 10
}

With respect to the swanctl.conf it has the advantage of working without modification on both hosts, but it does not enable setting the priority of the policies, so you'll need to check, what StrongSwan chooses to set a higher priority.

Piotr P. Karwasz
  • 5,748
  • 2
  • 11
  • 21
  • Haven't played with StrongSwan, but looking at the configuration: Should `local_ts` and `remote_ts` belong to same subnet? `10.0.20.0/24` is not in same subnet as `10.0.30.0/24`. – Lasse Michael Mølgaard Jan 12 '20 at 09:59
  • No, they are the private networks behind the VPN endpoints. StrongSwan will install on serverA a counter intuitive route `10.0.20.0/24 via dev `. The packets with destination `10.0.20.0/24` will not actually be sent as is to the next Internet hop towards serverB: the `xfrm` framework will check the policy and wrap + encrypt the packet in a packet from `192.0.2.1` to `198.51.100.1`. – Piotr P. Karwasz Jan 12 '20 at 10:12
  • Right. Looks like I have some advanced VPN networking to read up on! Head spinning as is. :-) – Lasse Michael Mølgaard Jan 12 '20 at 14:15