2023-03-28 18:39:22 +00:00
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
2018-03-17 04:28:27 +00:00
package consul
import (
2018-04-25 18:34:08 +00:00
"errors"
2018-03-19 21:36:17 +00:00
"fmt"
2018-06-21 22:42:28 +00:00
"time"
2018-03-19 21:36:17 +00:00
2020-01-28 23:50:41 +00:00
"github.com/hashicorp/go-hclog"
2021-04-20 18:55:24 +00:00
"github.com/hashicorp/go-memdb"
2019-01-22 17:19:36 +00:00
2018-03-19 21:36:17 +00:00
"github.com/hashicorp/consul/agent/connect"
2018-03-17 04:28:27 +00:00
"github.com/hashicorp/consul/agent/consul/state"
"github.com/hashicorp/consul/agent/structs"
)
2019-01-22 17:19:36 +00:00
var (
// Err strings. net/rpc doesn't have a way to transport typed/rich errors so
// we currently rely on sniffing the error string in a few cases where we need
// to change client behavior. These are the canonical error strings to use.
// Note though that client code can't use `err == consul.Err*` directly since
// the error returned by RPC will be a plain error.errorString created by
// net/rpc client so will not be the same _instance_ that this package
// variable points to. Clients need to compare using `err.Error() ==
// consul.ErrRateLimited.Error()` which is very sad. Short of replacing our
// RPC mechanism it's hard to know how to make that much better though.
2019-06-24 18:21:51 +00:00
ErrConnectNotEnabled = errors . New ( "Connect must be enabled in order to use this endpoint" )
2022-04-14 13:26:14 +00:00
ErrRateLimited = errors . New ( "Rate limit reached, try again later" ) // Note: we depend on this error message in the gRPC ConnectCA.Sign endpoint (see: isRateLimitError).
2019-06-24 18:21:51 +00:00
ErrNotPrimaryDatacenter = errors . New ( "not the primary datacenter" )
2019-11-21 17:40:29 +00:00
ErrStateReadOnly = errors . New ( "CA Provider State is read-only" )
2019-01-22 17:19:36 +00:00
)
const (
// csrLimitWait is the maximum time we'll wait for a slot when CSR concurrency
2019-03-06 17:13:28 +00:00
// limiting or rate limiting is occurring. It's intentionally short so small
2019-01-22 17:19:36 +00:00
// batches of requests can be accommodated when server has capacity (assuming
// signing one cert takes much less than this) but failing requests fast when
// a thundering herd comes along.
csrLimitWait = 500 * time . Millisecond
)
2018-04-25 18:34:08 +00:00
2018-03-17 04:28:27 +00:00
// ConnectCA manages the Connect CA.
type ConnectCA struct {
// srv is a pointer back to the server.
srv * Server
2019-01-22 17:19:36 +00:00
2020-01-28 23:50:41 +00:00
logger hclog . Logger
2018-03-17 04:28:27 +00:00
}
2018-04-09 04:58:31 +00:00
// ConfigurationGet returns the configuration for the CA.
func ( s * ConnectCA ) ConfigurationGet (
args * structs . DCSpecificRequest ,
reply * structs . CAConfiguration ) error {
2018-04-25 18:34:08 +00:00
// Exit early if Connect hasn't been enabled.
if ! s . srv . config . ConnectEnabled {
return ErrConnectNotEnabled
}
2021-04-20 18:55:24 +00:00
if done , err := s . srv . ForwardRPC ( "ConnectCA.ConfigurationGet" , args , reply ) ; done {
2018-04-09 04:58:31 +00:00
return err
2018-03-21 19:42:42 +00:00
}
2018-04-09 04:58:31 +00:00
// This action requires operator read access.
2023-02-07 20:19:09 +00:00
authz , err := s . srv . ResolveToken ( args . Token )
2018-04-09 04:58:31 +00:00
if err != nil {
return err
2018-03-21 19:42:42 +00:00
}
2022-03-11 02:48:27 +00:00
if err := authz . ToAllowAuthorizer ( ) . OperatorWriteAllowed ( nil ) ; err != nil {
return err
2018-03-21 19:42:42 +00:00
}
2018-04-09 04:58:31 +00:00
state := s . srv . fsm . State ( )
2019-08-19 18:03:03 +00:00
_ , config , err := state . CAConfig ( nil )
2018-04-09 04:58:31 +00:00
if err != nil {
return err
2018-03-21 19:42:42 +00:00
}
2018-04-09 04:58:31 +00:00
* reply = * config
2018-03-21 19:42:42 +00:00
2018-04-09 04:58:31 +00:00
return nil
}
2018-03-21 19:42:42 +00:00
2018-04-09 04:58:31 +00:00
// ConfigurationSet updates the configuration for the CA.
func ( s * ConnectCA ) ConfigurationSet (
args * structs . CARequest ,
reply * interface { } ) error {
2018-04-25 18:34:08 +00:00
// Exit early if Connect hasn't been enabled.
if ! s . srv . config . ConnectEnabled {
return ErrConnectNotEnabled
}
2021-04-20 18:55:24 +00:00
if done , err := s . srv . ForwardRPC ( "ConnectCA.ConfigurationSet" , args , reply ) ; done {
2018-03-21 19:42:42 +00:00
return err
}
2018-04-24 18:50:31 +00:00
// This action requires operator write access.
2023-02-07 20:19:09 +00:00
authz , err := s . srv . ResolveToken ( args . Token )
2018-03-21 19:42:42 +00:00
if err != nil {
return err
}
2022-03-11 02:48:27 +00:00
if err := authz . ToAllowAuthorizer ( ) . OperatorWriteAllowed ( nil ) ; err != nil {
return err
2018-04-09 04:58:31 +00:00
}
2018-03-21 19:42:42 +00:00
2020-11-13 22:33:19 +00:00
return s . srv . caManager . UpdateConfiguration ( args )
2018-03-21 19:42:42 +00:00
}
2018-03-17 04:28:27 +00:00
// Roots returns the currently trusted root certificates.
func ( s * ConnectCA ) Roots (
args * structs . DCSpecificRequest ,
reply * structs . IndexedCARoots ) error {
// Forward if necessary
2021-04-20 18:55:24 +00:00
if done , err := s . srv . ForwardRPC ( "ConnectCA.Roots" , args , reply ) ; done {
2018-03-17 04:28:27 +00:00
return err
}
2018-07-25 19:26:27 +00:00
// Exit early if Connect hasn't been enabled.
if ! s . srv . config . ConnectEnabled {
return ErrConnectNotEnabled
}
2018-03-17 04:28:27 +00:00
return s . srv . blockingQuery (
& args . QueryOptions , & reply . QueryMeta ,
func ( ws memdb . WatchSet , state * state . Store ) error {
2020-07-24 14:00:51 +00:00
roots , err := s . srv . getCARoots ( ws , state )
2018-03-17 04:28:27 +00:00
if err != nil {
return err
}
2020-07-24 14:00:51 +00:00
* reply = * roots
2018-03-17 04:28:27 +00:00
return nil
} ,
)
}
2018-03-19 21:36:17 +00:00
// Sign signs a certificate for a service.
func ( s * ConnectCA ) Sign (
args * structs . CASignRequest ,
2018-03-20 04:00:01 +00:00
reply * structs . IssuedCert ) error {
2018-04-25 18:34:08 +00:00
// Exit early if Connect hasn't been enabled.
if ! s . srv . config . ConnectEnabled {
return ErrConnectNotEnabled
}
2021-04-20 18:55:24 +00:00
if done , err := s . srv . ForwardRPC ( "ConnectCA.Sign" , args , reply ) ; done {
2018-04-09 04:58:31 +00:00
return err
}
2018-03-19 21:36:17 +00:00
csr , err := connect . ParseCSR ( args . CSR )
if err != nil {
return err
}
2023-02-07 20:19:09 +00:00
authz , err := s . srv . ResolveToken ( args . Token )
2018-05-09 13:25:48 +00:00
if err != nil {
return err
}
2020-07-24 14:00:51 +00:00
2022-04-05 18:16:20 +00:00
cert , err := s . srv . caManager . AuthorizeAndSignCertificate ( csr , authz )
2018-11-07 06:46:06 +00:00
if err != nil {
return err
}
2020-07-24 14:00:51 +00:00
* reply = * cert
2018-03-19 21:36:17 +00:00
return nil
}
2019-06-24 18:21:51 +00:00
// SignIntermediate signs an intermediate certificate for a remote datacenter.
func ( s * ConnectCA ) SignIntermediate (
args * structs . CASignRequest ,
reply * string ) error {
// Exit early if Connect hasn't been enabled.
if ! s . srv . config . ConnectEnabled {
return ErrConnectNotEnabled
}
2021-04-20 18:55:24 +00:00
if done , err := s . srv . ForwardRPC ( "ConnectCA.SignIntermediate" , args , reply ) ; done {
2019-06-24 18:21:51 +00:00
return err
}
// Verify we are allowed to serve this request
if s . srv . config . PrimaryDatacenter != s . srv . config . Datacenter {
return ErrNotPrimaryDatacenter
}
// This action requires operator write access.
2023-02-07 20:19:09 +00:00
authz , err := s . srv . ResolveToken ( args . Token )
2019-06-24 18:21:51 +00:00
if err != nil {
return err
}
2022-03-11 02:48:27 +00:00
if err := authz . ToAllowAuthorizer ( ) . OperatorWriteAllowed ( nil ) ; err != nil {
return err
2019-06-24 18:21:51 +00:00
}
2020-11-12 01:05:04 +00:00
provider , _ := s . srv . caManager . getCAProvider ( )
2019-06-24 18:21:51 +00:00
if provider == nil {
return fmt . Errorf ( "internal error: CA provider is nil" )
}
csr , err := connect . ParseCSR ( args . CSR )
if err != nil {
return err
}
cert , err := provider . SignIntermediate ( csr )
if err != nil {
return err
}
* reply = cert
return nil
}