Skip to content

Commit

Permalink
Add new post
Browse files Browse the repository at this point in the history
  • Loading branch information
vercas committed Oct 28, 2023
1 parent 8c20393 commit 9c90f2a
Showing 1 changed file with 78 additions and 0 deletions.
78 changes: 78 additions & 0 deletions _posts/2023-10-28-zerotier-hooks-linux.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
---
layout: post
title: ZeroTier Hooks on Linux
---

ZeroTier on Linux lacks any facilities for running hooks when it connects to or disconnects from a network.
Other VPN software provides this feature so the user can do things like setting up and tearing down firewall rules.
Luckily, systemd provides a trivial way to solve this problem by starting and stopping a service when a device shows up or disappears.

----

# The Code

The following `systemd` unit should be placed in a file like `/etc/systemd/system/[email protected]`

```yaml
[Unit]
Description=Hooks for Network Interfaces
BindsTo=sys-subsystem-net-devices-%i.device
After=sys-subsystem-net-devices-%i.device

[Service]
Type=oneshot
ExecStartPre=-/usr/local/lib/%i.down
ExecStart=/usr/local/lib/%i.up
ExecStop=/usr/local/lib/%i.down
RemainAfterExit=yes

[Install]
WantedBy=sys-subsystem-net-devices-%i.device
```

An example "up" hook for an example `ztFooBar` interface for a network with subnet `10.11.12.0/24` would be placed in `/usr/local/lib/ztFooBar.up` and look like:

```bash
#!/usr/bin/env bash

# This forwards TCP and UDP traffic on port 1234 to 10.11.12.13:1234. It's assumed the forwardee is in the ZeroTier network.
iptables -t nat -I PREROUTING 1 -i eno1 -p tcp --dport 1234 -j DNAT --to-destination 10.11.12.13:1234
iptables -t nat -I PREROUTING 1 -i eno1 -p udp --dport 1234 -j DNAT --to-destination 10.11.12.13:1234
# These rules are placed in the DOCKER-USER chain, which is where forwarding rules need to go if you have Docker installed. Without Docker installed, these would go in the FORWARD chain instead.
iptables -I DOCKER-USER 1 -i eno1 -p tcp -d 10.11.12.13 --dport 1234 -m state --state NEW,ESTABLISHED,RELATED -j ACCEPT
iptables -I DOCKER-USER 1 -i eno1 -p udp -d 10.11.12.13 --dport 1234 -m state --state NEW,ESTABLISHED,RELATED -j ACCEPT

# This allows all traffic to be forwarded from the ZeroTier network into eno1
iptables -I DOCKER-USER 1 -i ztFooBar -o eno1 -m state --state NEW,ESTABLISHED,RELATED -j ACCEPT

# This allows established and related packets to flow from eno1 into ZeroTier, so packets in the other direction belonging to connections allowed by the rule above can reach the right host.
iptables -I DOCKER-USER 1 -i eno1 -o ztFooBar -m state --state ESTABLISHED,RELATED -j ACCEPT

# This changes the source IP address of the traffic leaving through eno1, forwarded from the ZeroTier network.
iptables -t nat -I POSTROUTING 1 -o eno1 -s 10.11.12.0/24 -j MASQUERADE
```

The corresponding `/usr/local/lib/ztFooBar.down` file would look like:

```bash
#!/usr/bin/env bash

iptables -D DOCKER-USER -i eno1 -o ztFooBar -m state --state ESTABLISHED,RELATED -j ACCEPT
iptables -D DOCKER-USER -i ztFooBar -o eno1 -m state --state NEW,ESTABLISHED,RELATED -j ACCEPT
iptables -t nat -D POSTROUTING -o eno1 -s 10.11.12.0/24 -j MASQUERADE

iptables -t nat -D PREROUTING -i eno1 -p tcp --dport 1234 -j DNAT --to-destination 10.11.12.13:1234
iptables -t nat -D PREROUTING -i eno1 -p udp --dport 1234 -j DNAT --to-destination 10.11.12.13:1234
iptables -D DOCKER-USER -i eno1 -p tcp -d 10.11.12.13 --dport 6881 -m state --state NEW,ESTABLISHED,RELATED -j ACCEPT
iptables -D DOCKER-USER -i eno1 -p udp -d 10.11.12.13 --dport 6881 -m state --state NEW,ESTABLISHED,RELATED -j ACCEPT

exit 0
```

It's best if the `down` hook is idempotent.

You can enable hooks for the example network interface by running this command:

```sh
systemctl enable [email protected]
```

0 comments on commit 9c90f2a

Please sign in to comment.