// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
package autoconf
import (
"context"
"fmt"
"net"
"github.com/hashicorp/consul/agent/cache"
cachetype "github.com/hashicorp/consul/agent/cache-types"
"github.com/hashicorp/consul/agent/connect"
"github.com/hashicorp/consul/agent/leafcert"
"github.com/hashicorp/consul/agent/structs"
"github.com/hashicorp/consul/proto/private/pbautoconf"
"github.com/hashicorp/consul/proto/private/pbconnect"
)
const (
// ID of the roots watch
rootsWatchID = "roots"
// ID of the leaf watch
leafWatchID = "leaf"
unknownTrustDomain = "unknown"
)
var (
defaultDNSSANs = [ ] string { "localhost" }
defaultIPSANs = [ ] net . IP { { 127 , 0 , 0 , 1 } , net . ParseIP ( "::1" ) }
)
func extractPEMs ( roots * structs . IndexedCARoots ) [ ] string {
var pems [ ] string
for _ , root := range roots . Roots {
pems = append ( pems , root . RootCert )
}
return pems
}
// updateTLSFromResponse will update the TLS certificate and roots in the shared
// TLS configurator.
func ( ac * AutoConfig ) updateTLSFromResponse ( resp * pbautoconf . AutoConfigResponse ) error {
var pems [ ] string
for _ , root := range resp . GetCARoots ( ) . GetRoots ( ) {
pems = append ( pems , root . RootCert )
}
err := ac . acConfig . TLSConfigurator . UpdateAutoTLS (
resp . ExtraCACertificates ,
pems ,
resp . Certificate . GetCertPEM ( ) ,
resp . Certificate . GetPrivateKeyPEM ( ) ,
resp . Config . GetTLS ( ) . GetVerifyServerHostname ( ) ,
)
if err != nil {
return fmt . Errorf ( "Failed to update the TLS configurator with new certificates: %w" , err )
}
return nil
}
func ( ac * AutoConfig ) setInitialTLSCertificates ( certs * structs . SignedResponse ) error {
if certs == nil {
return nil
}
if err := ac . populateCertificateCache ( certs ) ; err != nil {
return fmt . Errorf ( "error populating cache with certificates: %w" , err )
}
connectCAPems := extractPEMs ( & certs . ConnectCARoots )
err := ac . acConfig . TLSConfigurator . UpdateAutoTLS (
certs . ManualCARoots ,
connectCAPems ,
certs . IssuedCert . CertPEM ,
certs . IssuedCert . PrivateKeyPEM ,
certs . VerifyServerHostname ,
)
if err != nil {
return fmt . Errorf ( "error updating TLS configurator with certificates: %w" , err )
}
return nil
}
func ( ac * AutoConfig ) populateCertificateCache ( certs * structs . SignedResponse ) error {
cert , err := connect . ParseCert ( certs . IssuedCert . CertPEM )
if err != nil {
return fmt . Errorf ( "Failed to parse certificate: %w" , err )
}
// prepolutate roots cache
rootRes := cache . FetchResult { Value : & certs . ConnectCARoots , Index : certs . ConnectCARoots . QueryMeta . Index }
rootsReq := ac . caRootsRequest ( )
// getting the roots doesn't require a token so in order to potentially share the cache with another
peering: initial sync (#12842)
- Add endpoints related to peering: read, list, generate token, initiate peering
- Update node/service/check table indexing to account for peers
- Foundational changes for pushing service updates to a peer
- Plumb peer name through Health.ServiceNodes path
see: ENT-1765, ENT-1280, ENT-1283, ENT-1283, ENT-1756, ENT-1739, ENT-1750, ENT-1679,
ENT-1709, ENT-1704, ENT-1690, ENT-1689, ENT-1702, ENT-1701, ENT-1683, ENT-1663,
ENT-1650, ENT-1678, ENT-1628, ENT-1658, ENT-1640, ENT-1637, ENT-1597, ENT-1634,
ENT-1613, ENT-1616, ENT-1617, ENT-1591, ENT-1588, ENT-1596, ENT-1572, ENT-1555
Co-authored-by: R.B. Boyer <rb@hashicorp.com>
Co-authored-by: freddygv <freddy@hashicorp.com>
Co-authored-by: Chris S. Kim <ckim@hashicorp.com>
Co-authored-by: Evan Culver <eculver@hashicorp.com>
Co-authored-by: Nitya Dhanushkodi <nitya@hashicorp.com>
3 years ago
if err := ac . acConfig . Cache . Prepopulate ( cachetype . ConnectCARootName , rootRes , ac . config . Datacenter , structs . DefaultPeerKeyword , "" , rootsReq . CacheInfo ( ) . Key ) ; err != nil {
return err
}
leafReq := ac . leafCertRequest ( )
// prepolutate leaf cache
err = ac . acConfig . LeafCertManager . Prepopulate (
context . Background ( ) ,
leafReq . Key ( ) ,
certs . IssuedCert . RaftIndex . ModifyIndex ,
& certs . IssuedCert ,
connect . EncodeSigningKeyID ( cert . AuthorityKeyId ) ,
)
if err != nil {
return err
}
return nil
}
func ( ac * AutoConfig ) setupCertificateCacheWatches ( ctx context . Context ) ( context . CancelFunc , error ) {
notificationCtx , cancel := context . WithCancel ( ctx )
rootsReq := ac . caRootsRequest ( )
err := ac . acConfig . Cache . Notify ( notificationCtx , cachetype . ConnectCARootName , & rootsReq , rootsWatchID , ac . cacheUpdates )
if err != nil {
cancel ( )
return nil , err
}
leafReq := ac . leafCertRequest ( )
err = ac . acConfig . LeafCertManager . Notify ( notificationCtx , & leafReq , leafWatchID , ac . cacheUpdates )
if err != nil {
cancel ( )
return nil , err
}
return cancel , nil
}
func ( ac * AutoConfig ) updateCARoots ( roots * structs . IndexedCARoots ) error {
switch {
case ac . config . AutoConfig . Enabled :
ac . Lock ( )
defer ac . Unlock ( )
var err error
ac . autoConfigResponse . CARoots , err = pbconnect . NewCARootsFromStructs ( roots )
if err != nil {
return err
}
if err := ac . updateTLSFromResponse ( ac . autoConfigResponse ) ; err != nil {
return err
}
return ac . persistAutoConfig ( ac . autoConfigResponse )
case ac . config . AutoEncryptTLS :
pems := extractPEMs ( roots )
if err := ac . acConfig . TLSConfigurator . UpdateAutoTLSCA ( pems ) ; err != nil {
return fmt . Errorf ( "failed to update Connect CA certificates: %w" , err )
}
return nil
default :
return nil
}
}
func ( ac * AutoConfig ) updateLeafCert ( cert * structs . IssuedCert ) error {
switch {
case ac . config . AutoConfig . Enabled :
ac . Lock ( )
defer ac . Unlock ( )
var err error
ac . autoConfigResponse . Certificate , err = pbconnect . NewIssuedCertFromStructs ( cert )
if err != nil {
return err
}
if err := ac . updateTLSFromResponse ( ac . autoConfigResponse ) ; err != nil {
return err
}
return ac . persistAutoConfig ( ac . autoConfigResponse )
case ac . config . AutoEncryptTLS :
if err := ac . acConfig . TLSConfigurator . UpdateAutoTLSCert ( cert . CertPEM , cert . PrivateKeyPEM ) ; err != nil {
return fmt . Errorf ( "failed to update the agent leaf cert: %w" , err )
}
return nil
default :
return nil
}
}
func ( ac * AutoConfig ) caRootsRequest ( ) structs . DCSpecificRequest {
return structs . DCSpecificRequest { Datacenter : ac . config . Datacenter }
}
func ( ac * AutoConfig ) leafCertRequest ( ) leafcert . ConnectCALeafRequest {
return leafcert . ConnectCALeafRequest {
Datacenter : ac . config . Datacenter ,
Agent : ac . config . NodeName ,
DNSSAN : ac . getDNSSANs ( ) ,
IPSAN : ac . getIPSANs ( ) ,
Token : ac . acConfig . Tokens . AgentToken ( ) ,
EnterpriseMeta : * structs . NodeEnterpriseMetaInPartition ( ac . config . PartitionOrEmpty ( ) ) ,
}
}
// generateCSR will generate a CSR for an Agent certificate. This should
// be sent along with the AutoConfig.InitialConfiguration RPC or the
// AutoEncrypt.Sign RPC. The generated CSR does NOT have a real trust domain
// as when generating this we do not yet have the CA roots. The server will
// update the trust domain for us though.
func ( ac * AutoConfig ) generateCSR ( ) ( csr string , key string , err error ) {
// We don't provide the correct host here, because we don't know any
// better at this point. Apart from the domain, we would need the
// ClusterID, which we don't have. This is why we go with
// unknownTrustDomain the first time. Subsequent CSRs will have the
// correct TrustDomain.
id := & connect . SpiffeIDAgent {
// will be replaced
Host : unknownTrustDomain ,
Datacenter : ac . config . Datacenter ,
Agent : ac . config . NodeName ,
Partition : ac . config . PartitionOrDefault ( ) ,
}
caConfig , err := ac . config . ConnectCAConfiguration ( )
if err != nil {
return "" , "" , fmt . Errorf ( "Cannot generate CSR: %w" , err )
}
conf , err := caConfig . GetCommonConfig ( )
if err != nil {
return "" , "" , fmt . Errorf ( "Failed to load common CA configuration: %w" , err )
}
if conf . PrivateKeyType == "" {
conf . PrivateKeyType = connect . DefaultPrivateKeyType
}
if conf . PrivateKeyBits == 0 {
// If using an RSA key, a key size of at least 2048 bits is recommended; 4096 bits is better.
if conf . PrivateKeyType == connect . PrivateKeyTypeRSA {
conf . PrivateKeyBits = connect . DefaultPrivateKeyBitsRSA
} else {
conf . PrivateKeyBits = connect . DefaultPrivateKeyBits
}
}
// Create a new private key
pk , pkPEM , err := connect . GeneratePrivateKeyWithConfig ( conf . PrivateKeyType , conf . PrivateKeyBits )
if err != nil {
return "" , "" , fmt . Errorf ( "Failed to generate private key: %w" , err )
}
dnsNames := ac . getDNSSANs ( )
ipAddresses := ac . getIPSANs ( )
// Create a CSR.
csr , err = connect . CreateCSR ( id , pk , dnsNames , ipAddresses )
if err != nil {
return "" , "" , err
}
return csr , pkPEM , nil
}
func ( ac * AutoConfig ) getDNSSANs ( ) [ ] string {
sans := defaultDNSSANs
switch {
case ac . config . AutoConfig . Enabled :
sans = append ( sans , ac . config . AutoConfig . DNSSANs ... )
case ac . config . AutoEncryptTLS :
sans = append ( sans , ac . config . AutoEncryptDNSSAN ... )
}
return sans
}
func ( ac * AutoConfig ) getIPSANs ( ) [ ] net . IP {
sans := defaultIPSANs
switch {
case ac . config . AutoConfig . Enabled :
sans = append ( sans , ac . config . AutoConfig . IPSANs ... )
case ac . config . AutoEncryptTLS :
sans = append ( sans , ac . config . AutoEncryptIPSAN ... )
}
return sans
}