Skip to content

firestoned/bindy

Repository files navigation

Bindy the Bee Bindy - BIND9 DNS Controller for Kubernetes

Pronounced: "bined-ee" (like BIND + ee)

Project Status

License GitHub Release GitHub commits since latest release Last Commit

CI/CD Status

Main Branch CI/CD Pull Request Checks Release Workflow Integration Tests Documentation

Code Quality

codecov OpenSSF Scorecard Security Scan

Technology & Compatibility

Rust Kubernetes BIND9 Linux Docker

Security & Compliance

SPDX SLSA 3 Cosign Signed Commits Signed SBOM Trivy

Regulatory Compliance

SOX Controls NIST 800-53 CIS Kubernetes FIPS 140-2

Community & Support

Issues Pull Requests Contributors Stars


Declarative DNS management for Kubernetes. Manage BIND9 infrastructure as code using Custom Resources. Built in Rust for high performance and security.

Built for Regulated Environments: Full SOX, NIST 800-53, and CIS compliance documentation. Designed for banking and financial services.


What is Bindy?

Bindy is a Kubernetes operator that manages BIND9 DNS infrastructure declaratively. Instead of manually configuring DNS servers, you define DNS resources in YAML and Bindy handles:

  • Deploying and configuring BIND9 instances
  • Creating DNS zones with SOA records
  • Adding/updating DNS records dynamically via RNDC
  • Platform-managed DNS accessible cluster-wide

Quick Example

# 1. Create a DNS cluster
apiVersion: bindy.firestoned.io/v1beta1
kind: Bind9Cluster
metadata:
  name: my-dns
  namespace: dns-system
spec:
  primary:
    replicas: 2
  secondary:
    replicas: 2

---
# 2. Create a zone
apiVersion: bindy.firestoned.io/v1beta1
kind: DNSZone
metadata:
  name: example-com
  namespace: dns-system
spec:
  zoneName: example.com
  clusterRef: my-dns

---
# 3. Add DNS records
apiVersion: bindy.firestoned.io/v1beta1
kind: ARecord
metadata:
  name: www
  namespace: dns-system
spec:
  zoneRef: example-com
  name: www
  ipv4Address: "192.0.2.1"

Apply and you're done:

kubectl apply -f dns.yaml
dig @<dns-service-ip> www.example.com  # Works!

Architecture

Custom Resources (CRDs)

Bindy provides 4 types of resources:

Infrastructure

Resource Purpose
Bind9Cluster Logical DNS cluster (manages multiple instances)
Bind9GlobalCluster Cluster-scoped DNS infrastructure (platform-managed)
Bind9Instance Individual BIND9 server deployment

DNS Management

Resource Purpose
DNSZone DNS zone with SOA record
ARecord IPv4 address (A)
AAAARecord IPv6 address (AAAA)
CNAMERecord Alias (CNAME)
MXRecord Mail server (MX)
TXTRecord Text data (TXT, SPF, DKIM)
NSRecord Nameserver delegation (NS)
SRVRecord Service location (SRV)
CAARecord Certificate authority authorization (CAA)

How It Works

┌─────────────────┐     ┌──────────────────┐     ┌─────────────┐
│ Bind9Cluster    │────▶│ Bind9Instance    │────▶│ BIND9 Pod   │
│ (Logical)       │     │ (Physical)       │     │ + Service   │
└─────────────────┘     └──────────────────┘     └─────────────┘
                              │
                              ▼
┌─────────────────┐     ┌──────────────────┐
│ DNSZone         │────▶│ Zone on BIND9    │
│ (example.com)   │     │ via RNDC         │
└─────────────────┘     └──────────────────┘
        │
        ▼
┌─────────────────┐     ┌──────────────────┐
│ ARecord         │────▶│ DNS Record       │
│ (www)           │     │ via RNDC         │
└─────────────────┘     └──────────────────┘

Key Points:

  • Bind9Cluster creates and manages Bind9Instance resources automatically
  • Bind9Instance generates Kubernetes Deployments, Services, ConfigMaps, and Secrets
  • DNSZone creates the zone on target BIND9 instances
  • Record resources add DNS records dynamically (no zone file edits!)

Cluster-Scoped DNS with Bind9GlobalCluster

For platform-managed DNS infrastructure accessible from all namespaces:

apiVersion: bindy.firestoned.io/v1beta1
kind: Bind9GlobalCluster
metadata:
  name: shared-dns
  # No namespace - cluster-scoped resource
spec:
  version: "9.18"
  primary:
    replicas: 3
  secondary:
    replicas: 2

Application teams can then reference this global cluster from any namespace using clusterProviderRef. See Multi-Tenancy Guide for details.

Installation

1. Install CRDs

kubectl create namespace dns-system
kubectl create -f https://github.com/firestoned/bindy/releases/latest/download/crds.yaml

2. Deploy Controller

kubectl apply -f https://github.com/firestoned/bindy/releases/latest/download/bindy.yaml

3. Verify

kubectl wait --for=condition=available --timeout=300s deployment/bindy -n dns-system

That's it! Now create DNS resources.

Usage Examples

Simple DNS Cluster

apiVersion: bindy.firestoned.io/v1beta1
kind: Bind9Cluster
metadata:
  name: simple-dns
  namespace: dns-system
spec:
  primary:
    replicas: 1

Creates 1 primary BIND9 instance. No secondaries needed for dev/test.

Production DNS Cluster

apiVersion: bindy.firestoned.io/v1beta1
kind: Bind9Cluster
metadata:
  name: prod-dns
  namespace: dns-system
