github.com/google/fleetspeak@v0.1.15-0.20240426164851-4f31f62c1aea/fleetspeak/src/config/certs/server.go (about) 1 // Copyright 2019 Google LLC 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // https://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // Package certs contains utility methods for reading and verifying the 16 // certificates needed to configure a Fleetspeak installation. 17 package certs 18 19 import ( 20 "crypto/ecdsa" 21 "crypto/elliptic" 22 "crypto/rand" 23 "crypto/tls" 24 "crypto/x509" 25 "crypto/x509/pkix" 26 "encoding/pem" 27 "errors" 28 "fmt" 29 "math/big" 30 "net" 31 "os" 32 "time" 33 34 cpb "github.com/google/fleetspeak/fleetspeak/src/config/proto/fleetspeak_config" 35 ) 36 37 // GetServerCert returns the server certificate associated with cfg, creating 38 // it if necessary. If available, priv is the private key associated with cert. 39 func GetServerCert(cfg *cpb.Config, ca *x509.Certificate, caPriv any) (cert, key []byte, err error) { 40 if cfg.ServerCertFile == "" { 41 return nil, nil, errors.New("server_cert_file not set") 42 } 43 if _, err := os.Stat(cfg.ServerCertFile); err != nil { 44 if os.IsNotExist(err) { 45 // Attempt to create a server certificate. 46 if caPriv == nil { 47 return nil, nil, errors.New("unable to create server certificate: CA key not loaded") 48 } 49 if err := makeServerCert(cfg, ca, caPriv); err != nil { 50 return nil, nil, err 51 } 52 } else { 53 return nil, nil, fmt.Errorf("unable to stat server_cert_file [%s]: %v", cfg.ServerCertFile, err) 54 } 55 } 56 return getServerCert(cfg) 57 } 58 59 func makeServerCert(cfg *cpb.Config, ca *x509.Certificate, caPriv any) error { 60 if cfg.ServerCertKeyFile == "" { 61 return errors.New("unable to create a server cert: server_cert_key_file not set") 62 } 63 privKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) 64 if err != nil { 65 return fmt.Errorf("unable to create a server cert: key generation failed: %v", err) 66 } 67 serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128) 68 serialNumber, err := rand.Int(rand.Reader, serialNumberLimit) 69 if err != nil { 70 return fmt.Errorf("unable to create a server cert: serial number generation failed: %v", err) 71 } 72 tmpl := x509.Certificate{ 73 Version: 1, 74 SerialNumber: serialNumber, 75 Subject: pkix.Name{CommonName: fmt.Sprintf("%s Fleetspeak Server", cfg.ConfigurationName)}, 76 NotBefore: time.Now(), 77 NotAfter: time.Now().Add(24 * 365 * time.Hour), // 1 year from now 78 KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign, 79 IsCA: true, 80 BasicConstraintsValid: true, 81 } 82 for _, hp := range cfg.PublicHostPort { 83 h, _, err := net.SplitHostPort(hp) 84 if err != nil { 85 return fmt.Errorf("unable to create a server cert: unable to parse host-port [%s]: %v", hp, err) 86 } 87 // If it quacks like a duck. 88 if ip := net.ParseIP(h); ip != nil { 89 tmpl.IPAddresses = append(tmpl.IPAddresses, ip) 90 } else { 91 tmpl.DNSNames = append(tmpl.DNSNames, h) 92 } 93 } 94 cert, err := x509.CreateCertificate(rand.Reader, &tmpl, ca, privKey.Public(), caPriv) 95 if err != nil { 96 return fmt.Errorf("unable to create a server cert: %v", err) 97 } 98 certPEM := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: cert}) 99 100 key, err := x509.MarshalECPrivateKey(privKey) 101 if err != nil { 102 return fmt.Errorf("unable to create server cert: failed to marshal private key: %v", err) 103 } 104 keyPEM := pem.EncodeToMemory(&pem.Block{Type: "EC PRIVATE KEY", Bytes: key}) 105 106 if err := os.WriteFile(cfg.ServerCertFile, certPEM, 0644); err != nil { 107 return fmt.Errorf("failed to write server cert file [%s]: %v", cfg.ServerCertFile, err) 108 } 109 if err := os.WriteFile(cfg.ServerCertKeyFile, keyPEM, 0600); err != nil { 110 return fmt.Errorf("failed to write server key file [%s]: %v", cfg.ServerCertKeyFile, err) 111 } 112 113 return nil 114 } 115 116 func getServerCert(cfg *cpb.Config) (cert, key []byte, err error) { 117 // Read the cert. 118 certPEM, err := os.ReadFile(cfg.ServerCertFile) 119 if err != nil { 120 return nil, nil, fmt.Errorf("unable to read server certificate file [%s]: %v", cfg.ServerCertFile, err) 121 } 122 123 // Read the key. 124 keyPEM, err := os.ReadFile(cfg.ServerCertKeyFile) 125 if err != nil { 126 return nil, nil, fmt.Errorf("uanble to read the server certificate key file [%s]: %v", cfg.ServerCertKeyFile, err) 127 } 128 129 // Direct check that the server will be able to instantiate a tls 130 // configuration using this PEM data. In particular, this fails if the 131 // key does not match the cert. 132 if _, err := tls.X509KeyPair(certPEM, keyPEM); err != nil { 133 return nil, nil, fmt.Errorf("error in server certificate/key [%s]/[%s] configuration: %v", cfg.ServerCertFile, cfg.ServerCertKeyFile, err) 134 } 135 return certPEM, keyPEM, nil 136 }