1package autoconf 2 3import ( 4 "context" 5 "fmt" 6 "net" 7 8 "github.com/hashicorp/consul/agent/cache" 9 cachetype "github.com/hashicorp/consul/agent/cache-types" 10 "github.com/hashicorp/consul/agent/connect" 11 "github.com/hashicorp/consul/agent/structs" 12 "github.com/hashicorp/consul/proto/pbautoconf" 13) 14 15const ( 16 // ID of the roots watch 17 rootsWatchID = "roots" 18 19 // ID of the leaf watch 20 leafWatchID = "leaf" 21 22 unknownTrustDomain = "unknown" 23) 24 25var ( 26 defaultDNSSANs = []string{"localhost"} 27 28 defaultIPSANs = []net.IP{{127, 0, 0, 1}, net.ParseIP("::1")} 29) 30 31func extractPEMs(roots *structs.IndexedCARoots) []string { 32 var pems []string 33 for _, root := range roots.Roots { 34 pems = append(pems, root.RootCert) 35 } 36 return pems 37} 38 39// updateTLSFromResponse will update the TLS certificate and roots in the shared 40// TLS configurator. 41func (ac *AutoConfig) updateTLSFromResponse(resp *pbautoconf.AutoConfigResponse) error { 42 var pems []string 43 for _, root := range resp.GetCARoots().GetRoots() { 44 pems = append(pems, root.RootCert) 45 } 46 47 err := ac.acConfig.TLSConfigurator.UpdateAutoTLS( 48 resp.ExtraCACertificates, 49 pems, 50 resp.Certificate.GetCertPEM(), 51 resp.Certificate.GetPrivateKeyPEM(), 52 resp.Config.GetTLS().GetVerifyServerHostname(), 53 ) 54 55 if err != nil { 56 return fmt.Errorf("Failed to update the TLS configurator with new certificates: %w", err) 57 } 58 59 return nil 60} 61 62func (ac *AutoConfig) setInitialTLSCertificates(certs *structs.SignedResponse) error { 63 if certs == nil { 64 return nil 65 } 66 67 if err := ac.populateCertificateCache(certs); err != nil { 68 return fmt.Errorf("error populating cache with certificates: %w", err) 69 } 70 71 connectCAPems := extractPEMs(&certs.ConnectCARoots) 72 73 err := ac.acConfig.TLSConfigurator.UpdateAutoTLS( 74 certs.ManualCARoots, 75 connectCAPems, 76 certs.IssuedCert.CertPEM, 77 certs.IssuedCert.PrivateKeyPEM, 78 certs.VerifyServerHostname, 79 ) 80 81 if err != nil { 82 return fmt.Errorf("error updating TLS configurator with certificates: %w", err) 83 } 84 85 return nil 86} 87 88func (ac *AutoConfig) populateCertificateCache(certs *structs.SignedResponse) error { 89 cert, err := connect.ParseCert(certs.IssuedCert.CertPEM) 90 if err != nil { 91 return fmt.Errorf("Failed to parse certificate: %w", err) 92 } 93 94 // prepolutate roots cache 95 rootRes := cache.FetchResult{Value: &certs.ConnectCARoots, Index: certs.ConnectCARoots.QueryMeta.Index} 96 rootsReq := ac.caRootsRequest() 97 // getting the roots doesn't require a token so in order to potentially share the cache with another 98 if err := ac.acConfig.Cache.Prepopulate(cachetype.ConnectCARootName, rootRes, ac.config.Datacenter, "", rootsReq.CacheInfo().Key); err != nil { 99 return err 100 } 101 102 leafReq := ac.leafCertRequest() 103 104 // prepolutate leaf cache 105 certRes := cache.FetchResult{ 106 Value: &certs.IssuedCert, 107 Index: certs.IssuedCert.RaftIndex.ModifyIndex, 108 State: cachetype.ConnectCALeafSuccess(connect.EncodeSigningKeyID(cert.AuthorityKeyId)), 109 } 110 if err := ac.acConfig.Cache.Prepopulate(cachetype.ConnectCALeafName, certRes, leafReq.Datacenter, leafReq.Token, leafReq.Key()); err != nil { 111 return err 112 } 113 114 return nil 115} 116 117func (ac *AutoConfig) setupCertificateCacheWatches(ctx context.Context) (context.CancelFunc, error) { 118 notificationCtx, cancel := context.WithCancel(ctx) 119 120 rootsReq := ac.caRootsRequest() 121 err := ac.acConfig.Cache.Notify(notificationCtx, cachetype.ConnectCARootName, &rootsReq, rootsWatchID, ac.cacheUpdates) 122 if err != nil { 123 cancel() 124 return nil, err 125 } 126 127 leafReq := ac.leafCertRequest() 128 err = ac.acConfig.Cache.Notify(notificationCtx, cachetype.ConnectCALeafName, &leafReq, leafWatchID, ac.cacheUpdates) 129 if err != nil { 130 cancel() 131 return nil, err 132 } 133 134 return cancel, nil 135} 136 137func (ac *AutoConfig) updateCARoots(roots *structs.IndexedCARoots) error { 138 switch { 139 case ac.config.AutoConfig.Enabled: 140 ac.Lock() 141 defer ac.Unlock() 142 var err error 143 ac.autoConfigResponse.CARoots, err = translateCARootsToProtobuf(roots) 144 if err != nil { 145 return err 146 } 147 148 if err := ac.updateTLSFromResponse(ac.autoConfigResponse); err != nil { 149 return err 150 } 151 return ac.persistAutoConfig(ac.autoConfigResponse) 152 case ac.config.AutoEncryptTLS: 153 pems := extractPEMs(roots) 154 155 if err := ac.acConfig.TLSConfigurator.UpdateAutoTLSCA(pems); err != nil { 156 return fmt.Errorf("failed to update Connect CA certificates: %w", err) 157 } 158 return nil 159 default: 160 return nil 161 } 162} 163 164func (ac *AutoConfig) updateLeafCert(cert *structs.IssuedCert) error { 165 switch { 166 case ac.config.AutoConfig.Enabled: 167 ac.Lock() 168 defer ac.Unlock() 169 var err error 170 ac.autoConfigResponse.Certificate, err = translateIssuedCertToProtobuf(cert) 171 if err != nil { 172 return err 173 } 174 175 if err := ac.updateTLSFromResponse(ac.autoConfigResponse); err != nil { 176 return err 177 } 178 return ac.persistAutoConfig(ac.autoConfigResponse) 179 case ac.config.AutoEncryptTLS: 180 if err := ac.acConfig.TLSConfigurator.UpdateAutoTLSCert(cert.CertPEM, cert.PrivateKeyPEM); err != nil { 181 return fmt.Errorf("failed to update the agent leaf cert: %w", err) 182 } 183 return nil 184 default: 185 return nil 186 } 187} 188 189func (ac *AutoConfig) caRootsRequest() structs.DCSpecificRequest { 190 return structs.DCSpecificRequest{Datacenter: ac.config.Datacenter} 191} 192 193func (ac *AutoConfig) leafCertRequest() cachetype.ConnectCALeafRequest { 194 return cachetype.ConnectCALeafRequest{ 195 Datacenter: ac.config.Datacenter, 196 Agent: ac.config.NodeName, 197 DNSSAN: ac.getDNSSANs(), 198 IPSAN: ac.getIPSANs(), 199 Token: ac.acConfig.Tokens.AgentToken(), 200 } 201} 202 203// generateCSR will generate a CSR for an Agent certificate. This should 204// be sent along with the AutoConfig.InitialConfiguration RPC or the 205// AutoEncrypt.Sign RPC. The generated CSR does NOT have a real trust domain 206// as when generating this we do not yet have the CA roots. The server will 207// update the trust domain for us though. 208func (ac *AutoConfig) generateCSR() (csr string, key string, err error) { 209 // We don't provide the correct host here, because we don't know any 210 // better at this point. Apart from the domain, we would need the 211 // ClusterID, which we don't have. This is why we go with 212 // unknownTrustDomain the first time. Subsequent CSRs will have the 213 // correct TrustDomain. 214 id := &connect.SpiffeIDAgent{ 215 // will be replaced 216 Host: unknownTrustDomain, 217 Datacenter: ac.config.Datacenter, 218 Agent: ac.config.NodeName, 219 } 220 221 caConfig, err := ac.config.ConnectCAConfiguration() 222 if err != nil { 223 return "", "", fmt.Errorf("Cannot generate CSR: %w", err) 224 } 225 226 conf, err := caConfig.GetCommonConfig() 227 if err != nil { 228 return "", "", fmt.Errorf("Failed to load common CA configuration: %w", err) 229 } 230 231 if conf.PrivateKeyType == "" { 232 conf.PrivateKeyType = connect.DefaultPrivateKeyType 233 } 234 if conf.PrivateKeyBits == 0 { 235 conf.PrivateKeyBits = connect.DefaultPrivateKeyBits 236 } 237 238 // Create a new private key 239 pk, pkPEM, err := connect.GeneratePrivateKeyWithConfig(conf.PrivateKeyType, conf.PrivateKeyBits) 240 if err != nil { 241 return "", "", fmt.Errorf("Failed to generate private key: %w", err) 242 } 243 244 dnsNames := ac.getDNSSANs() 245 ipAddresses := ac.getIPSANs() 246 247 // Create a CSR. 248 // 249 // The Common Name includes the dummy trust domain for now but Server will 250 // override this when it is signed anyway so it's OK. 251 cn := connect.AgentCN(ac.config.NodeName, unknownTrustDomain) 252 csr, err = connect.CreateCSR(id, cn, pk, dnsNames, ipAddresses) 253 if err != nil { 254 return "", "", err 255 } 256 257 return csr, pkPEM, nil 258} 259 260func (ac *AutoConfig) getDNSSANs() []string { 261 sans := defaultDNSSANs 262 switch { 263 case ac.config.AutoConfig.Enabled: 264 sans = append(sans, ac.config.AutoConfig.DNSSANs...) 265 case ac.config.AutoEncryptTLS: 266 sans = append(sans, ac.config.AutoEncryptDNSSAN...) 267 } 268 return sans 269} 270 271func (ac *AutoConfig) getIPSANs() []net.IP { 272 sans := defaultIPSANs 273 switch { 274 case ac.config.AutoConfig.Enabled: 275 sans = append(sans, ac.config.AutoConfig.IPSANs...) 276 case ac.config.AutoEncryptTLS: 277 sans = append(sans, ac.config.AutoEncryptIPSAN...) 278 } 279 return sans 280} 281