DNS

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.

Leave a Reply

Your email address will not be published. Required fields are marked *