WireGuard Notes

After being first exposed to WireGuard, it took a while before its simple-yet-powerful nature started to dawn on me. By "simple" I mean simple from an end-user's perspective; the implementation, while supposedly a few orders of magnitude less hairy than e.g. OpenVPN or IPsec, isn't something that I can claim to have grasped. But the ascetic, no bells and whistles, do one thing well approach is something that resonates and got me to invest some time to play with the thing to see how it works in some common scenarios.

Getting packets to travel from A to B to C and back doesn't happen automagically with WireGuard. In fact there's no magic going on behind the scenes to begin with, which can be A Good Thing™ or not, depending on cirumstances. The only thing that WireGuard provides is making packets flow between the two ends of a link in a secure manner, everything else is left to be taken care of by whatever means is appropriate. For setting things up there's the wg program which helps with key generation and with setting up the WireGuard interface, and further the wg-quick script which does a bit more and sets up the interfaces, peers and routes based on an INI-format file. The latter also ships with a handy systemd service for running things permanently. These tools can be installed on Debian-like systems by saying apt install wireguard-tools. Getting the thing running on Raspberry Pis requires a few extra steps, see here.

Before going into the example configuration, a refresher about configuring cryptokey routing. When peers are configured, the "allowed IPs" settings for the peers mean that:

  • when a packet is sent to the WireGuard interface (e.g. wg0), the packet gets passed to the peer that has a prefix matching the packets destination address
  • which means that the prefixes can't overlap between the peers
  • when a packet is received from a peer, if the peer has a prefix mathing the packet's source address, the packet is allowed in
  • when either sending to or receiving from a peer, if no matching prefix is found for a packet, the packet is dropped
Or like it says in the cryptokey routing section linked to above:
In other words, when sending packets, the list of allowed IPs behaves as a sort of routing table, and when receiving packets, the list of allowed IPs behaves as a sort of access control list.

Connecting two sites, both behind NAT

This example connects two sites that are both behind NAT, which requires that there's a publicly accesible host running in between:

+-----------+      +-----------+       +-----------+
|           |      |           |       |           |
|    NAT    +------+ WireGuard +-------+ Network 1 |
|           |      |           |       |           |
+-----+-----+      +-----------+       +-----------+
      |
      |
      |           +-------------+
      |           |             |
      +---------->+ example.com +<-----------+
                  |             |            |
                  +-------------+            |
                                             |
                                             |
+-----------+      +-----------+       +-----+-----+
|           |      |           |       |           |
| Network 2 +------+ WireGuard +-------+    NAT    |
|           |      |           |       |           |
+-----------+      +-----------+       +-----------+

Suppose that the public host's network is 10.0.0.0/24, Network 1 is 192.168.1.0/24, Network 2 is 172.16.1.0/24, and that the WireGuard network is 10.1.1.0/24. And that all hosts in both behind-NAT networks should see each other.

The WireGuard hosts on the behind-NAT networks connect to example.com:51820, which has the following configuration:

[Interface]
Address = 10.1.1.1/32
ListenPort = 51820
PrivateKey = kKELbYxqmwHGUyjdHiVhQ/lzyiLep2kLgAocLF4CR3Q=

[Peer]
PublicKey = D+GcHTk8uRiggEj79IhbbsLWHSdZynYjUVPWcP8aJFg=
AllowedIPs = 10.1.1.11/32,192.168.1.0/24

[Peer]
PublicKey = up9LDZjYw8/LHH29ZQdp7Mg9bB+LIE7T4OsYLlEXLng=
AllowedIPs = 10.1.1.12/32,172.16.1.0/24

WireGuard host on Network 1 (192.168.1.0/24):

[Interface]
Address = 10.1.1.11/32
PrivateKey = 4DQYFpL2kkVd/rjEYLTES8Ah6K2BMOrH504TXRQyv0E=
Table = off
PostUp = ip -4 route add 10.1.1.0/24 dev %i
PostUp = ip -4 route add 172.16.1.0/24 dev %i

[Peer]
PublicKey = 2LbLqgg0hGjsQ+Y15l+mPhEtGN53Uhvzj8n9dpxVqDQ=
AllowedIPs = 10.1.1.0/24,192.168.1.0/24,172.16.1.0/24
Endpoint = example.com:51820
PersistentKeepalive = 25

WireGuard host on Network 2 (172.16.1.0/24):

[Interface]
Address = 10.1.1.12/32
PrivateKey = CPZBHHLywkMqgW70MIgnvJRculKKGyYaBP7rIUJbpXs=
Table = off
PostUp = ip -4 route add 10.1.1.0/24 dev %i
PostUp = ip -4 route add 192.168.1.0/24 dev %i

