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  }