DNSZoneSpec

Struct DNSZoneSpec 

Source
pub struct DNSZoneSpec {
    pub zone_name: String,
    pub soa_record: SOARecord,
    pub ttl: Option<i32>,
    pub cluster_ref: Option<String>,
    pub name_servers: Option<Vec<NameServer>>,
    pub name_server_ips: Option<HashMap<String, String>>,
    pub records_from: Option<Vec<RecordSource>>,
    pub bind9_instances_from: Option<Vec<InstanceSource>>,
    pub dnssec_policy: Option<String>,
}
Expand description

DNSZone defines a DNS zone to be managed by BIND9.

A DNSZone represents an authoritative DNS zone (e.g., example.com) that will be served by a BIND9 cluster. The zone includes SOA record information and will be synchronized to all instances in the referenced cluster via AXFR/IXFR.

DNSZones can reference either:

  • A namespace-scoped Bind9Cluster (using clusterRef)
  • A cluster-scoped ClusterBind9Provider (using clusterProviderRef)

Exactly one of clusterRef or clusterProviderRef must be specified.

§Example: Namespace-scoped Cluster

apiVersion: bindy.firestoned.io/v1beta1
kind: DNSZone
metadata:
  name: example-com
  namespace: dev-team-alpha
spec:
  zoneName: example.com
  clusterRef: dev-team-dns  # References Bind9Cluster in same namespace
  soaRecord:
    primaryNs: ns1.example.com.
    adminEmail: admin.example.com.
    serial: 2024010101
    refresh: 3600
    retry: 600
    expire: 604800
    negativeTtl: 86400
  ttl: 3600

§Example: Cluster-scoped Global Cluster

apiVersion: bindy.firestoned.io/v1beta1
kind: DNSZone
metadata:
  name: production-example-com
  namespace: production
spec:
  zoneName: example.com
  clusterProviderRef: shared-production-dns  # References ClusterBind9Provider (cluster-scoped)
  soaRecord:
    primaryNs: ns1.example.com.
    adminEmail: admin.example.com.
    serial: 2024010101
    refresh: 3600
    retry: 600
    expire: 604800
    negativeTtl: 86400
  ttl: 3600

Fields§

§zone_name: String

DNS zone name (e.g., “example.com”).

Must be a valid DNS zone name. Can be a domain or subdomain. Examples: “example.com”, “internal.example.com”, “10.in-addr.arpa”

§soa_record: SOARecord

SOA (Start of Authority) record - defines zone authority and refresh parameters.

The SOA record is required for all authoritative zones and contains timing information for zone transfers and caching.

§ttl: Option<i32>

Default TTL (Time To Live) for records in this zone, in seconds.

If not specified, individual records must specify their own TTL. Typical values: 300-86400 (5 minutes to 1 day).

§cluster_ref: Option<String>

Reference to a Bind9Cluster or ClusterBind9Provider to serve this zone.

When specified, this zone will be automatically configured on all Bind9Instance resources that belong to the referenced cluster. This provides a simple way to assign zones to entire clusters.

Relationship with bind9_instances_from:

  • If only cluster_ref is specified: Zone targets all instances in that cluster
  • If only bind9_instances_from is specified: Zone targets instances matching label selectors
  • If both are specified: Zone targets union of cluster instances AND label-selected instances

§Example

spec:
  clusterRef: production-dns  # Target all instances in this cluster
  zoneName: example.com
§name_servers: Option<Vec<NameServer>>

Authoritative nameservers for this zone (v0.4.0+).

NS records are automatically generated at the zone apex (@) for all entries. The primary nameserver from soaRecord.primaryNs is always included automatically.

Each entry can optionally include IP addresses to generate glue records (A/AAAA) for in-zone nameservers. Glue records are required when the nameserver is within the zone’s own domain to avoid circular dependencies.

§Examples

# In-zone nameservers with glue records
nameServers:
  - hostname: ns2.example.com.
    ipv4Address: "192.0.2.2"
  - hostname: ns3.example.com.
    ipv4Address: "192.0.2.3"
    ipv6Address: "2001:db8::3"

# Out-of-zone nameserver (no glue needed)
  - hostname: ns4.external-provider.net.

