@@ -116,6 +116,21 @@ type DNSSpec struct {
116116 // 30 seconds or as noted in the respective Corefile for your version of OpenShift.
117117 // +optional
118118 Cache DNSCache `json:"cache,omitempty"`
119+
120+ // template is an optional configuration for custom DNS query handling via the CoreDNS template plugin.
121+ // The template defines how to handle queries matching specific zones and query types.
122+ //
123+ // The template is injected into ALL Corefile server blocks (both custom servers from spec.servers
124+ // and the default .:5353 block). This ensures consistent behavior across all DNS resolution paths.
125+ //
126+ // AAAA filtering is intended for IPv4-only clusters. In IPv6 or dual-stack clusters, use specific
127+ // zones instead of "." to avoid filtering internal IPv6 service addresses (e.g., cluster.local).
128+ //
129+ // When this field is not set, no template plugin configuration is added to CoreDNS.
130+ //
131+ // +optional
132+ // +openshift:enable:FeatureGate=DNSTemplatePlugin
133+ Template * Template `json:"template,omitempty"`
119134}
120135
121136// DNSCache defines the fields for configuring DNS caching.
@@ -467,6 +482,112 @@ const (
467482 DNSAvailable = "Available"
468483)
469484
485+ // QueryType represents DNS query types supported by templates.
486+ // +kubebuilder:validation:Enum=AAAA
487+ type QueryType string
488+
489+ const (
490+ // QueryTypeAAAA represents IPv6 address records (AAAA).
491+ QueryTypeAAAA QueryType = "AAAA"
492+ )
493+
494+ // QueryClass represents DNS query classes supported by templates.
495+ // Valid value is "IN".
496+ // +kubebuilder:validation:Enum=IN
497+ type QueryClass string
498+
499+ const (
500+ // QueryClassIN represents the Internet class.
501+ QueryClassIN QueryClass = "IN"
502+ )
503+
504+ // ResponseCode represents DNS response codes.
505+ // +kubebuilder:validation:Enum=NOERROR
506+ type ResponseCode string
507+
508+ const (
509+ // ResponseCodeNOERROR indicates a successful DNS query with or without answer records.
510+ // When used with returnEmpty action, this returns an empty response (no AAAA records)
511+ // without indicating an error, which is the standard behavior for AAAA filtering.
512+ ResponseCodeNOERROR ResponseCode = "NOERROR"
513+ )
514+
515+ // Template defines a template for custom DNS query handling via the CoreDNS template plugin.
516+ // Templates enable filtering or custom responses for DNS queries matching specific zones and query types.
517+ // +openshift:enable:FeatureGate=DNSTemplatePlugin
518+ type Template struct {
519+ // zones specifies the DNS zones this template applies to.
520+ // Each zone must be a valid DNS name as defined in RFC 1123.
521+ // The special zone "." matches all domains (catch-all).
522+ // Multiple zones can be specified to apply the same template actions to multiple domains.
523+ //
524+ // Examples:
525+ // - ["."] matches all domains (catch-all for global AAAA filtering)
526+ // - ["example.com"] matches only example.com and its subdomains
527+ // - ["example.com", "test.com"] matches both domains and their subdomains
528+ //
529+ // +kubebuilder:validation:Required
530+ // +kubebuilder:validation:MinItems=1
531+ // +required
532+ Zones []string `json:"zones"`
533+
534+ // queryType specifies the DNS query type to match.
535+ //
536+ // +kubebuilder:validation:Required
537+ // +kubebuilder:default=AAAA
538+ // +required
539+ QueryType QueryType `json:"queryType"`
540+
541+ // queryClass specifies the DNS query class to match.
542+ //
543+ // +kubebuilder:validation:Required
544+ // +kubebuilder:default=IN
545+ // +required
546+ QueryClass QueryClass `json:"queryClass"`
547+
548+ // actions defines a list of actions to apply to matching queries.
549+ //
550+ // +kubebuilder:validation:Required
551+ // +kubebuilder:validation:MinItems=1
552+ // +required
553+ Actions []TemplateAction `json:"actions"`
554+ }
555+
556+ // TemplateAction defines the action taken by the template for matching queries.
557+ // This is a discriminated union - exactly one action type must be specified.
558+ //
559+ // +union
560+ // +kubebuilder:validation:XValidation:rule="has(self.returnEmpty)",message="only returnEmpty action is supported"
561+ type TemplateAction struct {
562+ // returnEmpty returns an empty DNS response with the specified response code.
563+ // This is useful for filtering queries (e.g., AAAA filtering in IPv4-only clusters).
564+ //
565+ // When set, the template returns a response with no answer records. For AAAA filtering,
566+ // this means IPv6 address queries return successfully but with no IPv6 addresses,
567+ // causing clients to fall back to IPv4 (A record) queries.
568+ //
569+ // +optional
570+ // +unionDiscriminator
571+ ReturnEmpty * ReturnEmptyAction `json:"returnEmpty,omitempty"`
572+ }
573+
574+ // ReturnEmptyAction configures the template to return empty DNS responses.
575+ // This is used for query filtering, such as AAAA filtering in IPv4-only clusters.
576+ type ReturnEmptyAction struct {
577+ // rcode is the DNS response code to return in the empty response.
578+ // Valid values are "NOERROR".
579+ //
580+ // NOERROR indicates a successful query with no answer records. This is the standard
581+ // response for AAAA filtering - the query succeeds but returns no IPv6 addresses,
582+ // causing clients to fall back to A record (IPv4) queries.
583+ //
584+ // +kubebuilder:validation:Required
585+ // +kubebuilder:validation:Enum=NOERROR
586+ // +kubebuilder:default=NOERROR
587+ // +required
588+ Rcode ResponseCode `json:"rcode"`
589+ }
590+
470591// DNSStatus defines the observed status of the DNS.
471592type DNSStatus struct {
472593 // clusterIP is the service IP through which this DNS is made available.
0 commit comments