// secretToPem composes a PEM file at the output directory from an input private key and crt file. func secretToPem(secPath, outName string) error { // The secret, when present, is mounted on /etc/pki/tls/private // The secret has two components crt.tls and key.tls // When the default cert is provided by the admin it is a pem // tls.crt is the supplied pem and tls.key is the key // extracted from the pem // When the admin does not provide a default cert, the secret // is created via the service annotation. In this case // tls.crt is the cert and tls.key is the key // The crt and key are concatenated to form the needed pem var fileCrtName = filepath.Join(secPath, "tls.crt") var fileKeyName = filepath.Join(secPath, "tls.key") pemBlock, err := ioutil.ReadFile(fileCrtName) if err != nil { return err } keys, err := cmdutil.PrivateKeysFromPEM(pemBlock) if err != nil { return err } if len(keys) == 0 { // Try to get the key from the tls.key file keyBlock, err := ioutil.ReadFile(fileKeyName) if err != nil { return err } pemBlock = append(pemBlock, keyBlock...) } return ioutil.WriteFile(outName, pemBlock, 0444) }
// generateSecretsConfig generates any Secret and Volume objects, such // as the TLS serving cert that are necessary for the registry container. // Runs true if the registry should be served over TLS. func generateSecretsConfig( cfg *RegistryConfig, namespace string, defaultCrt, defaultKey []byte, ) ([]*kapi.Secret, []kapi.Volume, []kapi.VolumeMount, app.Environment, bool, error) { var secrets []*kapi.Secret var volumes []kapi.Volume var mounts []kapi.VolumeMount extraEnv := app.Environment{} if len(defaultCrt) > 0 && len(defaultKey) == 0 { keys, err := cmdutil.PrivateKeysFromPEM(defaultCrt) if err != nil { return nil, nil, nil, nil, false, err } if len(keys) == 0 { return nil, nil, nil, nil, false, fmt.Errorf("the default cert must contain a private key") } defaultKey = keys } if len(defaultCrt) > 0 { secret := &kapi.Secret{ ObjectMeta: kapi.ObjectMeta{ Name: fmt.Sprintf("%s-certs", cfg.Name), }, Type: kapi.SecretTypeTLS, Data: map[string][]byte{ kapi.TLSCertKey: defaultCrt, kapi.TLSPrivateKeyKey: defaultKey, }, } secrets = append(secrets, secret) volume := kapi.Volume{ Name: "server-certificate", VolumeSource: kapi.VolumeSource{ Secret: &kapi.SecretVolumeSource{ SecretName: secret.Name, }, }, } volumes = append(volumes, volume) mount := kapi.VolumeMount{ Name: volume.Name, ReadOnly: true, MountPath: defaultCertificateDir, } mounts = append(mounts, mount) extraEnv.Add(app.Environment{ "REGISTRY_HTTP_TLS_CERTIFICATE": path.Join(defaultCertificateDir, kapi.TLSCertKey), "REGISTRY_HTTP_TLS_KEY": path.Join(defaultCertificateDir, kapi.TLSPrivateKeyKey), }) } secretBytes := make([]byte, randomSecretSize) if _, err := cryptorand.Read(secretBytes); err != nil { return nil, nil, nil, nil, false, fmt.Errorf("registry does not exist; could not generate random bytes for HTTP secret: %v", err) } httpSecretString := base64.StdEncoding.EncodeToString(secretBytes) extraEnv["REGISTRY_HTTP_SECRET"] = httpSecretString return secrets, volumes, mounts, extraEnv, len(defaultCrt) > 0, nil }
// generateSecretsConfig generates any Secret and Volume objects, such // as SSH private keys, that are necessary for the router container. func generateSecretsConfig(cfg *RouterConfig, kClient *kclient.Client, namespace string, defaultCert []byte, certName string) ([]*kapi.Secret, []kapi.Volume, []kapi.VolumeMount, error) { var secrets []*kapi.Secret var volumes []kapi.Volume var mounts []kapi.VolumeMount if len(cfg.ExternalHostPrivateKey) != 0 { privkeyData, err := fileutil.LoadData(cfg.ExternalHostPrivateKey) if err != nil { return secrets, volumes, mounts, fmt.Errorf("error reading private key for external host: %v", err) } secret := &kapi.Secret{ ObjectMeta: kapi.ObjectMeta{ Name: privkeySecretName, }, Data: map[string][]byte{privkeyName: privkeyData}, } secrets = append(secrets, secret) volume := kapi.Volume{ Name: secretsVolumeName, VolumeSource: kapi.VolumeSource{ Secret: &kapi.SecretVolumeSource{ SecretName: privkeySecretName, }, }, } volumes = append(volumes, volume) mount := kapi.VolumeMount{ Name: secretsVolumeName, ReadOnly: true, MountPath: secretsPath, } mounts = append(mounts, mount) } if len(defaultCert) > 0 { // When the user sets the default cert from the "oadm router --default-cert ..." // command we end up here. In this case the default cert must be in pem format. // The secret has a crt and key. The crt contains the supplied default cert (pem) // and the key is extracted from the default cert but its ultimately not used. // NOTE: If the default cert is not provided by the user, we generate one by // adding an annotation to the service associated with the router (see RunCmdRouter()) keys, err := cmdutil.PrivateKeysFromPEM(defaultCert) if err != nil { return nil, nil, nil, err } if len(keys) == 0 { return nil, nil, nil, fmt.Errorf("the default cert must contain a private key") } // The TLSCertKey contains the pem file passed in as the default cert secret := &kapi.Secret{ ObjectMeta: kapi.ObjectMeta{ Name: certName, }, Type: kapi.SecretTypeTLS, Data: map[string][]byte{ kapi.TLSCertKey: defaultCert, kapi.TLSPrivateKeyKey: keys, }, } secrets = append(secrets, secret) } // The secret in this volume is either the one created for the // user supplied default cert (pem format) or the secret generated // by the service anotation (cert only format). // In either case the secret has the same name and it has the same mount point. volume := kapi.Volume{ Name: "server-certificate", VolumeSource: kapi.VolumeSource{ Secret: &kapi.SecretVolumeSource{ SecretName: certName, }, }, } volumes = append(volumes, volume) mount := kapi.VolumeMount{ Name: volume.Name, ReadOnly: true, MountPath: defaultCertificateDir, } mounts = append(mounts, mount) return secrets, volumes, mounts, nil }
// generateSecretsConfig generates any Secret and Volume objects, such // as SSH private keys, that are necessary for the router container. func generateSecretsConfig(cfg *RouterConfig, kClient *kclient.Client, namespace string, defaultCert []byte) ([]*kapi.Secret, []kapi.Volume, []kapi.VolumeMount, error) { var secrets []*kapi.Secret var volumes []kapi.Volume var mounts []kapi.VolumeMount if len(cfg.ExternalHostPrivateKey) != 0 { privkeyData, err := fileutil.LoadData(cfg.ExternalHostPrivateKey) if err != nil { return secrets, volumes, mounts, fmt.Errorf("error reading private key for external host: %v", err) } secret := &kapi.Secret{ ObjectMeta: kapi.ObjectMeta{ Name: privkeySecretName, }, Data: map[string][]byte{privkeyName: privkeyData}, } secrets = append(secrets, secret) volume := kapi.Volume{ Name: secretsVolumeName, VolumeSource: kapi.VolumeSource{ Secret: &kapi.SecretVolumeSource{ SecretName: privkeySecretName, }, }, } volumes = append(volumes, volume) mount := kapi.VolumeMount{ Name: secretsVolumeName, ReadOnly: true, MountPath: secretsPath, } mounts = append(mounts, mount) } if len(defaultCert) > 0 { keys, err := cmdutil.PrivateKeysFromPEM(defaultCert) if err != nil { return nil, nil, nil, err } if len(keys) == 0 { return nil, nil, nil, fmt.Errorf("the default cert must contain a private key") } secret := &kapi.Secret{ ObjectMeta: kapi.ObjectMeta{ Name: fmt.Sprintf("%s-certs", cfg.Name), }, Type: kapi.SecretTypeTLS, Data: map[string][]byte{ kapi.TLSCertKey: defaultCert, kapi.TLSPrivateKeyKey: keys, }, } secrets = append(secrets, secret) volume := kapi.Volume{ Name: "server-certificate", VolumeSource: kapi.VolumeSource{ Secret: &kapi.SecretVolumeSource{ SecretName: secret.Name, }, }, } volumes = append(volumes, volume) mount := kapi.VolumeMount{ Name: volume.Name, ReadOnly: true, MountPath: defaultCertificateDir, } mounts = append(mounts, mount) } return secrets, volumes, mounts, nil }