spec:
  global:
    recursion: false
    allowQuery: ["0.0.0.0/0"]
    allowTransfer: ["10.0.0.0/8"]
  primary:
    replicas: 3
    config:
      dnssec:
        enabled: true
  secondary:
    replicas: 2

Creates 3 primaries + 2 secondaries with DNSSEC enabled and zone transfers configured.

DNS Zone with Records

# Zone
apiVersion: bindy.firestoned.io/v1beta1
kind: DNSZone
metadata:
  name: example-com
  namespace: dns-system
spec:
  zoneName: example.com
  clusterRef: prod-dns
  soaRecord:
    primaryNS: ns1.example.com.
    adminEmail: admin.example.com.
  ttl: 3600

---
# A Record
apiVersion: bindy.firestoned.io/v1beta1
kind: ARecord
metadata:
  name: www
  namespace: dns-system
spec:
  zoneRef: example-com
  name: www
  ipv4Address: "192.0.2.1"
  ttl: 300

---
# CNAME
apiVersion: bindy.firestoned.io/v1beta1
kind: CNAMERecord
metadata:
  name: blog
  namespace: dns-system
spec:
  zoneRef: example-com
  name: blog
  target: www.example.com.

---
# MX Record
apiVersion: bindy.firestoned.io/v1beta1
kind: MXRecord
metadata:
  name: mail
  namespace: dns-system
spec:
  zoneRef: example-com
  name: "@"
  priority: 10
  mailServer: mail.example.com.

---
# TXT (SPF)
apiVersion: bindy.firestoned.io/v1beta1
kind: TXTRecord
metadata:
  name: spf
  namespace: dns-system
spec:
  zoneRef: example-com
  name: "@"
  text:
    - "v=spf1 include:_spf.example.com ~all"

Standalone Instance (Advanced)

Skip the cluster abstraction and create a single instance directly:

apiVersion: bindy.firestoned.io/v1beta1
kind: Bind9Instance
metadata:
  name: standalone
  namespace: dns-system
spec:
  replicas: 1
  version: "9.18"
  role: primary
  config:
    recursion: false
    allowQuery: ["0.0.0.0/0"]

Useful for testing or when you need full control over a single instance.

Key Features

Declarative - Manage DNS as Kubernetes resources (GitOps-ready) ✅ Dynamic Updates - Records added via RNDC (no zone file restarts) ✅ High Performance - Written in Rust, minimal overhead ✅ Cluster-Scoped - Bind9GlobalCluster for platform-managed DNS ✅ DNSSEC - Automatic key management and zone signing ✅ High Availability - Leader election, automatic failover ✅ Compliance - SOX, NIST 800-53, CIS documented ✅ Secure - Non-root containers, RBAC, signed releases

Configuration

The controller is configured via environment variables in the deployment:

Variable Default Description
RUST_LOG info Log level (debug, info, warn, error)
BINDY_ENABLE_LEADER_ELECTION true Enable leader election for HA
BINDY_LEASE_DURATION_SECONDS 15 Lease duration

See deployment.yaml for all options.

Monitoring

Check resource status:

# Clusters
kubectl get bind9clusters -n dns-system

# Instances
kubectl get bind9instances -n dns-system

# Zones
kubectl get dnszones -n dns-system

# Records
kubectl get arecords,cnamerecords,mxrecords,txtrecords -n dns-system

View detailed status:

kubectl describe arecord www -n dns-system

Output includes annotations showing cluster, instance, and zone:

metadata:
  annotations:
    bindy.firestoned.io/cluster: prod-dns
    bindy.firestoned.io/instance: primary-0
    bindy.firestoned.io/zone: example.com
status:
  conditions:
    - type: Ready
      status: "True"
      reason: RecordCreated
      message: A record www.example.com created successfully

Troubleshooting

Controller logs:

kubectl logs -n dns-system -l app=bindy -f

Test DNS resolution:

# Get service IP
kubectl get svc -n dns-system

# Query DNS
dig @<service-ip> www.example.com A

Verify BIND9 config:

# Find BIND9 pod
kubectl get pods -n dns-system -l app.kubernetes.io/name=bind9

# Check config
kubectl exec -it <pod> -n dns-system -- named-checkconf /etc/bind/named.conf

Common issues:

  • Records not appearing? Check kubectl describe <record> for error status
  • BIND9 not starting? Check RNDC key in Secret: kubectl get secret -n dns-system
  • Cluster not creating instances? Check Bind9Cluster status: kubectl describe bind9cluster

Documentation

📚 Complete docs: https://firestoned.github.io/bindy/

Includes:

  • Installation guide
  • Multi-tenancy patterns
  • High availability setup
  • DNSSEC configuration
  • Compliance documentation (SOX, NIST, CIS)
  • API reference

Development

Prerequisites:

  • Rust 1.85+
  • Kubernetes 1.27+

Build & Test:

cargo build
cargo test

See the Developer Guide for detailed development instructions.

Contributing

Contributions welcome! Please:

  1. Sign commits with GPG/SSH (required for compliance)
  2. Run cargo fmt and cargo clippy
  3. Add tests for new features
  4. Update CHANGELOG.md

See CONTRIBUTING.md for details.

Security

  • Signed Releases: All releases signed with Cosign (keyless). Verify releases →
  • SLSA Level 3: Build provenance for supply chain security
  • SBOM: CycloneDX SBOM included with every release
  • Vulnerability Scanning: Daily cargo audit runs

Report security issues to: security@firestoned.io

License

MIT License - see LICENSE

Copyright (c) 2025 Erick Bourgeois, firestoned


Need help?

About

Bind9 Kubernetes Controller

Resources

License

Contributing

Security policy

Stars

Watchers

Forks

Packages

 
 
 

Languages