@@ -17,10 +17,17 @@ limitations under the License.
1717package  v1beta2
1818
1919import  (
20+ 	"encoding/base64" 
21+ 	"fmt" 
22+ 	"net" 
23+ 	"net/url" 
24+ 	"strings" 
25+ 
2026	"github.com/google/go-cmp/cmp" 
2127	"github.com/pkg/errors" 
2228	apierrors "k8s.io/apimachinery/pkg/api/errors" 
2329	"k8s.io/apimachinery/pkg/runtime" 
30+ 	"k8s.io/apimachinery/pkg/util/validation" 
2431	"k8s.io/apimachinery/pkg/util/validation/field" 
2532	ctrl "sigs.k8s.io/controller-runtime" 
2633	"sigs.k8s.io/controller-runtime/pkg/webhook" 
@@ -171,17 +178,132 @@ func (r *AWSMachine) ignitionEnabled() bool {
171178
172179func  (r  * AWSMachine ) validateIgnitionAndCloudInit () field.ErrorList  {
173180	var  allErrs  field.ErrorList 
181+ 	if  ! r .ignitionEnabled () {
182+ 		return  allErrs 
183+ 	}
174184
175185	// Feature gate is not enabled but ignition is enabled then send a forbidden error. 
176- 	if  ! feature .Gates .Enabled (feature .BootstrapFormatIgnition ) &&   r . ignitionEnabled ()  {
186+ 	if  ! feature .Gates .Enabled (feature .BootstrapFormatIgnition ) {
177187		allErrs  =  append (allErrs , field .Forbidden (field .NewPath ("spec" , "ignition" ),
178188			"can be set only if the BootstrapFormatIgnition feature gate is enabled" ))
179189	}
180190
181- 	if  r .ignitionEnabled () &&  r .cloudInitConfigured () {
191+ 	// If ignition is enabled, cloudInit should not be configured. 
192+ 	if  r .cloudInitConfigured () {
182193		allErrs  =  append (allErrs , field .Forbidden (field .NewPath ("spec" , "cloudInit" ), "cannot be set if spec.ignition is set" ))
183194	}
184195
196+ 	// Proxy and TLS are only valid for Ignition versions >= 3.1. 
197+ 	if  r .Spec .Ignition .Version  ==  "2.3"  ||  r .Spec .Ignition .Version  ==  "3.0"  {
198+ 		if  r .Spec .Ignition .Proxy  !=  nil  {
199+ 			allErrs  =  append (allErrs , field .Forbidden (field .NewPath ("spec" , "ignition" , "proxy" ), "cannot be set if spec.ignition.version is 2.3 or 3.0" ))
200+ 		}
201+ 		if  r .Spec .Ignition .TLS  !=  nil  {
202+ 			allErrs  =  append (allErrs , field .Forbidden (field .NewPath ("spec" , "ignition" , "tls" ), "cannot be set if spec.ignition.version is 2.3 or 3.0" ))
203+ 		}
204+ 	}
205+ 
206+ 	allErrs  =  append (allErrs , r .validateIgnitionProxy ()... )
207+ 	allErrs  =  append (allErrs , r .validateIgnitionTLS ()... )
208+ 
209+ 	return  allErrs 
210+ }
211+ 
212+ func  (r  * AWSMachine ) validateIgnitionProxy () field.ErrorList  {
213+ 	var  allErrs  field.ErrorList 
214+ 
215+ 	if  r .Spec .Ignition .Proxy  ==  nil  {
216+ 		return  allErrs 
217+ 	}
218+ 
219+ 	// Validate HTTPProxy. 
220+ 	if  r .Spec .Ignition .Proxy .HTTPProxy  !=  nil  {
221+ 		// Parse the url to check if it is valid. 
222+ 		_ , err  :=  url .Parse (* r .Spec .Ignition .Proxy .HTTPProxy )
223+ 		if  err  !=  nil  {
224+ 			allErrs  =  append (allErrs , field .Invalid (field .NewPath ("spec" , "ignition" , "proxy" , "httpProxy" ), * r .Spec .Ignition .Proxy .HTTPProxy , "invalid URL" ))
225+ 		}
226+ 	}
227+ 
228+ 	// Validate HTTPSProxy. 
229+ 	if  r .Spec .Ignition .Proxy .HTTPSProxy  !=  nil  {
230+ 		// Parse the url to check if it is valid. 
231+ 		_ , err  :=  url .Parse (* r .Spec .Ignition .Proxy .HTTPSProxy )
232+ 		if  err  !=  nil  {
233+ 			allErrs  =  append (allErrs , field .Invalid (field .NewPath ("spec" , "ignition" , "proxy" , "httpsProxy" ), * r .Spec .Ignition .Proxy .HTTPSProxy , "invalid URL" ))
234+ 		}
235+ 	}
236+ 
237+ 	// Validate NoProxy. 
238+ 	for  _ , noProxy  :=  range  r .Spec .Ignition .Proxy .NoProxy  {
239+ 		noProxy  :=  string (noProxy )
240+ 		// Validate here that the value `noProxy` is: 
241+ 		// - A domain name 
242+ 		//   - A domain name matches that name and all subdomains 
243+ 		//   - A domain name with a leading . matches subdomains only 
244+ 
245+ 		// A special DNS label (*). 
246+ 		if  noProxy  ==  "*"  {
247+ 			continue 
248+ 		}
249+ 		// An IP address prefix (1.2.3.4). 
250+ 		if  ip  :=  net .ParseIP (noProxy ); ip  !=  nil  {
251+ 			continue 
252+ 		}
253+ 		// An IP address prefix in CIDR notation (1.2.3.4/8). 
254+ 		if  _ , _ , err  :=  net .ParseCIDR (noProxy ); err  ==  nil  {
255+ 			continue 
256+ 		}
257+ 		// An IP or domain name with a port. 
258+ 		if  _ , _ , err  :=  net .SplitHostPort (noProxy ); err  ==  nil  {
259+ 			continue 
260+ 		}
261+ 		// A domain name. 
262+ 		if  noProxy [0 ] ==  '.'  {
263+ 			// If it starts with a dot, it should be a domain name. 
264+ 			noProxy  =  noProxy [1 :]
265+ 		}
266+ 		// Validate that the value matches DNS 1123. 
267+ 		if  errs  :=  validation .IsDNS1123Subdomain (noProxy ); len (errs ) >  0  {
268+ 			allErrs  =  append (allErrs , field .Invalid (field .NewPath ("spec" , "ignition" , "proxy" , "noProxy" ), noProxy , fmt .Sprintf ("invalid noProxy value, please refer to the field documentation: %s" , strings .Join (errs , "; " ))))
269+ 		}
270+ 	}
271+ 
272+ 	return  allErrs 
273+ }
274+ 
275+ func  (r  * AWSMachine ) validateIgnitionTLS () field.ErrorList  {
276+ 	var  allErrs  field.ErrorList 
277+ 
278+ 	if  r .Spec .Ignition .TLS  ==  nil  {
279+ 		return  allErrs 
280+ 	}
281+ 
282+ 	for  _ , source  :=  range  r .Spec .Ignition .TLS .CASources  {
283+ 		// Validate that source is RFC 2397 data URL. 
284+ 		u , err  :=  url .Parse (string (source ))
285+ 		if  err  !=  nil  {
286+ 			allErrs  =  append (allErrs , field .Invalid (field .NewPath ("spec" , "ignition" , "tls" , "caSources" ), source , "invalid URL" ))
287+ 		}
288+ 
289+ 		switch  u .Scheme  {
290+ 		case  "http" , "https" , "tftp" , "s3" , "arn" , "gs" :
291+ 			// Valid schemes. 
292+ 		case  "data" :
293+ 			// Validate that the data URL is base64 encoded. 
294+ 			i  :=  strings .Index (u .Opaque , "," )
295+ 			if  i  <  0  {
296+ 				allErrs  =  append (allErrs , field .Invalid (field .NewPath ("spec" , "ignition" , "tls" , "caSources" ), source , "invalid data URL" ))
297+ 			}
298+ 			// Validate that the data URL is base64 encoded. 
299+ 			if  _ , err  :=  base64 .StdEncoding .DecodeString (u .Opaque [i + 1 :]); err  !=  nil  {
300+ 				allErrs  =  append (allErrs , field .Invalid (field .NewPath ("spec" , "ignition" , "tls" , "caSources" ), source , "invalid base64 encoding for data url" ))
301+ 			}
302+ 		default :
303+ 			allErrs  =  append (allErrs , field .Invalid (field .NewPath ("spec" , "ignition" , "tls" , "caSources" ), source , "unsupported URL scheme" ))
304+ 		}
305+ 	}
306+ 
185307	return  allErrs 
186308}
187309
0 commit comments