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 }