123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222 |
- /*
- Copyright 2017 The Kubernetes Authors.
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- */
- package certificate
- import (
- "context"
- "crypto/tls"
- "crypto/x509"
- "fmt"
- "math/big"
- "net/http"
- "net/http/httptest"
- "sync/atomic"
- "testing"
- "time"
- "k8s.io/apimachinery/pkg/runtime"
- "k8s.io/apimachinery/pkg/runtime/serializer"
- certificatesclient "k8s.io/client-go/kubernetes/typed/certificates/v1beta1"
- "k8s.io/client-go/rest"
- )
- var (
- client1CertData = newCertificateData(`-----BEGIN CERTIFICATE-----
- MIICBDCCAW2gAwIBAgIJAPgVBh+4xbGoMA0GCSqGSIb3DQEBCwUAMBsxGTAXBgNV
- BAMMEHdlYmhvb2tfdGVzdHNfY2EwIBcNMTcwNzI4MjMxNTI4WhgPMjI5MTA1MTMy
- MzE1MjhaMB8xHTAbBgNVBAMMFHdlYmhvb2tfdGVzdHNfY2xpZW50MIGfMA0GCSqG
- SIb3DQEBAQUAA4GNADCBiQKBgQDkGXXSm6Yun5o3Jlmx45rItcQ2pmnoDk4eZfl0
- rmPa674s2pfYo3KywkXQ1Fp3BC8GUgzPLSfJ8xXya9Lg1Wo8sHrDln0iRg5HXxGu
- uFNhRBvj2S0sIff0ZG/IatB9I6WXVOUYuQj6+A0CdULNj1vBqH9+7uWbLZ6lrD4b
- a44x/wIDAQABo0owSDAJBgNVHRMEAjAAMAsGA1UdDwQEAwIF4DAdBgNVHSUEFjAU
- BggrBgEFBQcDAgYIKwYBBQUHAwEwDwYDVR0RBAgwBocEfwAAATANBgkqhkiG9w0B
- AQsFAAOBgQCpN27uh/LjUVCaBK7Noko25iih/JSSoWzlvc8CaipvSPofNWyGx3Vu
- OdcSwNGYX/pp4ZoAzFij/Y5u0vKTVLkWXATeTMVmlPvhmpYjj9gPkCSY6j/SiKlY
- kGy0xr+0M5UQkMBcfIh9oAp9um1fZHVWAJAGP/ikZgkcUey0LmBn8w==
- -----END CERTIFICATE-----`, `-----BEGIN RSA PRIVATE KEY-----
- MIICWwIBAAKBgQDkGXXSm6Yun5o3Jlmx45rItcQ2pmnoDk4eZfl0rmPa674s2pfY
- o3KywkXQ1Fp3BC8GUgzPLSfJ8xXya9Lg1Wo8sHrDln0iRg5HXxGuuFNhRBvj2S0s
- Iff0ZG/IatB9I6WXVOUYuQj6+A0CdULNj1vBqH9+7uWbLZ6lrD4ba44x/wIDAQAB
- AoGAZbWwowvCq1GBq4vPPRI3h739Uz0bRl1ymf1woYXNguXRtCB4yyH+2BTmmrrF
- 6AIWkePuUEdbUaKyK5nGu3iOWM+/i6NP3kopQANtbAYJ2ray3kwvFlhqyn1bxX4n
- gl/Cbdw1If4zrDrB66y8mYDsjzK7n/gFaDNcY4GArjvOXKkCQQD9Lgv+WD73y4RP
- yS+cRarlEeLLWVsX/pg2oEBLM50jsdUnrLSW071MjBgP37oOXzqynF9SoDbP2Y5C
- x+aGux9LAkEA5qPlQPv0cv8Wc3qTI+LixZ/86PPHKWnOnwaHm3b9vQjZAkuVQg3n
- Wgg9YDmPM87t3UFH7ZbDihUreUxwr9ZjnQJAZ9Z95shMsxbOYmbSVxafu6m1Sc+R
- M+sghK7/D5jQpzYlhUspGf8n0YBX0hLhXUmjamQGGH5LXL4Owcb4/mM6twJAEVio
- SF/qva9jv+GrKVrKFXT374lOJFY53Qn/rvifEtWUhLCslCA5kzLlctRBafMZPrfH
- Mh5RrJP1BhVysDbenQJASGcc+DiF7rB6K++ZGyC11E2AP29DcZ0pgPESSV7npOGg
- +NqPRZNVCSZOiVmNuejZqmwKhZNGZnBFx1Y+ChAAgw==
- -----END RSA PRIVATE KEY-----`)
- client2CertData = newCertificateData(`-----BEGIN CERTIFICATE-----
- MIICBDCCAW2gAwIBAgIJAPgVBh+4xbGnMA0GCSqGSIb3DQEBCwUAMBsxGTAXBgNV
- BAMMEHdlYmhvb2tfdGVzdHNfY2EwIBcNMTcwNzI4MjMxNTI4WhgPMjI5MTA1MTMy
- MzE1MjhaMB8xHTAbBgNVBAMMFHdlYmhvb2tfdGVzdHNfY2xpZW50MIGfMA0GCSqG
- SIb3DQEBAQUAA4GNADCBiQKBgQDQQLzbrmHbtlxE7wViaoXFp5tQx7zzM2Ed7O1E
- gs3JUws5KkPbNrejLwixvLkzzU152M43UGsyKDn7HPyjXDogTZSW6C257XpYodk3
- S/gZS9oZtPss4UJuJioQk/M8X1ZjYP8kCTArOvVRJeNQL8GM7h5QQ6J5LUq+IdZb
- T0retQIDAQABo0owSDAJBgNVHRMEAjAAMAsGA1UdDwQEAwIF4DAdBgNVHSUEFjAU
- BggrBgEFBQcDAgYIKwYBBQUHAwEwDwYDVR0RBAgwBocEfwAAATANBgkqhkiG9w0B
- AQsFAAOBgQBdAxoU5YAmp0d+5b4qg/xOGC5rKcnksQEXYoGwFBWwaKvh9oUlGGxI
- A5Ykf2TEl24br4tLmicpdxUX4H4PbkdPxOjM9ghIKlmgHo8vBRC0iVIwYgQsw1W8
- ETY34Or+PJqaeslqx/t7kUKY5UIF9DLVolsIiAHveJNR2uBWiP0KiQ==
- -----END CERTIFICATE-----`, `-----BEGIN RSA PRIVATE KEY-----
- MIICXQIBAAKBgQDQQLzbrmHbtlxE7wViaoXFp5tQx7zzM2Ed7O1Egs3JUws5KkPb
- NrejLwixvLkzzU152M43UGsyKDn7HPyjXDogTZSW6C257XpYodk3S/gZS9oZtPss
- 4UJuJioQk/M8X1ZjYP8kCTArOvVRJeNQL8GM7h5QQ6J5LUq+IdZbT0retQIDAQAB
- AoGBAMFjTL4IKvG4X+jXub1RxFXvNkkGos2Jaec7TH5xpZ4OUv7L4+We41tTYxSC
- d83GGetLzPwK3vDd8DHkEiu1incket78rwmQ89LnQNyM0B5ejaTjW2zHcvKJ0Mtn
- nM32juQfq8St9JZVweS87k8RkLt9cOrg6219MRbFO+1Vn8WhAkEA+/rqHCspBdXr
- 7RL+H63k7RjqBllVEYlw1ukqTw1gp5IImmeOwgl3aRrJJfFV6gxxEqQ4CCb2vf9M
- yjrGEvP9KQJBANOTPcpskT/0dyipsAkvLFZTKjN+4fdfq37H3dVgMR6oQcMJwukd
- cEio1Hx+XzXuD0RHXighq7bUzel+IqzRuq0CQBJkzpIf1G7InuA/cq19VCi6mNq9
- yqftEH+fpab/ov6YemhLBvDDICRcADL02wCqx9ZEhpKRxZE5AbIBeFQJ24ECQG4f
- 9cmnOPNRC7TengIpy6ojH5QuNu/LnDghUBYAO5D5g0FBk3JDIG6xceha3rPzdX7U
- pu28mORRX9xpCyNpBwECQQCtDNZoehdPVuZA3Wocno31Rjmuy83ajgRRuEzqv0tj
- uC6Jo2eLcSV1sSdzTjaaWdM6XeYj6yHOAm8ZBIQs7m6V
- -----END RSA PRIVATE KEY-----`)
- )
- type certificateData struct {
- keyPEM []byte
- certificatePEM []byte
- certificate *tls.Certificate
- }
- func newCertificateData(certificatePEM string, keyPEM string) *certificateData {
- certificate, err := tls.X509KeyPair([]byte(certificatePEM), []byte(keyPEM))
- if err != nil {
- panic(fmt.Sprintf("Unable to initialize certificate: %v", err))
- }
- certs, err := x509.ParseCertificates(certificate.Certificate[0])
- if err != nil {
- panic(fmt.Sprintf("Unable to initialize certificate leaf: %v", err))
- }
- certificate.Leaf = certs[0]
- return &certificateData{
- keyPEM: []byte(keyPEM),
- certificatePEM: []byte(certificatePEM),
- certificate: &certificate,
- }
- }
- type fakeManager struct {
- cert atomic.Value // Always a *tls.Certificate
- healthy bool
- }
- func (f *fakeManager) SetCertificateSigningRequestClient(certificatesclient.CertificateSigningRequestInterface) error {
- return nil
- }
- func (f *fakeManager) ServerHealthy() bool { return f.healthy }
- func (f *fakeManager) Start() {}
- func (f *fakeManager) Stop() {}
- func (f *fakeManager) RotateCerts() (bool, error) { return false, nil }
- func (f *fakeManager) Current() *tls.Certificate {
- if val := f.cert.Load(); val != nil {
- return val.(*tls.Certificate)
- }
- return nil
- }
- func (f *fakeManager) setCurrent(cert *tls.Certificate) {
- f.cert.Store(cert)
- }
- func TestRotateShutsDownConnections(t *testing.T) {
- // This test fails if you comment out the t.closeAllConns() call in
- // transport.go and don't close connections on a rotate.
- stop := make(chan struct{})
- defer close(stop)
- m := new(fakeManager)
- m.setCurrent(client1CertData.certificate)
- // The last certificate we've seen.
- lastSeenLeafCert := new(atomic.Value) // Always *x509.Certificate
- lastSerialNumber := func() *big.Int {
- if cert := lastSeenLeafCert.Load(); cert != nil {
- return cert.(*x509.Certificate).SerialNumber
- }
- return big.NewInt(0)
- }
- h := func(w http.ResponseWriter, r *http.Request) {
- if r.TLS != nil && len(r.TLS.PeerCertificates) != 0 {
- // Record the last TLS certificate the client sent.
- lastSeenLeafCert.Store(r.TLS.PeerCertificates[0])
- }
- w.Write([]byte(`{}`))
- }
- s := httptest.NewUnstartedServer(http.HandlerFunc(h))
- s.TLS = &tls.Config{
- // Just request a cert, we don't need to verify it.
- ClientAuth: tls.RequestClientCert,
- }
- s.StartTLS()
- defer s.Close()
- c := &rest.Config{
- Host: s.URL,
- TLSClientConfig: rest.TLSClientConfig{
- // We don't care about the server's cert.
- Insecure: true,
- },
- ContentConfig: rest.ContentConfig{
- // This is a hack. We don't actually care about the serializer.
- NegotiatedSerializer: serializer.NegotiatedSerializerWrapper(runtime.SerializerInfo{}),
- },
- }
- // Check for a new cert every 10 milliseconds
- if _, err := updateTransport(stop, 10*time.Millisecond, c, m, 0); err != nil {
- t.Fatal(err)
- }
- client, err := rest.UnversionedRESTClientFor(c)
- if err != nil {
- t.Fatal(err)
- }
- if err := client.Get().Do(context.TODO()).Error(); err != nil {
- t.Fatal(err)
- }
- firstCertSerial := lastSerialNumber()
- // Change the manager's certificate. This should cause the client to shut down
- // its connections to the server.
- m.setCurrent(client2CertData.certificate)
- for i := 0; i < 5; i++ {
- time.Sleep(time.Millisecond * 10)
- client.Get().Do(context.TODO())
- if firstCertSerial.Cmp(lastSerialNumber()) != 0 {
- // The certificate changed!
- return
- }
- }
- t.Errorf("certificate rotated but client never reconnected with new cert")
- }
|