Skip to content

Commit 81a22ae

Browse files
authored
Manual Backport of [Cloud][CC-6925] Updates to pushing server state into release/1.17.x (#19810)
* [Cloud][CC-6925] Updates to pushing server state (#19682) * Upgrade hcp-sdk-go to latest version v0.73 Changes: - go get github.com/hashicorp/hcp-sdk-go - go mod tidy * From upgrade: regenerate protobufs for upgrade from 1.30 to 1.31 Ran: `make proto` Slack: https://hashicorp.slack.com/archives/C0253EQ5B40/p1701105418579429 * From upgrade: fix mock interface implementation After upgrading, there is the following compile error: cannot use &mockHCPCfg{} (value of type *mockHCPCfg) as "github.com/hashicorp/hcp-sdk-go/config".HCPConfig value in return statement: *mockHCPCfg does not implement "github.com/hashicorp/hcp-sdk-go/config".HCPConfig (missing method Logout) Solution: update the mock to have the missing Logout method * From upgrade: Lint: remove usage of deprecated req.ServerState.TLS Due to upgrade, linting is erroring due to usage of a newly deprecated field 22:47:56 [consul]: make lint --> Running golangci-lint (.) agent/hcp/testing.go:157:24: SA1019: req.ServerState.TLS is deprecated: use server_tls.internal_rpc instead. (staticcheck) time.Until(time.Time(req.ServerState.TLS.CertExpiry)).Hours()/24, ^ * From upgrade: adjust oidc error message From the upgrade, this test started failing: === FAIL: internal/go-sso/oidcauth TestOIDC_ClaimsFromAuthCode/failed_code_exchange (re-run 2) (0.01s) oidc_test.go:393: unexpected error: Provider login failed: Error exchanging oidc code: oauth2: "invalid_grant" "unexpected auth code" Prior to the upgrade, the error returned was: ``` Provider login failed: Error exchanging oidc code: oauth2: cannot fetch token: 401 Unauthorized\nResponse: {\"error\":\"invalid_grant\",\"error_description\":\"unexpected auth code\"}\n ``` Now the error returned is as below and does not contain "cannot fetch token" ``` Provider login failed: Error exchanging oidc code: oauth2: "invalid_grant" "unexpected auth code" ``` * Update AgentPushServerState structs with new fields HCP-side changes for the new fields are in: https://github.com/hashicorp/cloud-global-network-manager-service/pull/1195/files * Minor refactor for hcpServerStatus to abstract tlsInfo into struct This will make it easier to set the same tls-info information to both - status.TLS (deprecated field) - status.ServerTLSMetadata (new field to use instead) * Update hcpServerStatus to parse out information for new fields Changes: - Improve error message and handling (encountered some issues and was confused) - Set new field TLSInfo.CertIssuer - Collect certificate authority metadata and set on TLSInfo.CertificateAuthorities - Set TLSInfo on both server.TLS and server.ServerTLSMetadata.InternalRPC * Update serverStatusToHCP to convert new fields to GNM rpc * Add changelog * Feedback: connect.ParseCert, caCerts * Feedback: refactor and unit test server status * Feedback: test to use expected struct * Feedback: certificate with intermediate * Feedback: catch no leaf, remove expectedErr * Feedback: update todos with jira ticket * Feedback: mock tlsConfigurator * make proto for additional file in 1.17 not in main
1 parent 45468ff commit 81a22ae

File tree

82 files changed

+516
-150
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

82 files changed

+516
-150
lines changed

.changelog/19682.txt

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
```release-note:improvement
2+
cloud: push additional server TLS metadata to HCP
3+
```

agent/consul/server.go

+81-18
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ import (
4040
"github.com/hashicorp/consul/acl"
4141
"github.com/hashicorp/consul/acl/resolver"
4242
"github.com/hashicorp/consul/agent/blockingquery"
43+
"github.com/hashicorp/consul/agent/connect"
4344
"github.com/hashicorp/consul/agent/consul/authmethod"
4445
"github.com/hashicorp/consul/agent/consul/authmethod/ssoauth"
4546
"github.com/hashicorp/consul/agent/consul/fsm"
@@ -2215,24 +2216,9 @@ func (s *Server) hcpServerStatus(deps Deps) hcp.StatusCallback {
22152216
status.RPCPort = s.config.RPCAddr.Port
22162217
status.Datacenter = s.config.Datacenter
22172218

2218-
tlsCert := s.tlsConfigurator.Cert()
2219-
if tlsCert != nil {
2220-
status.TLS.Enabled = true
2221-
leaf := tlsCert.Leaf
2222-
if leaf == nil {
2223-
// Parse the leaf cert
2224-
leaf, err = x509.ParseCertificate(tlsCert.Certificate[0])
2225-
if err != nil {
2226-
// Shouldn't be possible
2227-
return
2228-
}
2229-
}
2230-
status.TLS.CertName = leaf.Subject.CommonName
2231-
status.TLS.CertSerial = leaf.SerialNumber.String()
2232-
status.TLS.CertExpiry = leaf.NotAfter
2233-
status.TLS.VerifyIncoming = s.tlsConfigurator.VerifyIncomingRPC()
2234-
status.TLS.VerifyOutgoing = s.tlsConfigurator.Base().InternalRPC.VerifyOutgoing
2235-
status.TLS.VerifyServerHostname = s.tlsConfigurator.VerifyServerHostname()
2219+
err = addServerTLSInfo(&status, s.tlsConfigurator)
2220+
if err != nil {
2221+
return status, fmt.Errorf("error adding server tls info: %w", err)
22362222
}
22372223

22382224
status.Raft.IsLeader = s.raft.State() == raft.Leader
@@ -2321,6 +2307,83 @@ func convertConsulConfigToRateLimitHandlerConfig(limitsConfig RequestLimits, mul
23212307
return hc
23222308
}
23232309

2310+
// addServerTLSInfo adds the server's TLS information if available to the status
2311+
func addServerTLSInfo(status *hcpclient.ServerStatus, tlsConfigurator tlsutil.ConfiguratorIface) error {
2312+
tlsCert := tlsConfigurator.Cert()
2313+
if tlsCert == nil {
2314+
return nil
2315+
}
2316+
2317+
leaf := tlsCert.Leaf
2318+
var err error
2319+
if leaf == nil {
2320+
// Parse the leaf cert
2321+
if len(tlsCert.Certificate) == 0 {
2322+
return fmt.Errorf("expected a leaf certificate but there was none")
2323+
}
2324+
leaf, err = x509.ParseCertificate(tlsCert.Certificate[0])
2325+
if err != nil {
2326+
// Shouldn't be possible
2327+
return fmt.Errorf("error parsing leaf cert: %w", err)
2328+
}
2329+
}
2330+
2331+
tlsInfo := hcpclient.ServerTLSInfo{
2332+
Enabled: true,
2333+
CertIssuer: leaf.Issuer.CommonName,
2334+
CertName: leaf.Subject.CommonName,
2335+
CertSerial: leaf.SerialNumber.String(),
2336+
CertExpiry: leaf.NotAfter,
2337+
VerifyIncoming: tlsConfigurator.VerifyIncomingRPC(),
2338+
VerifyOutgoing: tlsConfigurator.Base().InternalRPC.VerifyOutgoing,
2339+
VerifyServerHostname: tlsConfigurator.VerifyServerHostname(),
2340+
}
2341+
2342+
// Collect metadata for all CA certs used for internal RPC
2343+
metadata := make([]hcpclient.CertificateMetadata, 0)
2344+
for _, pemStr := range tlsConfigurator.ManualCAPems() {
2345+
cert, err := connect.ParseCert(pemStr)
2346+
if err != nil {
2347+
return fmt.Errorf("error parsing manual ca pem: %w", err)
2348+
}
2349+
2350+
metadatum := hcpclient.CertificateMetadata{
2351+
CertExpiry: cert.NotAfter,
2352+
CertName: cert.Subject.CommonName,
2353+
CertSerial: cert.SerialNumber.String(),
2354+
}
2355+
metadata = append(metadata, metadatum)
2356+
}
2357+
for ix, certBytes := range tlsCert.Certificate {
2358+
if ix == 0 {
2359+
// Skip the leaf cert at index 0. Only collect intermediates
2360+
continue
2361+
}
2362+
2363+
cert, err := x509.ParseCertificate(certBytes)
2364+
if err != nil {
2365+
return fmt.Errorf("error parsing tls cert index %d: %w", ix, err)
2366+
}
2367+
2368+
metadatum := hcpclient.CertificateMetadata{
2369+
CertExpiry: cert.NotAfter,
2370+
CertName: cert.Subject.CommonName,
2371+
CertSerial: cert.SerialNumber.String(),
2372+
}
2373+
metadata = append(metadata, metadatum)
2374+
}
2375+
tlsInfo.CertificateAuthorities = metadata
2376+
2377+
status.ServerTLSMetadata.InternalRPC = tlsInfo
2378+
2379+
// TODO: remove status.TLS in preference for server.ServerTLSMetadata.InternalRPC
2380+
// when deprecation path is ready
2381+
// https://hashicorp.atlassian.net/browse/CC-7015
2382+
status.TLS = tlsInfo
2383+
2384+
return nil
2385+
}
2386+
23242387
// peersInfoContent is used to help operators understand what happened to the
23252388
// peers.json file. This is written to a file called peers.info in the same
23262389
// location.

agent/consul/server_test.go

+145
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ package consul
55

66
import (
77
"context"
8+
"crypto/tls"
89
"crypto/x509"
910
"fmt"
1011
"net"
@@ -2101,3 +2102,147 @@ func TestServer_hcpManager(t *testing.T) {
21012102
hcp1.AssertExpectations(t)
21022103

21032104
}
2105+
2106+
func TestServer_addServerTLSInfo(t *testing.T) {
2107+
testCases := map[string]struct {
2108+
errMsg string
2109+
setupConfigurator func(*testing.T) tlsutil.ConfiguratorIface
2110+
checkStatus func(*testing.T, hcpclient.ServerStatus)
2111+
}{
2112+
"Success": {
2113+
setupConfigurator: func(t *testing.T) tlsutil.ConfiguratorIface {
2114+
tlsConfig := tlsutil.Config{
2115+
InternalRPC: tlsutil.ProtocolConfig{
2116+
CAFile: "../../test/ca/root.cer",
2117+
CertFile: "../../test/key/ourdomain_with_intermediate.cer",
2118+
KeyFile: "../../test/key/ourdomain.key",
2119+
VerifyIncoming: true,
2120+
VerifyOutgoing: true,
2121+
VerifyServerHostname: true,
2122+
},
2123+
}
2124+
2125+
tlsConfigurator, err := tlsutil.NewConfigurator(tlsConfig, hclog.NewNullLogger())
2126+
require.NoError(t, err)
2127+
return tlsConfigurator
2128+
},
2129+
checkStatus: func(t *testing.T, s hcpclient.ServerStatus) {
2130+
expected := hcpclient.ServerTLSInfo{
2131+
Enabled: true,
2132+
CertIssuer: "test.internal",
2133+
CertName: "testco.internal",
2134+
CertSerial: "40",
2135+
CertExpiry: time.Date(2123, time.October, 9, 17, 20, 16, 0, time.UTC),
2136+
VerifyIncoming: true,
2137+
VerifyOutgoing: true,
2138+
VerifyServerHostname: true,
2139+
CertificateAuthorities: []hcpclient.CertificateMetadata{
2140+
{ // manual ca pem
2141+
CertExpiry: time.Date(2033, time.October, 30, 15, 50, 29, 0, time.UTC),
2142+
CertName: "test.internal",
2143+
CertSerial: "191297809789001034260919865367524695178070761520",
2144+
},
2145+
{ // certificate intermediate
2146+
CertExpiry: time.Date(2033, time.October, 30, 15, 50, 29, 0, time.UTC),
2147+
CertName: "test.internal",
2148+
CertSerial: "191297809789001034260919865367524695178070761520",
2149+
},
2150+
},
2151+
}
2152+
2153+
require.Equal(t, expected, s.ServerTLSMetadata.InternalRPC)
2154+
2155+
// TODO: remove check for status.TLS once deprecation is ready
2156+
// https://hashicorp.atlassian.net/browse/CC-7015
2157+
require.Equal(t, expected, s.TLS)
2158+
},
2159+
},
2160+
"Nil Cert": {
2161+
setupConfigurator: func(t *testing.T) tlsutil.ConfiguratorIface {
2162+
tlsConfigurator, err := tlsutil.NewConfigurator(tlsutil.Config{},
2163+
hclog.NewNullLogger())
2164+
require.NoError(t, err)
2165+
return tlsConfigurator
2166+
},
2167+
checkStatus: func(t *testing.T, s hcpclient.ServerStatus) {
2168+
require.Empty(t, s.TLS)
2169+
require.Empty(t, s.ServerTLSMetadata.InternalRPC)
2170+
},
2171+
},
2172+
"Fail: No leaf": {
2173+
errMsg: "expected a leaf certificate",
2174+
setupConfigurator: func(t *testing.T) tlsutil.ConfiguratorIface {
2175+
return tlsutil.MockConfigurator{
2176+
TlsCert: &tls.Certificate{},
2177+
}
2178+
},
2179+
},
2180+
"Fail: Parse leaf cert": {
2181+
errMsg: "error parsing leaf cert",
2182+
setupConfigurator: func(t *testing.T) tlsutil.ConfiguratorIface {
2183+
return tlsutil.MockConfigurator{
2184+
TlsCert: &tls.Certificate{
2185+
Certificate: [][]byte{{}},
2186+
},
2187+
}
2188+
},
2189+
},
2190+
"Fail: Parse manual ca pems": {
2191+
errMsg: "error parsing manual ca pem",
2192+
setupConfigurator: func(t *testing.T) tlsutil.ConfiguratorIface {
2193+
tlsConfig := tlsutil.Config{
2194+
InternalRPC: tlsutil.ProtocolConfig{
2195+
CertFile: "../../test/key/ourdomain.cer",
2196+
KeyFile: "../../test/key/ourdomain.key",
2197+
},
2198+
}
2199+
tlsConfigurator, err := tlsutil.NewConfigurator(tlsConfig, hclog.NewNullLogger())
2200+
require.NoError(t, err)
2201+
2202+
return tlsutil.MockConfigurator{
2203+
TlsCert: tlsConfigurator.Cert(),
2204+
ManualCAPemsArr: []string{"invalid-format"},
2205+
}
2206+
},
2207+
},
2208+
"Fail: Parse tls cert intermediate": {
2209+
errMsg: "error parsing tls cert",
2210+
setupConfigurator: func(t *testing.T) tlsutil.ConfiguratorIface {
2211+
tlsConfig := tlsutil.Config{
2212+
InternalRPC: tlsutil.ProtocolConfig{
2213+
CertFile: "../../test/key/ourdomain.cer",
2214+
KeyFile: "../../test/key/ourdomain.key",
2215+
},
2216+
}
2217+
tlsConfigurator, err := tlsutil.NewConfigurator(tlsConfig, hclog.NewNullLogger())
2218+
require.NoError(t, err)
2219+
cert := tlsConfigurator.Cert().Certificate
2220+
cert = append(cert, []byte{})
2221+
return tlsutil.MockConfigurator{
2222+
TlsCert: &tls.Certificate{
2223+
Certificate: cert,
2224+
},
2225+
}
2226+
},
2227+
},
2228+
}
2229+
for name, tc := range testCases {
2230+
t.Run(name, func(t *testing.T) {
2231+
require.NotNil(t, tc.setupConfigurator)
2232+
tlsConfigurator := tc.setupConfigurator(t)
2233+
2234+
status := hcpclient.ServerStatus{}
2235+
err := addServerTLSInfo(&status, tlsConfigurator)
2236+
2237+
if len(tc.errMsg) > 0 {
2238+
require.Error(t, err)
2239+
require.Contains(t, err.Error(), tc.errMsg)
2240+
require.Empty(t, status)
2241+
} else {
2242+
require.NoError(t, err)
2243+
require.NotNil(t, tc.checkStatus)
2244+
tc.checkStatus(t, status)
2245+
}
2246+
})
2247+
}
2248+
}

agent/grpc-middleware/testutil/testservice/simple.pb.go

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)