Preventing IPSec-less L2TP

I’ve got a setup like this:

Home computer ↔ Mikrotik with NAT ↔ Internet ↔ Server

Home connects to Server using L2TP

There is an IPSec transport established between the Mikrotik and the Server, so the Home computer’s L2TP traffic is encrypted as it goes out to server.

So far so good.

Now I’d also like to make this IPSec mandatory - i.e. stop unencrypted L2TP from leaving the Mikrotik.

I’ve tried this:

/ip firewall filter
add action=drop chain=output dst-port=1701 ipsec-policy=out,none out-interface-list=WAN protocol=udp

And tested by disabling the IPSec L2TP policy.

Sadly, now I see (in tcpdump udp proto 1701) that the Home computer is still talking to the Server, over UDP 1701. In other words, the rule to block unencrypted L2TP did not work.

An almost identical rule works just fine for GRE but the GRE tunnel is configured on the Mikrotik itself.

/ip firewall filter
add action=drop chain=output comment="prevent unencrypted gre" ipsec-policy=out,none out-interface-list=WAN \
    protocol=gre

1 - any ideas on how to stop unecnrypted L2TP?

2 - why the difference between my GRE rule (which works and blocks when the IPSec policy is turned off as a test) and my L2TP rule?

Even more weird… I can change the rule like this, removing “ipsec-policy=out,none out-interface-list=WAN”:

add action=drop chain=output comment="prevent unencrypted l2tp" dst-port=1701 protocol=udp

and move it to top place in filter rule list - and L2TP (still unencrypted) still reaches the server from home computer through the Mikrotik…

I’m either going nuts or missing something obvious?

You’re missing something obvious:

  1. The chain=output is for packets from router itself (process running on router). Packets from home computer to server will go through chain=forward.

  2. The ipsec-policy matcher also works only for IPSec configured on router itself. Router has no way of knowing that some packets from home computer to server should use IPSec, because IPSec is on home computer and it didn’t tell router about it (there’s no way how it could).

That explains it, thanks!

… so what you actually need to do is to block access to protocol=udp dst-port=1701 dst-address=the.ip.of.the.server in chain=forward. The L2TP packets encapsulated into IPsec transport ones won’t be affected by that rule.

And having a policy on the router doesn’t help this one?

/ip ipsec policy
add comment="my l2tp" dst-address=139.0.0.1/32 dst-port=1701 peer=\
    linode-1 protocol=udp src-address=178.0.0.1/32

Guess I could just block udp port 1701 in the “forward” chain and it would only match packets that come from the Home computer.

About the policy, probably not. I’m not exactly sure about behaviour of policies for unconnected peers without testing it, maybe there’s a chance to do something with it that way, but IMHO it would be a dirty trick. If you want to make sure that home computer won’t be able to use bare L2TP, blocking the port in forward chain is the right solution.

Thanks. As it turned out, blocking on forward will block no matter if it’s encrypted or not.

Maybe I’m doing something wrong, but it seems kind of logical - as there is no special chain that specifically targets “forwarding, but after IPSec encryption before it’s sent out”.

I was able to block unencrypted L2TP on the server - where it’s possible to use iptables directly - and the connection just wouldn’t establish if not encrypted. Fine. But this made things unstable, xl2tp on the client behind the Mikrotik would start getting weird errors after a while and abort.

Long story short… I already had a GRE / IPSec tunnel to this server - so ended up creating a firewall mangle to route L2TP through that encrypted GRE. A hack but it works and seems stable.

Thanks again for the explanations!

Matching on IPsec policy is only working when your router itself is terminating the IPsec.
It can do that, but this is not what you are doing now (the router is forwarding encrypted traffic to your server).
In this case, you simply allow only UDP port 500, UDP port 4500, and IP protocol 50 to be forwarded to your server.
(the latter is probably not necessary as you have NAT in your path anyway)

Don’t allow UDP port 1701 forwarding across your router. It is the unencrypted L2TP traffic.

It can do that, but this is not what you are doing now (the router is forwarding encrypted traffic to your server).

Actually no, encryption was handled by the router.

There is an IPSec transport established between the Mikrotik and the Server, so the Home computer’s L2TP traffic is encrypted as it goes out to server.

Home computer L2TP client ↔ Mikrotik with IPSec policy for UDP dport 1701 ↔ Internet ↔ Server with IPSec and L2TP

And now it’s like this and it works:

Home computer L2TP client ↔ Mikrotik with IPSec policy for GRE, wraps L2TP into GRE ↔ Internet ↔ Server unpacks IPSec, GRE, deals with L2TP

That changes a lot - better to say, it invalidates all the advice we’ve given you so far.

When the IPsec encryption of the L2TP is provided by the Mikrotik, the task of preventing non-encapsulated L2TP from leaving the router cannot be done using firewall rules as they are handled before the IPsec policies. If things worked the way they should, you could place the policy with action=encrypt first, and place another policy with the same traffic selector (src-address, dst-address, protocol, src-port, dst-port) and action=discard next to it, so whenever the first policy wouldn’t be active, the second one would discard the traffic, but it doesn’t work this way because a second policy with traffic selector identical to another one is ignored, and using a wider traffic selector works but you it would cause you other problems.

