bindcar 0.7.0

HTTP REST API for managing BIND9 zones via rndc
# Copyright (c) 2025 Erick Bourgeois, firestoned
# SPDX-License-Identifier: MIT
#
# Pod/container hardening reference for the bindcar sidecar (K-3) plus an egress
# companion to the ingress NetworkPolicy (N-1).
#
# bindcar runs as a sidecar inside the BIND9 pods created by the bindy operator
# and serves a privileged mutate API. Without a runtime securityContext, a single
# RCE in the HTTP handler lands in a container that can read its auto-mounted
# ServiceAccount token and pivot. These controls are independent defense-in-depth
# layers; apply them to the bindcar container in the bindy pod template.
#
# This file is REFERENCE configuration: the snippet below shows the exact fields
# to set on the bindy-managed pod (it is not directly applyable on its own).
---
# --- Container securityContext (apply to the bindcar sidecar container) -------
# containers:
#   - name: bindcar
#     securityContext:
#       allowPrivilegeEscalation: false      # no setuid escalation
#       readOnlyRootFilesystem: true         # immutable rootfs; mount an
#                                            #   emptyDir at $TMPDIR for the
#                                            #   0600 TSIG key file (nsupdate -k)
#       runAsNonRoot: true
#       runAsUser: 65532                     # matches the distroless/chainguard image
#       capabilities:
#         drop: ["ALL"]                      # bindcar needs no Linux capabilities
#       seccompProfile:
#         type: RuntimeDefault
#
# --- Pod-level settings -------------------------------------------------------
# spec:
#   serviceAccountName: bindcar              # the least-privilege SA (deploy/rbac.yaml)
#   automountServiceAccountToken: false      # withhold the token unless TokenReview
#                                            #   is in use; in drone/explicit mode
#                                            #   bindcar needs no cluster token
#   securityContext:
#     seccompProfile:
#       type: RuntimeDefault
#
# NOTE: if readOnlyRootFilesystem is true, give bindcar a writable $TMPDIR so the
# TSIG key file can be created (it is 0600 and removed immediately after nsupdate
# exits). Mount a small tmpfs/emptyDir and set TMPDIR to it:
#   volumeMounts: [{ name: tmp, mountPath: /tmp }]
#   volumes:      [{ name: tmp, emptyDir: { medium: Memory } }]
---
# ENFORCED control (A5). The securityContext block above is a reference an
# operator must transcribe into the bindy pod template — nothing stops a pod that
# omits it. Pod Security Admission is the applyable backstop: with these labels
# the API server REJECTS any pod admitted to bindy-system that is not
# "restricted" (non-root, no privilege escalation, seccomp RuntimeDefault, all
# capabilities dropped). "restricted" still permits adding back only
# NET_BIND_SERVICE, so the co-located `named` container can bind port 53.
#
# If the bindy operator owns/creates this namespace, apply these labels to ITS
# manifest instead of shipping a competing Namespace object, or run:
#   kubectl label ns bindy-system \
#     pod-security.kubernetes.io/enforce=restricted --overwrite
apiVersion: v1
kind: Namespace
metadata:
  name: bindy-system
  labels:
    app.kubernetes.io/name: bindcar
    app.kubernetes.io/component: pod-security
    pod-security.kubernetes.io/enforce: restricted
    pod-security.kubernetes.io/enforce-version: latest
    pod-security.kubernetes.io/warn: restricted
    pod-security.kubernetes.io/audit: restricted
---
# Egress companion to deploy/networkpolicy.yaml (N-1). The ingress policy is
# Ingress-only, which leaves egress wide open — a compromised pod could exfiltrate
# the SA token or zone data anywhere. This restricts egress to what bindcar/named
# actually need: DNS resolution, the Kubernetes API server (TokenReview), and the
# RNDC/zone-transfer peers. Tighten the CIDRs/selectors to your environment.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: bindcar-egress-restrict
  namespace: bindy-system
  labels:
    app.kubernetes.io/name: bindcar
    app.kubernetes.io/component: network-policy
spec:
  podSelector:
    matchLabels:
      app: bind9
      app.kubernetes.io/part-of: bindy
  policyTypes:
    - Egress
  egress:
    # DNS resolution — scoped to the in-cluster DNS service (CoreDNS/kube-dns in
    # kube-system). This removes the "DNS to anywhere" tunneling/exfil channel
    # (A6). If `named` must reach EXTERNAL zone-transfer peers or upstream
    # forwarders on 53, add their explicit CIDRs to the `to:` list below — do NOT
    # drop the selector to make it open again.
    - to:
        - namespaceSelector:
            matchLabels:
              kubernetes.io/metadata.name: kube-system
        # - ipBlock: { cidr: <secondary-or-forwarder-CIDR> }  # zone transfers / forwarding
      ports:
        - protocol: UDP
          port: 53
        - protocol: TCP
          port: 53
    # Kubernetes API server for TokenReview. This MUST be scoped to your API
    # server endpoint. The placeholder below is intentionally restrictive
    # (fail-closed): left unedited, TokenReview egress is denied and auth fails
    # loudly, rather than the pod being able to reach ANY 443/6443 host on the
    # network — which was the SA-token exfil / lateral-movement channel (A6).
    # Replace the CIDR with your kube-apiserver address (or use a
    # namespaceSelector for a private-endpoint service).
    - to:
        - ipBlock:
            cidr: 10.0.0.1/32 # REPLACE: kube-apiserver endpoint/CIDR
      ports:
        - protocol: TCP
          port: 443
        - protocol: TCP
          port: 6443