Merge pull request #91754 from liggitt/csr-v1-client-go-manager

CSR v1 - switch client-go certificate manager utility to v1 by default
This commit is contained in:
Kubernetes Prow Robot
2020-06-10 04:39:52 -07:00
committed by GitHub
15 changed files with 537 additions and 263 deletions

View File

@@ -16,13 +16,12 @@ go_library(
deps = [
"//pkg/kubelet/apis/config:go_default_library",
"//pkg/kubelet/metrics:go_default_library",
"//staging/src/k8s.io/api/certificates/v1beta1:go_default_library",
"//staging/src/k8s.io/api/certificates/v1:go_default_library",
"//staging/src/k8s.io/api/core/v1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/types:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/net:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/wait:go_default_library",
"//staging/src/k8s.io/client-go/kubernetes:go_default_library",
"//staging/src/k8s.io/client-go/kubernetes/typed/certificates/v1beta1:go_default_library",
"//staging/src/k8s.io/client-go/rest:go_default_library",
"//staging/src/k8s.io/client-go/util/certificate:go_default_library",
"//staging/src/k8s.io/client-go/util/connrotation:go_default_library",

View File

@@ -11,12 +11,16 @@ go_test(
srcs = ["bootstrap_test.go"],
embed = [":go_default_library"],
deps = [
"//staging/src/k8s.io/api/certificates/v1beta1:go_default_library",
"//staging/src/k8s.io/api/certificates/v1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/api/errors:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/diff:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/watch:go_default_library",
"//staging/src/k8s.io/client-go/kubernetes/fake:go_default_library",
"//staging/src/k8s.io/client-go/kubernetes/typed/certificates/v1beta1:go_default_library",
"//staging/src/k8s.io/client-go/rest:go_default_library",
"//staging/src/k8s.io/client-go/testing:go_default_library",
"//staging/src/k8s.io/client-go/util/keyutil:go_default_library",
],
)
@@ -26,12 +30,12 @@ go_library(
srcs = ["bootstrap.go"],
importpath = "k8s.io/kubernetes/pkg/kubelet/certificate/bootstrap",
deps = [
"//staging/src/k8s.io/api/certificates/v1beta1:go_default_library",
"//staging/src/k8s.io/api/certificates/v1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/types:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/runtime:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/wait:go_default_library",
"//staging/src/k8s.io/client-go/kubernetes:go_default_library",
"//staging/src/k8s.io/client-go/kubernetes/scheme:go_default_library",
"//staging/src/k8s.io/client-go/kubernetes/typed/certificates/v1beta1:go_default_library",
"//staging/src/k8s.io/client-go/rest:go_default_library",
"//staging/src/k8s.io/client-go/tools/clientcmd:go_default_library",
"//staging/src/k8s.io/client-go/tools/clientcmd/api:go_default_library",

View File

@@ -31,12 +31,12 @@ import (
"k8s.io/klog/v2"
certificates "k8s.io/api/certificates/v1beta1"
certificatesv1 "k8s.io/api/certificates/v1"
"k8s.io/apimachinery/pkg/types"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/apimachinery/pkg/util/wait"
clientset "k8s.io/client-go/kubernetes"
"k8s.io/client-go/kubernetes/scheme"
certificatesv1beta1 "k8s.io/client-go/kubernetes/typed/certificates/v1beta1"
restclient "k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
@@ -123,7 +123,7 @@ func LoadClientCert(kubeconfigPath, bootstrapPath, certDir string, nodeName type
return fmt.Errorf("unable to load bootstrap kubeconfig: %v", err)
}
bootstrapClient, err := certificatesv1beta1.NewForConfig(bootstrapClientConfig)
bootstrapClient, err := clientset.NewForConfig(bootstrapClientConfig)
if err != nil {
return fmt.Errorf("unable to create certificates signing request client: %v", err)
}
@@ -160,7 +160,7 @@ func LoadClientCert(kubeconfigPath, bootstrapPath, certDir string, nodeName type
klog.Warningf("Error waiting for apiserver to come up: %v", err)
}
certData, err := requestNodeCertificate(bootstrapClient.CertificateSigningRequests(), keyData, nodeName)
certData, err := requestNodeCertificate(bootstrapClient, keyData, nodeName)
if err != nil {
return err
}
@@ -312,7 +312,7 @@ func waitForServer(cfg restclient.Config, deadline time.Duration) error {
// certificate (pem-encoded). If there is any errors, or the watch timeouts, it
// will return an error. This is intended for use on nodes (kubelet and
// kubeadm).
func requestNodeCertificate(client certificatesv1beta1.CertificateSigningRequestInterface, privateKeyData []byte, nodeName types.NodeName) (certData []byte, err error) {
func requestNodeCertificate(client clientset.Interface, privateKeyData []byte, nodeName types.NodeName) (certData []byte, err error) {
subject := &pkix.Name{
Organization: []string{"system:nodes"},
CommonName: "system:node:" + string(nodeName),
@@ -327,10 +327,10 @@ func requestNodeCertificate(client certificatesv1beta1.CertificateSigningRequest
return nil, fmt.Errorf("unable to generate certificate request: %v", err)
}
usages := []certificates.KeyUsage{
certificates.UsageDigitalSignature,
certificates.UsageKeyEncipherment,
certificates.UsageClientAuth,
usages := []certificatesv1.KeyUsage{
certificatesv1.UsageDigitalSignature,
certificatesv1.UsageKeyEncipherment,
certificatesv1.UsageClientAuth,
}
// The Signer interface contains the Public() method to get the public key.
@@ -344,7 +344,7 @@ func requestNodeCertificate(client certificatesv1beta1.CertificateSigningRequest
return nil, err
}
req, err := csr.RequestCertificate(client, csrData, name, certificates.KubeAPIServerClientKubeletSignerName, usages, privateKey)
reqName, reqUID, err := csr.RequestCertificate(client, csrData, name, certificatesv1.KubeAPIServerClientKubeletSignerName, usages, privateKey)
if err != nil {
return nil, err
}
@@ -353,7 +353,7 @@ func requestNodeCertificate(client certificatesv1beta1.CertificateSigningRequest
defer cancel()
klog.V(2).Infof("Waiting for client certificate to be issued")
return csr.WaitForCertificate(ctx, client, req)
return csr.WaitForCertificate(ctx, client, reqName, reqUID)
}
// This digest should include all the relevant pieces of the CSR we care about.
@@ -361,7 +361,7 @@ func requestNodeCertificate(client certificatesv1beta1.CertificateSigningRequest
// regenerate every loop and we include usages which are not contained in the
// CSR. This needs to be kept up to date as we add new fields to the node
// certificates and with ensureCompatible.
func digestedName(publicKey interface{}, subject *pkix.Name, usages []certificates.KeyUsage) (string, error) {
func digestedName(publicKey interface{}, subject *pkix.Name, usages []certificatesv1.KeyUsage) (string, error) {
hash := sha512.New512_256()
// Here we make sure two different inputs can't write the same stream

View File

@@ -17,19 +17,22 @@ limitations under the License.
package bootstrap
import (
"context"
"fmt"
"io/ioutil"
"os"
"reflect"
"testing"
certificates "k8s.io/api/certificates/v1beta1"
certificatesv1 "k8s.io/api/certificates/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/diff"
"k8s.io/apimachinery/pkg/watch"
"k8s.io/client-go/kubernetes/fake"
certificatesclient "k8s.io/client-go/kubernetes/typed/certificates/v1beta1"
restclient "k8s.io/client-go/rest"
clienttesting "k8s.io/client-go/testing"
"k8s.io/client-go/util/keyutil"
)
@@ -92,7 +95,7 @@ users:
}
func TestRequestNodeCertificateNoKeyData(t *testing.T) {
certData, err := requestNodeCertificate(&fakeClient{}, []byte{}, "fake-node-name")
certData, err := requestNodeCertificate(newClientset(fakeClient{}), []byte{}, "fake-node-name")
if err == nil {
t.Errorf("Got no error, wanted error an error because there was an empty private key passed in.")
}
@@ -102,9 +105,9 @@ func TestRequestNodeCertificateNoKeyData(t *testing.T) {
}
func TestRequestNodeCertificateErrorCreatingCSR(t *testing.T) {
client := &fakeClient{
client := newClientset(fakeClient{
failureType: createError,
}
})
privateKeyData, err := keyutil.MakeEllipticPrivateKeyPEM()
if err != nil {
t.Fatalf("Unable to generate a new private key: %v", err)
@@ -125,7 +128,7 @@ func TestRequestNodeCertificate(t *testing.T) {
t.Fatalf("Unable to generate a new private key: %v", err)
}
certData, err := requestNodeCertificate(&fakeClient{}, privateKeyData, "fake-node-name")
certData, err := requestNodeCertificate(newClientset(fakeClient{}), privateKeyData, "fake-node-name")
if err != nil {
t.Errorf("Got %v, wanted no error.", err)
}
@@ -144,54 +147,74 @@ const (
type fakeClient struct {
certificatesclient.CertificateSigningRequestInterface
watch *watch.FakeWatcher
failureType failureType
}
func (c *fakeClient) Create(context.Context, *certificates.CertificateSigningRequest, metav1.CreateOptions) (*certificates.CertificateSigningRequest, error) {
if c.failureType == createError {
return nil, fmt.Errorf("fakeClient failed creating request")
func newClientset(opts fakeClient) *fake.Clientset {
f := fake.NewSimpleClientset()
switch opts.failureType {
case createError:
f.PrependReactor("create", "certificatesigningrequests", func(action clienttesting.Action) (handled bool, ret runtime.Object, err error) {
switch action.GetResource().Version {
case "v1":
return true, nil, fmt.Errorf("create error")
default:
return true, nil, apierrors.NewNotFound(certificatesv1.Resource("certificatesigningrequests"), "")
}
})
default:
f.PrependReactor("create", "certificatesigningrequests", func(action clienttesting.Action) (handled bool, ret runtime.Object, err error) {
switch action.GetResource().Version {
case "v1":
return true, &certificatesv1.CertificateSigningRequest{ObjectMeta: metav1.ObjectMeta{Name: "fake-certificate-signing-request-name", UID: "fake-uid"}}, nil
default:
return true, nil, apierrors.NewNotFound(certificatesv1.Resource("certificatesigningrequests"), "")
}
})
f.PrependReactor("list", "certificatesigningrequests", func(action clienttesting.Action) (handled bool, ret runtime.Object, err error) {
switch action.GetResource().Version {
case "v1":
return true, &certificatesv1.CertificateSigningRequestList{Items: []certificatesv1.CertificateSigningRequest{{ObjectMeta: metav1.ObjectMeta{Name: "fake-certificate-signing-request-name", UID: "fake-uid"}}}}, nil
default:
return true, nil, apierrors.NewNotFound(certificatesv1.Resource("certificatesigningrequests"), "")
}
})
f.PrependWatchReactor("certificatesigningrequests", func(action clienttesting.Action) (handled bool, ret watch.Interface, err error) {
switch action.GetResource().Version {
case "v1":
w := watch.NewFakeWithChanSize(1, false)
w.Add(opts.generateCSR())
w.Stop()
return true, w, nil
default:
return true, nil, apierrors.NewNotFound(certificatesv1.Resource("certificatesigningrequests"), "")
}
})
}
csr := certificates.CertificateSigningRequest{
ObjectMeta: metav1.ObjectMeta{
UID: "fake-uid",
Name: "fake-certificate-signing-request-name",
},
}
return &csr, nil
return f
}
func (c *fakeClient) List(_ context.Context, opts metav1.ListOptions) (*certificates.CertificateSigningRequestList, error) {
return &certificates.CertificateSigningRequestList{}, nil
}
func (c *fakeClient) Watch(_ context.Context, opts metav1.ListOptions) (watch.Interface, error) {
c.watch = watch.NewFakeWithChanSize(1, false)
c.watch.Add(c.generateCSR())
c.watch.Stop()
return c.watch, nil
}
func (c *fakeClient) generateCSR() *certificates.CertificateSigningRequest {
var condition certificates.CertificateSigningRequestCondition
func (c fakeClient) generateCSR() runtime.Object {
var condition certificatesv1.CertificateSigningRequestCondition
var certificateData []byte
if c.failureType == certificateSigningRequestDenied {
condition = certificates.CertificateSigningRequestCondition{
Type: certificates.CertificateDenied,
condition = certificatesv1.CertificateSigningRequestCondition{
Type: certificatesv1.CertificateDenied,
}
} else {
condition = certificates.CertificateSigningRequestCondition{
Type: certificates.CertificateApproved,
condition = certificatesv1.CertificateSigningRequestCondition{
Type: certificatesv1.CertificateApproved,
}
certificateData = []byte(`issued certificate`)
}
csr := certificates.CertificateSigningRequest{
csr := certificatesv1.CertificateSigningRequest{
ObjectMeta: metav1.ObjectMeta{
UID: "fake-uid",
},
Status: certificates.CertificateSigningRequestStatus{
Conditions: []certificates.CertificateSigningRequestCondition{
Status: certificatesv1.CertificateSigningRequestStatus{
Conditions: []certificatesv1.CertificateSigningRequestCondition{
condition,
},
Certificate: certificateData,

View File

@@ -26,11 +26,10 @@ import (
"sort"
"time"
certificates "k8s.io/api/certificates/v1beta1"
certificates "k8s.io/api/certificates/v1"
v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/types"
clientset "k8s.io/client-go/kubernetes"
certificatesclient "k8s.io/client-go/kubernetes/typed/certificates/v1beta1"
"k8s.io/client-go/util/certificate"
compbasemetrics "k8s.io/component-base/metrics"
"k8s.io/component-base/metrics/legacyregistry"
@@ -41,9 +40,11 @@ import (
// NewKubeletServerCertificateManager creates a certificate manager for the kubelet when retrieving a server certificate
// or returns an error.
func NewKubeletServerCertificateManager(kubeClient clientset.Interface, kubeCfg *kubeletconfig.KubeletConfiguration, nodeName types.NodeName, getAddresses func() []v1.NodeAddress, certDirectory string) (certificate.Manager, error) {
var certSigningRequestClient certificatesclient.CertificateSigningRequestInterface
if kubeClient != nil && kubeClient.CertificatesV1beta1() != nil {
certSigningRequestClient = kubeClient.CertificatesV1beta1().CertificateSigningRequests()
var clientsetFn certificate.ClientsetFunc
if kubeClient != nil {
clientsetFn = func(current *tls.Certificate) (clientset.Interface, error) {
return kubeClient, nil
}
}
certificateStore, err := certificate.NewFileStore(
"kubelet-server",
@@ -103,9 +104,7 @@ func NewKubeletServerCertificateManager(kubeClient clientset.Interface, kubeCfg
}
m, err := certificate.NewManager(&certificate.Config{
ClientFn: func(current *tls.Certificate) (certificatesclient.CertificateSigningRequestInterface, error) {
return certSigningRequestClient, nil
},
ClientsetFn: clientsetFn,
GetTemplate: getTemplate,
SignerName: certificates.KubeletServingSignerName,
Usages: []certificates.KeyUsage{
@@ -198,7 +197,7 @@ func NewKubeletClientCertificateManager(
bootstrapKeyData []byte,
certFile string,
keyFile string,
clientFn certificate.CSRClientFunc,
clientsetFn certificate.ClientsetFunc,
) (certificate.Manager, error) {
certificateStore, err := certificate.NewFileStore(
@@ -222,7 +221,7 @@ func NewKubeletClientCertificateManager(
legacyregistry.Register(certificateRenewFailure)
m, err := certificate.NewManager(&certificate.Config{
ClientFn: clientFn,
ClientsetFn: clientsetFn,
Template: &x509.CertificateRequest{
Subject: pkix.Name{
CommonName: fmt.Sprintf("system:node:%s", nodeName),