[Peer]
PublicKey = 2LbLqgg0hGjsQ+Y15l+mPhEtGN53Uhvzj8n9dpxVqDQ=
AllowedIPs = 10.1.1.0/24,192.168.1.0/24,172.16.1.0/24
Endpoint = example.com:51820
PersistentKeepalive = 25

Routing Table is off and routes are manually added in PostUp because otherwise wg-quick would set routes for the local network's traffic which is in AllowedIPs.

All three hosts should enable forwarding:

sysctl -w net.ipv4.ip_forward=1

In order to route traffic to WireGuard from other hosts in the networks, the hosts need a route. If fiddling with the gateway isn't an option, the route needs to be set on each separately, for example (assuming the WireGuard hosts on each network are .1.10):

# Network 1
ip -4 route add 10.1.1.0/24 via 192.168.1.10
ip -4 route add 172.16.1.0/24 via 192.168.1.10

# Network 2
ip -4 route add 10.1.1.0/24 via 172.16.1.10
ip -4 route add 192.168.1.0/24 via 172.16.1.10

Then, supposing that each of the configurations was in /etc/wireguard/wg0.conf, one can say wg-quick up wg0. To make the WireGuard configuration come up automatically, the Systemd service should be enabled:

systemctl enable wg-quick@wg0.service
systemctl daemon-reload
systemctl start wg-quick@wg0.service

Tagged with:

Categorised as:


Salt Notes

I decided to go for Salt when picking a solution that would help me automate server management. Here are some things that required some figuring out.

Including keys in pillar data

Using Git as an example; deploy key is set in Github repo's settings:

sites:
  example.com:
    gitsource: git+ssh://git@github.com/you/your_repo.git
    gitidentity: |
      -----BEGIN RSA PRIVATE KEY-----
      <Deploy key goes here – mind the indentation!>
      -----END RSA PRIVATE KEY-----
    

Using the above in states:

{% if 'gitsource' in args and 'gitidentity' in args %}
/etc/deploy-keys/{{ site }}:
  file.directory:
    - makedirs: True
    - require:
      - pkg: nginx
    - watch_in:
      - service: nginx

/etc/deploy-keys/{{ site }}/identity:
  file.managed:
    - mode: 600
    - contents_pillar: sites:{{ site }}:gitidentity
    - require:
      - pkg: nginx
    - watch_in:
      - service: nginx

{{ args.gitsource }}:
  git.latest:
    - identity: /etc/deploy-keys/{{ site }}/identity
    - target: /var/www/{{ site }}
    - rev: master
    - force: True
    - require:
      - pkg: nginx
    - watch_in:
      - service: nginx
{% endif %}
    

Swap

Using a swap file here because DigitalOcean instances, at least the small ones that I've tested, don't include any swap.

/swapfile:
  cmd.run:
    - name: "fallocate -l 1024M /swapfile && chmod 600 /swapfile && mkswap /swapfile"
    - unless: test -f /swapfile
  mount.swap:
    - require:
      - cmd: /swapfile
    

Logentries

The "agent" of the excellent Logentries log gathering service doesn't use a config file, and instead relies on the le tool that is used to set thing up. After config changes, the Logentries daemon must be restarted (that last restart part can likely be streamlined but I couldn't get a hard service restart to work otherwise).

logentries:
  pkgrepo.managed:
    - name: deb http://rep.logentries.com/ trusty main
    - dist: trusty
    - file: /etc/apt/sources.list.d/logentries.list
    - keyid: C43C79AD
    - keyserver: pgp.mit.edu
  pkg:
    - latest

logentries_registered:
  cmd.run:
    - unless: le whoami
    - name: le register --force --account-key={{ pillar['logentries']['account_key'] }} --hostname={{ grains.id }} --name={{ grains.id }}-`date +'%Y-%m-%dT%H:%M:%S'`
    - require:
      - pkg: logentries
    - require_in:
      - pkg: logentries-daemon

logentries_follow:
  cmd.run:
    - name: |
        le follow /var/log/syslog
        le follow /var/log/auth.log
        le follow /var/log/salt/minion
{% for site, args in pillar.get('sites', {}).items() %}
        le follow /var/log/nginx/{{ site }}.access.log
        le follow /var/log/nginx/{{ site }}.error.log
{% endfor %}
    - require:
      - pkg: logentries
    - require_in:
      - pkg: logentries-daemon

logentries-daemon:
  pkg:
    - latest

logentries_daemon_stop:
  service.dead:
    - name: logentries
    - require:
      - pkg: logentries-daemon
    - require_in:
      - logentries_daemon_start

logentries_daemon_start:
  service.running:
    - name: logentries
    

Tagged with:

Categorised as:


Basic iptables setup (Ubuntu)

Accept anything coming in from 127.0.0.1:

iptables -A INPUT -i lo -j ACCEPT
    

Accept "related" ("packet is starting a new connection, but is associated with an existing connection") and "established" ("packet is associated with a connection which has seen packets in both directions") packets:

