Skip to content

Commit df0d214

Browse files
authored
Merge pull request #38 from cmars/k8s
Add k8s deployment, standalone app Dockerfile.
2 parents 73186ee + c64d756 commit df0d214

22 files changed

+531
-0
lines changed

Diff for: Dockerfile

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
FROM golang:latest AS build
2+
ENV GOPATH /go
3+
COPY . /go/src/headscale
4+
WORKDIR /go/src/headscale
5+
RUN go install -a -ldflags="-extldflags=-static" -tags netgo,sqlite_omit_load_extension ./cmd/headscale
6+
RUN test -e /go/bin/headscale
7+
8+
FROM scratch
9+
COPY --from=build /go/bin/headscale /go/bin/headscale
10+
ENV TZ UTC
11+
EXPOSE 8080/tcp
12+
ENTRYPOINT ["/go/bin/headscale"]

Diff for: k8s/.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
/**/site
2+
/**/secrets

Diff for: k8s/README.md

+99
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
# Deploying Headscale on Kubernetes
2+
3+
This directory contains [Kustomize](https://kustomize.io) templates that deploy
4+
Headscale in various configurations.
5+
6+
These templates currently support Rancher k3s. Other clusters may require
7+
adaptation, especially around volume claims and ingress.
8+
9+
Commands below assume this directory is your current working directory.
10+
11+
# Generate secrets and site configuration
12+
13+
Run `./init.bash` to generate keys, passwords, and site configuration files.
14+
15+
Edit `base/site/public.env`, changing `public-hostname` to the public DNS name
16+
that will be used for your headscale deployment.
17+
18+
Set `public-proto` to "https" if you're planning to use TLS & Let's Encrypt.
19+
20+
Configure DERP servers by editing `base/site/derp.yaml` if needed.
21+
22+
# Add the image to the registry
23+
24+
You'll somehow need to get `headscale:latest` into your cluster image registry.
25+
26+
An easy way to do this with k3s:
27+
- Reconfigure k3s to use docker instead of containerd (`k3s server --docker`)
28+
- `docker build -t headscale:latest ..` from here
29+
30+
# Create the namespace
31+
32+
If it doesn't already exist, `kubectl create ns headscale`.
33+
34+
# Deploy headscale
35+
36+
## sqlite
37+
38+
`kubectl -n headscale apply -k ./sqlite`
39+
40+
## postgres
41+
42+
`kubectl -n headscale apply -k ./postgres`
43+
44+
# TLS & Let's Encrypt
45+
46+
Test a staging certificate with your configured DNS name and Let's Encrypt.
47+
48+
`kubectl -n headscale apply -k ./staging-tls`
49+
50+
Replace with a production certificate.
51+
52+
`kubectl -n headscale apply -k ./production-tls`
53+
54+
## Static / custom TLS certificates
55+
56+
Only Let's Encrypt is supported. If you need other TLS settings, modify or patch the ingress.
57+
58+
# Administration
59+
60+
Use the wrapper script to remotely operate headscale to perform administrative
61+
tasks like creating namespaces, authkeys, etc.
62+
63+
```
64+
[c@nix-slate:~/Projects/headscale/k8s]$ ./headscale.bash
65+
66+
headscale is an open source implementation of the Tailscale control server
67+
68+
Juan Font Alonso <[email protected]> - 2021
69+
https://gitlab.com/juanfont/headscale
70+
71+
Usage:
72+
headscale [command]
73+
74+
Available Commands:
75+
help Help about any command
76+
namespace Manage the namespaces of Headscale
77+
node Manage the nodes of Headscale
78+
preauthkey Handle the preauthkeys in Headscale
79+
routes Manage the routes of Headscale
80+
serve Launches the headscale server
81+
version Print the version.
82+
83+
Flags:
84+
-h, --help help for headscale
85+
-o, --output string Output format. Empty for human-readable, 'json' or 'json-line'
86+
87+
Use "headscale [command] --help" for more information about a command.
88+
89+
```
90+
91+
# TODO / Ideas
92+
93+
- Github action to publish the docker image
94+
- Interpolate `email:` option to the ClusterIssuer from site configuration.
95+
This probably needs to be done with a transformer, kustomize vars don't seem to work.
96+
- Add kustomize examples for cloud-native ingress, load balancer
97+
- CockroachDB for the backend
98+
- DERP server deployment
99+
- Tor hidden service

Diff for: k8s/base/configmap.yaml

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
apiVersion: v1
2+
kind: ConfigMap
3+
metadata:
4+
name: headscale-config
5+
data:
6+
server_url: $(PUBLIC_PROTO)://$(PUBLIC_HOSTNAME)
7+
listen_addr: "0.0.0.0:8080"
8+
ephemeral_node_inactivity_timeout: "30m"

Diff for: k8s/base/ingress.yaml

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
apiVersion: networking.k8s.io/v1
2+
kind: Ingress
3+
metadata:
4+
name: headscale
5+
annotations:
6+
kubernetes.io/ingress.class: traefik
7+
spec:
8+
rules:
9+
- host: $(PUBLIC_HOSTNAME)
10+
http:
11+
paths:
12+
- backend:
13+
service:
14+
name: headscale
15+
port:
16+
number: 8080
17+
path: /
18+
pathType: Prefix

Diff for: k8s/base/kustomization.yaml

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
namespace: headscale
2+
resources:
3+
- configmap.yaml
4+
- ingress.yaml
5+
- service.yaml
6+
generatorOptions:
7+
disableNameSuffixHash: true
8+
configMapGenerator:
9+
- name: headscale-site
10+
files:
11+
- derp.yaml=site/derp.yaml
12+
envs:
13+
- site/public.env
14+
- name: headscale-etc
15+
literals:
16+
- config.json={}
17+
secretGenerator:
18+
- name: headscale
19+
files:
20+
- secrets/private-key
21+
vars:
22+
- name: PUBLIC_PROTO
23+
objRef:
24+
kind: ConfigMap
25+
name: headscale-site
26+
apiVersion: v1
27+
fieldRef:
28+
fieldPath: data.public-proto
29+
- name: PUBLIC_HOSTNAME
30+
objRef:
31+
kind: ConfigMap
32+
name: headscale-site
33+
apiVersion: v1
34+
fieldRef:
35+
fieldPath: data.public-hostname
36+
- name: CONTACT_EMAIL
37+
objRef:
38+
kind: ConfigMap
39+
name: headscale-site
40+
apiVersion: v1
41+
fieldRef:
42+
fieldPath: data.contact-email

Diff for: k8s/base/service.yaml

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
apiVersion: v1
2+
kind: Service
3+
metadata:
4+
name: headscale
5+
labels:
6+
app: headscale
7+
spec:
8+
selector:
9+
app: headscale
10+
ports:
11+
- name: http
12+
targetPort: http
13+
port: 8080

Diff for: k8s/headscale.bash

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
#!/usr/bin/env bash
2+
set -eu
3+
exec kubectl -n headscale exec -ti pod/headscale-0 -- /go/bin/headscale "$@"

Diff for: k8s/init.bash

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
#!/usr/bin/env bash
2+
set -eux
3+
cd $(dirname $0)
4+
5+
umask 022
6+
mkdir -p base/site/
7+
[ ! -e base/site/public.env ] && (
8+
cat >base/site/public.env <<EOF
9+
public-hostname=localhost
10+
public-proto=http
11+
12+
EOF
13+
)
14+
[ ! -e base/site/derp.yaml ] && cp ../derp.yaml base/site/derp.yaml
15+
16+
umask 077
17+
mkdir -p base/secrets/
18+
[ ! -e base/secrets/private-key ] && (
19+
wg genkey > base/secrets/private-key
20+
)
21+
mkdir -p postgres/secrets/
22+
[ ! -e postgres/secrets/password ] && (head -c 32 /dev/urandom | base64 -w0 > postgres/secrets/password)

Diff for: k8s/install-cert-manager.bash

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
#!/usr/bin/env bash
2+
set -eux
3+
kubectl apply -f https://github.com/jetstack/cert-manager/releases/download/v1.4.0/cert-manager.yaml

Diff for: k8s/postgres/deployment.yaml

+78
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
apiVersion: apps/v1
2+
kind: Deployment
3+
metadata:
4+
name: headscale
5+
spec:
6+
replicas: 2
7+
selector:
8+
matchLabels:
9+
app: headscale
10+
template:
11+
metadata:
12+
labels:
13+
app: headscale
14+
spec:
15+
containers:
16+
- name: headscale
17+
image: "headscale:latest"
18+
imagePullPolicy: IfNotPresent
19+
command: ["/go/bin/headscale", "serve"]
20+
env:
21+
- name: SERVER_URL
22+
value: $(PUBLIC_PROTO)://$(PUBLIC_HOSTNAME)
23+
- name: LISTEN_ADDR
24+
valueFrom:
25+
configMapKeyRef:
26+
name: headscale-config
27+
key: listen_addr
28+
- name: PRIVATE_KEY_PATH
29+
value: /vol/secret/private-key
30+
- name: DERP_MAP_PATH
31+
value: /vol/config/derp.yaml
32+
- name: EPHEMERAL_NODE_INACTIVITY_TIMEOUT
33+
valueFrom:
34+
configMapKeyRef:
35+
name: headscale-config
36+
key: ephemeral_node_inactivity_timeout
37+
- name: DB_TYPE
38+
value: postgres
39+
- name: DB_HOST
40+
value: postgres.headscale.svc.cluster.local
41+
- name: DB_PORT
42+
value: "5432"
43+
- name: DB_USER
44+
value: headscale
45+
- name: DB_PASS
46+
valueFrom:
47+
secretKeyRef:
48+
name: postgresql
49+
key: password
50+
- name: DB_NAME
51+
value: headscale
52+
ports:
53+
- name: http
54+
protocol: TCP
55+
containerPort: 8080
56+
livenessProbe:
57+
tcpSocket:
58+
port: http
59+
initialDelaySeconds: 30
60+
timeoutSeconds: 5
61+
periodSeconds: 15
62+
volumeMounts:
63+
- name: config
64+
mountPath: /vol/config
65+
- name: secret
66+
mountPath: /vol/secret
67+
- name: etc
68+
mountPath: /etc/headscale
69+
volumes:
70+
- name: config
71+
configMap:
72+
name: headscale-site
73+
- name: etc
74+
configMap:
75+
name: headscale-etc
76+
- name: secret
77+
secret:
78+
secretName: headscale

Diff for: k8s/postgres/kustomization.yaml

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
namespace: headscale
2+
bases:
3+
- ../base
4+
resources:
5+
- deployment.yaml
6+
- postgres-service.yaml
7+
- postgres-statefulset.yaml
8+
generatorOptions:
9+
disableNameSuffixHash: true
10+
secretGenerator:
11+
- name: postgresql
12+
files:
13+
- secrets/password

Diff for: k8s/postgres/postgres-service.yaml

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
apiVersion: v1
2+
kind: Service
3+
metadata:
4+
name: postgres
5+
labels:
6+
app: postgres
7+
spec:
8+
selector:
9+
app: postgres
10+
ports:
11+
- name: postgres
12+
targetPort: postgres
13+
port: 5432

Diff for: k8s/postgres/postgres-statefulset.yaml

+49
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
apiVersion: apps/v1
2+
kind: StatefulSet
3+
metadata:
4+
name: postgres
5+
spec:
6+
serviceName: postgres
7+
replicas: 1
8+
selector:
9+
matchLabels:
10+
app: postgres
11+
template:
12+
metadata:
13+
labels:
14+
app: postgres
15+
spec:
16+
containers:
17+
- name: postgres
18+
image: "postgres:13"
19+
imagePullPolicy: IfNotPresent
20+
env:
21+
- name: POSTGRES_PASSWORD
22+
valueFrom:
23+
secretKeyRef:
24+
name: postgresql
25+
key: password
26+
- name: POSTGRES_USER
27+
value: headscale
28+
ports:
29+
- name: postgres
30+
protocol: TCP
31+
containerPort: 5432
32+
livenessProbe:
33+
tcpSocket:
34+
port: 5432
35+
initialDelaySeconds: 30
36+
timeoutSeconds: 5
37+
periodSeconds: 15
38+
volumeMounts:
39+
- name: pgdata
40+
mountPath: /var/lib/postgresql/data
41+
volumeClaimTemplates:
42+
- metadata:
43+
name: pgdata
44+
spec:
45+
storageClassName: local-path
46+
accessModes: ["ReadWriteOnce"]
47+
resources:
48+
requests:
49+
storage: 1Gi

0 commit comments

Comments
 (0)