ca: move SignCertificate to CAManager

To reduce the scope of Server, and keep all the CA logic together
This commit is contained in:
Daniel Nephin 2021-06-23 16:30:59 -04:00
parent a0320169fe
commit c2e85f25d4
7 changed files with 54 additions and 33 deletions

View File

@ -109,11 +109,18 @@ type AutoConfigBackend interface {
CreateACLToken(template *structs.ACLToken) (*structs.ACLToken, error)
DatacenterJoinAddresses(segment string) ([]string, error)
ForwardRPC(method string, info structs.RPCInfo, reply interface{}) (bool, error)
GetCARoots() (*structs.IndexedCARoots, error)
SignCertificate(csr *x509.CertificateRequest, id connect.CertURI) (*structs.IssuedCert, error)
}
type autoConfigBackend struct {
*Server
}
func (b autoConfigBackend) SignCertificate(csr *x509.CertificateRequest, id connect.CertURI) (*structs.IssuedCert, error) {
return b.Server.caManager.SignCertificate(csr, id)
}
// AutoConfig endpoint is used for cluster auto configuration operations
type AutoConfig struct {
// currently AutoConfig does not support pushing down any configuration that would be reloadable on the servers

View File

@ -192,7 +192,7 @@ func (s *ConnectCA) Sign(
}
}
cert, err := s.srv.SignCertificate(csr, spiffeID)
cert, err := s.srv.caManager.SignCertificate(csr, spiffeID)
if err != nil {
return err
}

View File

