github.com/outbrain/consul@v1.4.5/agent/connect/ca/provider_consul.go (about) 1 package ca 2 3 import ( 4 "bytes" 5 "crypto/rand" 6 "crypto/sha256" 7 "crypto/x509" 8 "crypto/x509/pkix" 9 "encoding/pem" 10 "errors" 11 "fmt" 12 "math/big" 13 "net/url" 14 "strings" 15 "sync" 16 "time" 17 18 "github.com/hashicorp/consul/agent/connect" 19 "github.com/hashicorp/consul/agent/consul/state" 20 "github.com/hashicorp/consul/agent/structs" 21 ) 22 23 var ErrNotInitialized = errors.New("provider not initialized") 24 25 type ConsulProvider struct { 26 Delegate ConsulProviderStateDelegate 27 28 config *structs.ConsulCAProviderConfig 29 id string 30 clusterID string 31 isRoot bool 32 spiffeID *connect.SpiffeIDSigning 33 34 sync.RWMutex 35 } 36 37 type ConsulProviderStateDelegate interface { 38 State() *state.Store 39 ApplyCARequest(*structs.CARequest) error 40 } 41 42 // Configure sets up the provider using the given configuration. 43 func (c *ConsulProvider) Configure(clusterID string, isRoot bool, rawConfig map[string]interface{}) error { 44 // Parse the raw config and update our ID. 45 config, err := ParseConsulCAConfig(rawConfig) 46 if err != nil { 47 return err 48 } 49 c.config = config 50 hash := sha256.Sum256([]byte(fmt.Sprintf("%s,%s,%v", config.PrivateKey, config.RootCert, isRoot))) 51 c.id = strings.Replace(fmt.Sprintf("% x", hash), " ", ":", -1) 52 c.clusterID = clusterID 53 c.isRoot = isRoot 54 c.spiffeID = connect.SpiffeIDSigningForCluster(&structs.CAConfiguration{ClusterID: clusterID}) 55 56 // Exit early if the state store has an entry for this provider's config. 57 _, providerState, err := c.Delegate.State().CAProviderState(c.id) 58 if err != nil { 59 return err 60 } 61 62 if providerState != nil { 63 return nil 64 } 65 66 // Check if there's an entry with the old ID scheme. 67 oldID := fmt.Sprintf("%s,%s", config.PrivateKey, config.RootCert) 68 _, providerState, err = c.Delegate.State().CAProviderState(oldID) 69 if err != nil { 70 return err 71 } 72 73 // Found an entry with the old ID, so update it to the new ID and 74 // delete the old entry. 75 if providerState != nil { 76 newState := *providerState 77 newState.ID = c.id 78 createReq := &structs.CARequest{ 79 Op: structs.CAOpSetProviderState, 80 ProviderState: &newState, 81 } 82 if err := c.Delegate.ApplyCARequest(createReq); err != nil { 83 return err 84 } 85 86 deleteReq := &structs.CARequest{ 87 Op: structs.CAOpDeleteProviderState, 88 ProviderState: providerState, 89 } 90 if err := c.Delegate.ApplyCARequest(deleteReq); err != nil { 91 return err 92 } 93 94 return nil 95 } 96 97 // Write the provider state to the state store. 98 newState := structs.CAConsulProviderState{ 99 ID: c.id, 100 } 101 102 args := &structs.CARequest{ 103 Op: structs.CAOpSetProviderState, 104 ProviderState: &newState, 105 } 106 if err := c.Delegate.ApplyCARequest(args); err != nil { 107 return err 108 } 109 110 return nil 111 } 112 113 // ActiveRoot returns the active root CA certificate. 114 func (c *ConsulProvider) ActiveRoot() (string, error) { 115 _, providerState, err := c.getState() 116 if err != nil { 117 return "", err 118 } 119 120 return providerState.RootCert, nil 121 } 122 123 // GenerateRoot initializes a new root certificate and private key 124 // if needed. 125 func (c *ConsulProvider) GenerateRoot() error { 126 idx, providerState, err := c.getState() 127 if err != nil { 128 return err 129 } 130 131 if !c.isRoot { 132 return fmt.Errorf("provider is not the root certificate authority") 133 } 134 if providerState.RootCert != "" { 135 return nil 136 } 137 138 // Generate a private key if needed 139 newState := *providerState 140 if c.config.PrivateKey == "" { 141 _, pk, err := connect.GeneratePrivateKey() 142 if err != nil { 143 return err 144 } 145 newState.PrivateKey = pk 146 } else { 147 newState.PrivateKey = c.config.PrivateKey 148 } 149 150 // Generate the root CA if necessary 151 if c.config.RootCert == "" { 152 ca, err := c.generateCA(newState.PrivateKey, idx+1) 153 if err != nil { 154 return fmt.Errorf("error generating CA: %v", err) 155 } 156 newState.RootCert = ca 157 } else { 158 newState.RootCert = c.config.RootCert 159 } 160 161 // Write the provider state 162 args := &structs.CARequest{ 163 Op: structs.CAOpSetProviderState, 164 ProviderState: &newState, 165 } 166 if err := c.Delegate.ApplyCARequest(args); err != nil { 167 return err 168 } 169 170 return nil 171 } 172 173 // GenerateIntermediateCSR creates a private key and generates a CSR 174 // for another datacenter's root to sign. 175 func (c *ConsulProvider) GenerateIntermediateCSR() (string, error) { 176 _, providerState, err := c.getState() 177 if err != nil { 178 return "", err 179 } 180 181 if c.isRoot { 182 return "", fmt.Errorf("provider is the root certificate authority, " + 183 "cannot generate an intermediate CSR") 184 } 185 186 // Create a new private key and CSR. 187 signer, pk, err := connect.GeneratePrivateKey() 188 if err != nil { 189 return "", err 190 } 191 192 csr, err := connect.CreateCACSR(c.spiffeID, signer) 193 if err != nil { 194 return "", err 195 } 196 197 // Write the new provider state to the store. 198 newState := *providerState 199 newState.PrivateKey = pk 200 args := &structs.CARequest{ 201 Op: structs.CAOpSetProviderState, 202 ProviderState: &newState, 203 } 204 if err := c.Delegate.ApplyCARequest(args); err != nil { 205 return "", err 206 } 207 208 return csr, nil 209 } 210 211 // SetIntermediate validates that the given intermediate is for the right private key 212 // and writes the given intermediate and root certificates to the state. 213 func (c *ConsulProvider) SetIntermediate(intermediatePEM, rootPEM string) error { 214 _, providerState, err := c.getState() 215 if err != nil { 216 return err 217 } 218 219 if c.isRoot { 220 return fmt.Errorf("cannot set an intermediate using another root in the primary datacenter") 221 } 222 223 // Get the key from the incoming intermediate cert so we can compare it 224 // to the currently stored key. 225 intermediate, err := connect.ParseCert(intermediatePEM) 226 if err != nil { 227 return fmt.Errorf("error parsing intermediate PEM: %v", err) 228 } 229 privKey, err := connect.ParseSigner(providerState.PrivateKey) 230 if err != nil { 231 return err 232 } 233 234 // Compare the two keys to make sure they match. 235 b1, err := x509.MarshalPKIXPublicKey(intermediate.PublicKey) 236 if err != nil { 237 return err 238 } 239 b2, err := x509.MarshalPKIXPublicKey(privKey.Public()) 240 if err != nil { 241 return err 242 } 243 if !bytes.Equal(b1, b2) { 244 return fmt.Errorf("intermediate cert is for a different private key") 245 } 246 247 // Validate the remaining fields and make sure the intermediate validates against 248 // the given root cert. 249 if !intermediate.IsCA { 250 return fmt.Errorf("intermediate is not a CA certificate") 251 } 252 if uriCount := len(intermediate.URIs); uriCount != 1 { 253 return fmt.Errorf("incoming intermediate cert has unexpected number of URIs: %d", uriCount) 254 } 255 if got, want := intermediate.URIs[0].String(), c.spiffeID.URI().String(); got != want { 256 return fmt.Errorf("incoming cert URI %q does not match current URI: %q", got, want) 257 } 258 259 pool := x509.NewCertPool() 260 pool.AppendCertsFromPEM([]byte(rootPEM)) 261 _, err = intermediate.Verify(x509.VerifyOptions{ 262 Roots: pool, 263 }) 264 if err != nil { 265 return fmt.Errorf("could not verify intermediate cert against root: %v", err) 266 } 267 268 // Update the state 269 newState := *providerState 270 newState.IntermediateCert = intermediatePEM 271 newState.RootCert = rootPEM 272 args := &structs.CARequest{ 273 Op: structs.CAOpSetProviderState, 274 ProviderState: &newState, 275 } 276 if err := c.Delegate.ApplyCARequest(args); err != nil { 277 return err 278 } 279 280 return nil 281 } 282 283 // We aren't maintaining separate root/intermediate CAs for the builtin 284 // provider, so just return the root. 285 func (c *ConsulProvider) ActiveIntermediate() (string, error) { 286 if c.isRoot { 287 return c.ActiveRoot() 288 } 289 290 _, providerState, err := c.getState() 291 if err != nil { 292 return "", err 293 } 294 295 return providerState.IntermediateCert, nil 296 } 297 298 // We aren't maintaining separate root/intermediate CAs for the builtin 299 // provider, so just return the root. 300 func (c *ConsulProvider) GenerateIntermediate() (string, error) { 301 return c.ActiveIntermediate() 302 } 303 304 // Remove the state store entry for this provider instance. 305 func (c *ConsulProvider) Cleanup() error { 306 args := &structs.CARequest{ 307 Op: structs.CAOpDeleteProviderState, 308 ProviderState: &structs.CAConsulProviderState{ID: c.id}, 309 } 310 if err := c.Delegate.ApplyCARequest(args); err != nil { 311 return err 312 } 313 314 return nil 315 } 316 317 // Sign returns a new certificate valid for the given SpiffeIDService 318 // using the current CA. 319 func (c *ConsulProvider) Sign(csr *x509.CertificateRequest) (string, error) { 320 // Lock during the signing so we don't use the same index twice 321 // for different cert serial numbers. 322 c.Lock() 323 defer c.Unlock() 324 325 // Get the provider state 326 idx, providerState, err := c.getState() 327 if err != nil { 328 return "", err 329 } 330 if providerState.PrivateKey == "" { 331 return "", ErrNotInitialized 332 } 333 334 // Create the keyId for the cert from the signing private key. 335 signer, err := connect.ParseSigner(providerState.PrivateKey) 336 if err != nil { 337 return "", err 338 } 339 if signer == nil { 340 return "", ErrNotInitialized 341 } 342 keyId, err := connect.KeyId(signer.Public()) 343 if err != nil { 344 return "", err 345 } 346 347 // Parse the SPIFFE ID 348 spiffeId, err := connect.ParseCertURI(csr.URIs[0]) 349 if err != nil { 350 return "", err 351 } 352 serviceId, ok := spiffeId.(*connect.SpiffeIDService) 353 if !ok { 354 return "", fmt.Errorf("SPIFFE ID in CSR must be a service ID") 355 } 356 357 // Parse the CA cert 358 certPEM, err := c.ActiveIntermediate() 359 if err != nil { 360 return "", err 361 } 362 caCert, err := connect.ParseCert(certPEM) 363 if err != nil { 364 return "", fmt.Errorf("error parsing CA cert: %s", err) 365 } 366 367 // Cert template for generation 368 sn := &big.Int{} 369 sn.SetUint64(idx + 1) 370 // Sign the certificate valid from 1 minute in the past, this helps it be 371 // accepted right away even when nodes are not in close time sync across the 372 // cluster. A minute is more than enough for typical DC clock drift. 373 effectiveNow := time.Now().Add(-1 * time.Minute) 374 template := x509.Certificate{ 375 SerialNumber: sn, 376 Subject: pkix.Name{CommonName: serviceId.Service}, 377 URIs: csr.URIs, 378 Signature: csr.Signature, 379 SignatureAlgorithm: csr.SignatureAlgorithm, 380 PublicKeyAlgorithm: csr.PublicKeyAlgorithm, 381 PublicKey: csr.PublicKey, 382 BasicConstraintsValid: true, 383 KeyUsage: x509.KeyUsageDataEncipherment | 384 x509.KeyUsageKeyAgreement | 385 x509.KeyUsageDigitalSignature | 386 x509.KeyUsageKeyEncipherment, 387 ExtKeyUsage: []x509.ExtKeyUsage{ 388 x509.ExtKeyUsageClientAuth, 389 x509.ExtKeyUsageServerAuth, 390 }, 391 NotAfter: effectiveNow.Add(c.config.LeafCertTTL), 392 NotBefore: effectiveNow, 393 AuthorityKeyId: keyId, 394 SubjectKeyId: keyId, 395 } 396 397 // Create the certificate, PEM encode it and return that value. 398 var buf bytes.Buffer 399 bs, err := x509.CreateCertificate( 400 rand.Reader, &template, caCert, csr.PublicKey, signer) 401 if err != nil { 402 return "", fmt.Errorf("error generating certificate: %s", err) 403 } 404 err = pem.Encode(&buf, &pem.Block{Type: "CERTIFICATE", Bytes: bs}) 405 if err != nil { 406 return "", fmt.Errorf("error encoding certificate: %s", err) 407 } 408 409 err = c.incrementProviderIndex(providerState) 410 if err != nil { 411 return "", err 412 } 413 414 // Set the response 415 return buf.String(), nil 416 } 417 418 // SignIntermediate will validate the CSR to ensure the trust domain in the 419 // URI SAN matches the local one and that basic constraints for a CA certificate 420 // are met. It should return a signed CA certificate with a path length constraint 421 // of 0 to ensure that the certificate cannot be used to generate further CA certs. 422 func (c *ConsulProvider) SignIntermediate(csr *x509.CertificateRequest) (string, error) { 423 idx, providerState, err := c.getState() 424 if err != nil { 425 return "", err 426 } 427 428 if uriCount := len(csr.URIs); uriCount != 1 { 429 return "", fmt.Errorf("incoming CSR has unexpected number of URIs: %d", uriCount) 430 } 431 certURI, err := connect.ParseCertURI(csr.URIs[0]) 432 if err != nil { 433 return "", err 434 } 435 436 // Verify that the trust domain is valid. 437 if !c.spiffeID.CanSign(certURI) { 438 return "", fmt.Errorf("incoming CSR domain %q is not valid for our domain %q", 439 certURI.URI().String(), c.spiffeID.URI().String()) 440 } 441 442 // Get the signing private key. 443 signer, err := connect.ParseSigner(providerState.PrivateKey) 444 if err != nil { 445 return "", err 446 } 447 subjectKeyId, err := connect.KeyId(csr.PublicKey) 448 if err != nil { 449 return "", err 450 } 451 452 // Parse the CA cert 453 caCert, err := connect.ParseCert(providerState.RootCert) 454 if err != nil { 455 return "", fmt.Errorf("error parsing CA cert: %s", err) 456 } 457 458 // Cert template for generation 459 sn := &big.Int{} 460 sn.SetUint64(idx + 1) 461 // Sign the certificate valid from 1 minute in the past, this helps it be 462 // accepted right away even when nodes are not in close time sync across the 463 // cluster. A minute is more than enough for typical DC clock drift. 464 effectiveNow := time.Now().Add(-1 * time.Minute) 465 template := x509.Certificate{ 466 SerialNumber: sn, 467 Subject: csr.Subject, 468 URIs: csr.URIs, 469 Signature: csr.Signature, 470 SignatureAlgorithm: csr.SignatureAlgorithm, 471 PublicKeyAlgorithm: csr.PublicKeyAlgorithm, 472 PublicKey: csr.PublicKey, 473 BasicConstraintsValid: true, 474 KeyUsage: x509.KeyUsageCertSign | 475 x509.KeyUsageCRLSign | 476 x509.KeyUsageDigitalSignature, 477 IsCA: true, 478 MaxPathLenZero: true, 479 NotAfter: effectiveNow.AddDate(1, 0, 0), 480 NotBefore: effectiveNow, 481 SubjectKeyId: subjectKeyId, 482 } 483 484 // Create the certificate, PEM encode it and return that value. 485 var buf bytes.Buffer 486 bs, err := x509.CreateCertificate( 487 rand.Reader, &template, caCert, csr.PublicKey, signer) 488 if err != nil { 489 return "", fmt.Errorf("error generating certificate: %s", err) 490 } 491 err = pem.Encode(&buf, &pem.Block{Type: "CERTIFICATE", Bytes: bs}) 492 if err != nil { 493 return "", fmt.Errorf("error encoding certificate: %s", err) 494 } 495 496 err = c.incrementProviderIndex(providerState) 497 if err != nil { 498 return "", err 499 } 500 501 // Set the response 502 return buf.String(), nil 503 } 504 505 // CrossSignCA returns the given CA cert signed by the current active root. 506 func (c *ConsulProvider) CrossSignCA(cert *x509.Certificate) (string, error) { 507 c.Lock() 508 defer c.Unlock() 509 510 // Get the provider state 511 idx, providerState, err := c.getState() 512 if err != nil { 513 return "", err 514 } 515 516 privKey, err := connect.ParseSigner(providerState.PrivateKey) 517 if err != nil { 518 return "", fmt.Errorf("error parsing private key %q: %s", providerState.PrivateKey, err) 519 } 520 521 rootCA, err := connect.ParseCert(providerState.RootCert) 522 if err != nil { 523 return "", err 524 } 525 526 keyId, err := connect.KeyId(privKey.Public()) 527 if err != nil { 528 return "", err 529 } 530 531 // Create the cross-signing template from the existing root CA 532 serialNum := &big.Int{} 533 serialNum.SetUint64(idx + 1) 534 template := *cert 535 template.SerialNumber = serialNum 536 template.SignatureAlgorithm = rootCA.SignatureAlgorithm 537 template.AuthorityKeyId = keyId 538 539 // Sign the certificate valid from 1 minute in the past, this helps it be 540 // accepted right away even when nodes are not in close time sync across the 541 // cluster. A minute is more than enough for typical DC clock drift. 542 effectiveNow := time.Now().Add(-1 * time.Minute) 543 template.NotBefore = effectiveNow 544 // This cross-signed cert is only needed during rotation, and only while old 545 // leaf certs are still in use. They expire within 3 days currently so 7 is 546 // safe. TODO(banks): make this be based on leaf expiry time when that is 547 // configurable. 548 template.NotAfter = effectiveNow.AddDate(0, 0, 7) 549 550 bs, err := x509.CreateCertificate( 551 rand.Reader, &template, rootCA, cert.PublicKey, privKey) 552 if err != nil { 553 return "", fmt.Errorf("error generating CA certificate: %s", err) 554 } 555 556 var buf bytes.Buffer 557 err = pem.Encode(&buf, &pem.Block{Type: "CERTIFICATE", Bytes: bs}) 558 if err != nil { 559 return "", fmt.Errorf("error encoding private key: %s", err) 560 } 561 562 err = c.incrementProviderIndex(providerState) 563 if err != nil { 564 return "", err 565 } 566 567 return buf.String(), nil 568 } 569 570 // getState returns the current provider state from the state delegate, and returns 571 // ErrNotInitialized if no entry is found. 572 func (c *ConsulProvider) getState() (uint64, *structs.CAConsulProviderState, error) { 573 state := c.Delegate.State() 574 idx, providerState, err := state.CAProviderState(c.id) 575 if err != nil { 576 return 0, nil, err 577 } 578 579 if providerState == nil { 580 return 0, nil, ErrNotInitialized 581 } 582 583 return idx, providerState, nil 584 } 585 586 // incrementProviderIndex does a write to increment the provider state store table index 587 // used for serial numbers when generating certificates. 588 func (c *ConsulProvider) incrementProviderIndex(providerState *structs.CAConsulProviderState) error { 589 newState := *providerState 590 args := &structs.CARequest{ 591 Op: structs.CAOpSetProviderState, 592 ProviderState: &newState, 593 } 594 if err := c.Delegate.ApplyCARequest(args); err != nil { 595 return err 596 } 597 598 return nil 599 } 600 601 // generateCA makes a new root CA using the current private key 602 func (c *ConsulProvider) generateCA(privateKey string, sn uint64) (string, error) { 603 state := c.Delegate.State() 604 _, config, err := state.CAConfig() 605 if err != nil { 606 return "", err 607 } 608 609 privKey, err := connect.ParseSigner(privateKey) 610 if err != nil { 611 return "", fmt.Errorf("error parsing private key %q: %s", privateKey, err) 612 } 613 614 name := fmt.Sprintf("Consul CA %d", sn) 615 616 // The URI (SPIFFE compatible) for the cert 617 id := connect.SpiffeIDSigningForCluster(config) 618 keyId, err := connect.KeyId(privKey.Public()) 619 if err != nil { 620 return "", err 621 } 622 623 // Create the CA cert 624 serialNum := &big.Int{} 625 serialNum.SetUint64(sn) 626 template := x509.Certificate{ 627 SerialNumber: serialNum, 628 Subject: pkix.Name{CommonName: name}, 629 URIs: []*url.URL{id.URI()}, 630 BasicConstraintsValid: true, 631 KeyUsage: x509.KeyUsageCertSign | 632 x509.KeyUsageCRLSign | 633 x509.KeyUsageDigitalSignature, 634 IsCA: true, 635 NotAfter: time.Now().AddDate(10, 0, 0), 636 NotBefore: time.Now(), 637 AuthorityKeyId: keyId, 638 SubjectKeyId: keyId, 639 } 640 641 bs, err := x509.CreateCertificate( 642 rand.Reader, &template, &template, privKey.Public(), privKey) 643 if err != nil { 644 return "", fmt.Errorf("error generating CA certificate: %s", err) 645 } 646 647 var buf bytes.Buffer 648 err = pem.Encode(&buf, &pem.Block{Type: "CERTIFICATE", Bytes: bs}) 649 if err != nil { 650 return "", fmt.Errorf("error encoding private key: %s", err) 651 } 652 653 return buf.String(), nil 654 }