|  | package component | 
|  |  | 
|  | import ( | 
|  | "crypto/ed25519" | 
|  | "crypto/rand" | 
|  | "crypto/tls" | 
|  | "crypto/x509" | 
|  | "encoding/pem" | 
|  | "fmt" | 
|  | "math/big" | 
|  | "os" | 
|  | "time" | 
|  |  | 
|  | "k8s.io/klog/v2" | 
|  |  | 
|  | "source.monogon.dev/metropolis/pkg/pki" | 
|  | ) | 
|  |  | 
|  | // GetDevCerts returns paths to this component's development certificate, key | 
|  | // and CA, or panics if unavailable. | 
|  | func (c *ComponentConfig) GetDevCerts() (certPath, keyPath, caPath string) { | 
|  | klog.Infof("Using developer certificates at %s", c.DevCertsPath) | 
|  |  | 
|  | caPath = c.ensureDevCA() | 
|  | certPath, keyPath = c.ensureDevComponent() | 
|  | return | 
|  | } | 
|  |  | 
|  | // ensureDevComponent ensures that a development certificate/key exists for this | 
|  | // component and returns paths to them. This data is either read from disk if it | 
|  | // already exists, or is generated when this function is called. If any problem | 
|  | // occurs, the code panics. | 
|  | func (c *ComponentConfig) ensureDevComponent() (certPath, keyPath string) { | 
|  | caKeyPath := c.DevCertsPath + "/ca.key" | 
|  | caCertPath := c.DevCertsPath + "/ca.cert" | 
|  |  | 
|  | // Load CA. By convention, we are always called after ensureDevCA. | 
|  | ca, err := tls.LoadX509KeyPair(caCertPath, caKeyPath) | 
|  | if err != nil { | 
|  | klog.Exitf("Could not load Dev CA: %v", err) | 
|  | } | 
|  | caCert, err := x509.ParseCertificate(ca.Certificate[0]) | 
|  | if err != nil { | 
|  | klog.Exitf("Could not parse Dev CA: %v", err) | 
|  | } | 
|  |  | 
|  | // Check if we have keys already. | 
|  | keyPath = c.DevCertsPath + fmt.Sprintf("/%s.key", c.ComponentName) | 
|  | certPath = c.DevCertsPath + fmt.Sprintf("/%s.crt", c.ComponentName) | 
|  | noKey := false | 
|  | if _, err := os.Stat(keyPath); os.IsNotExist(err) { | 
|  | noKey = true | 
|  | } | 
|  | noCert := false | 
|  | if _, err := os.Stat(certPath); os.IsNotExist(err) { | 
|  | noCert = true | 
|  | } | 
|  |  | 
|  | if noKey || noCert { | 
|  | klog.Infof("Generating developer %s certificate...", c.ComponentName) | 
|  | } else { | 
|  | return | 
|  | } | 
|  |  | 
|  | // Generate key/certificate. | 
|  | cert := pki.Server([]string{ | 
|  | fmt.Sprintf("%s.local", c.ComponentName), | 
|  | }, nil) | 
|  |  | 
|  | serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 127) | 
|  | serialNumber, err := rand.Int(rand.Reader, serialNumberLimit) | 
|  | if err != nil { | 
|  | klog.Exitf("Failed to generate %s serial number: %v", c.ComponentName, err) | 
|  | } | 
|  | cert.ExtKeyUsage = append(cert.ExtKeyUsage, x509.ExtKeyUsageClientAuth) | 
|  | cert.SerialNumber = serialNumber | 
|  | cert.NotBefore = time.Now() | 
|  | cert.NotAfter = pki.UnknownNotAfter | 
|  | cert.BasicConstraintsValid = true | 
|  |  | 
|  | pub, priv, err := ed25519.GenerateKey(rand.Reader) | 
|  | if err != nil { | 
|  | klog.Exitf("Failed to generate %s key: %v", c.ComponentName, err) | 
|  | } | 
|  | certBytes, err := x509.CreateCertificate(rand.Reader, &cert, caCert, pub, ca.PrivateKey) | 
|  | if err != nil { | 
|  | klog.Exitf("Failed to generate %s certificate: %v", c.ComponentName, err) | 
|  | } | 
|  |  | 
|  | // And marshal them to disk. | 
|  | privPKCS, err := x509.MarshalPKCS8PrivateKey(priv) | 
|  | if err != nil { | 
|  | klog.Exitf("Failed to marshal %s private key: %v", c.ComponentName, err) | 
|  | } | 
|  | err = os.WriteFile(keyPath, pem.EncodeToMemory(&pem.Block{ | 
|  | Type:  "PRIVATE KEY", | 
|  | Bytes: privPKCS, | 
|  | }), 0600) | 
|  | if err != nil { | 
|  | klog.Exitf("Failed to write %s private key: %v", c.ComponentName, err) | 
|  | } | 
|  | err = os.WriteFile(certPath, pem.EncodeToMemory(&pem.Block{ | 
|  | Type:  "CERTIFICATE", | 
|  | Bytes: certBytes, | 
|  | }), 0644) | 
|  | if err != nil { | 
|  | klog.Exitf("Failed to write %s certificate: %v", c.ComponentName, err) | 
|  | } | 
|  |  | 
|  | return | 
|  | } | 
|  |  | 
|  | // ensureDevCA ensures that a development CA certificate/key exists and returns | 
|  | // paths to them. This data is either read from disk if it already exists, or is | 
|  | // generated when this function is called. If any problem occurs, the code | 
|  | // panics. | 
|  | func (c *ComponentConfig) ensureDevCA() (caCertPath string) { | 
|  | caKeyPath := c.DevCertsPath + "/ca.key" | 
|  | caCertPath = c.DevCertsPath + "/ca.cert" | 
|  |  | 
|  | if err := os.MkdirAll(c.DevCertsPath, 0700); err != nil { | 
|  | klog.Exitf("Failed to make developer certificate directory: %v", err) | 
|  | } | 
|  |  | 
|  | // Check if we already have a key/certificate. | 
|  | noKey := false | 
|  | if _, err := os.Stat(caKeyPath); os.IsNotExist(err) { | 
|  | noKey = true | 
|  | } | 
|  | noCert := false | 
|  | if _, err := os.Stat(caCertPath); os.IsNotExist(err) { | 
|  | noCert = true | 
|  | } | 
|  |  | 
|  | if noKey || noCert { | 
|  | klog.Infof("Generating developer CA certificate...") | 
|  | } else { | 
|  | return | 
|  | } | 
|  | hostname, err := os.Hostname() | 
|  | if err != nil { | 
|  | hostname = "unknown" | 
|  | } | 
|  |  | 
|  | // No key/certificate, generate them. | 
|  | ca := pki.CA(fmt.Sprintf("monogon dev certs CA (%s)", hostname)) | 
|  |  | 
|  | serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 127) | 
|  | serialNumber, err := rand.Int(rand.Reader, serialNumberLimit) | 
|  | if err != nil { | 
|  | klog.Exitf("Failed to generate CA serial number: %v", err) | 
|  | } | 
|  | ca.SerialNumber = serialNumber | 
|  | ca.NotBefore = time.Now() | 
|  | ca.NotAfter = pki.UnknownNotAfter | 
|  | ca.BasicConstraintsValid = true | 
|  |  | 
|  | caPub, caPriv, err := ed25519.GenerateKey(rand.Reader) | 
|  | if err != nil { | 
|  | klog.Exitf("Failed to generate CA key: %v", err) | 
|  | } | 
|  | caBytes, err := x509.CreateCertificate(rand.Reader, &ca, &ca, caPub, caPriv) | 
|  | if err != nil { | 
|  | klog.Exitf("Failed to generate CA certificate: %v", err) | 
|  | } | 
|  |  | 
|  | // And marshal them to disk. | 
|  | caPrivPKCS, err := x509.MarshalPKCS8PrivateKey(caPriv) | 
|  | if err != nil { | 
|  | klog.Exitf("Failed to marshal %s private key: %v", c.ComponentName, err) | 
|  | } | 
|  | err = os.WriteFile(caKeyPath, pem.EncodeToMemory(&pem.Block{ | 
|  | Type:  "PRIVATE KEY", | 
|  | Bytes: caPrivPKCS, | 
|  | }), 0600) | 
|  | if err != nil { | 
|  | klog.Exitf("Failed to write CA private key: %v", err) | 
|  | } | 
|  | err = os.WriteFile(caCertPath, pem.EncodeToMemory(&pem.Block{ | 
|  | Type:  "CERTIFICATE", | 
|  | Bytes: caBytes, | 
|  | }), 0644) | 
|  | if err != nil { | 
|  | klog.Exitf("Failed to write CA certificate: %v", err) | 
|  | } | 
|  |  | 
|  | return | 
|  | } |