44package v1alpha1
55
66import (
7+ "fmt"
8+
79 corev1 "k8s.io/api/core/v1"
810 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
911)
@@ -67,24 +69,26 @@ type DeviceSpec struct {
6769 Banner * TemplateSource `json:"banner,omitempty"`
6870}
6971
72+ func (d * DeviceSpec ) Validate () error {
73+ for _ , acl := range d .ACL {
74+ if err := acl .Validate (); err != nil {
75+ return err
76+ }
77+ }
78+ return nil
79+ }
80+
7081type TLS struct {
7182 // The CA certificate to verify the server's identity.
7283 // +required
73- CA * CertificateAuthority `json:"ca"`
84+ CA * corev1. SecretKeySelector `json:"ca"`
7485
7586 // The client certificate and private key to use for mutual TLS authentication.
7687 // Leave empty if mTLS is not desired.
7788 // +optional
7889 Certificate * CertificateSource `json:"certificate,omitempty"`
7990}
8091
81- // CertificateAuthority represents a source for the value of a certificate authority.
82- type CertificateAuthority struct {
83- // The secret must contain the following key: 'ca.crt'.
84- // +required
85- SecretRef * corev1.SecretReference `json:"secretRef,omitempty"`
86- }
87-
8892// Bootstrap defines the configuration for device bootstrap.
8993type Bootstrap struct {
9094 // Template defines the multiline string template that contains the initial configuration for the device.
@@ -155,6 +159,20 @@ type ACL struct {
155159 Entries []* ACLEntry `json:"entries"`
156160}
157161
162+ func (acl * ACL ) Validate () error {
163+ set := map [int ]struct {}{}
164+ for _ , entry := range acl .Entries {
165+ if _ , exists := set [entry .Sequence ]; exists {
166+ return fmt .Errorf ("duplicate sequence number %d in ACL %q" , entry .Sequence , acl .Name )
167+ }
168+ set [entry .Sequence ] = struct {}{}
169+ if err := entry .Validate (); err != nil {
170+ return fmt .Errorf ("invalid entry in acl %q: %w" , acl .Name , err )
171+ }
172+ }
173+ return nil
174+ }
175+
158176type ACLEntry struct {
159177 // The sequence number of the ACL entry.
160178 // +required
@@ -164,15 +182,35 @@ type ACLEntry struct {
164182 // +required
165183 Action ACLAction `json:"action"`
166184
185+ // The protocol to match. If not specified, defaults to "ip" (IPv4).
186+ // +kubebuilder:validation:Enum=ahp;eigrp;esp;gre;icmp;igmp;ip;nos;ospf;pcp;pim;tcp;udf;udp
187+ // +kubebuilder:default=ip
188+ // +optional
189+ Protocol string `json:"protocol,omitempty"`
190+
167191 // Source IPv4 address prefix. Use 0.0.0.0/0 to represent 'any'.
168- // +kubebuilder:validation:Pattern=`^(\d{1,3}\.){3}\d{1,3}\/\d{1,2}$`
169192 // +required
170- SourceAddress string `json:"sourceAddress"`
193+ SourceAddress IPPrefix `json:"sourceAddress"`
171194
172195 // Destination IPv4 address prefix. Use 0.0.0.0/0 to represent 'any'.
173- // +kubebuilder:validation:Pattern=`^(\d{1,3}\.){3}\d{1,3}\/\d{1,2}$`
174196 // +required
175- DestinationAddress string `json:"destinationAddress"`
197+ DestinationAddress IPPrefix `json:"destinationAddress"`
198+ }
199+
200+ func (e * ACLEntry ) Validate () error {
201+ if ! e .SourceAddress .IsValid () {
202+ return fmt .Errorf ("invalid IP prefix: %s" , e .SourceAddress .String ())
203+ }
204+ if ! e .SourceAddress .Addr ().Is4 () {
205+ return fmt .Errorf ("source address must be an IPv4 address: %s" , e .SourceAddress .String ())
206+ }
207+ if ! e .DestinationAddress .IsValid () {
208+ return fmt .Errorf ("invalid IP prefix: %s" , e .SourceAddress .String ())
209+ }
210+ if ! e .DestinationAddress .Addr ().Is4 () {
211+ return fmt .Errorf ("destination address must be an IPv4 address: %s" , e .DestinationAddress .String ())
212+ }
213+ return nil
176214}
177215
178216// ACLAction represents the type of action that can be taken by an ACL rule.
@@ -230,7 +268,7 @@ type LogServer struct {
230268
231269 // The destination port number for syslog UDP messages to
232270 // the server. The default is 514.
233- // +kubebuilder:validation:Default =514
271+ // +kubebuilder:default =514
234272 // +optional
235273 Port int64 `json:"port"`
236274}
@@ -436,7 +474,7 @@ type CertificateSource struct {
436474type PasswordSource struct {
437475 // Selects a key of a secret.
438476 // +required
439- SecretKeyRef * corev1.SecretReference `json:"secretKeyRef,omitempty"`
477+ SecretKeyRef * corev1.SecretKeySelector `json:"secretKeyRef,omitempty"`
440478}
441479
442480// DeviceStatus defines the observed state of Device.
@@ -494,6 +532,57 @@ type Device struct {
494532 Status DeviceStatus `json:"status,omitempty"`
495533}
496534
535+ // GetSecretRefs returns the list of secrets referenced in the [Device] resource.
536+ func (d * Device ) GetSecretRefs () []corev1.SecretReference {
537+ refs := []corev1.SecretReference {}
538+ if d .Spec .SecretRef != nil {
539+ refs = append (refs , * d .Spec .SecretRef )
540+ }
541+ if d .Spec .TLS != nil {
542+ refs = append (refs , corev1.SecretReference {Name : d .Spec .TLS .CA .Name })
543+ if d .Spec .TLS .Certificate != nil {
544+ refs = append (refs , * d .Spec .TLS .Certificate .SecretRef )
545+ }
546+ }
547+ if d .Spec .Bootstrap != nil && d .Spec .Bootstrap .Template != nil {
548+ if d .Spec .Bootstrap .Template .SecretRef != nil {
549+ refs = append (refs , corev1.SecretReference {Name : d .Spec .Bootstrap .Template .SecretRef .Name })
550+ }
551+ }
552+ if d .Spec .PKI != nil {
553+ for _ , cert := range d .Spec .PKI .Certificates {
554+ if cert .Source != nil && cert .Source .SecretRef != nil {
555+ refs = append (refs , * cert .Source .SecretRef )
556+ }
557+ }
558+ }
559+ for _ , user := range d .Spec .User {
560+ refs = append (refs , corev1.SecretReference {Name : user .Password .SecretKeyRef .Name })
561+ }
562+ for i := range refs {
563+ if refs [i ].Namespace == "" {
564+ refs [i ].Namespace = d .Namespace
565+ }
566+ }
567+ return refs
568+ }
569+
570+ // GetConfigMapRefs returns the list of configmaps referenced in the [Device] resource.
571+ func (d * Device ) GetConfigMapRefs () []corev1.ObjectReference {
572+ refs := []corev1.ObjectReference {}
573+ if d .Spec .Bootstrap != nil && d .Spec .Bootstrap .Template != nil {
574+ if d .Spec .Bootstrap .Template .ConfigMapRef != nil {
575+ refs = append (refs , corev1.ObjectReference {Name : d .Spec .Bootstrap .Template .ConfigMapRef .Name })
576+ }
577+ }
578+ for i := range refs {
579+ if refs [i ].Namespace == "" {
580+ refs [i ].Namespace = d .Namespace
581+ }
582+ }
583+ return refs
584+ }
585+
497586// +kubebuilder:object:root=true
498587
499588// DeviceList contains a list of Device.
0 commit comments