Generated Records:

  • @ IN NS ns2.example.com. (NS record)
  • ns2.example.com. IN A 192.0.2.2 (glue record for in-zone NS)
  • @ IN NS ns3.example.com. (NS record)
  • ns3.example.com. IN A 192.0.2.3 (IPv4 glue)
  • ns3.example.com. IN AAAA 2001:db8::3 (IPv6 glue)
  • @ IN NS ns4.external-provider.net. (NS record only, no glue)

Benefits over nameServerIps (deprecated):

  • Clearer purpose: authoritative nameservers, not just glue records
  • IPv6 support via ipv6Address field
  • Automatic NS record generation (no manual NSRecord CRs needed)

Migration: See docs/src/operations/migration-guide.md

§name_server_ips: Option<HashMap<String, String>>
👎Deprecated since 0.4.0: Use name_servers instead. This field will be removed in v1.0.0. See migration guide at docs/src/operations/migration-guide.md

(DEPRECATED in v0.4.0) Map of nameserver hostnames to IP addresses for glue records.

Use nameServers instead. This field will be removed in v1.0.0.

Glue records provide IP addresses for nameservers within the zone’s own domain. This is necessary when delegating subdomains where the nameserver is within the delegated zone itself.

Example: When delegating sub.example.com with nameserver ns1.sub.example.com, you must provide the IP address of ns1.sub.example.com as a glue record.

Format: {"ns1.example.com.": "192.0.2.1", "ns2.example.com.": "192.0.2.2"}

Note: Nameserver hostnames should end with a dot (.) for FQDN.

Migration to nameServers:

# Old (deprecated):
nameServerIps:
  ns2.example.com.: "192.0.2.2"

# New (recommended):
nameServers:
  - hostname: ns2.example.com.
    ipv4Address: "192.0.2.2"
§records_from: Option<Vec<RecordSource>>

Sources for DNS records to include in this zone.

This field defines label selectors that automatically associate DNS records with this zone. Records with matching labels will be included in the zone’s DNS configuration.

This follows the standard Kubernetes selector pattern used by Services, NetworkPolicies, and other resources for declarative resource association.

§Example: Match podinfo records in dev/staging environments

recordsFrom:
  - selector:
      matchLabels:
        app: podinfo
      matchExpressions:
        - key: environment
          operator: In
          values:
            - dev
            - staging

§Selector Operators

  • In: Label value must be in the specified values list
  • NotIn: Label value must NOT be in the specified values list
  • Exists: Label key must exist (any value)
  • DoesNotExist: Label key must NOT exist

§Use Cases

  • Multi-environment zones: Dynamically include records based on environment labels
  • Application-specific zones: Group all records for an application using app label
  • Team-based zones: Use team labels to automatically route records to team-owned zones
  • Temporary records: Use labels to include/exclude records without changing zoneRef
§bind9_instances_from: Option<Vec<InstanceSource>>

Select Bind9Instance resources to target for zone configuration using label selectors.

This field enables dynamic, label-based selection of DNS instances to serve this zone. Instances matching these selectors will automatically receive zone configuration from the DNSZone controller.

This follows the standard Kubernetes selector pattern used by Services, NetworkPolicies, and other resources for declarative resource association.

IMPORTANT: This is the preferred method for zone-instance association. It provides:

  • Decoupled Architecture: Zones select instances, not vice versa
  • Zone Ownership: Zone authors control which instances serve their zones
  • Dynamic Scaling: New instances matching labels automatically pick up zones
  • Multi-Tenancy: Zones can target specific instance groups (prod, staging, team-specific)

§Example: Target production primary instances

apiVersion: bindy.firestoned.io/v1beta1
kind: DNSZone
metadata:
  name: example-com
  namespace: bindy-system
spec:
  zoneName: example.com
  bind9InstancesFrom:
    - selector:
        matchLabels:
          environment: production
          bindy.firestoned.io/role: primary

§Example: Target instances by region and tier

bind9InstancesFrom:
  - selector:
      matchLabels:
        tier: frontend
      atchExpressions:
        - key: region
          operator: In
          values:
            - us-east-1
            - us-west-2

§Selector Operators

  • In: Label value must be in the specified values list
  • NotIn: Label value must NOT be in the specified values list
  • Exists: Label key must exist (any value)
  • DoesNotExist: Label key must NOT exist

§Use Cases

  • Environment Isolation: Target only production instances (environment: production)
  • Role-Based Selection: Select only primary or secondary instances
  • Geographic Distribution: Target instances in specific regions
  • Team Boundaries: Select instances managed by specific teams
  • Testing Zones: Target staging instances for non-production zones

