bindy/
constants.rs

1// Copyright (c) 2025 Erick Bourgeois, firestoned
2// SPDX-License-Identifier: MIT
3
4//! Global constants for the Bindy operator.
5//!
6//! This module contains all numeric and string constants used throughout the codebase.
7//! Constants are organized by category for easy maintenance.
8
9// ============================================================================
10// API Constants
11// ============================================================================
12
13/// API group for all Bindy DNS CRDs
14pub const API_GROUP: &str = "bindy.firestoned.io";
15
16/// API version for all Bindy DNS CRDs
17pub const API_VERSION: &str = "v1beta1";
18
19/// Fully qualified API version (group/version)
20pub const API_GROUP_VERSION: &str = "bindy.firestoned.io/v1beta1";
21
22/// Kind name for `DNSZone` resource
23pub const KIND_DNS_ZONE: &str = "DNSZone";
24
25/// Kind name for `ARecord` resource
26pub const KIND_A_RECORD: &str = "ARecord";
27
28/// Kind name for `AAAARecord` resource
29pub const KIND_AAAA_RECORD: &str = "AAAARecord";
30
31/// Kind name for `TXTRecord` resource
32pub const KIND_TXT_RECORD: &str = "TXTRecord";
33
34/// Kind name for `CNAMERecord` resource
35pub const KIND_CNAME_RECORD: &str = "CNAMERecord";
36
37/// Kind name for `MXRecord` resource
38pub const KIND_MX_RECORD: &str = "MXRecord";
39
40/// Kind name for `NSRecord` resource
41pub const KIND_NS_RECORD: &str = "NSRecord";
42
43/// Kind name for `SRVRecord` resource
44pub const KIND_SRV_RECORD: &str = "SRVRecord";
45
46/// Kind name for `CAARecord` resource
47pub const KIND_CAA_RECORD: &str = "CAARecord";
48
49/// Kind name for `Bind9Cluster` resource
50pub const KIND_BIND9_CLUSTER: &str = "Bind9Cluster";
51
52/// Kind name for `ClusterBind9Provider` resource
53pub const KIND_CLUSTER_BIND9_PROVIDER: &str = "ClusterBind9Provider";
54
55/// Kind name for `Bind9Instance` resource
56pub const KIND_BIND9_INSTANCE: &str = "Bind9Instance";
57
58// ============================================================================
59// DNS Protocol Constants
60// ============================================================================
61
62/// Standard DNS service port exposed externally
63pub const DNS_PORT: u16 = 53;
64
65/// DNS container port (non-privileged port for non-root execution)
66pub const DNS_CONTAINER_PORT: u16 = 5353;
67
68/// Standard RNDC control port (non-privileged)
69pub const RNDC_PORT: u16 = 9530;
70
71/// Default bindcar HTTP API container port
72pub const BINDCAR_API_PORT: u16 = 8080;
73
74/// Default bindcar HTTP API service port (exposed via Kubernetes Service)
75pub const BINDCAR_SERVICE_PORT: u16 = 80;
76
77/// Default TTL for DNS records (5 minutes)
78pub const DEFAULT_DNS_RECORD_TTL_SECS: i32 = 300;
79
80/// Default TTL for zone files (1 hour)
81pub const DEFAULT_ZONE_TTL_SECS: u32 = 3600;
82
83/// Default SOA refresh interval (1 hour)
84pub const DEFAULT_SOA_REFRESH_SECS: u32 = 3600;
85
86/// Default SOA retry interval (10 minutes)
87pub const DEFAULT_SOA_RETRY_SECS: u32 = 600;
88
89/// Default SOA expire time (7 days)
90pub const DEFAULT_SOA_EXPIRE_SECS: u32 = 604_800;
91
92/// Default SOA negative TTL (1 day)
93pub const DEFAULT_SOA_NEGATIVE_TTL_SECS: u32 = 86400;
94
95/// TSIG fudge time in seconds (allows for clock skew)
96pub const TSIG_FUDGE_TIME_SECS: u64 = 300;
97
98// ============================================================================
99// Kubernetes Health Check Constants
100// ============================================================================
101
102/// Liveness probe initial delay (wait for BIND9 to start)
103pub const LIVENESS_INITIAL_DELAY_SECS: i32 = 30;
104
105/// Liveness probe period (how often to check)
106pub const LIVENESS_PERIOD_SECS: i32 = 10;
107
108/// Liveness probe timeout
109pub const LIVENESS_TIMEOUT_SECS: i32 = 5;
110
111/// Liveness probe failure threshold
112pub const LIVENESS_FAILURE_THRESHOLD: i32 = 3;
113
114/// Readiness probe initial delay
115pub const READINESS_INITIAL_DELAY_SECS: i32 = 10;
116
117/// Readiness probe period
118pub const READINESS_PERIOD_SECS: i32 = 5;
119
120/// Readiness probe timeout
121pub const READINESS_TIMEOUT_SECS: i32 = 3;
122
123/// Readiness probe failure threshold
124pub const READINESS_FAILURE_THRESHOLD: i32 = 3;
125
126// ============================================================================
127// Controller Error Handling Constants
128// ============================================================================
129
130/// Requeue duration for controller errors (30 seconds)
131pub const ERROR_REQUEUE_DURATION_SECS: u64 = 30;
132
133// ============================================================================
134// Leader Election Constants
135// ============================================================================
136
137/// Default leader election lease duration (15 seconds)
138pub const DEFAULT_LEASE_DURATION_SECS: u64 = 15;
139
140/// Default leader election renew deadline (10 seconds)
141pub const DEFAULT_LEASE_RENEW_DEADLINE_SECS: u64 = 10;
142
143/// Default leader election retry period (2 seconds)
144pub const DEFAULT_LEASE_RETRY_PERIOD_SECS: u64 = 2;
145
146// ============================================================================
147// BIND9 Version Constants
148// ============================================================================
149
150/// Default BIND9 version tag
151pub const DEFAULT_BIND9_VERSION: &str = "9.18";
152
153/// `ServiceAccount` name for BIND9 pods
154pub const BIND9_SERVICE_ACCOUNT: &str = "bind9";
155
156/// `MALLOC_CONF` environment variable value for BIND9 containers
157///
158/// Optimizes jemalloc memory decay for containerized environments:
159/// - `dirty_decay_ms:0` - Immediately return dirty pages to OS
160/// - `muzzy_decay_ms:0` - Immediately return muzzy pages to OS
161///
162/// This enables more aggressive memory reclamation in environments where
163/// memory pressure is monitored closely.
164pub const BIND9_MALLOC_CONF: &str = "dirty_decay_ms:0,muzzy_decay_ms:0";
165
166/// UID for running BIND9 and bindcar containers as non-root
167///
168/// This UID corresponds to the 'bind' or 'named' user in most BIND9 images.
169/// Running as non-root improves container security by following the principle
170/// of least privilege.
171pub const BIND9_NONROOT_UID: i64 = 101;
172
173// ============================================================================
174// Bindcar Container Constants
175// ============================================================================
176
177/// Default bindcar sidecar container image
178///
179/// This is the default image used for the bindcar HTTP API sidecar container
180/// when no image is specified in the `BindcarConfig` of a `Bind9Instance`,
181/// `Bind9Cluster`, or `ClusterBind9Provider`.
182pub const DEFAULT_BINDCAR_IMAGE: &str = "ghcr.io/firestoned/bindcar:v0.6.0";
183
184// ============================================================================
185// Container Name Constants
186// ============================================================================
187
188/// Name of the BIND9 container in the pod
189pub const CONTAINER_NAME_BIND9: &str = "bind9";
190
191/// Name of the bindcar API sidecar container in the pod
192pub const CONTAINER_NAME_BINDCAR: &str = "api";
193
194// ============================================================================
195// Runtime Constants
196// ============================================================================
197
198/// Number of worker threads for Tokio runtime
199pub const TOKIO_WORKER_THREADS: usize = 4;
200
201// ============================================================================
202// Replica Count Constants
203// ============================================================================
204
205/// Minimum number of replicas for testing
206pub const MIN_TEST_REPLICAS: i32 = 2;
207
208/// Maximum reasonable number of replicas for testing
209pub const MAX_TEST_REPLICAS: i32 = 10;
210
211// ============================================================================
212// Metrics Server Constants
213// ============================================================================
214
215/// Port for Prometheus metrics HTTP server
216pub const METRICS_SERVER_PORT: u16 = 8080;
217
218/// Path for Prometheus metrics endpoint
219pub const METRICS_SERVER_PATH: &str = "/metrics";
220
221/// Bind address for metrics HTTP server
222pub const METRICS_SERVER_BIND_ADDRESS: &str = "0.0.0.0";
223
224// ============================================================================
225// DNSZone Record Ownership Constants
226// ============================================================================
227
228/// Annotation key for marking which zone owns a DNS record
229///
230/// When a `DNSZone`'s label selector matches a DNS record, the `DNSZone` controller
231/// sets this annotation on the record with the value being the zone's FQDN.
232/// Record reconcilers read this annotation to determine which zone to update.
233pub const ANNOTATION_ZONE_OWNER: &str = "bindy.firestoned.io/zone";
234
235/// Annotation key for marking which zone previously owned a record
236///
237/// When a record stops matching a zone's selector, the `DNSZone` controller sets
238/// this annotation before removing the zone ownership. This helps track orphaned
239/// records and enables cleanup workflows.
240pub const ANNOTATION_ZONE_PREVIOUS_OWNER: &str = "bindy.firestoned.io/previous-zone";
241
242/// Annotation key on `Bind9Instance` that lists namespaces from which a
243/// `DNSZone` (in a *different* namespace) is permitted to target this
244/// instance via `spec.bind9InstancesFrom` selectors.
245///
246/// **F-003 mitigation.** A label-selector match alone is not enough to
247/// enrol a cross-namespace `Bind9Instance` in a zone — the platform admin
248/// who owns the instance must also annotate it with the zone's namespace.
249/// Same-namespace targeting (zone and instance in the same namespace) is
250/// always permitted and does not require this annotation.
251///
252/// Value format: comma-separated list of namespace names. The literal
253/// value `*` re-enables the pre-F-003 cluster-wide behaviour for
254/// platform admins who explicitly accept the risk.
255///
256/// Examples:
257/// - `"tenant-a,tenant-b"` — only zones in tenant-a or tenant-b may
258///   claim this instance.
259/// - `"*"` — any namespace may claim (back to pre-F-003 behaviour).
260/// - annotation absent — only same-namespace zones may claim.
261///
262/// Why an annotation rather than a CRD field on `ClusterBind9Provider`?
263/// The platform-admin contract for a cluster-wide operator is "platform
264/// admin labels their instances; tenants match those labels." The
265/// security gate must live on the side the tenant cannot forge — i.e.
266/// metadata on the platform-owned `Bind9Instance` — and an annotation
267/// keeps the admin's mental model intact without requiring tenants to
268/// add a `clusterRef` they had no reason to set previously.
269pub const ANNOTATION_ALLOW_ZONE_NAMESPACES: &str = "bindy.firestoned.io/allow-zone-namespaces";
270
271/// Wildcard value for [`ANNOTATION_ALLOW_ZONE_NAMESPACES`] meaning "any
272/// namespace may target this instance." Use with care — restores the
273/// pre-F-003 cluster-wide behaviour.
274pub const ALLOW_ZONE_NAMESPACES_WILDCARD: &str = "*";
275
276// ============================================================================
277// RNDC Key Rotation Constants
278// ============================================================================
279
280/// Annotation key for RNDC key creation timestamp (ISO 8601 format)
281///
282/// Tracks when the current RNDC key was created or last rotated.
283/// Used by the rotation reconciler to determine when rotation is due.
284///
285/// Example value: `"2025-01-26T10:00:00Z"`
286pub const ANNOTATION_RNDC_CREATED_AT: &str = "bindy.firestoned.io/rndc-created-at";
287
288/// Annotation key for RNDC key rotation timestamp (ISO 8601 format)
289///
290/// Tracks when the RNDC key should be rotated next.
291/// Calculated as: `created_at + rotate_after`
292///
293/// Only present when `auto_rotate` is enabled.
294///
295/// Example value: `"2025-02-25T10:00:00Z"` (30 days after creation)
296pub const ANNOTATION_RNDC_ROTATE_AT: &str = "bindy.firestoned.io/rndc-rotate-at";
297
298/// Annotation key for RNDC key rotation count
299///
300/// Tracks the number of times the RNDC key has been rotated.
301/// Starts at `0` for newly-created keys and increments on each rotation.
302///
303/// Example value: `"5"` (key has been rotated 5 times)
304pub const ANNOTATION_RNDC_ROTATION_COUNT: &str = "bindy.firestoned.io/rndc-rotation-count";
305
306/// Annotation key for tracking pod restarts after RNDC rotation
307///
308/// Added to Deployment pod template to trigger rolling restart when RNDC key is rotated.
309/// Value is the timestamp when rotation occurred (ISO 8601 format).
310///
311/// Example value: `"2025-01-26T10:30:00Z"`
312pub const ANNOTATION_RNDC_ROTATED_AT: &str = "bindy.firestoned.io/rndc-rotated-at";
313
314/// Minimum rotation interval in hours (1 hour)
315///
316/// RNDC keys cannot be rotated more frequently than once per hour.
317/// This prevents infinite reconciliation loops and rate-limits rotation operations.
318pub const MIN_ROTATION_INTERVAL_HOURS: u64 = 1;
319
320/// Maximum rotation interval in hours (8760 hours = 365 days = 1 year)
321///
322/// RNDC keys must be rotated at least once per year for security compliance.
323/// This is the upper bound for the `rotate_after` configuration.
324pub const MAX_ROTATION_INTERVAL_HOURS: u64 = 8760;
325
326/// Default rotation interval (720 hours = 30 days)
327///
328/// Default value for the `rotate_after` field when `auto_rotate` is enabled.
329/// Balances security (regular rotation) with operational stability (not too frequent).
330///
331/// This is specified as a Go duration string: `"720h"`
332pub const DEFAULT_ROTATION_INTERVAL: &str = "720h";
333
334/// Minimum time between rotations in hours (1 hour)
335///
336/// Even if rotation is due (based on `rotate_at` timestamp), the reconciler
337/// will not rotate a key if it was created or rotated within the last hour.
338///
339/// This prevents rapid successive rotations in edge cases (e.g., clock skew,
340/// manual timestamp manipulation, reconciliation loops).
341pub const MIN_TIME_BETWEEN_ROTATIONS_HOURS: i64 = 1;
342
343// ============================================================================
344// Kubernetes API Client Rate Limiting Constants
345// ============================================================================
346
347/// Kubernetes API client queries per second (sustained rate)
348///
349/// This matches kubectl default rate limits and has been tested at scale.
350/// Prevents overwhelming the API server with too many requests.
351/// Can be overridden via `BINDY_KUBE_QPS` environment variable.
352pub const KUBE_CLIENT_QPS: f32 = 20.0;
353
354/// Kubernetes API client burst size (max concurrent requests)
355///
356/// Allows temporary bursts above the QPS limit for reconciliation spikes.
357/// Matches kubectl defaults for optimal API server behavior.
358/// Can be overridden via `BINDY_KUBE_BURST` environment variable.
359pub const KUBE_CLIENT_BURST: u32 = 30;
360
361/// Page size for Kubernetes API list operations
362///
363/// Balances memory usage vs. number of API calls.
364/// Limits each list response to 100 items, reducing memory pressure
365/// when listing large resource sets (e.g., 1000+ `DNSZone`s).
366///
367/// With 100 items per page:
368/// - 1000 resources = 10 API calls
369/// - Memory usage remains constant (O(1) relative to total count)
370/// - Reduces API server load per request
371pub const KUBE_LIST_PAGE_SIZE: u32 = 100;
372
373// ============================================================================
374// User-volume Allow-list (F-001 mitigation)
375// ============================================================================
376//
377// `Bind9Instance` and `Bind9Cluster` accept user-supplied `volumes` and
378// `volumeMounts` fields that flow into the managed Pod spec. To prevent a
379// namespace-tenant from mounting `hostPath`, `csi`, foreign Secrets, or any
380// volume into a container the operator stamps with cluster-wide RBAC, we
381// validate every user-supplied volume against the constants below before
382// constructing the Pod. See `src/safe_volume.rs`.
383
384/// Mount-path prefixes allowed for user-supplied `volumeMounts`.
385///
386/// Anything outside these prefixes is rejected at reconcile time. Operator-
387/// managed mounts (`/etc/bind/...`, `/var/cache/bind`) are added by the
388/// resource builder and bypass this check.
389pub const ALLOWED_USER_MOUNT_PREFIXES: &[&str] = &["/data/", "/var/log/bind/"];
390
391/// Required name prefix for any Secret that the user references via a
392/// `secret:` volume. Prevents the user from mounting an arbitrary Secret
393/// (including the operator's own credentials) into the BIND9/bindcar pod.
394pub const ALLOWED_USER_SECRET_PREFIX: &str = "bindy-";
395
396/// Required name prefix for any PVC that the user references via a
397/// `persistentVolumeClaim:` volume. Same rationale as
398/// [`ALLOWED_USER_SECRET_PREFIX`].
399pub const ALLOWED_USER_PVC_PREFIX: &str = "bindy-";
400
401/// Required name prefix for any ConfigMap that the user references via a
402/// `configMap:` volume. Same rationale as [`ALLOWED_USER_SECRET_PREFIX`].
403pub const ALLOWED_USER_CONFIGMAP_PREFIX: &str = "bindy-";