├── system-config.ign
├── human-config.yaml
└── README.md
/system-config.ign:
--------------------------------------------------------------------------------
1 | {
2 | "ignition": {
3 | "version": "3.3.0"
4 | },
5 | "storage": {
6 | "files": [
7 | {
8 | "path": "/var/lib/iptables/rules-save",
9 | "contents": {
10 | "compression": "gzip",
11 | "source": "data:;base64,H4sIAAAAAAAC/5SQQUvEMBCF7/0VcxYHUlxEeotNxMKuKdksHsRDyEaMZjehOx7899JtlSr20NOD974vkLl4CZF8V1TNQ7szILRq4YlV7Lmo7pR+5Fr86tTO9Biva9masUQOg4wBYgJ8G+fJcACXjkfqrHsHREcnsuRByzU3UlzKreG362Z7L8W/doaPfe4fOQfuc+oIVuX1qpzBgzuc+SGxD6TP7IEtFa6WCjdLhfLPJ76vjgE8vTLABGRDPDkbPZtFp0gaxR+0VptNY4qvAAAA//9Y7QzJ6wEAAA=="
12 | },
13 | "mode": 420
14 | },
15 | {
16 | "overwrite": true,
17 | "path": "/etc/flatcar/update.conf",
18 | "contents": {
19 | "compression": "gzip",
20 | "source": "data:;base64,H4sIAAAAAAAC/wpydfL3D4kPDglyDHF1j7QtSk3Kzy/h8vF39g729QzxcImHqgj39HPxD48PDnEMCrE1MLIyMMCtyMfVzz3Ew9YwQwEOuAABAAD//xtak2VpAAAA"
21 | },
22 | "mode": 272
23 | }
24 | ],
25 | "links": [
26 | {
27 | "overwrite": true,
28 | "path": "/etc/localtime",
29 | "target": "/usr/share/zoneinfo/Etc/UTC"
30 | }
31 | ]
32 | },
33 | "systemd": {
34 | "units": [
35 | {
36 | "contents": "[Unit]\nDescription=Tailscale Docker Container\nAfter=docker.service\nRequires=docker.service\n\n[Service]\nExecStart=/usr/bin/docker run \\\n --name tailscale \\\n --rm \\\n --hostname vpn-server \\\n --network host \\\n --label com.centurylinklabs.watchtower.enable=true \\\n -e TS_AUTHKEY=ENTER_TAILSCALE_AUTH_KEY_HERE \\\n -e TS_STATE_DIR=/var/lib/tailscale \\\n -e TS_USERSPACE=false \\\n -e TS_EXTRA_ARGS=\"--advertise-exit-node\" \\\n -e TS_TAILSCALED_EXTRA_ARGS=\"--port=41641 --no-logs-no-support\" \\\n -v tailscale:/var/lib/tailscale \\\n --device=/dev/net/tun:/dev/net/tun \\\n --cap-add=NET_ADMIN \\\n ghcr.io/tailscale/tailscale:latest\nExecStop=/usr/bin/docker stop tailscale\nRestart=always\nRestartSec=5\n\n[Install]\nWantedBy=multi-user.target\n",
37 | "enabled": true,
38 | "name": "tailscale.service"
39 | },
40 | {
41 | "contents": "[Unit]\nDescription=Watchtower Docker Container Auto-Updater\nAfter=docker.service\nRequires=docker.service\n\n[Service]\nExecStart=/usr/bin/docker run \\\n --name watchtower \\\n --rm \\\n --label com.centurylinklabs.watchtower.enable=true \\\n -e WATCHTOWER_LABEL_ENABLE=true \\\n -e WATCHTOWER_CLEANUP=true \\\n -e WATCHTOWER_NO_RESTART=true \\\n -v /var/run/docker.sock:/var/run/docker.sock \\\n ghcr.io/containrrr/watchtower:latest\nExecStop=/usr/bin/docker stop watchtower\nRestart=always\nRestartSec=5\n\n[Install]\nWantedBy=multi-user.target\n",
42 | "enabled": true,
43 | "name": "watchtower.service"
44 | },
45 | {
46 | "contents": "[Unit]\nDescription=Restore iptables rules\nAfter=network.target\n\n[Service]\nType=oneshot\nExecStart=/sbin/iptables-restore -n /var/lib/iptables/rules-save\nRemainAfterExit=yes\n\n[Install]\nWantedBy=multi-user.target",
47 | "enabled": true,
48 | "name": "iptables-restore.service"
49 | }
50 | ]
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/human-config.yaml:
--------------------------------------------------------------------------------
1 | variant: flatcar
2 | version: 1.0.0
3 | storage:
4 | files:
5 | - path: /var/lib/iptables/rules-save
6 | mode: 0644
7 | contents:
8 | inline: |
9 | *filter
10 | :INPUT DROP [0:0]
11 | :FORWARD DROP [0:0]
12 | :OUTPUT ACCEPT [0:0]
13 | -A INPUT -i lo -j ACCEPT
14 | -A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
15 | -A INPUT -p udp -m udp --dport 41641 -j ACCEPT
16 | -A INPUT -p icmp -m icmp --icmp-type 0 -j ACCEPT
17 | -A INPUT -p icmp -m icmp --icmp-type 3 -j ACCEPT
18 | -A INPUT -p icmp -m icmp --icmp-type 8 -j ACCEPT
19 | -A INPUT -p icmp -m icmp --icmp-type 11 -j ACCEPT
20 | -A FORWARD -i eth0 -o tailscale0 -j ACCEPT
21 | -A FORWARD -i tailscale0 -o eth0 -j ACCEPT
22 | COMMIT
23 | - path: /etc/flatcar/update.conf
24 | overwrite: true
25 | contents:
26 | inline: |
27 | REBOOT_STRATEGY=reboot
28 | LOCKSMITHD_REBOOT_WINDOW_START=02:00
29 | LOCKSMITHD_REBOOT_WINDOW_LENGTH=1h
30 | mode: 0420
31 | links:
32 | - path: /etc/localtime
33 | overwrite: true
34 | target: /usr/share/zoneinfo/Etc/UTC
35 | systemd:
36 | units:
37 | - name: tailscale.service
38 | enabled: true
39 | contents: |
40 | [Unit]
41 | Description=Tailscale Docker Container
42 | After=docker.service
43 | Requires=docker.service
44 |
45 | [Service]
46 | ExecStart=/usr/bin/docker run \
47 | --name tailscale \
48 | --rm \
49 | --hostname vpn-server \
50 | --network host \
51 | --label com.centurylinklabs.watchtower.enable=true \
52 | -e TS_AUTHKEY=ENTER_TAILSCALE_AUTH_KEY_HERE \
53 | -e TS_STATE_DIR=/var/lib/tailscale \
54 | -e TS_USERSPACE=false \
55 | -e TS_EXTRA_ARGS="--advertise-exit-node" \
56 | -e TS_TAILSCALED_EXTRA_ARGS="--port=41641 --no-logs-no-support" \
57 | -v tailscale:/var/lib/tailscale \
58 | --device=/dev/net/tun:/dev/net/tun \
59 | --cap-add=NET_ADMIN \
60 | ghcr.io/tailscale/tailscale:latest
61 | ExecStop=/usr/bin/docker stop tailscale
62 | Restart=always
63 | RestartSec=5
64 |
65 | [Install]
66 | WantedBy=multi-user.target
67 | - name: watchtower.service
68 | enabled: true
69 | contents: |
70 | [Unit]
71 | Description=Watchtower Docker Container Auto-Updater
72 | After=docker.service
73 | Requires=docker.service
74 |
75 | [Service]
76 | ExecStart=/usr/bin/docker run \
77 | --name watchtower \
78 | --rm \
79 | --label com.centurylinklabs.watchtower.enable=true \
80 | -e WATCHTOWER_LABEL_ENABLE=true \
81 | -e WATCHTOWER_CLEANUP=true \
82 | -e WATCHTOWER_NO_RESTART=true \
83 | -v /var/run/docker.sock:/var/run/docker.sock \
84 | ghcr.io/containrrr/watchtower:latest
85 | ExecStop=/usr/bin/docker stop watchtower
86 | Restart=always
87 | RestartSec=5
88 |
89 | [Install]
90 | WantedBy=multi-user.target
91 | - name: iptables-restore.service
92 | enabled: true
93 | contents: |
94 | [Unit]
95 | Description=Restore iptables rules
96 | After=network.target
97 |
98 | [Service]
99 | Type=oneshot
100 | ExecStart=/sbin/iptables-restore -n /var/lib/iptables/rules-save
101 | RemainAfterExit=yes
102 |
103 | [Install]
104 | WantedBy=multi-user.target
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Easy Tailscale VPN Server (Flatcar Container Linux Config)
2 |
3 | **Ignition/Butane configuration file to deploy a firewalled Tailscale exit node as a personal VPN.**
4 | - Designed for use with Flatcar Container OS (may also work with Fedora CoreOS).
5 | - Automatic Tailscale updates (via Watchtower)
6 | - Automatic OS updates (via Flatcar)
7 | - Using Vultr's high frequency tier of VPS, you can achieve ~1Gb/s VPN speeds for $6/m with 1TB of bandwidth.
8 | - Disables most of Tailscale's logging features for security
9 |
10 | **Configuration Tool**: https://jakelmg.github.io/tailscale-flatcar-config-tool/
11 |
12 | ## Instructions (using the [configuration tool](https://jakelmg.github.io/tailscale-flatcar-config-tool/))
13 | 1. **Create a free [Tailscale account](https://login.tailscale.com/start)**
14 | 2. **Go to [Settings>Keys](https://login.tailscale.com/admin/settings/keys) and generate an auth key.**
15 | 3. **Open the [configuration tool](https://jakelmg.github.io/tailscale-flatcar-config-tool/), paste in the auth key you just generated, and click "Generate Config" or "Copy" in the preview window.**
16 | - Optionally:
17 | - Enable SSH access and enter an SSH public key
18 | - Select your timezone for automatic update reboot timing / logs
19 | - Adjust the automatic update reboot time window
20 | 4. **Create a VPS on [Vultr](https://lmg.gg/vultr-gh)** (affiliate link)
21 | 1. Select the location you want to deploy your VPN server to.
22 | 2. Select "Shared CPU" as the server "Type" (the other types may also work but are much more expensive)
23 | 3. Disable "Automatic Backups" (they cost money, and we aren't storing data on this VPS anyways)
24 | 4. Select "High Frequency" under Plans, and then "vhf-1c-1gb"
25 | - This is the cheapest high frequency plan, and includes 1 shared CPU core, 1GB of RAM, and 1TB of bandwidth for $6/month. It can handle around 1Gb/s VPN speeds
26 | - Other tiers seemed to have worst performance - but if you have a less than 1Gb/s internet connection, that might not matter, and a cheaper plan might be a better value.
27 | 5. On the "Step 2" page, select the "stable x64" version of Flatcar Container Linux
28 | 6. Paste the config we generated earlier into the "Ignition Configuration" box
29 | 7. Click "Deploy"
30 | 5. **Once `vpn-server` appears in the Tailscale "[Machines](https://login.tailscale.com/admin/machines)" page, approve the device as an exit node.**
31 | 1. Click the "..." icon next to the "vpn-server" machine, then "Edit route settings..."
32 | 2. Check the "Use as exit node" box.
33 | 3. Click "Save"
34 | 6. **[Download Tailscale](https://tailscale.com/download) and install it on the device you want to tunnel through your new VPN.**
35 | 7. **Login into the Tailscale client with the same account you used when generating the auth key.**
36 | 8. **Open cmd/terminal and run `ping vpn-server` to make sure you can ping your vpn server over Tailscale.**
37 | 9. **Run `tailscale status` (mac instructions [here](https://tailscale.com/kb/1080/cli?tab=macos)) and make sure the connection shows "Direct" and not relayed.**
38 | - In most cases, it shouldn't be possible for the connection to relay. If that happens, you may be dealing with an extremely restrictive or non standard firewall on the client.
39 | 10. **Once you've verified you have a working direct connection, select your VPN server as your exit node. This will start tunneling your traffic through your new DIY VPN server :D**
40 | 1. Right click on Tailscale in your device tray > Exit nodes > Select "`vpn-server`"
41 | 11. **Check to see if your external IP address has changed with a service like [ipinfo.io](https://ipinfo.io/)**
42 | - "org" should show "Vultr" or similar
43 | - city/region/count should match the location you selected when creating the VPS on Vultr
44 | - "ip" should be different than your non-VPN'd IP (you can disconnect from Tailscale to compare)
45 |
46 | ## Manual Instructions
47 | 1. **Create a free [Tailscale account](https://login.tailscale.com/start)**
48 | 2. **Go to [Settings>Keys](https://login.tailscale.com/admin/settings/keys) and generate an auth key.**
49 | 3. **Download the human-readable Butane config: human-config.yaml**
50 | 1. Replace `ENTER_TAILSCALE_AUTH_KEY_HERE` with the auth key you generated in step 2.
51 | 2. Optionally:
52 | - Enable SSH by adding a firewall allow rule for port 22/tcp
53 | ```
54 | - path: /var/lib/iptables/rules-save
55 | mode: 0644
56 | contents:
57 | inline: |
58 | *filter
59 | :INPUT DROP [0:0]
60 | :FORWARD DROP [0:0]
61 | :OUTPUT ACCEPT [0:0]
62 | -A INPUT -i lo -j ACCEPT
63 | -A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
64 | -A INPUT -p udp -m udp --dport 41641 -j ACCEPT
65 | -A INPUT -p icmp -m icmp --icmp-type 0 -j ACCEPT
66 | -A INPUT -p icmp -m icmp --icmp-type 3 -j ACCEPT
67 | -A INPUT -p icmp -m icmp --icmp-type 8 -j ACCEPT
68 | -A INPUT -p icmp -m icmp --icmp-type 11 -j ACCEPT
69 | -A INPUT -p tcp -m tcp --dport 22 -j ACCEPT <--- Add this line to open port 22 for SSH access
70 | -A FORWARD -i eth0 -o tailscale0 -j ACCEPT
71 | -A FORWARD -i tailscale0 -o eth0 -j ACCEPT
72 | COMMIT
73 | ```
74 | - Add an SSH key in Butane format for the default `core` user.
75 | ```
76 | passwd:
77 | users:
78 | - name: core
79 | ssh_authorized_keys:
80 | - "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDGdByTgSVHq......." <--- Replace with your SSH public key
81 | ```
82 | - Enter your timezone
83 | ```
84 | links:
85 | - path: /etc/localtime
86 | overwrite: true
87 | target: /usr/share/zoneinfo/Etc/UTC <--- Replace "Etc/UTC" with your desired timezone
88 | ```
89 | - Adjust the automatic update reboot time window
90 | ```
91 | - path: /etc/flatcar/update.conf
92 | overwrite: true
93 | contents:
94 | inline: |
95 | REBOOT_STRATEGY=reboot
96 | LOCKSMITHD_REBOOT_WINDOW_START=02:00 <--- Replace "02:00" with your desired start time for the automatic update reboot time window
97 | LOCKSMITHD_REBOOT_WINDOW_LENGTH=1h <--- Replace "1h" with your desired reboot time window length
98 | mode: 0420
99 | ```
100 |
101 | 4. **Install Butane, which you'll need to convert the human-readable Butane config to a system-readable Ignition config:**
102 | - Windows (in cmd)
103 | ```
104 | winget install butane
105 | ```
106 | - MacOS (in terminal, requires [Homebrew](https://brew.sh/)):
107 | ```
108 | brew install butane
109 | ```
110 | - Debian/Ubuntu Linux:
111 | ```
112 | sudo apt install butane
113 | ```
114 | 5. **Convert the human-readable Butane config to a system-readable Ignition config:**
115 | ```
116 | butane --pretty --strict human-config.yaml --output=system-config.ign
117 | ```
118 | 6. **Create a VPS on [Vultr](https://lmg.gg/vultr-gh)** (affiliate link)
119 | 1. Select the location you want to deploy your VPN server to.
120 | 2. Select "Shared CPU" as the server "Type" (the other types may also work but are much more expensive)
121 | 3. Disable "Automatic Backups" (they cost money, and we aren't storing data on this VPS anyways)
122 | 4. Select "High Frequency" under Plans, and then "vhf-1c-1gb"
123 | - This is the cheapest high frequency plan, and includes 1 shared CPU core, 1GB of RAM, and 1TB of bandwidth for $6/month. It can handle around 1Gb/s VPN speeds
124 | - Other tiers seemed to have worst performance - but if you have a less than 1Gb/s internet connection, that might not matter, and a cheaper plan might be a better value.
125 | 5. On the "Step 2" page, select the "stable x64" version of Flatcar Container Linux
126 | 6. Paste the config we generated earlier into the "Ignition Configuration" box
127 | 7. Click "Deploy"
128 | 7. **Once vpn-server appears in the Tailscale "[Machines](https://login.tailscale.com/admin/machines)" page, approve the device as an exit node.**
129 | 1. Click the "..." icon next to the "vpn-server" machine, then "Edit route settings..."
130 | 2. Check the "Use as exit node" box.
131 | 3. Click "Save"
132 | 8. **[Download Tailscale](https://tailscale.com/download) and install it on the device you want to tunnel through your new VPN.**
133 | 9. **Login into the Tailscale client with the same account you used when generating the auth key.**
134 | 10. **Open cmd/terminal and run `ping vpn-server` to make sure you can ping your vpn server over Tailscale.**
135 | 11. **Run `tailscale status` (mac instructions [here](https://tailscale.com/kb/1080/cli?tab=macos)) and make sure the connection shows "Direct" and not relayed.**
136 | - In most cases, it shouldn't be possible for the connection to relay. If that happens, you may be dealing with an extremely restrictive or non standard firewall on the client.
137 | 12. **Once you've verified you have a working direct connection, select your VPN server as your exit node. This will start tunneling your traffic through your new DIY VPN server :D**
138 | 1. Right click on Tailscale in your device tray > Exit nodes > Select "`vpn-server`"
139 | 13. **Check to see if your external IP address has changed with a service like [ipinfo.io](https://ipinfo.io/)**
140 | - "org" should show "Vultr" or similar
141 | - city/region/count should match the location you selected when creating the VPS on Vultr
142 | - "ip" should be different than your non-VPN'd IP (you can disconnect from Tailscale to compare)
143 |
144 |
145 |
146 | ## Deploying Tailscale/Flatcar on Other Cloud Providers
147 | - Digital Ocean (easy & tested)
148 | - Upload Flatcar's official DigitalOcean image as a Custom Image
149 | - [Instructions](https://docs.digitalocean.com/products/custom-images/getting-started/quickstart/)
150 | - [Image](https://stable.release.flatcar-linux.net/amd64-usr/current/flatcar_production_digitalocean_image.bin.bz2)
151 |
152 | - OVH VPS (more difficult & untested)
153 | - Use Flatcar's community supported OpenStack (OVH compatible) image
154 | - [Instructions](https://www.flatcar.org/docs/latest/installing/community-platforms/ovhcloud/)
155 | - [Image](https://stable.release.flatcar-linux.net/amd64-usr/current/flatcar_production_openstack_image.img)
--------------------------------------------------------------------------------