Target upstream ISC bind9 image
Uses internetsystemsconsortium/bind9 as the default base image instead of a self-hosted one, verified against internetsystemsconsortium/bind9:9.20 (runs as root; named/rndc/nsupdate at /usr/sbin,/usr/sbin,/usr/bin). - project operator config at /etc/bind-operator instead of overmounting the image's /etc/bind (keeps bind.keys / base config intact) - reference named/rndc/nsupdate by absolute path (exec PATH may exclude /usr/sbin) - centralise filesystem + binary paths in internal/bind/consts.go - default spec.image to internetsystemsconsortium/bind9:9.20
This commit is contained in:
@@ -90,8 +90,10 @@ on pull requests; pushing a `v*` tag builds and pushes
|
||||
## Notes & caveats
|
||||
|
||||
- The BIND container image (`spec.image`, default
|
||||
`git.unkin.net/unkin/bind9:latest`) must ship `named`, `rndc` and `nsupdate`,
|
||||
read `/run/named/named.conf`, and honour the operator's `/etc/bind` layout.
|
||||
`internetsystemsconsortium/bind9:9.20`) must ship `named`, `rndc` and
|
||||
`nsupdate`. The operator projects its config at `/etc/bind-operator` (leaving
|
||||
the image's own `/etc/bind`, including `bind.keys`, intact) and runs
|
||||
`named -g -c /run/named/named.conf`.
|
||||
- Dynamic updates authenticate with `nsupdate -y`; the TSIG secret is passed on
|
||||
the argv of an exec'd process inside the pod.
|
||||
- RPZ IP-trigger encodings (`ip`, `client-ip`, `nsip`) are emitted verbatim;
|
||||
|
||||
@@ -52,8 +52,8 @@ type BindClusterSpec struct {
|
||||
// +optional
|
||||
Replicas int32 `json:"replicas,omitempty"`
|
||||
|
||||
// Image is the BIND9 container image.
|
||||
// +kubebuilder:default="git.unkin.net/unkin/bind9:latest"
|
||||
// Image is the BIND9 container image. Must ship named, rndc and nsupdate.
|
||||
// +kubebuilder:default="internetsystemsconsortium/bind9:9.20"
|
||||
// +optional
|
||||
Image string `json:"image,omitempty"`
|
||||
|
||||
|
||||
@@ -994,8 +994,9 @@ spec:
|
||||
type: string
|
||||
type: array
|
||||
image:
|
||||
default: git.unkin.net/unkin/bind9:latest
|
||||
description: Image is the BIND9 container image.
|
||||
default: internetsystemsconsortium/bind9:9.20
|
||||
description: Image is the BIND9 container image. Must ship named,
|
||||
rndc and nsupdate.
|
||||
type: string
|
||||
imagePullPolicy:
|
||||
description: ImagePullPolicy for the BIND container.
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
package bind
|
||||
|
||||
// Filesystem layout and binary locations inside a BIND pod. The operator mounts
|
||||
// its rendered config at ConfigDir (a path distinct from the image's own
|
||||
// /etc/bind, so the base image's bind.keys trust anchors remain available for
|
||||
// dnssec-validation).
|
||||
const (
|
||||
// ContainerName is the BIND container name within each pod.
|
||||
ContainerName = "bind"
|
||||
|
||||
// ConfigDir is where the operator projects named.conf, keys and the
|
||||
// entrypoint. Kept separate from the image's /etc/bind.
|
||||
ConfigDir = "/etc/bind-operator"
|
||||
|
||||
// DataDir is BIND's writable working directory (backed by the PVC): zone
|
||||
// databases and journals.
|
||||
DataDir = "/var/lib/named"
|
||||
|
||||
// RunDir holds the ordinal-selected named.conf (writable emptyDir).
|
||||
RunDir = "/run/named"
|
||||
|
||||
// Binary locations in the ISC BIND9 image (Debian/Ubuntu layout).
|
||||
NamedBin = "/usr/sbin/named"
|
||||
RndcBin = "/usr/sbin/rndc"
|
||||
NsupdateBin = "/usr/bin/nsupdate"
|
||||
)
|
||||
|
||||
// Config file paths derived from ConfigDir.
|
||||
const (
|
||||
NamedConfRun = RunDir + "/named.conf"
|
||||
NamedConfPrimary = ConfigDir + "/named.conf.primary"
|
||||
NamedConfSecondary = ConfigDir + "/named.conf.secondary"
|
||||
EntrypointPath = ConfigDir + "/entrypoint.sh"
|
||||
KeysConfPath = ConfigDir + "/keys.conf"
|
||||
RndcKeyPath = ConfigDir + "/rndc.key"
|
||||
RndcConfPath = ConfigDir + "/rndc.conf"
|
||||
)
|
||||
@@ -14,9 +14,6 @@ import (
|
||||
"k8s.io/client-go/tools/remotecommand"
|
||||
)
|
||||
|
||||
// ContainerName is the BIND container name within each pod.
|
||||
const ContainerName = "bind"
|
||||
|
||||
// Executor runs commands inside BIND pods via the exec subresource.
|
||||
type Executor struct {
|
||||
config *rest.Config
|
||||
|
||||
@@ -41,7 +41,7 @@ func (e *Executor) NSUpdate(ctx context.Context, namespace, pod, zone string, cr
|
||||
}
|
||||
b.WriteString("send\n")
|
||||
|
||||
cmd := []string{"nsupdate", "-y", fmt.Sprintf("%s:%s:%s", creds.Algorithm, creds.Name, creds.Secret)}
|
||||
cmd := []string{NsupdateBin, "-y", fmt.Sprintf("%s:%s:%s", creds.Algorithm, creds.Name, creds.Secret)}
|
||||
if out, err := e.Exec(ctx, namespace, pod, cmd, b.String()); err != nil {
|
||||
return fmt.Errorf("nsupdate zone %s: %w (out: %s)", zone, err, out)
|
||||
}
|
||||
|
||||
@@ -20,9 +20,6 @@ type RenderInput struct {
|
||||
PrimaryAddress string
|
||||
}
|
||||
|
||||
// DataDir is where BIND keeps zone databases and journals (backed by the PVC).
|
||||
const DataDir = "/var/lib/named"
|
||||
|
||||
// RenderNamedConf returns the primary and secondary named.conf contents for a
|
||||
// cluster. Both variants are shipped in the ConfigMap; the entrypoint selects
|
||||
// one based on the pod ordinal.
|
||||
@@ -35,7 +32,8 @@ func render(in RenderInput, isPrimary bool) string {
|
||||
var b strings.Builder
|
||||
|
||||
b.WriteString("// Managed by bind-operator. Do not edit.\n")
|
||||
b.WriteString(`include "/etc/bind/keys/keys.conf";` + "\n\n")
|
||||
b.WriteString(fmt.Sprintf("include \"%s\";\n", RndcKeyPath))
|
||||
b.WriteString(fmt.Sprintf("include \"%s\";\n\n", KeysConfPath))
|
||||
|
||||
// Named ACLs (global scope).
|
||||
acls := append([]bindv1alpha1.BindACL(nil), in.ACLs...)
|
||||
|
||||
@@ -6,12 +6,9 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// RndcConfPath is the operator-managed rndc client config mounted in each pod.
|
||||
const RndcConfPath = "/etc/bind/rndc.conf"
|
||||
|
||||
// Rndc runs `rndc <args...>` on a pod and returns its output.
|
||||
func (e *Executor) Rndc(ctx context.Context, namespace, pod string, args ...string) (string, error) {
|
||||
base := []string{"rndc", "-c", RndcConfPath}
|
||||
base := []string{RndcBin, "-c", RndcConfPath}
|
||||
return e.Exec(ctx, namespace, pod, append(base, args...), "")
|
||||
}
|
||||
|
||||
|
||||
@@ -109,7 +109,7 @@ func (r *BindClusterReconciler) reconcileRNDCSecret(ctx context.Context, c *bind
|
||||
return genErr
|
||||
}
|
||||
keyClause := bind.KeyClause("rndc-key", "hmac-sha256", secret)
|
||||
rndcConf := fmt.Sprintf("include \"/etc/bind/rndc.key\";\noptions {\n default-key \"rndc-key\";\n default-server 127.0.0.1;\n default-port 953;\n};\n")
|
||||
rndcConf := fmt.Sprintf("include \"%s\";\noptions {\n default-key \"rndc-key\";\n default-server 127.0.0.1;\n default-port 953;\n};\n", bind.RndcKeyPath)
|
||||
s := &corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: name, Namespace: c.Namespace, Labels: commonLabels(c.Name)},
|
||||
Data: map[string][]byte{
|
||||
@@ -259,7 +259,7 @@ func (r *BindClusterReconciler) reconcileStatefulSet(ctx context.Context, c *bin
|
||||
replicas := c.Spec.Replicas
|
||||
image := c.Spec.Image
|
||||
if image == "" {
|
||||
image = "git.unkin.net/unkin/bind9:latest"
|
||||
image = defaultBindImage
|
||||
}
|
||||
storageSize := c.Spec.StorageSize
|
||||
if storageSize == "" {
|
||||
@@ -295,15 +295,15 @@ func (r *BindClusterReconciler) reconcileStatefulSet(ctx context.Context, c *bin
|
||||
Name: bind.ContainerName,
|
||||
Image: image,
|
||||
ImagePullPolicy: c.Spec.ImagePullPolicy,
|
||||
Command: []string{"/bin/sh", "/etc/bind/entrypoint.sh"},
|
||||
Command: []string{"/bin/sh", bind.EntrypointPath},
|
||||
Ports: []corev1.ContainerPort{
|
||||
{Name: "dns-udp", ContainerPort: 53, Protocol: corev1.ProtocolUDP},
|
||||
{Name: "dns-tcp", ContainerPort: 53, Protocol: corev1.ProtocolTCP},
|
||||
},
|
||||
Resources: c.Spec.Resources,
|
||||
VolumeMounts: []corev1.VolumeMount{
|
||||
{Name: "bind-etc", MountPath: "/etc/bind", ReadOnly: true},
|
||||
{Name: "run", MountPath: "/run/named"},
|
||||
{Name: "bind-etc", MountPath: bind.ConfigDir, ReadOnly: true},
|
||||
{Name: "run", MountPath: bind.RunDir},
|
||||
{Name: "data", MountPath: bind.DataDir},
|
||||
},
|
||||
ReadinessProbe: &corev1.Probe{
|
||||
|
||||
@@ -23,6 +23,9 @@ const (
|
||||
clusterLabel = "bind.unkin.net/cluster"
|
||||
|
||||
finalizer = "bind.unkin.net/finalizer"
|
||||
|
||||
// defaultBindImage is used when BindCluster.spec.image is empty.
|
||||
defaultBindImage = "internetsystemsconsortium/bind9:9.20"
|
||||
)
|
||||
|
||||
func headlessServiceName(cluster string) string { return cluster + "-headless" }
|
||||
|
||||
@@ -2,6 +2,7 @@ package controller
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
@@ -10,6 +11,7 @@ import (
|
||||
ctrl "sigs.k8s.io/controller-runtime"
|
||||
|
||||
bindv1alpha1 "git.unkin.net/unkin/bind-operator/api/v1alpha1"
|
||||
"git.unkin.net/unkin/bind-operator/internal/bind"
|
||||
)
|
||||
|
||||
func intstrFromInt(i int) intstr.IntOrString { return intstr.FromInt(i) }
|
||||
@@ -26,17 +28,17 @@ func podReady(pod *corev1.Pod) bool {
|
||||
// entrypointScript selects the primary or secondary named.conf based on the
|
||||
// pod's StatefulSet ordinal and launches named in the foreground.
|
||||
func entrypointScript() string {
|
||||
return `#!/bin/sh
|
||||
return fmt.Sprintf(`#!/bin/sh
|
||||
set -eu
|
||||
ORD="${HOSTNAME##*-}"
|
||||
if [ "$ORD" = "0" ]; then
|
||||
cp /etc/bind/named.conf.primary /run/named/named.conf
|
||||
cp %[1]s %[3]s
|
||||
else
|
||||
cp /etc/bind/named.conf.secondary /run/named/named.conf
|
||||
cp %[2]s %[3]s
|
||||
fi
|
||||
mkdir -p /var/lib/named/zones /var/lib/named/catalog
|
||||
exec named -g -c /run/named/named.conf
|
||||
`
|
||||
mkdir -p %[4]s/zones %[4]s/catalog
|
||||
exec %[5]s -g -c %[3]s
|
||||
`, bind.NamedConfPrimary, bind.NamedConfSecondary, bind.NamedConfRun, bind.DataDir, bind.NamedBin)
|
||||
}
|
||||
|
||||
func (r *BindClusterReconciler) upsertService(ctx context.Context, c *bindv1alpha1.BindCluster, desired *corev1.Service) error {
|
||||
|
||||
Reference in New Issue
Block a user