Intro
Ahoy, Homelabbers! Ever wanted to create a private WireGuard VPN between multiple devices without the headache of configuring firewalls or exposing ports publicly? Look no further! Tailscale is here to make your dreams come true.
In this tutorial, we’ll be using k3s (but feel free to use any Kubernetes distribution) to host our Tailscale subrouter and exit node. This setup will grant us:
In this tutorial, we’ll be using k3s (but feel free to use any Kubernetes distribution) to host our Tailscale subrouter and exit node. This setup will grant us:
- A reliable Tailscale hosting on Kubernetes, thanks to its health checks
- Subnets that provide access to our private network or Kubernetes cluster from an outside network
- An exit node that gives us a full WireGuard VPN experience from anywhere on the globe
Ready to get started? Let’s go!
Getting a Tailscale Auth Key
We’ll need an ephemeral key for this. Ephemeral keys are perfect for temporary or non-human-managed machines. To generate an auth key, simply log in to Tailscale’s web console and follow these steps: Settings > Keys > Auth keys > Generate auth key. In the modal window, make sure to set the following:
- Reusable: true
- Expiration: 90 days
- Ephemeral: true
- Tags (optional): any applicable to your setup, but you can leave this blank for now
Once generated, you’ll get a key looking like this: tskey-auth-xxXXxxxXX-xxxxx
Deploying our manifests
Next, create a tailscale.yaml file with the following manifest and replace the secret stringData with the API key you just got. Save the file, and run kubectl apply -f tailscale.yaml
apiVersion: v1
kind: Namespace
metadata:
name: tailscale
---
apiVersion: v1
kind: Secret
metadata:
name: tailscale-auth
namespace: tailscale
stringData:
AUTH_KEY: tskey-auth-xxXXxxxXX-xxxxx
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: tailscale
namespace: tailscale
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: tailscale
namespace: tailscale
subjects:
- kind: ServiceAccount
name: "tailscale"
roleRef:
kind: Role
name: tailscale
apiGroup: rbac.authorization.k8s.io
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: tailscale
namespace: tailscale
rules:
- apiGroups: [""]
resources: ["secrets"]
verbs: ["create"]
- apiGroups: [""]
resourceNames: ["tailscale-auth"]
resources: ["secrets"]
verbs: ["get", "update", "patch"]
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: tailscale
namespace: tailscale
spec:
selector:
matchLabels:
app: tailscale
strategy:
rollingUpdate:
maxSurge: 0
maxUnavailable: 1
template:
metadata:
labels:
app: tailscale
spec:
serviceAccountName: "tailscale"
containers:
- name: tailscale
image: ghcr.io/tailscale/tailscale:latest
imagePullPolicy: Always
env:
- name: TZ
value: "Australia/Sydney"
- name: TS_KUBE_SECRET
value: "tailscale-auth"
- name: TS_USERSPACE
value: "true"
- name: TS_EXTRA_ARGS
value: "--advertise-exit-node --hostname=k8s-node"
- name: TS_ROUTES
value: "192.168.0.0/24"
- name: TS_AUTH_KEY
valueFrom:
secretKeyRef:
name: tailscale-auth
key: AUTH_KEY
optional: false
livenessProbe:
exec:
command:
- tailscale
- --socket=/tmp/tailscaled.sock
- status || exit 1
initialDelaySeconds: 30
periodSeconds: 15
failureThreshold: 2
startupProbe:
exec:
command:
- tailscale
- --socket=/tmp/tailscaled.sock
- status || exit 1
initialDelaySeconds: 15
periodSeconds: 30
failureThreshold: 2
Breakdown
TS_EXTRA_ARGS: These are used inside the Tailscale image to pass additional command line arguments into the startup command. In our example, we’re setting a hostname (to identify the pod to Tailscale) and specifying that we want this pod to act as an exit node. Feel free to modify these as needed.
TS_ROUTES: Update this to any CIDR block appropriate for your network requirements. In this example, we’re advertising routes typically found in home networks.
Confirming Everything is Running
Run kubectl get pod -n tailscale and check if it shows 1/1 as Running. If not, consult the logs with kubectl logs -l app=tailscale -n tailscale for more information. It’s likely the manifest wasn’t set up correctly, or the auth key needs verification.
Setting Up Our New Node in Tailscale Console
With our new node connected to Tailscale and ready for use, it’s time for some housekeeping:
- Head over to Tailscale and go to the “Machines” page
- Verify it’s listed as “Connected”
- Hover over this new machine, click the 3 dots menu on the right, and select “Edit Route Settings”
- Enable any “Subnet Routes” and “Exit Node” use as desired
- (Optional) To disable key expiry, hover over the new machine, click the 3 dots menu on the right, and select “Disable Key Expiry” (this prevents the need to rotate the key in the next 90 days)
Summing up
With Tailscale up and running in your homelab, you’ve now unlocked the power to access anything available on the exposed subnet. This includes privately hosted web apps that were once out of reach on another network. When using subnets we can use our devices main internet connection for most outbound requests, but route private connections to our homelab.
But that’s not all! When you’re traveling and feeling a little homesick, you can use the exit node feature on any other Tailscale device. This nifty trick provides a secure WireGuard VPN back to your home internet service, making your devices believe you’re still at home sweet home. Want to watch your local Netflix library while globetrotting? No problem! Tailscale has your back.
Updates
- April 2023 - Updating manifest for new tailscale image and recommendations, small text changes