mirror of
https://github.com/cloudflare/cloudflared.git
synced 2026-10-12 02:12:14 +00:00
TUN-10725: Remove unused certificate configuration
This commit is contained in:
committed by
João "Pisco" Fernandes
parent
a9c0d487b9
commit
001e9aa9cf
@@ -1,3 +1,4 @@
|
||||
// Package tlsconfig builds base TLS configuration for edge and origin connections.
|
||||
package tlsconfig
|
||||
|
||||
import (
|
||||
@@ -6,9 +7,7 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"runtime"
|
||||
"sync"
|
||||
|
||||
"github.com/getsentry/sentry-go"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/rs/zerolog"
|
||||
)
|
||||
@@ -17,59 +16,6 @@ const (
|
||||
OriginCAPoolFlag = "origin-ca-pool"
|
||||
)
|
||||
|
||||
// CertReloader can load and reload a TLS certificate from a particular filepath.
|
||||
// Hooks into tls.Config's GetCertificate to allow a TLS server to update its certificate without restarting.
|
||||
type CertReloader struct {
|
||||
sync.Mutex
|
||||
certificate *tls.Certificate
|
||||
certPath string
|
||||
keyPath string
|
||||
}
|
||||
|
||||
// NewCertReloader makes a CertReloader. It loads the cert during initialization to make sure certPath and keyPath are valid
|
||||
func NewCertReloader(certPath, keyPath string) (*CertReloader, error) {
|
||||
cr := new(CertReloader)
|
||||
cr.certPath = certPath
|
||||
cr.keyPath = keyPath
|
||||
if err := cr.LoadCert(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return cr, nil
|
||||
}
|
||||
|
||||
// Cert returns the TLS certificate most recently read by the CertReloader.
|
||||
// This method works as a direct utility method for tls.Config#Cert.
|
||||
func (cr *CertReloader) Cert(clientHello *tls.ClientHelloInfo) (*tls.Certificate, error) {
|
||||
cr.Lock()
|
||||
defer cr.Unlock()
|
||||
return cr.certificate, nil
|
||||
}
|
||||
|
||||
// ClientCert returns the TLS certificate most recently read by the CertReloader.
|
||||
// This method works as a direct utility method for tls.Config#ClientCert.
|
||||
func (cr *CertReloader) ClientCert(certRequestInfo *tls.CertificateRequestInfo) (*tls.Certificate, error) {
|
||||
cr.Lock()
|
||||
defer cr.Unlock()
|
||||
return cr.certificate, nil
|
||||
}
|
||||
|
||||
// LoadCert loads a TLS certificate from the CertReloader's specified filepath.
|
||||
// Call this after writing a new certificate to the disk (e.g. after renewing a certificate)
|
||||
func (cr *CertReloader) LoadCert() error {
|
||||
cr.Lock()
|
||||
defer cr.Unlock()
|
||||
|
||||
cert, err := tls.LoadX509KeyPair(cr.certPath, cr.keyPath)
|
||||
|
||||
// Keep the old certificate if there's a problem reading the new one.
|
||||
if err != nil {
|
||||
sentry.CaptureException(fmt.Errorf("error parsing X509 key pair: %v", err))
|
||||
return err
|
||||
}
|
||||
cr.certificate = &cert
|
||||
return nil
|
||||
}
|
||||
|
||||
func LoadOriginCA(originCAPoolFilename string, log *zerolog.Logger) (*x509.CertPool, error) {
|
||||
var originCustomCAPool []byte
|
||||
|
||||
@@ -128,15 +74,18 @@ func LoadCustomOriginCA(originCAFilename string) (*x509.CertPool, error) {
|
||||
}
|
||||
|
||||
func CreateTunnelConfig(caCert string, serverName string) (*tls.Config, error) {
|
||||
var rootCAs []string
|
||||
tlsConfig := &tls.Config{ServerName: serverName}
|
||||
if caCert != "" {
|
||||
rootCAs = append(rootCAs, caCert)
|
||||
}
|
||||
caCertPEM, err := os.ReadFile(caCert) //nolint:gosec
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("read CA certificate %s: %w", caCert, err)
|
||||
}
|
||||
|
||||
userConfig := &TLSParameters{RootCAs: rootCAs, ServerName: serverName}
|
||||
tlsConfig, err := GetConfig(userConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
rootCAPool := x509.NewCertPool()
|
||||
if !rootCAPool.AppendCertsFromPEM(caCertPEM) {
|
||||
return nil, fmt.Errorf("parse CA certificate %s", caCert)
|
||||
}
|
||||
tlsConfig.RootCAs = rootCAPool
|
||||
}
|
||||
|
||||
if tlsConfig.RootCAs == nil {
|
||||
@@ -1,102 +0,0 @@
|
||||
// Package tlsconfig provides convenience functions for configuring TLS connections from the
|
||||
// command line.
|
||||
package tlsconfig
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"os"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// Config is the user provided parameters to create a tls.Config
|
||||
type TLSParameters struct {
|
||||
Cert string
|
||||
Key string
|
||||
GetCertificate *CertReloader
|
||||
GetClientCertificate *CertReloader
|
||||
ClientCAs []string
|
||||
RootCAs []string
|
||||
ServerName string
|
||||
CurvePreferences []tls.CurveID
|
||||
MinVersion uint16 // min tls version. If zero, TLS1.0 is defined as minimum.
|
||||
MaxVersion uint16 // max tls version. If zero, last TLS version is used defined as limit (currently TLS1.3)
|
||||
}
|
||||
|
||||
// GetConfig returns a TLS configuration according to the Config set by the user.
|
||||
func GetConfig(p *TLSParameters) (*tls.Config, error) {
|
||||
tlsconfig := &tls.Config{}
|
||||
if p.Cert != "" && p.Key != "" {
|
||||
cert, err := tls.LoadX509KeyPair(p.Cert, p.Key)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "Error parsing X509 key pair")
|
||||
}
|
||||
tlsconfig.Certificates = []tls.Certificate{cert}
|
||||
// BuildNameToCertificate parses Certificates and builds NameToCertificate from common name
|
||||
// and SAN fields of leaf certificates
|
||||
tlsconfig.BuildNameToCertificate()
|
||||
}
|
||||
|
||||
if p.GetCertificate != nil {
|
||||
// GetCertificate is called when client supplies SNI info or Certificates is empty.
|
||||
// Order of retrieving certificate is GetCertificate, NameToCertificate and lastly first element of Certificates
|
||||
tlsconfig.GetCertificate = p.GetCertificate.Cert
|
||||
}
|
||||
|
||||
if p.GetClientCertificate != nil {
|
||||
// GetClientCertificate is called when using an HTTP client library and mTLS is required.
|
||||
tlsconfig.GetClientCertificate = p.GetClientCertificate.ClientCert
|
||||
}
|
||||
|
||||
if len(p.ClientCAs) > 0 {
|
||||
// set of root certificate authorities that servers use if required to verify a client certificate
|
||||
// by the policy in ClientAuth
|
||||
clientCAs, err := LoadCert(p.ClientCAs)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "Error loading client CAs")
|
||||
}
|
||||
tlsconfig.ClientCAs = clientCAs
|
||||
// server's policy for TLS Client Authentication. Default is no client cert
|
||||
tlsconfig.ClientAuth = tls.RequireAndVerifyClientCert
|
||||
}
|
||||
|
||||
if len(p.RootCAs) > 0 {
|
||||
rootCAs, err := LoadCert(p.RootCAs)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "Error loading root CAs")
|
||||
}
|
||||
tlsconfig.RootCAs = rootCAs
|
||||
}
|
||||
|
||||
if p.ServerName != "" {
|
||||
tlsconfig.ServerName = p.ServerName
|
||||
}
|
||||
|
||||
if len(p.CurvePreferences) > 0 {
|
||||
tlsconfig.CurvePreferences = p.CurvePreferences
|
||||
} else {
|
||||
// Cloudflare optimize CurveP256
|
||||
tlsconfig.CurvePreferences = []tls.CurveID{tls.CurveP256}
|
||||
}
|
||||
|
||||
tlsconfig.MinVersion = p.MinVersion
|
||||
tlsconfig.MaxVersion = p.MaxVersion
|
||||
|
||||
return tlsconfig, nil
|
||||
}
|
||||
|
||||
// LoadCert creates a CertPool containing all certificates in a PEM-format file.
|
||||
func LoadCert(certPaths []string) (*x509.CertPool, error) {
|
||||
ca := x509.NewCertPool()
|
||||
for _, certPath := range certPaths {
|
||||
caCert, err := os.ReadFile(certPath)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "Error reading certificate %s", certPath)
|
||||
}
|
||||
if !ca.AppendCertsFromPEM(caCert) {
|
||||
return nil, errors.Wrapf(err, "Error parsing certificate %s", certPath)
|
||||
}
|
||||
}
|
||||
return ca, nil
|
||||
}
|
||||
+11
-68
@@ -1,82 +1,25 @@
|
||||
package tlsconfig
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// testcert.pem and testcert2.pem are Generated using `openssl req -newkey rsa:512 -nodes -x509 -days 3650`
|
||||
const (
|
||||
testcertCommonName = "localhost"
|
||||
)
|
||||
|
||||
func TestGetFromEmptyConfig(t *testing.T) {
|
||||
c := &TLSParameters{}
|
||||
|
||||
tlsConfig, err := GetConfig(c)
|
||||
assert.NoError(t, err)
|
||||
assert.Empty(t, tlsConfig.Certificates)
|
||||
|
||||
assert.Empty(t, tlsConfig.NameToCertificate)
|
||||
|
||||
assert.Nil(t, tlsConfig.ClientCAs)
|
||||
assert.Equal(t, tls.NoClientCert, tlsConfig.ClientAuth)
|
||||
|
||||
assert.Nil(t, tlsConfig.RootCAs)
|
||||
|
||||
assert.Len(t, tlsConfig.CurvePreferences, 1)
|
||||
assert.Equal(t, tls.CurveP256, tlsConfig.CurvePreferences[0])
|
||||
}
|
||||
|
||||
func TestGetConfig(t *testing.T) {
|
||||
cert, err := tls.LoadX509KeyPair("testcert.pem", "testkey.pem")
|
||||
assert.NoError(t, err)
|
||||
|
||||
c := &TLSParameters{
|
||||
Cert: "testcert.pem",
|
||||
Key: "testkey.pem",
|
||||
ClientCAs: []string{"testcert.pem", "testcert2.pem"},
|
||||
RootCAs: []string{"testcert.pem", "testcert2.pem"},
|
||||
ServerName: "test",
|
||||
CurvePreferences: []tls.CurveID{tls.CurveP384},
|
||||
}
|
||||
tlsConfig, err := GetConfig(c)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, tlsConfig.Certificates, 1)
|
||||
assert.Equal(t, cert, tlsConfig.Certificates[0])
|
||||
|
||||
assert.Equal(t, cert, *tlsConfig.NameToCertificate[testcertCommonName])
|
||||
|
||||
assert.NotNil(t, tlsConfig.ClientCAs)
|
||||
assert.Equal(t, tls.RequireAndVerifyClientCert, tlsConfig.ClientAuth)
|
||||
func TestCreateTunnelConfig(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tlsConfig, err := CreateTunnelConfig("testcert.pem", "edge.example.com")
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "edge.example.com", tlsConfig.ServerName)
|
||||
assert.NotNil(t, tlsConfig.RootCAs)
|
||||
|
||||
assert.Len(t, tlsConfig.CurvePreferences, 1)
|
||||
assert.Equal(t, tls.CurveP384, tlsConfig.CurvePreferences[0])
|
||||
assert.Empty(t, tlsConfig.CurvePreferences)
|
||||
}
|
||||
|
||||
func TestCertReloader(t *testing.T) {
|
||||
expectedCert, err := tls.LoadX509KeyPair("testcert.pem", "testkey.pem")
|
||||
assert.NoError(t, err)
|
||||
func TestCreateTunnelConfigRequiresServerName(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
certReloader, err := NewCertReloader("testcert.pem", "testkey.pem")
|
||||
assert.NoError(t, err)
|
||||
|
||||
chi := &tls.ClientHelloInfo{ServerName: testcertCommonName}
|
||||
cert, err := certReloader.Cert(chi)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, expectedCert, *cert)
|
||||
|
||||
c := &TLSParameters{
|
||||
GetCertificate: certReloader,
|
||||
}
|
||||
tlsConfig, err := GetConfig(c)
|
||||
assert.NoError(t, err)
|
||||
|
||||
cert, err = tlsConfig.GetCertificate(chi)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, expectedCert, *cert)
|
||||
_, err := CreateTunnelConfig("testcert.pem", "")
|
||||
assert.EqualError(t, err, "either ServerName or InsecureSkipVerify must be specified in the tls.Config")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user