github.com/telepresenceio/telepresence/v2@v2.20.0-pro.6.0.20240517030216-236ea954e789/build-aux/admission_controller_tls/main.go (about)

     1  package main
     2  
     3  import (
     4  	"bytes"
     5  	cryptorand "crypto/rand"
     6  	"crypto/rsa"
     7  	"crypto/sha1"
     8  	"crypto/x509"
     9  	"crypto/x509/pkix"
    10  	"encoding/base64"
    11  	"encoding/pem"
    12  	"fmt"
    13  	"math/big"
    14  	"os"
    15  	"path/filepath"
    16  	"time"
    17  )
    18  
    19  // The program creates the crt.pem, key.pem, and ca.pem needed when
    20  // setting up the mutator webhook for agent auto-injection.
    21  func main() {
    22  	if len(os.Args) != 3 {
    23  		fmt.Fprintf(os.Stderr, "usage: %s <manager-namespace> <directory>", os.Args[0])
    24  		os.Exit(1)
    25  	}
    26  	if err := generateKeys(os.Args[1], os.Args[2]); err != nil {
    27  		fmt.Fprintln(os.Stderr, err.Error())
    28  		os.Exit(1)
    29  	}
    30  }
    31  
    32  func generateKeys(mgrNamespace, dir string) error {
    33  	err := os.MkdirAll(dir, 0o777)
    34  	if err != nil {
    35  		return fmt.Errorf("failed to create directory %q: %w", dir, err)
    36  	}
    37  	crtPem, keyPem, caPem, err := generateKeyTriplet(mgrNamespace)
    38  	if err != nil {
    39  		return err
    40  	}
    41  
    42  	if err = writeFile(dir, "ca.pem", caPem); err != nil {
    43  		return err
    44  	}
    45  
    46  	if err = writeFile(dir, "crt.pem", crtPem); err != nil {
    47  		return err
    48  	}
    49  	return writeFile(dir, "key.pem", keyPem)
    50  }
    51  
    52  // writeFile writes the file verbatim and as base64 encoded in the given directory.
    53  func writeFile(dir, file string, data []byte) error {
    54  	filePath := filepath.Join(dir, file)
    55  	f, err := os.Create(filePath)
    56  	if err != nil {
    57  		return fmt.Errorf("failed to create file %q, %w", filePath, err)
    58  	}
    59  	defer f.Close()
    60  
    61  	if _, err = f.Write(data); err != nil {
    62  		return fmt.Errorf("failed to write to file %q, %w", filePath, err)
    63  	}
    64  
    65  	filePath64 := filePath + ".base64"
    66  	f64, err := os.Create(filePath64)
    67  	if err != nil {
    68  		return fmt.Errorf("failed to create file %q, %w", filePath64, err)
    69  	}
    70  	defer f64.Close()
    71  
    72  	enc := base64.NewEncoder(base64.StdEncoding, f64)
    73  	defer enc.Close()
    74  	if _, err = enc.Write(data); err != nil {
    75  		return fmt.Errorf("failed to write to file %q, %w", filePath64, err)
    76  	}
    77  	return nil
    78  }
    79  
    80  // generateKeyTriplet creates the crt.pem, key.pem, and ca.pem needed when
    81  // setting up the mutator webhook for agent auto-injection.
    82  func generateKeyTriplet(mgrNamespace string) (crtPem, keyPem, caPem []byte, err error) {
    83  	caCert := &x509.Certificate{
    84  		SerialNumber: big.NewInt(0xefecab0),
    85  		Subject: pkix.Name{
    86  			Organization: []string{"getambassador.io"},
    87  		},
    88  		NotBefore:             time.Now(),
    89  		NotAfter:              time.Now().AddDate(1, 0, 0),
    90  		IsCA:                  true,
    91  		ExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth},
    92  		KeyUsage:              x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
    93  		BasicConstraintsValid: true,
    94  	}
    95  
    96  	caPrivKey, err := rsa.GenerateKey(cryptorand.Reader, 4096)
    97  	if err != nil {
    98  		return nil, nil, nil, fmt.Errorf("failed to generate CA private key: %w", err)
    99  	}
   100  	caBytes, err := x509.CreateCertificate(cryptorand.Reader, caCert, caCert, &caPrivKey.PublicKey, caPrivKey)
   101  	if err != nil {
   102  		return nil, nil, nil, fmt.Errorf("failed to generate CA certificate: %w", err)
   103  	}
   104  	if caPem, err = ToPEM("ca.pem", "CERTIFICATE", caBytes); err != nil {
   105  		return nil, nil, nil, err
   106  	}
   107  
   108  	commonName := fmt.Sprintf("agent-injector.%s.svc", mgrNamespace)
   109  	dnsNames := []string{"agent-injector", "agent-injector." + mgrNamespace, commonName}
   110  
   111  	// server cert config
   112  	cert := &x509.Certificate{
   113  		DNSNames:     dnsNames,
   114  		SerialNumber: big.NewInt(0xefecab1),
   115  		Subject: pkix.Name{
   116  			CommonName:   commonName,
   117  			Organization: []string{"getambassador.io"},
   118  		},
   119  		NotBefore:    time.Now(),
   120  		NotAfter:     time.Now().AddDate(10, 0, 0), // Valid 10 years
   121  		SubjectKeyId: bigIntHash(caPrivKey.N),
   122  		ExtKeyUsage:  []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth},
   123  		KeyUsage:     x509.KeyUsageDigitalSignature,
   124  	}
   125  
   126  	serverPrivateKey, err := rsa.GenerateKey(cryptorand.Reader, 4096)
   127  	if err != nil {
   128  		return nil, nil, nil, fmt.Errorf("failed to server private key: %w", err)
   129  	}
   130  
   131  	serverCert, err := x509.CreateCertificate(cryptorand.Reader, cert, caCert, &serverPrivateKey.PublicKey, caPrivKey)
   132  	if err != nil {
   133  		return nil, nil, nil, fmt.Errorf("failed to sign the server certificate: %w", err)
   134  	}
   135  
   136  	if keyPem, err = ToPEM("key.pem", "RSA PRIVATE KEY", x509.MarshalPKCS1PrivateKey(serverPrivateKey)); err != nil {
   137  		return nil, nil, nil, err
   138  	}
   139  	if crtPem, err = ToPEM("crt.pem", "CERTIFICATE", serverCert); err != nil {
   140  		return nil, nil, nil, err
   141  	}
   142  	return crtPem, keyPem, caPem, nil
   143  }
   144  
   145  func bigIntHash(n *big.Int) []byte {
   146  	h := sha1.New()
   147  	_, _ = h.Write(n.Bytes())
   148  	return h.Sum(nil)
   149  }
   150  
   151  // ToPEM returns the PEM encoding of data.
   152  func ToPEM(file, keyType string, data []byte) ([]byte, error) {
   153  	wrt := bytes.Buffer{}
   154  	if err := pem.Encode(&wrt, &pem.Block{Type: keyType, Bytes: data}); err != nil {
   155  		return nil, fmt.Errorf("failed to PEM encode %s %s: %w", keyType, file, err)
   156  	}
   157  	return wrt.Bytes(), nil
   158  }