2

I have a server which I VPN into using strongswan with a IKEv2 setup and it works as expected. This setup assigns my client machine a IP in the 10.10.10.0/24 range. What I'd like to be able to do is open all ports to the server from clients connected to it via VPN. Is something like this possible? Can I write a rule that targets everything coming from a VPN client?

Here's my current set of iptables.rules:

*filter

# default policies
-P INPUT DROP
-P OUTPUT ACCEPT
-P FORWARD DROP

# established connections keep working
-A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT

# connections from loopback are accepted
-A INPUT -i lo -j ACCEPT

# invalid requests
-A INPUT -m state --state INVALID -j DROP

# sshd
-A INPUT -p tcp --dport 22 -j ACCEPT

# ikev2/ipsec
-A INPUT -p udp -m udp --dport 500 -j ACCEPT
-A INPUT -p udp -m udp --dport 4500 -j ACCEPT

# pings
-A INPUT -p icmp --icmp-type echo-reply -j ACCEPT
-A INPUT -p icmp --icmp-type echo-request -j ACCEPT

# forward VPN traffic anywhere
-A FORWARD -s 10.10.10.0/24 -m policy --dir in --pol ipsec --proto esp -j ACCEPT
-A FORWARD -d 10.10.10.0/24 -m policy --dir out --pol ipsec --proto esp -j ACCEPT

-A INPUT -j REJECT --reject-with icmp-host-unreachable
-A FORWARD -j REJECT --reject-with icmp-host-unreachable
COMMIT

*mangle
# reduce MTU/MSS values for dumb VPN clients
-A FORWARD -s 10.10.10.0/24 -o eth0 -p tcp -m policy --dir in --pol ipsec -m tcp --tcp-flags SYN,RST SYN -m tcpmss --mss 1361:1536 -j TCPMSS --set-mss 1360
COMMIT

*nat
# masquerade VPN traffic over eth0
-A POSTROUTING -s 10.10.10.0/24 -o eth0 -m policy --dir out --pol ipsec -j ACCEPT
-A POSTROUTING -s 10.10.10.0/24 -o eth0 -j MASQUERADE
COMMIT
daaku
  • 211
  • 1
  • 7
  • Just add rules similar to those in FORWARD (using policy matching) in INPUT/OUTPUT to allow access to certain ports/protocols via IPsec. – ecdsa Apr 30 '18 at 09:02
  • I tried a few variations like `-A INPUT -s 10.10.10.0/24 -m policy --dir in --pol ipsec -j ACCEPT` but couldn't figure out the right set of rules to apply. – daaku Apr 30 '18 at 11:43
  • What didn't work with that rule? – ecdsa Apr 30 '18 at 13:24
  • With that rule in place I was expecting to be able to hit any port on the public IP of the server while my client was connected to it via VPN, but instead my connections were getting rejected. I was testing this with a HTTP server running on port 8080. – daaku May 01 '18 at 04:57
  • 1
    Did you add the rule in the file above (if so, where?) or manually via iptables? How does the output of `iptables-save` look like after you added the rule? – ecdsa May 01 '18 at 07:39
  • I added it to `/etc/iptables/iptables.rules` along with my other rules between the "pings" and "forward VPN" sections and restarted the associated `systemctl restart iptables.service`. This is the output from `iptables-save`: https://gist.github.com/daaku/8b8cc11cdbf7f6146136ae3aa0535728 – daaku May 01 '18 at 14:18
  • That works fine in my tests (the only rules I didn't use were the two using the `recent` module, because the kernel I used hadn't built that in). How exactly was the connection rejected? ICMP host unreachable (i.e. by the REJECT rule)? Something else? – ecdsa May 02 '18 at 10:24
  • That's surprising, without the `recent` module rules I still can't get VPN clients to be able to hit a port not explicitly opened to everyone. `curl` fails with `Network is unreachable`, which I believe indicates it's getting to the `reject-with icmp-host-unreachable` rule. – daaku May 02 '18 at 19:53
  • Found out with some logging it's getting rejected because the src shows as my clients public IP, not the VPN IP. `curl` on the client is confirmed sending the traffic over VPN because `curl ifconfig.co/json` shows the public IP as the server's public IP. – daaku May 02 '18 at 20:20
  • My guess is the `FORWARD` or `MASQUERADE` happens first and so by the time the `INPUT` sees the packet its already made to look like it came from the public IP of the client? Not really sure I know how this works... – daaku May 02 '18 at 20:25
  • If anything it should show up with the virtual IP as source address (i.e. an address in 10.10.10.0/24), if the traffic shows up with the client's public IP it means the traffic is not tunneled via VPN (you can check with tcpdump/Wireshark or via traffic counters). So you should first make sure the VPN connection works as it should. And no, the `POSTROUTING` chain (i.e. the `MASQUERADE` rule) is not hit for local traffic. – ecdsa May 03 '18 at 07:40
  • Hmm, I wonder if hitting the VPN server itself doesn't go thru the tunnel on my client? My client here is OS X (10.13.4, the current version). Like I mentioned in an earlier comment, running `curl ifconfig.co/json` on the client shows the IP as the VPN server's IP when I'm connected to VPN, so I believe it is working as expected (at least when hitting other IPs besides the VPN server itself). – daaku May 04 '18 at 10:07
  • I just came across https://serverfault.com/questions/906661/routing-for-strongswan-to-access-service-on-same-server where you were helping as well. Reading it makes it seem like maybe the OS X/iOS IKEv2 setup doesn't encrypt the packets going to the VPN server for some reason? Maybe a real (and possibly bad security wise) bug? – daaku May 04 '18 at 13:06
  • I see. The problem is probably that there is a specific route on your macOS client for your VPN server that forces it over a different interface and therefore the packets will not use the virtual IP as source address and won't match the IPsec policies. So traffic to your server's public IP won't get tunneled through the IPsec tunnel. You'll have to assign a virtual IP to the server (from the same subnet you use for the clients, or a different one) and access the services that way and not via public IP. – ecdsa May 04 '18 at 13:07
  • Yes, seems to be the same problem. – ecdsa May 04 '18 at 13:07
  • Yep -- confirmed there's an explicit route for my VPN server added which is causing this. I guess I need to get a static IP on the VPN subnet like you suggested. – daaku May 04 '18 at 13:26
  • You can easily do that if you assign the addresses from the in-memory address pool in strongSwan (e.g. `rightsourceip` in ipsec.conf). Just set the value to 10.10.10.1/24 then the first IP assigned to clients will be 10.10.10.2 and you can use 10.10.10.1 for the server. You may also define an address range (e.g. 10.10.10.2-10.10.10.254). – ecdsa May 04 '18 at 13:49
  • Should have been 10.10.10.2/24 above and not 10.10.10.1/24, sorry about that. – ecdsa May 04 '18 at 14:16
  • Thanks for the tip regarding `rightsourceip` -- the range syntax worked. Additionally I added this to my systemd-networkd config for eth0: [Address] Label=eth0:0 Address=10.10.10.1/24 And now I can see with some logging that packets are coming in and getting logged like such: IN=eth0 OUT= MAC=<...> SRC=10.10.10.2 DST=10.10.10.1 And my input rule as I originally tried seems to be letting these thru: -A INPUT -s 10.10.10.0/24 -m policy --dir in --pol ipsec -p tcp -j ACCEPT But the packets don't seem to get to a service bound to `0.0.0.0:8080` as I want. – daaku May 04 '18 at 15:00
  • Let us [continue this discussion in chat](https://chat.stackexchange.com/rooms/77058/discussion-between-daaku-and-ecdsa). – daaku May 04 '18 at 20:40

0 Answers0