tls_client_wire_test.go 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  1. package runtime
  2. import (
  3. "crypto/sha256"
  4. "crypto/tls"
  5. "crypto/x509"
  6. "encoding/hex"
  7. "io"
  8. "net/http"
  9. "net/http/httptest"
  10. "sync"
  11. "testing"
  12. "time"
  13. )
  14. type wireObservation struct {
  15. pin string
  16. remoteAddr string
  17. }
  18. func startLeafRecordingServer(t *testing.T) (*httptest.Server, *x509.CertPool, func() []wireObservation) {
  19. t.Helper()
  20. var mu sync.Mutex
  21. var seen []wireObservation
  22. srv := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  23. observation := wireObservation{remoteAddr: r.RemoteAddr}
  24. if r.TLS != nil && len(r.TLS.PeerCertificates) > 0 {
  25. sum := sha256.Sum256(r.TLS.PeerCertificates[0].Raw)
  26. observation.pin = hex.EncodeToString(sum[:])
  27. }
  28. mu.Lock()
  29. seen = append(seen, observation)
  30. mu.Unlock()
  31. w.WriteHeader(http.StatusOK)
  32. _, _ = w.Write([]byte("ok"))
  33. }))
  34. srv.TLS = &tls.Config{ClientAuth: tls.RequestClientCert}
  35. srv.StartTLS()
  36. t.Cleanup(srv.Close)
  37. pool := x509.NewCertPool()
  38. pool.AddCert(srv.Certificate())
  39. return srv, pool, func() []wireObservation {
  40. mu.Lock()
  41. defer mu.Unlock()
  42. result := make([]wireObservation, len(seen))
  43. copy(result, seen)
  44. return result
  45. }
  46. }
  47. func pinOf(t *testing.T, cert tls.Certificate) string {
  48. t.Helper()
  49. sum := sha256.Sum256(cert.Certificate[0])
  50. return hex.EncodeToString(sum[:])
  51. }
  52. func rotatingClientForTest(t *testing.T, roots *x509.CertPool) *http.Client {
  53. t.Helper()
  54. build := func() (idleClosingRoundTripper, error) {
  55. cert, err := getMasterClientCert()
  56. if err != nil {
  57. return nil, err
  58. }
  59. return &http.Transport{
  60. MaxIdleConns: 64,
  61. MaxIdleConnsPerHost: 4,
  62. IdleConnTimeout: 60 * time.Second,
  63. TLSClientConfig: &tls.Config{
  64. Certificates: []tls.Certificate{cert},
  65. RootCAs: roots,
  66. MinVersion: tls.VersionTLS12,
  67. },
  68. }, nil
  69. }
  70. transport, err := newCredentialRotatingTransport(build)
  71. if err != nil {
  72. t.Fatalf("newCredentialRotatingTransport: %v", err)
  73. }
  74. return &http.Client{Transport: transport, Timeout: 10 * time.Second}
  75. }
  76. func doWireRequest(t *testing.T, client *http.Client, url string) {
  77. t.Helper()
  78. response, err := client.Get(url)
  79. if err != nil {
  80. t.Fatalf("request: %v", err)
  81. }
  82. _, _ = io.Copy(io.Discard, response.Body)
  83. _ = response.Body.Close()
  84. if response.StatusCode != http.StatusOK {
  85. t.Fatalf("status=%d want=%d", response.StatusCode, http.StatusOK)
  86. }
  87. }
  88. func TestCredentialRotationPresentsNewLeafOnNextConnection(t *testing.T) {
  89. server, roots, observations := startLeafRecordingServer(t)
  90. oldCert := masterCertForTest(t)
  91. newCert := masterCertForTest(t)
  92. oldPin := pinOf(t, oldCert)
  93. newPin := pinOf(t, newCert)
  94. if oldPin == newPin {
  95. t.Fatal("test fixture produced identical leaves")
  96. }
  97. var providerMu sync.Mutex
  98. current := oldCert
  99. SetMasterClientCertProvider(func() (tls.Certificate, error) {
  100. providerMu.Lock()
  101. defer providerMu.Unlock()
  102. return current, nil
  103. })
  104. t.Cleanup(func() { SetMasterClientCertProvider(nil) })
  105. client := rotatingClientForTest(t, roots)
  106. doWireRequest(t, client, server.URL)
  107. doWireRequest(t, client, server.URL)
  108. baseline := observations()
  109. if len(baseline) != 2 || baseline[0].pin != oldPin || baseline[1].pin != oldPin {
  110. t.Fatalf("baseline=%v", baseline)
  111. }
  112. if baseline[0].remoteAddr != baseline[1].remoteAddr {
  113. t.Fatalf("baseline connections differ: %v", baseline)
  114. }
  115. providerMu.Lock()
  116. current = newCert
  117. providerMu.Unlock()
  118. InvalidateMasterClientConnections()
  119. doWireRequest(t, client, server.URL)
  120. after := observations()
  121. if len(after) != 3 || after[2].pin != newPin {
  122. t.Fatalf("rotation observations=%v want new leaf=%s", after, newPin)
  123. }
  124. if after[2].remoteAddr == baseline[1].remoteAddr {
  125. t.Fatalf("rotated request reused stale connection %s", after[2].remoteAddr)
  126. }
  127. }
  128. func TestCredentialRotationControlKeepsOldLeafWithoutInvalidation(t *testing.T) {
  129. server, roots, observations := startLeafRecordingServer(t)
  130. oldCert := masterCertForTest(t)
  131. newCert := masterCertForTest(t)
  132. oldPin := pinOf(t, oldCert)
  133. var providerMu sync.Mutex
  134. current := oldCert
  135. SetMasterClientCertProvider(func() (tls.Certificate, error) {
  136. providerMu.Lock()
  137. defer providerMu.Unlock()
  138. return current, nil
  139. })
  140. t.Cleanup(func() { SetMasterClientCertProvider(nil) })
  141. client := rotatingClientForTest(t, roots)
  142. doWireRequest(t, client, server.URL)
  143. providerMu.Lock()
  144. current = newCert
  145. providerMu.Unlock()
  146. doWireRequest(t, client, server.URL)
  147. got := observations()
  148. if len(got) != 2 || got[1].pin != oldPin {
  149. t.Fatalf("control observations=%v want stale leaf=%s", got, oldPin)
  150. }
  151. if got[0].remoteAddr != got[1].remoteAddr {
  152. t.Fatalf("control did not reuse connection: %v", got)
  153. }
  154. }