WireGuard on Kubernetes with Adblocking
The Internet is simply unusable with all the ads floating around. While browser extensions like uBlock Origin work for tech-savvy users, most people lack ad protection. I wanted a DNS-level solution combined with VPN capabilities to serve my family.
Why This Approach?
Three key use cases motivated this setup:
- Preventing ISP data collection on browsing patterns
- Circumventing internet censorship
- Remote access to home network
I evaluated mobile solutions like Blokada and DNS66 but found limitations. Android’s VPN service restriction prevents simultaneous VPN connections, meaning custom VPN solutions expose browsing to ISPs. A publicly accessible DNS and VPN server resolves this constraint.
Technology Selection
AdGuard Home was chosen over Pi-Hole because it offers:
- Native DNS-over-TLS support
- Single configuration file
- Golang implementation using fewer resources
WireGuard was selected for VPNs due to its speed, auditable codebase, cross-platform support, and Linux kernel integration. Kilo, a WireGuard-based network overlay for Kubernetes, enabled the complete setup while securing inter-pod communication and allowing cluster debugging.
AdGuard Deployment
apiVersion: apps/v1kind: Deploymentmetadata: name: adguardhomespec: selector: matchLabels: app: adguardhome replicas: 1 strategy: type: RollingUpdate rollingUpdate: maxUnavailable: 1 maxSurge: 0 template: metadata: labels: app: adguardhome spec: volumes: - name: tls-cert-secret secret: secretName: production-tls-cert - name: adguard-config hostPath: path: "/path/to/store/conf" type: DirectoryOrCreate - name: adguard-logs hostPath: path: "/path/to/store/work" type: DirectoryOrCreate containers: - name: adguardhome image: adguard/adguardhome:v0.102.0 ports: - containerPort: 53 hostPort: 53 protocol: UDP - containerPort: 53 hostPort: 53 protocol: TCP - containerPort: 853 hostPort: 853 protocol: TCP volumeMounts: - name: tls-cert-secret mountPath: /certs - name: adguard-config mountPath: /opt/adguardhome/conf - name: adguard-logs mountPath: /opt/adguardhome/work terminationGracePeriodSeconds: 20---apiVersion: v1kind: Servicemetadata: name: adguardhome labels: app: adguardhomespec: type: ClusterIP selector: app: adguardhome ports: - port: 80 targetPort: 80 protocol: TCPThe rolling update strategy intentionally terminates existing pods without waiting for new ones. This is necessary because hostPort binding to port 53 prevents concurrent pod scheduling.
Initial setup requires accessing the admin UI on port 3000. After configuration, revert the service targetPort to 80.
Optional enhancements include:
- DNSSEC for DNS response authenticity verification
- DNS-over-TLS using certificate paths
/certs/tls.crtand/certs/tls.key
Mobile DNS Configuration
Android Pie and later: Navigate to Settings > WiFi and Internet > Private DNS, select “Private DNS Hostname Provider,” and enter your configured domain. Configure routers at the network level for device-wide protection.
WireGuard Setup
Prerequisites
Install WireGuard on both server (k3s cluster) and client machines. Install kgctl on the client:
go get github.com/squat/kilo/cmd/kgctlKilo Installation
Download and modify the k3s manifest:
curl -LO https://raw.githubusercontent.com/squat/kilo/master/manifests/kilo-k3s.yamlAdd --mesh-granularity=full to the kilo container args in the DaemonSet:
containers:- name: kilo image: squat/kilo args: - --kubeconfig=/etc/kubernetes/kubeconfig - --hostname=$(NODE_NAME) - --mesh-granularity=full env: - name: NODE_NAME valueFrom: fieldRef: fieldPath: spec.nodeNameApply the manifest:
kubectl apply -f kilo-k3s.yamlClient Key Generation
wg genkey | tee privatekey | wg pubkey > publickeyServer-Side Peer Authorization
Create archie.yaml:
apiVersion: kilo.squat.ai/v1alpha1kind: Peermetadata: name: archiespec: allowedIPs: - 10.120.120.1/32 publicKey: CLIENT_PUBLIC_KEY persistentKeepalive: 10kubectl apply -f archie.yamlGenerate Client Configuration
kgctl showconf peer archieOutput:
[Peer]AllowedIPs = 10.42.0.0/24, 10.42.0.0/32, 10.4.0.1/32Endpoint = YOUR_SERVER_IP:51820PersistentKeepalive = 10PublicKey = SERVER_PUBLIC_KEYClient Configuration
Create /etc/wireguard/adguard.conf:
[Interface]Address = 10.120.120.1/32PrivateKey = CLIENT_PRIVATE_KEYDNS = YOUR_SERVER_IP
[Peer]AllowedIPs = 0.0.0.0/0, ::/0Endpoint = YOUR_SERVER_IP:51820PersistentKeepalive = 10PublicKey = SERVER_PUBLIC_KEYThe 0.0.0.0/0, ::/0 configuration routes all traffic through the VPN interface.
Connection Management
wg-quick up adguardVerify connectivity at https://ifconfig.io - your IP should match the server’s IP.
wg-quick down adguardMobile Integration
Generate a QR code for Android app scanning:
qrencode -t ansiutf8 < /etc/wireguard/adguard.confInstall the WireGuard Android app from F-Droid and scan the code to import the configuration.
Results
This setup provides:
- Complete ISP traffic isolation
- DNS-level ad filtering across all connected devices
- Network malware protection
- Cross-network device communication regardless of physical location
- Centralized infrastructure management via Kubernetes