§Relationship with clusterRef

  • clusterRef: Explicitly assigns zone to ALL instances in a cluster
  • bind9InstancesFrom: Dynamically selects specific instances using labels (more flexible)

You can use both approaches together - the zone will target the union of:

  • All instances in clusterRef cluster
  • Plus any additional instances matching bind9InstancesFrom selectors

§Event-Driven Architecture

The DNSZone controller watches both DNSZone and Bind9Instance resources. When labels change on either:

  1. Controller re-evaluates label selector matching
  2. Automatically configures zones on newly-matched instances
  3. Removes zone configuration from instances that no longer match
§dnssec_policy: Option<String>

Override DNSSEC policy for this zone

Allows per-zone override of the cluster’s global DNSSEC signing policy. If not specified, the zone inherits the DNSSEC configuration from the cluster’s global.dnssec.signing.policy.

Use this to:

  • Disable signing for specific zones in a signing-enabled cluster
  • Use stricter security policies for sensitive zones
  • Test different signing algorithms on specific zones

§Example: Custom High-Security Policy

apiVersion: bindy.firestoned.io/v1beta1
kind: DNSZone
metadata:
  name: secure-zone
spec:
  zoneName: secure.example.com
  clusterRef: production-dns
  dnssecPolicy: "high-security"  # Override cluster default

§Example: Disable Signing for One Zone

dnssecPolicy: "none"  # Disable signing (cluster has signing enabled)

Note: Custom policies require BIND9 dnssec-policy configuration. Built-in policies: "default", "none"

Trait Implementations§

Source§

impl Clone for DNSZoneSpec

Source§

fn clone(&self) -> DNSZoneSpec

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for DNSZoneSpec

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl<'de> Deserialize<'de> for DNSZoneSpec

Source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>
where __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
Source§

impl JsonSchema for DNSZoneSpec

Source§

fn schema_name() -> Cow<'static, str>

The name of the generated JSON Schema. Read more
Source§

fn schema_id() -> Cow<'static, str>

Returns a string that uniquely identifies the schema produced by this type. Read more
Source§

fn json_schema(generator: &mut SchemaGenerator) -> Schema

Generates a JSON Schema for this type. Read more
Source§

fn inline_schema() -> bool

Whether JSON Schemas generated for this type should be included directly in parent schemas, rather than being re-used where possible using the $ref keyword. Read more
Source§

impl Serialize for DNSZoneSpec

Source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>
where __S: Serializer,

Serialize this value into the given Serde serializer. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> DynClone for T
where T: Clone,

Source§

fn __clone_box(&self, _: Private) -> *mut ()

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

§

impl<T> FromRef<T> for T
where T: Clone,

§

fn from_ref(input: &T) -> T

Converts to this type from a reference to the input type.
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
§

impl<T> IntoRequest<T> for T

§

fn into_request(self) -> Request<T>

Wrap the input message T in a tonic::Request
§

impl<L> LayerExt<L> for L

§

fn named_layer<S>(&self, service: S) -> Layered<<L as Layer<S>>::Service, S>
where L: Layer<S>,

Applies the layer to a service and wraps it in [Layered].
§

impl<T> PolicyExt for T
where T: ?Sized,

§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns [Action::Follow] only if self and other return Action::Follow. Read more
§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns [Action::Follow] if either self or other returns Action::Follow. Read more
Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
§

impl<T> ServiceExt for T

§

fn map_response_body<F>(self, f: F) -> MapResponseBody<Self, F>
where Self: Sized,

Apply a transformation to the response body. Read more
§

fn trace_for_http(self) -> Trace<Self, SharedClassifier<ServerErrorsAsFailures>>
where Self: Sized,

High level tracing that classifies responses using HTTP status codes. Read more
§

fn trace_for_grpc(self) -> Trace<Self, SharedClassifier<GrpcErrorsAsFailures>>
where Self: Sized,

High level tracing that classifies responses using gRPC headers. Read more
§

fn follow_redirects(self) -> FollowRedirect<Self>
where Self: Sized,

Follow redirect resposes using the Standard policy. Read more
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a [WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a [WithDispatch] wrapper. Read more
Source§

impl<T> DeserializeOwned for T
where T: for<'de> Deserialize<'de>,