1
16
17 package validation
18
19 import (
20 "fmt"
21 "net/netip"
22 "regexp"
23
24 "k8s.io/apimachinery/pkg/util/sets"
25 "k8s.io/apimachinery/pkg/util/validation/field"
26
27 gatewayv1 "sigs.k8s.io/gateway-api/apis/v1"
28 gatewayv1b1 "sigs.k8s.io/gateway-api/apis/v1beta1"
29 )
30
31 var (
32
33 protocolsHostnameInvalid = map[gatewayv1b1.ProtocolType]struct{}{
34 gatewayv1.TCPProtocolType: {},
35 gatewayv1.UDPProtocolType: {},
36 }
37
38 protocolsTLSInvalid = map[gatewayv1b1.ProtocolType]struct{}{
39 gatewayv1.HTTPProtocolType: {},
40 gatewayv1.UDPProtocolType: {},
41 gatewayv1.TCPProtocolType: {},
42 }
43
44 protocolsTLSRequired = map[gatewayv1b1.ProtocolType]struct{}{
45 gatewayv1.HTTPSProtocolType: {},
46 gatewayv1.TLSProtocolType: {},
47 }
48
49 validHostnameAddress = `^(\*\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$`
50 validHostnameRegexp = regexp.MustCompile(validHostnameAddress)
51 )
52
53
54
55
56
57
58
59
60 func ValidateGateway(gw *gatewayv1b1.Gateway) field.ErrorList {
61 return ValidateGatewaySpec(&gw.Spec, field.NewPath("spec"))
62 }
63
64
65
66 func ValidateGatewaySpec(spec *gatewayv1b1.GatewaySpec, path *field.Path) field.ErrorList {
67 var errs field.ErrorList
68 errs = append(errs, validateGatewayListeners(spec.Listeners, path.Child("listeners"))...)
69 errs = append(errs, validateGatewayAddresses(spec.Addresses, path.Child("addresses"))...)
70 return errs
71 }
72
73
74
75 func validateGatewayListeners(listeners []gatewayv1b1.Listener, path *field.Path) field.ErrorList {
76 var errs field.ErrorList
77 errs = append(errs, ValidateListenerTLSConfig(listeners, path)...)
78 errs = append(errs, validateListenerHostname(listeners, path)...)
79 errs = append(errs, ValidateTLSCertificateRefs(listeners, path)...)
80 errs = append(errs, ValidateListenerNames(listeners, path)...)
81 errs = append(errs, validateHostnameProtocolPort(listeners, path)...)
82 return errs
83 }
84
85
86
87 func ValidateListenerTLSConfig(listeners []gatewayv1b1.Listener, path *field.Path) field.ErrorList {
88 var errs field.ErrorList
89 for i, l := range listeners {
90 if isProtocolInSubset(l.Protocol, protocolsTLSRequired) && l.TLS == nil {
91 errs = append(errs, field.Forbidden(path.Index(i).Child("tls"), fmt.Sprintf("must be set for protocol %v", l.Protocol)))
92 }
93 if isProtocolInSubset(l.Protocol, protocolsTLSInvalid) && l.TLS != nil {
94 errs = append(errs, field.Forbidden(path.Index(i).Child("tls"), fmt.Sprintf("should be empty for protocol %v", l.Protocol)))
95 }
96 }
97 return errs
98 }
99
100 func isProtocolInSubset(protocol gatewayv1b1.ProtocolType, set map[gatewayv1b1.ProtocolType]struct{}) bool {
101 _, ok := set[protocol]
102 return ok
103 }
104
105
106
107 func validateListenerHostname(listeners []gatewayv1b1.Listener, path *field.Path) field.ErrorList {
108 var errs field.ErrorList
109 for i, h := range listeners {
110 if isProtocolInSubset(h.Protocol, protocolsHostnameInvalid) && h.Hostname != nil {
111 errs = append(errs, field.Forbidden(path.Index(i).Child("hostname"), fmt.Sprintf("should be empty for protocol %v", h.Protocol)))
112 }
113 }
114 return errs
115 }
116
117
118
119
120 func ValidateTLSCertificateRefs(listeners []gatewayv1b1.Listener, path *field.Path) field.ErrorList {
121 var errs field.ErrorList
122 for i, c := range listeners {
123 if isProtocolInSubset(c.Protocol, protocolsTLSRequired) && c.TLS != nil {
124 if *c.TLS.Mode == gatewayv1.TLSModeTerminate && len(c.TLS.CertificateRefs) == 0 {
125 errs = append(errs, field.Forbidden(path.Index(i).Child("tls").Child("certificateRefs"), "should be set and not empty when TLSModeType is Terminate"))
126 }
127 }
128 }
129 return errs
130 }
131
132
133
134 func ValidateListenerNames(listeners []gatewayv1b1.Listener, path *field.Path) field.ErrorList {
135 var errs field.ErrorList
136 nameMap := make(map[gatewayv1b1.SectionName]struct{}, len(listeners))
137 for i, c := range listeners {
138 if _, found := nameMap[c.Name]; found {
139 errs = append(errs, field.Duplicate(path.Index(i).Child("name"), "must be unique within the Gateway"))
140 }
141 nameMap[c.Name] = struct{}{}
142 }
143 return errs
144 }
145
146
147
148 func validateHostnameProtocolPort(listeners []gatewayv1b1.Listener, path *field.Path) field.ErrorList {
149 var errs field.ErrorList
150 hostnameProtocolPortSets := sets.Set[string]{}
151 for i, listener := range listeners {
152 hostname := new(gatewayv1b1.Hostname)
153 if listener.Hostname != nil {
154 hostname = listener.Hostname
155 }
156 protocol := listener.Protocol
157 port := listener.Port
158 hostnameProtocolPort := fmt.Sprintf("%s:%s:%d", *hostname, protocol, port)
159 if hostnameProtocolPortSets.Has(hostnameProtocolPort) {
160 errs = append(errs, field.Duplicate(path.Index(i), "combination of port, protocol, and hostname must be unique for each listener"))
161 } else {
162 hostnameProtocolPortSets.Insert(hostnameProtocolPort)
163 }
164 }
165 return errs
166 }
167
168
169
170 func validateGatewayAddresses(addresses []gatewayv1b1.GatewayAddress, path *field.Path) field.ErrorList {
171 var errs field.ErrorList
172 ipAddrSet, hostnameAddrSet := sets.Set[string]{}, sets.Set[string]{}
173 for i, address := range addresses {
174 if address.Type != nil {
175 if *address.Type == gatewayv1b1.IPAddressType {
176 if _, err := netip.ParseAddr(address.Value); err != nil {
177 errs = append(errs, field.Invalid(path.Index(i), address.Value, "invalid ip address"))
178 }
179 if ipAddrSet.Has(address.Value) {
180 errs = append(errs, field.Duplicate(path.Index(i), address.Value))
181 } else {
182 ipAddrSet.Insert(address.Value)
183 }
184 } else if *address.Type == gatewayv1b1.HostnameAddressType {
185 if !validHostnameRegexp.MatchString(address.Value) {
186 errs = append(errs, field.Invalid(path.Index(i), address.Value, fmt.Sprintf("must only contain valid characters (matching %s)", validHostnameAddress)))
187 }
188 if hostnameAddrSet.Has(address.Value) {
189 errs = append(errs, field.Duplicate(path.Index(i), address.Value))
190 } else {
191 hostnameAddrSet.Insert(address.Value)
192 }
193 }
194 }
195 }
196 return errs
197 }
198
View as plain text