mirror of
				https://github.com/k3s-io/kubernetes.git
				synced 2025-11-04 07:49:35 +00:00 
			
		
		
		
	If we go a certain amount of time without being able to create a client cert and we have no current client cert from the store, exit. This prevents a corrupted local copy of the cert from leaving the Kubelet in a zombie state forever. Exiting allows a config loop outside the Kubelet to clean up the file or the bootstrap client cert to get another client cert.
		
			
				
	
	
		
			219 lines
		
	
	
		
			7.5 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			219 lines
		
	
	
		
			7.5 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
/*
 | 
						|
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 (
 | 
						|
	"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) 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().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()
 | 
						|
		if firstCertSerial.Cmp(lastSerialNumber()) != 0 {
 | 
						|
			// The certificate changed!
 | 
						|
			return
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	t.Errorf("certificate rotated but client never reconnected with new cert")
 | 
						|
}
 |