@ -492,7 +492,6 @@ func (c *FSM) applyConnectCALeafOperation(buf []byte, index uint64) interface{}
switch req.Op {
case structs.CALeafOpIncrementIndex:
// Use current index as the new value as well as the value to write at.
// TODO(banks) do we even use this op any more?
if err := c.state.CALeafSetIndex(index, index); err != nil {
return err
}

View File

@ -34,6 +34,7 @@ const (
type caServerDelegate interface {
ca.ConsulProviderStateDelegate
IsLeader() bool
ApplyCALeafRequest() (uint64, error)
forwardDC(method, dc string, args interface{}, reply interface{}) error
generateCASignRequest(csr string) *structs.CASignRequest
@ -48,6 +49,8 @@ type CAManager struct {
delegate caServerDelegate
serverConf *Config
logger hclog.Logger
// rate limiter to use when signing leaf certificates
caLeafLimiter connectSignRateLimiter
providerLock sync.RWMutex
// provider is the current CA provider in use for Connect. This is
@ -82,6 +85,29 @@ func (c *caDelegateWithState) ApplyCARequest(req *structs.CARequest) (interface{
return c.Server.raftApplyMsgpack(structs.ConnectCARequestType, req)
}
func (c *caDelegateWithState) ApplyCALeafRequest() (uint64, error) {
// TODO(banks): when we implement IssuedCerts table we can use the insert to
// that as the raft index to return in response.
//
// UPDATE(mkeeler): The original implementation relied on updating the CAConfig
// and using its index as the ModifyIndex for certs. This was buggy. The long
// term goal is still to insert some metadata into raft about the certificates
// and use that raft index for the ModifyIndex. This is a partial step in that
// direction except that we only are setting an index and not storing the
// metadata.
req := structs.CALeafRequest{
Op: structs.CALeafOpIncrementIndex,
Datacenter: c.Server.config.Datacenter,
}
resp, err := c.Server.raftApplyMsgpack(structs.ConnectCALeafRequestType|structs.IgnoreUnknownTypeFlag, &req)
modIdx, ok := resp.(uint64)
if !ok {
return 0, fmt.Errorf("Invalid response from updating the leaf cert index")
}
return modIdx, err
}
func (c *caDelegateWithState) generateCASignRequest(csr string) *structs.CASignRequest {
return &structs.CASignRequest{
Datacenter: c.Server.config.PrimaryDatacenter,

View File

@ -62,6 +62,10 @@ func (m *mockCAServerDelegate) CheckServers(datacenter string, fn func(*metadata
})
}
func (m *mockCAServerDelegate) ApplyCALeafRequest() (uint64, error) {
return 3, nil
}
// ApplyCARequest mirrors FSM.applyConnectCAOperation because that functionality
// is not exported.
func (m *mockCAServerDelegate) ApplyCARequest(req *structs.CARequest) (interface{}, error) {

View File

@ -878,7 +878,7 @@ func (s *Server) setupRPC() error {
authz = &disabledAuthorizer{}
}
// now register with the insecure RPC server
s.insecureRPCServer.Register(NewAutoConfig(s.config, s.tlsConfigurator, s, authz))
s.insecureRPCServer.Register(NewAutoConfig(s.config, s.tlsConfigurator, autoConfigBackend{Server: s}, authz))
ln, err := net.ListenTCP("tcp", s.config.RPCAddr)
if err != nil {
@ -1456,6 +1456,7 @@ func (s *Server) isReadyForConsistentReads() bool {
}
// CreateACLToken will create an ACL token from the given template
// TODO: move to autoConfigBackend
func (s *Server) CreateACLToken(template *structs.ACLToken) (*structs.ACLToken, error) {
// we have to require local tokens or else it would require having these servers use a token with acl:write to make a
// token create RPC to the servers in the primary DC.
@ -1504,6 +1505,7 @@ func (s *Server) CreateACLToken(template *structs.ACLToken) (*structs.ACLToken,
// DatacenterJoinAddresses will return all the strings suitable for usage in
// retry join operations to connect to the the LAN or LAN segment gossip pool.
// TODO: move to autoConfigBackend
func (s *Server) DatacenterJoinAddresses(segment string) ([]string, error) {
members, err := s.LANSegmentMembers(segment)
if err != nil {

View File

@ -17,6 +17,7 @@ import (
"github.com/hashicorp/consul/lib/semaphore"
)
// TODO: move to leader_connect_ca.go
type connectSignRateLimiter struct {
// csrRateLimiter limits the rate of signing new certs if configured. Lazily
// initialized from current config to support dynamic changes.
@ -70,7 +71,8 @@ func (l *connectSignRateLimiter) getCSRRateLimiterWithLimit(limit rate.Limit) *r
return l.csrRateLimiter
}
// GetCARoots will retrieve
// GetCARoots will retrieve CARoots
// TODO: move to autoConfigBackend
func (s *Server) GetCARoots() (*structs.IndexedCARoots, error) {
return s.getCARoots(nil, s.fsm.State())
}
@ -138,9 +140,9 @@ func (s *Server) getCARoots(ws memdb.WatchSet, state *state.Store) (*structs.Ind
return indexedRoots, nil
}
// TODO: Move this off Server. This is only called by RPC endpoints.
func (s *Server) SignCertificate(csr *x509.CertificateRequest, spiffeID connect.CertURI) (*structs.IssuedCert, error) {
provider, caRoot := s.caManager.getCAProvider()
// TODO: Move this to leader_connect_ca.go
func (c *CAManager) SignCertificate(csr *x509.CertificateRequest, spiffeID connect.CertURI) (*structs.IssuedCert, error) {
provider, caRoot := c.getCAProvider()
if provider == nil {
return nil, fmt.Errorf("CA is uninitialized and unable to sign certificates yet: provider is nil")
} else if caRoot == nil {
@ -148,7 +150,7 @@ func (s *Server) SignCertificate(csr *x509.CertificateRequest, spiffeID connect.
}
// Verify that the CSR entity is in the cluster's trust domain
state := s.fsm.State()
state := c.delegate.State()
_, config, err := state.CAConfig(nil)
if err != nil {
return nil, err
@ -198,7 +200,7 @@ func (s *Server) SignCertificate(csr *x509.CertificateRequest, spiffeID connect.
return nil, err
}
if commonCfg.CSRMaxPerSecond > 0 {
lim := s.caLeafLimiter.getCSRRateLimiterWithLimit(rate.Limit(commonCfg.CSRMaxPerSecond))
lim := c.caLeafLimiter.getCSRRateLimiterWithLimit(rate.Limit(commonCfg.CSRMaxPerSecond))
// Wait up to the small threshold we allow for a token.
ctx, cancel := context.WithTimeout(context.Background(), csrLimitWait)
defer cancel()
@ -206,13 +208,13 @@ func (s *Server) SignCertificate(csr *x509.CertificateRequest, spiffeID connect.
return nil, ErrRateLimited
}
} else if commonCfg.CSRMaxConcurrent > 0 {
s.caLeafLimiter.csrConcurrencyLimiter.SetSize(int64(commonCfg.CSRMaxConcurrent))
c.caLeafLimiter.csrConcurrencyLimiter.SetSize(int64(commonCfg.CSRMaxConcurrent))
ctx, cancel := context.WithTimeout(context.Background(), csrLimitWait)
defer cancel()
if err := s.caLeafLimiter.csrConcurrencyLimiter.Acquire(ctx); err != nil {
if err := c.caLeafLimiter.csrConcurrencyLimiter.Acquire(ctx); err != nil {
return nil, ErrRateLimited
}
defer s.caLeafLimiter.csrConcurrencyLimiter.Release()
defer c.caLeafLimiter.csrConcurrencyLimiter.Release()
}
connect.HackSANExtensionForCSR(csr)
@ -245,30 +247,11 @@ func (s *Server) SignCertificate(csr *x509.CertificateRequest, spiffeID connect.
pem = pem + ca.EnsureTrailingNewline(inter)
}
// TODO(banks): when we implement IssuedCerts table we can use the insert to
// that as the raft index to return in response.
//
// UPDATE(mkeeler): The original implementation relied on updating the CAConfig
// and using its index as the ModifyIndex for certs. This was buggy. The long
// term goal is still to insert some metadata into raft about the certificates
// and use that raft index for the ModifyIndex. This is a partial step in that
// direction except that we only are setting an index and not storing the
// metadata.
req := structs.CALeafRequest{
Op: structs.CALeafOpIncrementIndex,
Datacenter: s.config.Datacenter,
}
resp, err := s.raftApply(structs.ConnectCALeafRequestType|structs.IgnoreUnknownTypeFlag, &req)
modIdx, err := c.delegate.ApplyCALeafRequest()
if err != nil {
return nil, err
}
modIdx, ok := resp.(uint64)
if !ok {
return nil, fmt.Errorf("Invalid response from updating the leaf cert index")
}
cert, err := connect.ParseCert(pem)
if err != nil {
return nil, err