Deploy Pi-Hole on Kubernetes
Pi-Hole is a network-wide ad blocker. It works by blocking ad’s at the DNS level instead of the browser level. Once we’re set up with Pi-Hole’s deployment, you’d change your DNS server either at your local workstation or in your router.
We’ll start off by creating our many manifest file’s that we’ll then apply. There is a helm chart for Pi-Hole located in https://github.com/MoJo2600/pihole-kubernetes/tree/master/charts/pihole, but to be honest, When I initially set up my deployment of Pi-Hole, I wasn’t a fan of the helm chart. I also initially set it up in a very insecure manner.
Before we do anything, let’s create another folder in ~/k3s/
titled “pihole” to keep our manifests tidy and clean. Once that’s completed, We’ll start with by creating the pihole namespace.
Namespace:
Personally, I like to create a namespace for each of my applications. It makes things tidy and clean, along with simplifying the debugging process(less clutter).
let’s cd into our ~/k3s/pihole/
folder and create a namespace.pihole.yaml file. Once that’s been completed, let’s input the following code block in to the namespace.pihole.yaml file:
apiVersion: v1
kind: Namespace
metadata:
name: pihole
labels:
name: pihole
Once the file has been created with the above code, let’s go ahead and apply it with $ kubectl apply -f namespace.pihole.yaml
Now let’s confirm the namespace was created successfully:
$ kubectl get ns
NAME STATUS AGE
default Active 39h
kube-system Active 39h
kube-public Active 39h
kube-node-lease Active 39h
metallb-system Active 36h
cert-manager Active 10h
ingress-nginx Active 9h
kubernetes-dashboard Active 8h
pihole Active 1m
If done successfully, you should see pihole
in the output.
Storage:
My local deployment is set up with a NFS drive which my deployments save data to. To see how you can setup a NFS drive for persistent storage, check out here: https://mujisayed.com/kubernetes/learn_by_doing/initial_setup/setup_nfs/ & https://mujisayed.com/kubernetes/learn_by_doing/initial_setup/setup_nfs_provisioner/
Persistent Volume Claim
We’ll create the persistent volume claim yaml file next. I’ve titled mine pvc.pihole.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
namespace: "pihole"
name: "pihole-pvc"
spec:
storageClassName: nfs-client
accessModes:
- ReadWriteOnce
resources:
requests:
storage: "1Gi"
Once we’ve created the persistent volume claim, we can go ahead and apply it with $ kubectl apply -f pvc.pihole.yaml
Confirm the PVC was created successfully:
$ kubectl get pvc -n pihole
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
pihole-pvc Bound pihole-pv 1Gi RWO nfs-client 23m
Services
Now we’re ready to create our services. Let’s expose some ports for both our DNS traffic, along with access to the web UI. We’ll first create a service for port 53 that will route UDP traffic to our PI-Hole application. Note, we’ll only be exposing the UDP DNS Port. The second part of the code block will expose port 80 for webui traffic.
Let’s create one file to create all of our services. I’ve titled mine svc.pihole.yaml and have inputted the code below:
## Pi-Hole DNS UDP
apiVersion: v1
kind: Service
metadata:
name: pihole-dns-udp
namespace: pihole
spec:
selector:
app: pihole
ports:
- protocol: UDP
port: 53
targetPort: 53
type: LoadBalancer
---
## Pi-Hole Web
apiVersion: v1
kind: Service
metadata:
name: pihole-web
namespace: pihole
spec:
selector:
app: pihole
ports:
- protocol: TCP
port: 80
targetPort: 80
type: LoadBalancer
Once our yaml file has been created and populated, we can apply it with $ kubectl apply -f svc.pihole.yaml
Confirm Services applied correctly:
$ kubectl get svc -n pihole
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
pihole-dns-udp LoadBalancer 10.43.242.159 192.168.122.201 53:30789/UDP 22m
pihole-web LoadBalancer 10.43.79.196 192.168.122.202 80:31685/TCP 22m
ConfigMap
With our PV’s and PVC’s set, we can now set our environment variables inside a configmap. I’ve titled mine configmap.pihole.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: pihole-config
namespace: pihole
labels:
app: pihole
data:
TZ: "America/New_York" #Set your timezone by replacing America/New_York. See https://en.wikipedia.org/wiki/List_of_tz_database_time_zones for more timezones
VIRTUAL_HOST: "pi.hole" #Location of the admin portal
PROXY_LOCATION: "pi.hole"
ServerIP: "192.168.122.202" #Set ServerIP to the External IP address for pihole-web that we got from the above confirmation
DNSSEC: "false" #Default is false, change to true to enable DNSSEC support
PIHOLE_DNS_: "8.8.8.8;8.8.4.4" #Upstream DNS Server, seperate by semicolon ";"
WEB_PORT: "80" #container port for Web UI
Once we’ve set the ConfigMap, let’s go ahead and deploy it with $ kubectl apply -f configmap.pihole.yaml
Confirm the ConfigMap was applied successfully:
$ kubectl get configmap -n pihole
NAME DATA AGE
kube-root-ca.crt 1 65m
pihole-config 7 63m
Secrets
MAKE SURE YOU CHANGE THE PASSWORD LISTED IN THE CODE BELOW TO SOMETHING MORE SECURE! You have been warned!
We’ll need to create a secret for our WebUI Admin Password. Lets create a secrets.pihole.yaml file and input the code below:
apiVersion: v1
kind: Secret
metadata:
name: webui-password
namespace: pihole
type: Opaque
stringData:
WEBPASSWORD: Password12345! #NOTE! You will need to change this password, especially considering this is definitely not secure!
Once the secrets.pihole.yaml file has been created, let’s apply it with $ kubectl apply -f secrets.pihole.yaml
Now let’s confirm the secret was applied successfully:
$ kubectl get secrets -n pihole
NAME TYPE DATA AGE
default-token-2l9tx kubernetes.io/service-account-token 3 65m
webui-password Opaque 1 64m
Deployment!
Phew, with all of that out of the way, we’re now ready to deploy our application! Since we want to deploy our application with persistent storage, It’d be best to deploy it as a statefulset. Let’s go ahead and create our sfs.pihole.yaml and populate it!
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: pihole
namespace: pihole
labels:
app: pihole
spec:
selector:
matchLabels:
app: pihole
serviceName: pihole
replicas: 1
template:
metadata:
labels:
app: pihole
spec:
containers:
- name: pihole
image: pihole/pihole:latest
imagePullPolicy: IfNotPresent
ports:
- name: pihole-web
containerPort: 80
protocol: TCP
- name: pihole-dns-udp
containerPort: 53
protocol: UDP
- name: pihole-dns-tcp
containerPort: 53
protocol: TCP
volumeMounts:
- name: pihole-data
mountPath: /etc/pihole
envFrom:
- configMapRef:
name: pihole-config
- secretRef:
name: webui-password
livenessProbe:
httpGet:
path: /admin.index.php
port: pihole-web
initialDelaySeconds: 60
failureThreshold: 10
timeoutSeconds: 5
readinessProbe:
httpGet:
path: /admin.index.php
port: pihole-web
initialDelaySeconds: 60
failureThreshold: 3
timeoutSeconds: 5
volumes:
- name: pihole-data
persistentVolumeClaim:
claimName: pihole-pvc
Now we’re ready to deploy our application! let’s deploy it with $ kubectl apply -f sfs.pihole.yaml
Now for a final check, let’s check to see it’s all deployed successfully!
$ kubectl get all -n pihole
NAME READY STATUS RESTARTS AGE
pod/pihole-0 1/1 Running 0 6m27s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/pihole-dns-udp LoadBalancer 10.43.242.159 192.168.122.201 53:30789/UDP 50m
service/pihole-web LoadBalancer 10.43.79.196 192.168.122.202 80:31685/TCP 50m
NAME READY AGE
statefulset.apps/pihole 1/1 64m
confirm DNS queries are being resolved by Pi-Hole:
$ dig pi-hole.net @192.168.122.201 #NOTE! the IP after the @ is the external ip of pihole-dns-udp service
; <<>> DiG 9.16.1-Ubuntu <<>> pi-hole.net @192.168.122.201
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 39254
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 512
;; QUESTION SECTION:
;pi-hole.net. IN A
;; ANSWER SECTION:
pi-hole.net. 300 IN A 3.18.136.52
;; Query time: 19 msec
;; SERVER: 192.168.122.201#53(192.168.122.201)
;; WHEN: Fri Mar 11 13:17:34 UTC 2022
;; MSG SIZE rcvd: 56
Once you’ve changed your DNS Servers either on your local workstation, or at the router, go ahead and visit http://pi.hole/admin(or if you haven’t, go to the IP address of service/pihole-web, e.g http://192.168.122.202/admin) and see your first deployment!