This is a simple implementation of a DNS over HTTPS (DoH) server using Kubernetes. The server is implemented using the dns-over-https protocol. The server is hosted on Kubernetes and uses the cert-manager to automatically provision TLS certificates for the server.
The container image for the Kubernetes deployment is maintained by GitHub.com/m13253, which is a DoH server implementation in Go.
Kubernetes Deployment
Below is a highly secure Deployment and ConfigMap of the DoH server on Kubernetes. It does not run as root and uses a non-root user. The deployment also uses a read-only file system, with a temporary -scratch- /server
and /tmp
directory (notice the initContainers
).
ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
name: doh-config
namespace: default
data:
UPSTREAM_DNS_SERVER: 'udp:1.1.1.1:53' # Cloudflare DNS
DOH_HTTP_PREFIX: '/query'
DOH_SERVER_LISTEN: ':8080'
DOH_SERVER_TIMEOUT: '10'
DOH_SERVER_TRIES: '3'
DOH_SERVER_VERBOSE: 'false'
Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: doh
namespace: default
spec:
progressDeadlineSeconds: 600
replicas: 1
revisionHistoryLimit: 1
selector:
matchLabels:
app: doh
strategy:
rollingUpdate:
maxSurge: 25%
maxUnavailable: 25%
type: RollingUpdate
template:
metadata:
labels:
app: doh
spec:
automountServiceAccountToken: false
initContainers:
- name: init-copy-server
image: satishweb/doh-server:v2.3.6-alpine
command: ['sh', '-c', 'cp -r /server/* /init-server/']
volumeMounts:
- mountPath: /init-server
name: server
containers:
- name: doh
image: satishweb/doh-server:v2.3.6-alpine
imagePullPolicy: IfNotPresent
ports:
- containerPort: 8080
protocol: TCP
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
volumeMounts:
- mountPath: /tmp
name: tmp
- mountPath: /server
name: server
resources:
limits:
cpu: 100m
memory: 128Mi
requests:
cpu: 100m
memory: 64Mi
securityContext:
runAsUser: 65534
runAsGroup: 65534
privileged: false
runAsNonRoot: true
readOnlyRootFilesystem: true
allowPrivilegeEscalation: false
procMount: Default
capabilities:
drop: ["ALL"]
seccompProfile:
type: RuntimeDefault
livenessProbe:
httpGet:
path: /q?name=en.wikipedia.org
port: 8080
readinessProbe:
httpGet:
path: /q?name=en.wikipedia.org
port: 8080
envFrom:
- configMapRef:
name: doh-config
dnsPolicy: ClusterFirst
restartPolicy: Always
schedulerName: default-scheduler
terminationGracePeriodSeconds: 30
volumes:
- name: tmp
emptyDir: {}
- name: server
emptyDir: {}
securityContext:
fsGroup: 65534
Kubernetes Service and Ingress
The service and ingress for the DoH server are as follows, I use the nginx-ingress controller for the ingress and a cert-manager cluster issuer named letsencrypt-prod
.
Service
apiVersion: v1
kind: Service
metadata:
name: doh-service
namespace: default
spec:
internalTrafficPolicy: Cluster
ipFamilies:
- IPv4
ipFamilyPolicy: SingleStack
ports:
- port: 8080
protocol: TCP
targetPort: 8080
selector:
app: doh
Ingress
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
nginx.ingress.kubernetes.io/rewrite-target: /
name: doh-ingress
namespace: default
spec:
ingressClassName: nginx
rules:
- host: doh.example.com
http:
paths:
- backend:
service:
name: doh-service
port:
number: 8080
path: /
pathType: Prefix
tls:
- hosts:
- doh.example.com
secretName: doh-tls
Testing and Implementing the DoH Server
To test the DoH server, you can use the curl
command to query the server. Below is an example of a query to the DoH server.
curl -s -H 'accept: application/dns-json' 'https://doh.example.com/query?name=example.com&type=A'
The above command will return a JSON response with the DNS query for the domain example.com
.
Firefox Configuration
You can also configure Firefox to use the DoH server. To do this, open Firefox and go to about:config
. Search for network.trr.mode
and set the value to 3
. Then search for network.trr.uri
and set the value to https://doh.example.com/query
.