Skip to content

Commit

Permalink
Merge branch 'main' of ssh://github.com/hashicorp/consul into NET-3860
Browse files Browse the repository at this point in the history
  • Loading branch information
absolutelightning committed Dec 5, 2023
2 parents 85a222e + 649aa56 commit d5b2dde
Show file tree
Hide file tree
Showing 114 changed files with 1,406 additions and 478 deletions.
3 changes: 3 additions & 0 deletions .changelog/19682.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:improvement
cloud: push additional server TLS metadata to HCP
```
99 changes: 81 additions & 18 deletions agent/consul/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import (
"github.com/hashicorp/consul/acl"
"github.com/hashicorp/consul/acl/resolver"
"github.com/hashicorp/consul/agent/blockingquery"
"github.com/hashicorp/consul/agent/connect"
"github.com/hashicorp/consul/agent/consul/authmethod"
"github.com/hashicorp/consul/agent/consul/authmethod/ssoauth"
"github.com/hashicorp/consul/agent/consul/fsm"
Expand Down Expand Up @@ -2235,24 +2236,9 @@ func (s *Server) hcpServerStatus(deps Deps) hcp.StatusCallback {
status.RPCPort = s.config.RPCAddr.Port
status.Datacenter = s.config.Datacenter

tlsCert := s.tlsConfigurator.Cert()
if tlsCert != nil {
status.TLS.Enabled = true
leaf := tlsCert.Leaf
if leaf == nil {
// Parse the leaf cert
leaf, err = x509.ParseCertificate(tlsCert.Certificate[0])
if err != nil {
// Shouldn't be possible
return
}
}
status.TLS.CertName = leaf.Subject.CommonName
status.TLS.CertSerial = leaf.SerialNumber.String()
status.TLS.CertExpiry = leaf.NotAfter
status.TLS.VerifyIncoming = s.tlsConfigurator.VerifyIncomingRPC()
status.TLS.VerifyOutgoing = s.tlsConfigurator.Base().InternalRPC.VerifyOutgoing
status.TLS.VerifyServerHostname = s.tlsConfigurator.VerifyServerHostname()
err = addServerTLSInfo(&status, s.tlsConfigurator)
if err != nil {
return status, fmt.Errorf("error adding server tls info: %w", err)
}

status.Raft.IsLeader = s.raft.State() == raft.Leader
Expand Down Expand Up @@ -2341,6 +2327,83 @@ func convertConsulConfigToRateLimitHandlerConfig(limitsConfig RequestLimits, mul
return hc
}

// addServerTLSInfo adds the server's TLS information if available to the status
func addServerTLSInfo(status *hcpclient.ServerStatus, tlsConfigurator tlsutil.ConfiguratorIface) error {
tlsCert := tlsConfigurator.Cert()
if tlsCert == nil {
return nil
}

leaf := tlsCert.Leaf
var err error
if leaf == nil {
// Parse the leaf cert
if len(tlsCert.Certificate) == 0 {
return fmt.Errorf("expected a leaf certificate but there was none")
}
leaf, err = x509.ParseCertificate(tlsCert.Certificate[0])
if err != nil {
// Shouldn't be possible
return fmt.Errorf("error parsing leaf cert: %w", err)
}
}

tlsInfo := hcpclient.ServerTLSInfo{
Enabled: true,
CertIssuer: leaf.Issuer.CommonName,
CertName: leaf.Subject.CommonName,
CertSerial: leaf.SerialNumber.String(),
CertExpiry: leaf.NotAfter,
VerifyIncoming: tlsConfigurator.VerifyIncomingRPC(),
VerifyOutgoing: tlsConfigurator.Base().InternalRPC.VerifyOutgoing,
VerifyServerHostname: tlsConfigurator.VerifyServerHostname(),
}

// Collect metadata for all CA certs used for internal RPC
metadata := make([]hcpclient.CertificateMetadata, 0)
for _, pemStr := range tlsConfigurator.ManualCAPems() {
cert, err := connect.ParseCert(pemStr)
if err != nil {
return fmt.Errorf("error parsing manual ca pem: %w", err)
}

metadatum := hcpclient.CertificateMetadata{
CertExpiry: cert.NotAfter,
CertName: cert.Subject.CommonName,
CertSerial: cert.SerialNumber.String(),
}
metadata = append(metadata, metadatum)
}
for ix, certBytes := range tlsCert.Certificate {
if ix == 0 {
// Skip the leaf cert at index 0. Only collect intermediates
continue
}

cert, err := x509.ParseCertificate(certBytes)
if err != nil {
return fmt.Errorf("error parsing tls cert index %d: %w", ix, err)
}

metadatum := hcpclient.CertificateMetadata{
CertExpiry: cert.NotAfter,
CertName: cert.Subject.CommonName,
CertSerial: cert.SerialNumber.String(),
}
metadata = append(metadata, metadatum)
}
tlsInfo.CertificateAuthorities = metadata

status.ServerTLSMetadata.InternalRPC = tlsInfo

// TODO: remove status.TLS in preference for server.ServerTLSMetadata.InternalRPC
// when deprecation path is ready
// https://hashicorp.atlassian.net/browse/CC-7015
status.TLS = tlsInfo

return nil
}

// peersInfoContent is used to help operators understand what happened to the
// peers.json file. This is written to a file called peers.info in the same
// location.
Expand Down
145 changes: 145 additions & 0 deletions agent/consul/server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ package consul

import (
"context"
"crypto/tls"
"crypto/x509"
"flag"
"fmt"
Expand Down Expand Up @@ -2107,6 +2108,150 @@ func TestServer_hcpManager(t *testing.T) {

}

func TestServer_addServerTLSInfo(t *testing.T) {
testCases := map[string]struct {
errMsg string
setupConfigurator func(*testing.T) tlsutil.ConfiguratorIface
checkStatus func(*testing.T, hcpclient.ServerStatus)
}{
"Success": {
setupConfigurator: func(t *testing.T) tlsutil.ConfiguratorIface {
tlsConfig := tlsutil.Config{
InternalRPC: tlsutil.ProtocolConfig{
CAFile: "../../test/ca/root.cer",
CertFile: "../../test/key/ourdomain_with_intermediate.cer",
KeyFile: "../../test/key/ourdomain.key",
VerifyIncoming: true,
VerifyOutgoing: true,
VerifyServerHostname: true,
},
}

tlsConfigurator, err := tlsutil.NewConfigurator(tlsConfig, hclog.NewNullLogger())
require.NoError(t, err)
return tlsConfigurator
},
checkStatus: func(t *testing.T, s hcpclient.ServerStatus) {
expected := hcpclient.ServerTLSInfo{
Enabled: true,
CertIssuer: "test.internal",
CertName: "testco.internal",
CertSerial: "40",
CertExpiry: time.Date(2123, time.October, 9, 17, 20, 16, 0, time.UTC),
VerifyIncoming: true,
VerifyOutgoing: true,
VerifyServerHostname: true,
CertificateAuthorities: []hcpclient.CertificateMetadata{
{ // manual ca pem
CertExpiry: time.Date(2033, time.October, 30, 15, 50, 29, 0, time.UTC),
CertName: "test.internal",
CertSerial: "191297809789001034260919865367524695178070761520",
},
{ // certificate intermediate
CertExpiry: time.Date(2033, time.October, 30, 15, 50, 29, 0, time.UTC),
CertName: "test.internal",
CertSerial: "191297809789001034260919865367524695178070761520",
},
},
}

require.Equal(t, expected, s.ServerTLSMetadata.InternalRPC)

// TODO: remove check for status.TLS once deprecation is ready
// https://hashicorp.atlassian.net/browse/CC-7015
require.Equal(t, expected, s.TLS)
},
},
"Nil Cert": {
setupConfigurator: func(t *testing.T) tlsutil.ConfiguratorIface {
tlsConfigurator, err := tlsutil.NewConfigurator(tlsutil.Config{},
hclog.NewNullLogger())
require.NoError(t, err)
return tlsConfigurator
},
checkStatus: func(t *testing.T, s hcpclient.ServerStatus) {
require.Empty(t, s.TLS)
require.Empty(t, s.ServerTLSMetadata.InternalRPC)
},
},
"Fail: No leaf": {
errMsg: "expected a leaf certificate",
setupConfigurator: func(t *testing.T) tlsutil.ConfiguratorIface {
return tlsutil.MockConfigurator{
TlsCert: &tls.Certificate{},
}
},
},
"Fail: Parse leaf cert": {
errMsg: "error parsing leaf cert",
setupConfigurator: func(t *testing.T) tlsutil.ConfiguratorIface {
return tlsutil.MockConfigurator{
TlsCert: &tls.Certificate{
Certificate: [][]byte{{}},
},
}
},
},
"Fail: Parse manual ca pems": {
errMsg: "error parsing manual ca pem",
setupConfigurator: func(t *testing.T) tlsutil.ConfiguratorIface {
tlsConfig := tlsutil.Config{
InternalRPC: tlsutil.ProtocolConfig{
CertFile: "../../test/key/ourdomain.cer",
KeyFile: "../../test/key/ourdomain.key",
},
}
tlsConfigurator, err := tlsutil.NewConfigurator(tlsConfig, hclog.NewNullLogger())
require.NoError(t, err)

return tlsutil.MockConfigurator{
TlsCert: tlsConfigurator.Cert(),
ManualCAPemsArr: []string{"invalid-format"},
}
},
},
"Fail: Parse tls cert intermediate": {
errMsg: "error parsing tls cert",
setupConfigurator: func(t *testing.T) tlsutil.ConfiguratorIface {
tlsConfig := tlsutil.Config{
InternalRPC: tlsutil.ProtocolConfig{
CertFile: "../../test/key/ourdomain.cer",
KeyFile: "../../test/key/ourdomain.key",
},
}
tlsConfigurator, err := tlsutil.NewConfigurator(tlsConfig, hclog.NewNullLogger())
require.NoError(t, err)
cert := tlsConfigurator.Cert().Certificate
cert = append(cert, []byte{})
return tlsutil.MockConfigurator{
TlsCert: &tls.Certificate{
Certificate: cert,
},
}
},
},
}
for name, tc := range testCases {
t.Run(name, func(t *testing.T) {
require.NotNil(t, tc.setupConfigurator)
tlsConfigurator := tc.setupConfigurator(t)

status := hcpclient.ServerStatus{}
err := addServerTLSInfo(&status, tlsConfigurator)

if len(tc.errMsg) > 0 {
require.Error(t, err)
require.Contains(t, err.Error(), tc.errMsg)
require.Empty(t, status)
} else {
require.NoError(t, err)
require.NotNil(t, tc.checkStatus)
tc.checkStatus(t, status)
}
})
}
}

// goldenMarkdown reads and optionally writes the expected data to the goldenMarkdown file,
// returning the contents as a string.
func goldenMarkdown(t *testing.T, name, got string) string {
Expand Down
3 changes: 2 additions & 1 deletion agent/consul/testdata/v2-resource-dependencies.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ flowchart TD
auth/v2beta1/computedtrafficpermissions --> auth/v2beta1/workloadidentity
catalog/v2beta1/failoverpolicy --> catalog/v2beta1/service
catalog/v2beta1/healthstatus
catalog/v2beta1/node --> catalog/v2beta1/healthstatus
catalog/v2beta1/node --> catalog/v2beta1/nodehealthstatus
catalog/v2beta1/nodehealthstatus
catalog/v2beta1/service
catalog/v2beta1/serviceendpoints --> catalog/v2beta1/service
catalog/v2beta1/serviceendpoints --> catalog/v2beta1/workload
Expand Down
2 changes: 1 addition & 1 deletion agent/grpc-middleware/testutil/testservice/simple.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit d5b2dde

Please sign in to comment.