-
Notifications
You must be signed in to change notification settings - Fork 4.6k
Add TCP+TLS Healthchecks #18381
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add TCP+TLS Healthchecks #18381
Changes from 23 commits
6ff0e7d
c6d8eaa
729f671
a382b2f
14a0d49
8ee986c
7c7db53
5b0dfc6
9620347
c76008b
562c0bc
a11c310
9e2012c
fd9f37c
dafbf48
c76e1b9
41e572d
af6ba92
8a2f5ed
f786916
4395cd1
2864f24
eb72235
67c60ee
4ca7dcc
8e8b035
9c53567
67d5add
576bcc4
0705235
b73cec2
7d4b533
97ab75f
dad8aca
1e4fa20
116be8b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -14,6 +14,8 @@ import ( | |
| "encoding/json" | ||
| "errors" | ||
| "fmt" | ||
| "io" | ||
| "log" | ||
| mathrand "math/rand" | ||
| "net" | ||
| "net/http" | ||
|
|
@@ -25,6 +27,7 @@ import ( | |
| "strconv" | ||
| "strings" | ||
| "sync" | ||
| "sync/atomic" | ||
| "testing" | ||
| "time" | ||
|
|
||
|
|
@@ -967,6 +970,135 @@ func TestAgent_AddServiceWithH2CPINGCheck(t *testing.T) { | |
| requireCheckExists(t, a, "test-h2cping-check") | ||
| } | ||
|
|
||
| // testTCPServer is a simple TCP echo server for use during tests. | ||
| type testTCPServer struct { | ||
| l net.Listener | ||
| stopped int32 | ||
| accepted, closed, active int32 | ||
| } | ||
|
|
||
| // newTestTCPServer opens as a listening socket on the given address and returns | ||
| // a TestTCPServer serving requests to it. The server is already started and can | ||
| // be stopped by calling Close(). | ||
| func newTestTCPServer(t *testing.T) *testTCPServer { | ||
| // Yes I get it, we're passing the same cert from the Consul Agent as is | ||
| // configured in this toy application server. Don't ever do this except in | ||
| // testing. I see you, thinking about it, don't. | ||
| certFile := "../test/key/ourdomain_server.cer" | ||
| keyFile := "../test/key/ourdomain_server.key" | ||
| caFile := "../test/ca/root.cer" | ||
| cert, err := tls.LoadX509KeyPair(certFile, keyFile) | ||
| require.Equal(t, err, nil) | ||
|
|
||
| rootCertPool := x509.NewCertPool() | ||
| if caFile != "" { | ||
| caCert, err := os.ReadFile(caFile) | ||
| require.Equal(t, err, nil) | ||
| rootCertPool.AppendCertsFromPEM(caCert) | ||
| } | ||
|
|
||
| // Configure TLS to require and verify the client's certificate | ||
| config := &tls.Config{ | ||
| Certificates: []tls.Certificate{cert}, | ||
| ClientAuth: tls.RequireAndVerifyClientCert, | ||
|
Comment on lines
+989
to
+990
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For lines 982 and 983, I chose not to generate new key material. The |
||
| ClientCAs: rootCertPool, | ||
| } | ||
|
|
||
| l, err := tls.Listen("tcp", "127.0.0.1:0", config) | ||
| require.Equal(t, err, nil) | ||
| log.Printf("test tcp server listening on %s", l.Addr()) | ||
|
pgporada marked this conversation as resolved.
Outdated
|
||
| server := &testTCPServer{l: l} | ||
| go server.accept(t) | ||
|
|
||
| return server | ||
| } | ||
|
|
||
| // Close stops the server | ||
| func (s *testTCPServer) close() { | ||
| atomic.StoreInt32(&s.stopped, 1) | ||
| if s.l != nil { | ||
| s.l.Close() | ||
| } | ||
| } | ||
|
|
||
| // Addr returns the address that this server is listening on. | ||
| func (s *testTCPServer) Addr() net.Addr { | ||
| return s.l.Addr() | ||
| } | ||
|
|
||
| func (s *testTCPServer) accept(t *testing.T) error { | ||
| for { | ||
| conn, err := s.l.Accept() | ||
| if err != nil { | ||
| if atomic.LoadInt32(&s.stopped) == 1 { | ||
| t.Logf("test tcp echo server %s stopped", s.l.Addr()) | ||
| return nil | ||
| } | ||
| t.Logf("test tcp echo server %s failed: %s", s.l.Addr(), err) | ||
| return err | ||
| } | ||
|
|
||
| t.Logf("test tcp echo server accepted connection from: %s\n", conn.RemoteAddr()) | ||
| atomic.AddInt32(&s.accepted, 1) | ||
| atomic.AddInt32(&s.active, 1) | ||
|
|
||
| go func(c net.Conn) { | ||
| io.Copy(c, c) | ||
| atomic.AddInt32(&s.closed, 1) | ||
| atomic.AddInt32(&s.active, -1) | ||
| }(conn) | ||
| } | ||
| } | ||
|
|
||
| func TestAgent_AddServiceWithTCPTLSCheck(t *testing.T) { | ||
| t.Parallel() | ||
| dataDir := testutil.TempDir(t, "agent") // we manage the data dir | ||
| a := NewTestAgent(t, ` | ||
| data_dir = "`+dataDir+`" | ||
| enable_agent_tls_for_checks = true | ||
| datacenter = "dc1" | ||
| tls { | ||
| defaults { | ||
| ca_file = "../test/ca/root.cer" | ||
| cert_file = "../test/key/ourdomain_server.cer" | ||
| key_file = "../test/key/ourdomain_server.key" | ||
| } | ||
| } | ||
| `) | ||
| defer a.Shutdown() | ||
| testrpc.WaitForTestAgent(t, a.RPC, "dc1") | ||
|
|
||
| // Create the temporary TCP socket server that the healthcheck will connect | ||
| // to. | ||
| testApp := newTestTCPServer(t) | ||
| defer testApp.close() | ||
|
|
||
| check := &structs.HealthCheck{ | ||
| Node: "foo", | ||
| CheckID: "arbitraryTCPServerTLSCheck", | ||
| Name: "arbitraryTCPServerTLSCheck", | ||
| Status: api.HealthCritical, | ||
| } | ||
|
|
||
| chkType := &structs.CheckType{ | ||
| TCP: testApp.Addr().String(), | ||
| TCPUseTLS: true, | ||
| TLSServerName: "server.dc1.consul", | ||
| Interval: 5 * time.Second, | ||
| } | ||
|
|
||
| err := a.AddCheck(check, chkType, false, "", ConfigSourceLocal) | ||
| require.Equal(t, err, nil) | ||
|
|
||
| // Retry until the healthcheck is passing. | ||
| retry.Run(t, func(r *retry.R) { | ||
| status := getCheck(a, "arbitraryTCPServerTLSCheck") | ||
| if status.Status != api.HealthPassing { | ||
| r.Fatalf("bad: %v", status.Status) | ||
| } | ||
| }) | ||
| } | ||
|
|
||
|
pgporada marked this conversation as resolved.
Outdated
|
||
| func TestAgent_AddServiceNoExec(t *testing.T) { | ||
| if testing.Short() { | ||
| t.Skip("too slow for testing.Short") | ||
|
|
@@ -4302,7 +4434,7 @@ func TestAgent_consulConfig_RequestLimits(t *testing.T) { | |
|
|
||
| t.Parallel() | ||
| hcl := ` | ||
| limits { | ||
| limits { | ||
| request_limits { | ||
| mode = "enforcing" | ||
| read_rate = 8888 | ||
|
|
@@ -6272,7 +6404,7 @@ func TestAgent_scadaProvider(t *testing.T) { | |
| }, | ||
| Overrides: ` | ||
| cloud { | ||
| resource_id = "organization/0b9de9a3-8403-4ca6-aba8-fca752f42100/project/0b9de9a3-8403-4ca6-aba8-fca752f42100/consul.cluster/0b9de9a3-8403-4ca6-aba8-fca752f42100" | ||
| resource_id = "organization/0b9de9a3-8403-4ca6-aba8-fca752f42100/project/0b9de9a3-8403-4ca6-aba8-fca752f42100/consul.cluster/0b9de9a3-8403-4ca6-aba8-fca752f42100" | ||
| client_id = "test" | ||
| client_secret = "test" | ||
| }`, | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.