...

Source file src/github.com/redhat-developer/odo/pkg/kclient/secrets.go

Documentation: github.com/redhat-developer/odo/pkg/kclient

     1  package kclient
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"crypto/rand"
     7  	"crypto/rsa"
     8  	"crypto/x509"
     9  	"crypto/x509/pkix"
    10  	"encoding/pem"
    11  	"fmt"
    12  	"math/big"
    13  	"strings"
    14  	"time"
    15  
    16  	"k8s.io/apimachinery/pkg/fields"
    17  	"k8s.io/klog"
    18  
    19  	corev1 "k8s.io/api/core/v1"
    20  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    21  )
    22  
    23  // ComponentPortAnnotationName annotation is used on the secrets that are created for each exposed port of the component
    24  const ComponentPortAnnotationName = "component-port"
    25  
    26  var SecretGVK = corev1.SchemeGroupVersion.WithKind("Secret")
    27  
    28  // CreateTLSSecret creates a TLS Secret with the given certificate and private key
    29  // serviceName is the name of the service for the target reference
    30  // ingressDomain is the ingress domain to use for the ingress
    31  func (c *Client) CreateTLSSecret(tlsCertificate []byte, tlsPrivKey []byte, objectMeta metav1.ObjectMeta) (*corev1.Secret, error) {
    32  	if objectMeta.Name == "" {
    33  		return nil, fmt.Errorf("tlsSecret name is empty")
    34  	}
    35  	data := make(map[string][]byte)
    36  	data["tls.crt"] = tlsCertificate
    37  	data["tls.key"] = tlsPrivKey
    38  	secretTemplate := corev1.Secret{
    39  		ObjectMeta: objectMeta,
    40  		Type:       corev1.SecretTypeTLS,
    41  		Data:       data,
    42  	}
    43  
    44  	secret, err := c.KubeClient.CoreV1().Secrets(c.Namespace).Create(context.TODO(), &secretTemplate, metav1.CreateOptions{FieldManager: FieldManager})
    45  	if err != nil {
    46  		return nil, fmt.Errorf("unable to create secret %s: %w", objectMeta.Name, err)
    47  	}
    48  	return secret, nil
    49  }
    50  
    51  // SelfSignedCertificate struct is the return type of function GenerateSelfSignedCertificate
    52  // CertPem is the byte array for certificate pem encode
    53  // KeyPem is the byte array for key pem encode
    54  type SelfSignedCertificate struct {
    55  	CertPem []byte
    56  	KeyPem  []byte
    57  }
    58  
    59  // GenerateSelfSignedCertificate creates a self-signed SSl certificate
    60  func GenerateSelfSignedCertificate(host string) (SelfSignedCertificate, error) {
    61  
    62  	privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
    63  	if err != nil {
    64  		return SelfSignedCertificate{}, fmt.Errorf("unable to generate rsa key: %w", err)
    65  	}
    66  	template := x509.Certificate{
    67  		SerialNumber: big.NewInt(time.Now().Unix()),
    68  		Subject: pkix.Name{
    69  			CommonName:   "Odo self-signed certificate",
    70  			Organization: []string{"Odo"},
    71  		},
    72  		NotBefore:             time.Now(),
    73  		NotAfter:              time.Now().Add(time.Hour * 24 * 365 * 10),
    74  		KeyUsage:              x509.KeyUsageCertSign | x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
    75  		BasicConstraintsValid: true,
    76  		DNSNames:              []string{"*." + host},
    77  	}
    78  
    79  	certificateDerEncoding, err := x509.CreateCertificate(rand.Reader, &template, &template, &privateKey.PublicKey, privateKey)
    80  	if err != nil {
    81  		return SelfSignedCertificate{}, fmt.Errorf("unable to create certificate: %w", err)
    82  	}
    83  	out := &bytes.Buffer{}
    84  	err = pem.Encode(out, &pem.Block{Type: "CERTIFICATE", Bytes: certificateDerEncoding})
    85  	if err != nil {
    86  		return SelfSignedCertificate{}, fmt.Errorf("unable to encode certificate: %w", err)
    87  	}
    88  	certPemEncode := out.String()
    89  	certPemByteArr := []byte(certPemEncode)
    90  
    91  	tlsPrivKeyEncoding := x509.MarshalPKCS1PrivateKey(privateKey)
    92  	err = pem.Encode(out, &pem.Block{Type: "RSA PRIVATE KEY", Bytes: tlsPrivKeyEncoding})
    93  	if err != nil {
    94  		return SelfSignedCertificate{}, fmt.Errorf("unable to encode rsa private key: %w", err)
    95  	}
    96  	keyPemEncode := out.String()
    97  	keyPemByteArr := []byte(keyPemEncode)
    98  
    99  	return SelfSignedCertificate{CertPem: certPemByteArr, KeyPem: keyPemByteArr}, nil
   100  }
   101  
   102  // GetSecret returns the Secret object in the given namespace
   103  func (c *Client) GetSecret(name, namespace string) (*corev1.Secret, error) {
   104  	secret, err := c.KubeClient.CoreV1().Secrets(namespace).Get(context.TODO(), name, metav1.GetOptions{})
   105  	if err != nil {
   106  		return nil, fmt.Errorf("unable to get the secret %s: %w", secret, err)
   107  	}
   108  	return secret, nil
   109  }
   110  
   111  // UpdateSecret updates the given Secret object in the given namespace
   112  func (c *Client) UpdateSecret(secret *corev1.Secret, namespace string) (*corev1.Secret, error) {
   113  	secret, err := c.KubeClient.CoreV1().Secrets(namespace).Update(context.TODO(), secret, metav1.UpdateOptions{})
   114  	if err != nil {
   115  		return nil, fmt.Errorf("unable to update the secret %s: %w", secret, err)
   116  	}
   117  	return secret, nil
   118  }
   119  
   120  // DeleteSecret updates the given Secret object in the given namespace
   121  func (c *Client) DeleteSecret(secretName, namespace string) error {
   122  	err := c.KubeClient.CoreV1().Secrets(namespace).Delete(context.TODO(), secretName, metav1.DeleteOptions{})
   123  	if err != nil {
   124  		return fmt.Errorf("unable to delete the secret %s: %w", secretName, err)
   125  	}
   126  	return nil
   127  }
   128  
   129  // CreateSecret generates and creates the secret
   130  // commonObjectMeta is the ObjectMeta for the service
   131  func (c *Client) CreateSecret(objectMeta metav1.ObjectMeta, data map[string]string, ownerReference metav1.OwnerReference) error {
   132  
   133  	secret := corev1.Secret{
   134  		ObjectMeta: objectMeta,
   135  		Type:       corev1.SecretTypeOpaque,
   136  		StringData: data,
   137  	}
   138  	secret.SetOwnerReferences(append(secret.GetOwnerReferences(), ownerReference))
   139  	_, err := c.KubeClient.CoreV1().Secrets(c.Namespace).Create(context.TODO(), &secret, metav1.CreateOptions{FieldManager: FieldManager})
   140  	if err != nil {
   141  		return fmt.Errorf("unable to create secret for %s: %w", objectMeta.Name, err)
   142  	}
   143  	return nil
   144  }
   145  
   146  // CreateSecrets creates a secret for each port, containing the host and port of the component
   147  // This is done so other components can later inject the secret into the environment
   148  // and have the "coordinates" to communicate with this component
   149  func (c *Client) CreateSecrets(componentName string, commonObjectMeta metav1.ObjectMeta, svc *corev1.Service, ownerReference metav1.OwnerReference) error {
   150  	originalName := commonObjectMeta.Name
   151  	for _, svcPort := range svc.Spec.Ports {
   152  		portAsString := fmt.Sprintf("%v", svcPort.Port)
   153  
   154  		// we need to create multiple secrets, so each one has to contain the port in it's name
   155  		// so we change the name of each secret by adding the port number
   156  		commonObjectMeta.Name = fmt.Sprintf("%v-%v", originalName, portAsString)
   157  
   158  		// we also add the port as an annotation to the secret
   159  		// this comes in handy when we need to "query" for the appropriate secret
   160  		// of a component based on the port
   161  		commonObjectMeta.Annotations[ComponentPortAnnotationName] = portAsString
   162  
   163  		err := c.CreateSecret(
   164  			commonObjectMeta,
   165  			map[string]string{
   166  				secretKeyName(componentName, "host"): svc.Name,
   167  				secretKeyName(componentName, "port"): portAsString,
   168  			},
   169  			ownerReference)
   170  
   171  		if err != nil {
   172  			return fmt.Errorf("unable to create Secret for %s: %w", commonObjectMeta.Name, err)
   173  		}
   174  	}
   175  
   176  	// restore the original values of the fields we changed
   177  	commonObjectMeta.Name = originalName
   178  	delete(commonObjectMeta.Annotations, ComponentPortAnnotationName)
   179  
   180  	return nil
   181  }
   182  
   183  // ListSecrets lists all the secrets based on the given label selector
   184  func (c *Client) ListSecrets(labelSelector string) ([]corev1.Secret, error) {
   185  	listOptions := metav1.ListOptions{}
   186  	if len(labelSelector) > 0 {
   187  		listOptions = metav1.ListOptions{
   188  			LabelSelector: labelSelector,
   189  		}
   190  	}
   191  
   192  	secretList, err := c.KubeClient.CoreV1().Secrets(c.Namespace).List(context.TODO(), listOptions)
   193  	if err != nil {
   194  		return nil, fmt.Errorf("unable to get secret list: %w", err)
   195  	}
   196  
   197  	return secretList.Items, nil
   198  }
   199  
   200  // WaitAndGetSecret blocks and waits until the secret is available
   201  func (c *Client) WaitAndGetSecret(name string, namespace string) (*corev1.Secret, error) {
   202  	klog.V(3).Infof("Waiting for secret %s to become available", name)
   203  
   204  	w, err := c.KubeClient.CoreV1().Secrets(namespace).Watch(context.TODO(), metav1.ListOptions{
   205  		FieldSelector: fields.Set{"metadata.name": name}.AsSelector().String(),
   206  	})
   207  	if err != nil {
   208  		return nil, fmt.Errorf("unable to watch secret: %w", err)
   209  	}
   210  	defer w.Stop()
   211  	for {
   212  		val, ok := <-w.ResultChan()
   213  		if !ok {
   214  			break
   215  		}
   216  		if e, ok := val.Object.(*corev1.Secret); ok {
   217  			klog.V(3).Infof("Secret %s now exists", e.Name)
   218  			return e, nil
   219  		}
   220  	}
   221  	return nil, fmt.Errorf("unknown error while waiting for secret '%s'", name)
   222  }
   223  
   224  func secretKeyName(componentName, baseKeyName string) string {
   225  	return fmt.Sprintf("COMPONENT_%v_%v", strings.Replace(strings.ToUpper(componentName), "-", "_", -1), strings.ToUpper(baseKeyName))
   226  }
   227  

View as plain text