package internal
import (
coreclient "k8s.io/client-go/kubernetes/typed/core/v1"
corelisters "k8s.io/client-go/listers/core/v1"
acmapi "gitbucket.jerxie.com/yangyangxie/AnthosCertManager/pkg/apis/anthoscertmanager/v1"
)
var (
certificateGvk = acmapi.SchemeGroupVersion.WithKind("Certificate")
)
// SecretsManager creates and updates secrets with certificate and key data.
type SecretsManager struct {
secretClient coreclient.SecretsGetter
secretLister corelisters.SecretLister
// fieldManager is the manager name used for the Apply operations on Secrets.
fieldManager string
// if true, Secret resources created by the controller will have an
// 'owner reference' set, meaning when the Certificate is deleted, the
// Secret resource will be automatically deleted.
// This option is disabled by default.
enableSecretOwnerReferences bool
}
// SecretData is a structure wrapping private key, Certificate and CA data
type SecretData struct {
PrivateKey, Certificate, CA []byte
}
// NewSecretsManager returns a new SecretsManager. Setting
// enableSecretOwnerReferences to true will mean that secrets will be deleted
// when the corresponding Certificate is deleted.
func NewSecretsManager(
secretClient coreclient.SecretsGetter,
secretLister corelisters.SecretLister,
fieldManager string,
enableSecretOwnerReferences bool,
) *SecretsManager {
return &SecretsManager{
secretClient: secretClient,
secretLister: secretLister,
fieldManager: fieldManager,
enableSecretOwnerReferences: enableSecretOwnerReferences,
}
}
// // UpdateData will ensure the Secret resource contains the given secret data as
// // well as appropriate metadata using an Apply call.
// // If the Secret resource does not exist, it will be created on Apply.
// // UpdateData will also update deprecated annotations if they exist.
// func (s *SecretsManager) UpdateData(ctx context.Context, crt *acmapi.Certificate, data SecretData) error {
// secret, err := s.getCertificateSecret(ctx, crt)
// if err != nil {
// return err
// }
// log := logf.FromContext(ctx).WithName("secrets_manager")
// log = logf.WithResource(log, secret)
// if err := s.setValues(crt, secret, data); err != nil {
// return err
// }
// // Build Secret apply configuration and options.
// applyOpts := metav1.ApplyOptions{FieldManager: s.fieldManager, Force: true}
// applyCnf := applycorev1.Secret(secret.Name, secret.Namespace).
// WithAnnotations(secret.Annotations).WithLabels(secret.Labels).
// WithData(secret.Data).WithType(secret.Type)
// // If Secret owner reference is enabled, set it on the Secret. This results
// // in a no-op if the Secret already exists and has the owner reference set,
// // and visa-versa.
// if s.enableSecretOwnerReferences {
// ref := *metav1.NewControllerRef(crt, certificateGvk)
// applyCnf = applyCnf.WithOwnerReferences(&applymetav1.OwnerReferenceApplyConfiguration{
// APIVersion: &ref.APIVersion, Kind: &ref.Kind,
// Name: &ref.Name, UID: &ref.UID,
// Controller: ref.Controller, BlockOwnerDeletion: ref.BlockOwnerDeletion,
// })
// }
// log.V(logf.DebugLevel).Info("applying secret")
// _, err = s.secretClient.Secrets(secret.Namespace).Apply(ctx, applyCnf, applyOpts)
// if err != nil {
// return fmt.Errorf("failed to apply secret %s/%s: %w", secret.Namespace, secret.Name, err)
// }
// return nil
// }
// // setValues will update the Secret resource 'secret' with the data contained
// // in the given secretData.
// // It will update labels and annotations on the Secret resource appropriately.
// // The Secret resource 's' must be non-nil, although may be a resource that does
// // not exist in the Kubernetes apiserver yet.
// // setValues will NOT actually update the resource in the apiserver.
// // It will also update depreciated issuer name and kind annotations if they
// // exist.
// func (s *SecretsManager) setValues(crt *acmapi.Certificate, secret *corev1.Secret, data SecretData) error {
// if err := s.setKeystores(crt, secret, data); err != nil {
// return fmt.Errorf("failed to add keystores to Secret: %w", err)
// }
// // Add additional output formats if feature enabled.
// if utilfeature.DefaultFeatureGate.Enabled(feature.AdditionalCertificateOutputFormats) {
// if err := setAdditionalOutputFormats(crt, secret, data); err != nil {
// return fmt.Errorf("failed to add additional output formats to Secret: %w", err)
// }
// }
// secret.Data[corev1.TLSPrivateKeyKey] = data.PrivateKey
// secret.Data[corev1.TLSCertKey] = data.Certificate
// if len(data.CA) > 0 {
// secret.Data[acmmeta.TLSCAKey] = data.CA
// }
// var certificate *x509.Certificate
// if len(data.Certificate) > 0 {
// var err error
// certificate, err = utilpki.DecodeX509CertificateBytes(data.Certificate)
// // TODO: handle InvalidData here?
// if err != nil {
// return err
// }
// }
// secret.Annotations = certificates.AnnotationsForCertificateSecret(crt, certificate)
// if secret.Labels == nil {
// secret.Labels = make(map[string]string)
// }
// if crt.Spec.SecretTemplate != nil {
// for k, v := range crt.Spec.SecretTemplate.Labels {
// secret.Labels[k] = v
// }
// for k, v := range crt.Spec.SecretTemplate.Annotations {
// secret.Annotations[k] = v
// }
// }
// return nil
// }
// // getCertificateSecret will return a secret which is ready for fields to be
// // applied. Only the Secret Type will be persisted from the original Secret.
// func (s *SecretsManager) getCertificateSecret(ctx context.Context, crt *cmapi.Certificate) (*corev1.Secret, error) {
// // Get existing secret if it exists.
// existingSecret, err := s.secretLister.Secrets(crt.Namespace).Get(crt.Spec.SecretName)
// // If secret doesn't exist yet, return an empty secret that should be
// // created.
// if apierrors.IsNotFound(err) {
// return &corev1.Secret{
// ObjectMeta: metav1.ObjectMeta{
// Name: crt.Spec.SecretName,
// Namespace: crt.Namespace,
// },
// Data: make(map[string][]byte),
// Type: corev1.SecretTypeTLS,
// }, nil
// }
// // Transient error.
// if err != nil {
// return nil, err
// }
// // Only copy Secret Type to not take ownership of annotations or labels on
// // Apply.
// return &corev1.Secret{
// ObjectMeta: metav1.ObjectMeta{
// Name: crt.Spec.SecretName,
// Namespace: crt.Namespace,
// },
// Data: make(map[string][]byte),
// // Use the existing Secret's type since this may not be of type
// // `kubernetes.io/tls`, if for example it was created beforehand. Type is
// // immutable, so we must keep it to its original value.
// Type: existingSecret.Type,
// }, nil
// }
// // setKeystores will set extra Secret Data keys according to any Keystores
// // which have been configured.
// func (s *SecretsManager) setKeystores(crt *acmapi.Certificate, secret *corev1.Secret, data SecretData) error {
// // Handle the experimental PKCS12 support
// if crt.Spec.Keystores != nil && crt.Spec.Keystores.PKCS12 != nil && crt.Spec.Keystores.PKCS12.Create {
// ref := crt.Spec.Keystores.PKCS12.PasswordSecretRef
// pwSecret, err := s.secretLister.Secrets(crt.Namespace).Get(ref.Name)
// if err != nil {
// return fmt.Errorf("fetching PKCS12 keystore password from Secret: %v", err)
// }
// if pwSecret.Data == nil || len(pwSecret.Data[ref.Key]) == 0 {
// return fmt.Errorf("PKCS12 keystore password Secret contains no data for key %q", ref.Key)
// }
// pw := pwSecret.Data[ref.Key]
// keystoreData, err := encodePKCS12Keystore(string(pw), data.PrivateKey, data.Certificate, data.CA)
// if err != nil {
// return fmt.Errorf("error encoding PKCS12 bundle: %w", err)
// }
// // always overwrite the keystore entry for now
// secret.Data[pkcs12SecretKey] = keystoreData
// if len(data.CA) > 0 {
// truststoreData, err := encodePKCS12Truststore(string(pw), data.CA)
// if err != nil {
// return fmt.Errorf("error encoding PKCS12 trust store bundle: %w", err)
// }
// // always overwrite the truststore entry
// secret.Data[pkcs12TruststoreKey] = truststoreData
// }
// }
// // Handle the experimental JKS support
// if crt.Spec.Keystores != nil && crt.Spec.Keystores.JKS != nil && crt.Spec.Keystores.JKS.Create {
// ref := crt.Spec.Keystores.JKS.PasswordSecretRef
// pwSecret, err := s.secretLister.Secrets(crt.Namespace).Get(ref.Name)
// if err != nil {
// return fmt.Errorf("fetching JKS keystore password from Secret: %v", err)
// }
// if pwSecret.Data == nil || len(pwSecret.Data[ref.Key]) == 0 {
// return fmt.Errorf("JKS keystore password Secret contains no data for key %q", ref.Key)
// }
// pw := pwSecret.Data[ref.Key]
// keystoreData, err := encodeJKSKeystore(pw, data.PrivateKey, data.Certificate, data.CA)
// if err != nil {
// return fmt.Errorf("error encoding JKS bundle: %w", err)
// }
// // always overwrite the keystore entry
// secret.Data[jksSecretKey] = keystoreData
// if len(data.CA) > 0 {
// truststoreData, err := encodeJKSTruststore(pw, data.CA)
// if err != nil {
// return fmt.Errorf("error encoding JKS trust store bundle: %w", err)
// }
// // always overwrite the keystore entry
// secret.Data[jksTruststoreKey] = truststoreData
// }
// }
// return nil
// }
// // setAdditionalOutputFormat will set extra Secret Data keys with additional
// // output formats according to any OutputFormats which have been configured.
// func setAdditionalOutputFormats(crt *acmapi.Certificate, secret *corev1.Secret, data SecretData) error {
// for _, format := range crt.Spec.AdditionalOutputFormats {
// switch format.Type {
// case cmapi.CertificateOutputFormatDER:
// // Store binary format of the private key
// secret.Data[cmapi.CertificateOutputFormatDERKey] = certificates.OutputFormatDER(data.PrivateKey)
// case cmapi.CertificateOutputFormatCombinedPEM:
// // Combine tls.key and tls.crt
// secret.Data[cmapi.CertificateOutputFormatCombinedPEMKey] = certificates.OutputFormatCombinedPEM(data.PrivateKey, data.Certificate)
// default:
// return fmt.Errorf("unknown additional output format %s", format.Type)
// }
// }
// return nil
// }