So my solution in these cases, when I want traffic to only get out if the IPsec security association for it is up and running, is to set up a route for that traffic to a gateway which leads to nowhere. You create an /interface bridge, assign no IP to it, and use it as a gateway for the /ip route for that traffic. As the IPsec policy steals the traffic just before it would be sent down the gateway, it saves it from being sent to nowhere if it is active.

In your case, as you want to drop only unencrypted traffic from your PC’s local IP to the server’s IP and UDP port 1701, it is not enough to create an /ip route with dst-address matching the server IP and gateway=br-blackhole, but you have to create a default route (dst-address=0.0.0.0/0 with that gateway and some routing-mark, and use a rule in /ip firewall mangle chain=output to assign that routing-mark to packets matching protocol=udp src-address=your.pc’s.ip.address dst-address=your.server’s.ip.address dst-port=1701.

Oh, and I haven’t noticed that you have GRE between the L2TP and IPsec layers; this only changes the mangle rule in what I wrote above, as you need to prevent the GRE transport packets from being delivered if the IPsec SA is down rather than the L2TP ones.

But I have to say I don’t understand why you need a three-layer tunneling, wasting so much of the available MTU.

Thanks @sindy for the update.

I should have been more clear… Instead of

There is an IPSec transport established between the Mikrotik and the Server, so the Home computer’s L2TP traffic is encrypted as it goes out to server.

I perhaps should have written

There is an IPSec transport established between the Mikrotik and the Server, so the Home computer’s L2TP traffic is encrypted > by the Mikrotik > as it goes through and out to server.

And then

cannot be done using firewall rules as they are handled before the IPsec policies

yes this makes sense.

And your tip about routing-mark - does too. Thanks again!

you have GRE between the L2TP and IPsec layers

The GRE / IPSec tunnel exists for different reasons and it was already there.

Routing L2TP into that GRE was just an experiment which seemed to work - yes inefficient but before I got your advice about mangle and stuff, it was the best I could come up with.

Sorry, I seem not to be concentrated enough, I’ve missed the part saying it was a workaround :slight_smile:

I had a look at my VPN and up goes no traffic over port 1701 up but down I traffic on port 1701 coming from the VPN connection and the packey count are almost the same as on ipsec-esp in the line above in RAW.

If I disable the accept for 1701 incoming, in RAW, my VPN is death.

Is my traffic down encrypted?

update: with the ipsec-esp disbabled the connection still works and I am going to try dropping the epsec-esp.

This is a feature of the IPsec implementation - packet sniffing shows both encrypted and decrypted version of the incoming traffic on an interface although only the encrypted version is actually present “on the wire”. In outbound direction, the not-yet-encrypted traffic is not sniffed. You can sniff on the opposite end (L2TP client) to double-check this.


If your client is behind a NAT, the ESP is encapsulated into UDP so it is not visible to the firewall at all. ESP is only used “naked” if no NAT exists between the peers.

Thanks Sindy, I am using mangle to mark connection and route. I hoped to be able skip NAT but I was not able to.

I run several VPN side to side and I get overlapping 172.20.12.x as local address.

Mangle

33    chain=route-vpn action=mark-routing new-routing-mark=VPN11 passthrough=no connection-mark=VPN11 log=no log-prefix="" 
.
.
37    chain=route-vpn action=mark-routing new-routing-mark=VPN15 passthrough=no connection-mark=VPN15 log=no log-prefix="" 

43    chain=mark-vpn action=mark-connection new-connection-mark=VPN11 passthrough=yes per-connection-classifier=src-port:5/0 log=no log-prefix="" 
.
.
47    chain=mark-vpn action=mark-connection new-connection-mark=VPN15 passthrough=yes per-connection-classifier=src-port:5/4 log=no log-prefix="" 

NAT

24    ;;; NAT
      chain=vpn action=src-nat to-addresses=172.94.3.47 routing-mark=SSTP1 log=no log-prefix="" 
25    chain=vpn action=src-nat to-addresses=10.64.72.83 routing-mark=SSTP2 log=no log-prefix="" 
.
.
29    chain=vpn action=src-nat to-addresses=172.20.12.2 routing-mark=VPN11 log=no log-prefix="" 
.
31    chain=vpn action=src-nat to-addresses=172.20.12.3 routing-mark=VPN15 log=no log-prefix="" 


Route

 8 A S  0.0.0.0/0                          VPN-11            1
.
.
12 A S  0.0.0.0/0                          VPN-15            1

17 ADC  10.112.120.156/32  172.20.12.2     VPN-11            0
.
.
21 ADC  10.112.120.160/32  172.20.12.3     VPN-15            0

Can you explain your goal in deeper detail and ask a clear question? From what you wrote I didn’t understand what you want to hear from me :slight_smile:

What I got from the previous post is that you have several VPN accounts (probably at the same provider) and you want to load balance your traffic between these accounts, but I don’t know what you expect from this an how is NAT related to that goal.

Thanks for your patience and I am looking for a way to skip NAT. I have marked the route in Mangle and it puzzles me why I still need NAT.

In the default client setup for L2TP(-IPSEC) the local address is set in the 172.20.12.x range and I changed that to a address that is my local network thinking that NAT would be then obsolete. That did not work.