package config import ( "bytes" "crypto/x509" "encoding/pem" "flag" "fmt" "log" "mutating-webhook/internal/certificate" "os" "reflect" "strings" "time" ) func getOSEnv(env, def string) string { if val, ok := os.LookupEnv(strings.ToUpper(env)); ok { return val } return def } // Init initializes the application configuration by reading default values from the struct's tags // and environment variables. Tags processed by this process are as follows: // `ignored:"true" env:"ENVIRONMENT_VARIABLE" default:"default value"` func Init() Config { cfg := DefaultConfig() cfgInfo, err := getStructInfo(&cfg) if err != nil { log.Fatalf("[FATAL] %v", err) } for _, info := range cfgInfo { switch info.Type.String() { case "string": var dv string if info.DefaultValue != nil { dv = info.DefaultValue.(string) } p := reflect.ValueOf(&cfg).Elem().FieldByName(info.Name).Addr().Interface().(*string) flag.StringVar(p, info.Name, dv, "("+info.Key+")") case "bool": var dv bool if info.DefaultValue != nil { dv = info.DefaultValue.(bool) } p := reflect.ValueOf(&cfg).Elem().FieldByName(info.Name).Addr().Interface().(*bool) flag.BoolVar(p, info.Name, dv, "("+info.Key+")") case "int": var dv int if info.DefaultValue != nil { dv = int(info.DefaultValue.(int64)) } p := reflect.ValueOf(&cfg).Elem().FieldByName(info.Name).Addr().Interface().(*int) flag.IntVar(p, info.Name, dv, "("+info.Key+")") } } flag.Parse() // set logging level setLogLevel(cfg) // timezone & format configuration cfg.TZoneUTC, _ = time.LoadLocation("UTC") if err != nil { log.Fatalf("[ERROR] Unable to parse timezone string. Please use one of the timezone database values listed here: %s", "https://en.wikipedia.org/wiki/List_of_tz_database_time_zones") } cfg.TZoneLocal, err = time.LoadLocation(cfg.TimeZoneLocal) if err != nil { log.Fatalf("[ERROR] Unable to parse timezone string. Please use one of the timezone database values listed here: %s", "https://en.wikipedia.org/wiki/List_of_tz_database_time_zones") } time.Now().Format(cfg.TimeFormat) // read config file configFileData, err := getConfigFileData(cfg.ConfigFile) if err != nil { log.Fatalf("[FATAL] Unable to read configuration file") } updateValues(&cfg, configFileData) // Generate certificates if needed if err := certificateInit(&cfg); err != nil { log.Fatalf("[FATAL] Unable to initialize certificate data: %v", err) } // print running config printRunningConfig(&cfg, cfgInfo) log.Println("[INFO] initialization sequence complete") return cfg } func updateValues(cfg *Config, configFileData configFileStruct) { if cfg.AllowAdminNoMutate == false && configFileData.AllowAdminNoMutate != false { cfg.AllowAdminNoMutate = configFileData.AllowAdminNoMutate } if cfg.AllowAdminNoMutateToggle == "7b068a99-c02b-410a-bd59-3514bac85e7a" && configFileData.AllowAdminNoMutateToggle != "2d77b689-dc14-40a5-8971-34c62999335c" { cfg.AllowAdminNoMutateToggle = configFileData.AllowAdminNoMutateToggle } if cfg.DockerhubRegistry == "registry.hub.docker.com" && configFileData.DockerhubRegistry != "registry.hub.docker.com" { cfg.DockerhubRegistry = configFileData.DockerhubRegistry } if len(configFileData.MutateIgnoredImages) != 0 { cfg.MutateIgnoredImages = configFileData.MutateIgnoredImages } if len(configFileData.CertificateAuthority.Certificate) != 0 { cfg.CACert = configFileData.CertificateAuthority.Certificate } if len(configFileData.CertificateAuthority.PrivateKey) != 0 { cfg.CAPrivateKey = configFileData.CertificateAuthority.PrivateKey } if len(configFileData.Certificate.Certificate) != 0 { cfg.CertCert = configFileData.Certificate.Certificate } if len(configFileData.Certificate.PrivateKey) != 0 { cfg.CertPrivateKey = configFileData.Certificate.PrivateKey } } func certificateInit(cfg *Config) error { // certificate authority private key does not exist, generate key pair if len(cfg.CAPrivateKey) == 0 { log.Printf("[TRACE] No certificate authority private key detected") keyPair, err := certificate.CreateRSAKeyPair(4096) if err != nil { return fmt.Errorf("Create RSA Key (%v)", err) } // pem encode private key k := new(bytes.Buffer) pem.Encode(k, &pem.Block{ Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(keyPair), }) cfg.CAPrivateKey = k.String() } // certificate authority certificate is missing, create it if len(cfg.CACert) == 0 { log.Printf("[TRACE] No certificate authority certificate detected") caCert, err := certificate.CreateCA(cfg.CAPrivateKey) if err != nil { return fmt.Errorf("Create CA (%v)", err) } cfg.CACert = caCert } // certificate private key does not exist, generate key pair if len(cfg.CertPrivateKey) == 0 { log.Printf("[TRACE] No server private key detected") keyPair, err := certificate.CreateRSAKeyPair(4096) if err != nil { return fmt.Errorf("Create RSA Key (%v)", err) } // pem encode private key k := new(bytes.Buffer) pem.Encode(k, &pem.Block{ Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(keyPair), }) cfg.CertPrivateKey = k.String() } // certificate certificate is missing, create it if len(cfg.CertCert) == 0 { log.Printf("[TRACE] No server certificate detected") csr, err := certificate.CreateCSR(cfg.CertPrivateKey) if err != nil { return fmt.Errorf("Create CSR (%v)", err) } cert, err := certificate.SignCert(cfg.CACert, cfg.CAPrivateKey, csr) if err != nil { return fmt.Errorf("Sign Cert (%v)", err) } cfg.CertCert = cert } return nil }