iptables -A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
    

SSH; set port (XXXXX) to 22 if you're running the default, which you perhaps should not do as the script kiddies will not leave you alone. If this is changed to something non-default then do not forget to change the port in /etc/ssh/sshd_config (the Port configuration directive) and do these changes coordinatedly. Otherwise you will be locked out.

iptables -A INPUT -p tcp -m tcp --dport XXXXX -j ACCEPT
    

HTTP and HTTPS:

iptables -A INPUT -p tcp -m tcp --dport 80 -j ACCEPT
iptables -A INPUT -p tcp -m tcp --dport 443 -j ACCEPT
    

NTP, because we're part of the NTP Pool Project:

iptables -A INPUT -p udp -m udp --dport 123 -j ACCEPT
    

Log dropped packets, but not too much:

iptables -A INPUT -m limit --limit 5/min -j LOG --log-prefix "iptables DROP: " --log-level 7
    

Do the actual dropping:

iptables -A INPUT -j DROP
    

In order to save these settings on shutdown, create the following file in /etc/network/if-post-down.d/iptables:

#!/bin/sh
iptables-save -c > /etc/iptables.rules
exit 0
    

To restore the settings on boot, create the following file in /etc/network/if-pre-up.d/iptables:

#!/bin/sh
iptables-restore < /etc/iptables.rules
exit 0
    

And make the two executable:

chmod +x /etc/network/if-post-down.d/iptables /etc/network/if-pre-up.d/iptables
    

Tagged with:

Categorised as:


Berlin/August 2012

Here are some bits of information that I think might be useful for others travelling to this destination.

Fonic prepaid

I arranged, beforehand, two Fonic Prepaid SIMs from this guy. Please note that while during the initial activation at fonic.de you can enter any German address in your details (I gave our rented apartment address) they will send a letter to that address and if the letter returns as non-deliverable (as happened in our case) then the prepaid will be put in “nur erreichbar” state. This means that you can receive calls and SMS, but can not place calls or send SMS or activate any of the flat rate Internet options. After contacting Fonic support, they asked for the original PIN numbers printed in SIM packaging, and also a copy (scan) of my Finnish passport. After this both SIMs were usable once again and I was able to activate the “Tagesflat” Internet option while in Germany.

In addition to top-up vouchers, transferring money to your prepaid account is possible with IBAN bank transfer; see instructions here.

7-Tage-Karte

These 7 day public transport tickets can be purchased from machines in S-Bahn/U-Bahn stations and bus/tram stops, or from a Spätkauf. As far as I know, the machines only accept cash. We got these for all zones (i.e. ABC which covers Berlin and some neighbouring towns) which cost a little over 30 euros per week.

Berlin public transportation works excellently and we took a cab only occasionally (cab rides were reasonably priced too, though).

Öffi Directions

This Android app will, given two stations (or addresses), present you with a selection of best routes using any mix of the above mentioned means of transportation, often in combinations you wouldn’t have thought of by just looking at the route maps and time tables. Can not be recommended enough.

Places to visit

While in no way an exhaustive list, here are some places we went to and that I find worth mentioning.

Teufelsberg

The now abandoned joint NSA/GCHQ listening station. There was some dude claiming to represent the current owner at the gate collecting a five euro admission per person and requiring our signatures in order to avoid any legal hassles should we hurt ourselves during our exploration.

There were people in the actual radomes but we just wandered around the site as climbing didn’t seem like the thing to do at the time. There is a staircase inside the main building leading up but the door leading to that staircase was locked. The key to that lock is supposedly held by the people arranging guided tours of the site.

Fernsehturm

You should book tickets in advance, this way you’re guaranteed the next free window table at the restaurant above the observation deck (we were there on a saturday night at around eight and the place was pretty packed and we had to wait for the table for around 15 minutes). The restaurant is expensive-ish but ok, though the food is nothing to write home about. But then again people come here for the view. Old-school, professional waitresses and live music.

Sightseeing flight with Manuel

I spotted this activity from Gidsy. Our pilot Manuel flew us from the Bienenfarm field on a small Cessna plane. Good photos.

Berliner Unterwelten

These tours of the underground Berlin are arranged by a non-profit organisation. We took the "Cold War” tour which took us first to a big war-era bomb shelter near Gesundbrunnen station and then to a modern facility at Pankstraße station. Both facilities are of course decommisioned (there wouldn't be tours if they weren't) but the guide (a Brit) gave a rather lively presentation about life in the facilities if they had ever been put to use, and the effects of nuclear war in general.

Türkenmarkt

Tuesdays and fridays. Looking at the prices, you could easily stock a weeks supply of vegetables here for ten euros. At the south-east end of the market there is a fresh pasta and gnocchi vendor with an excellent product (same vendor at Hackescher Markt on saturdays).

Tagged with:

Categorised as: