From d2032fd83ce9875f13fda4b2cddfda7e34083e84 Mon Sep 17 00:00:00 2001 From: Mike Danese Date: Tue, 10 Jan 2017 16:23:53 -0800 Subject: [PATCH] kubelet: request client auth certificates from certificate API. --- pkg/controller/certificates/BUILD | 5 +- pkg/controller/certificates/groupapprove.go | 28 ++++++++ .../certificates/groupapprove_test.go | 71 +++++++++++++++++++ pkg/kubelet/util/csr/csr.go | 11 ++- 4 files changed, 111 insertions(+), 4 deletions(-) create mode 100644 pkg/controller/certificates/groupapprove_test.go diff --git a/pkg/controller/certificates/BUILD b/pkg/controller/certificates/BUILD index 3b661ba9971..1884128ece7 100644 --- a/pkg/controller/certificates/BUILD +++ b/pkg/controller/certificates/BUILD @@ -56,7 +56,10 @@ filegroup( go_test( name = "go_default_test", - srcs = ["cfssl_signer_test.go"], + srcs = [ + "cfssl_signer_test.go", + "groupapprove_test.go", + ], data = [ "testdata/ca.crt", "testdata/ca.key", diff --git a/pkg/controller/certificates/groupapprove.go b/pkg/controller/certificates/groupapprove.go index c3e913004b8..756db9449c6 100644 --- a/pkg/controller/certificates/groupapprove.go +++ b/pkg/controller/certificates/groupapprove.go @@ -76,6 +76,9 @@ func (cc *groupApprover) AutoApprove(csr *certificates.CertificateSigningRequest if len(x509cr.DNSNames)+len(x509cr.EmailAddresses)+len(x509cr.IPAddresses) != 0 { return csr, nil } + if !hasExactUsages(csr, kubeletClientUsages) { + return csr, nil + } csr.Status.Conditions = append(csr.Status.Conditions, certificates.CertificateSigningRequestCondition{ Type: certificates.CertificateApproved, @@ -84,3 +87,28 @@ func (cc *groupApprover) AutoApprove(csr *certificates.CertificateSigningRequest }) return cc.client.UpdateApproval(csr) } + +var kubeletClientUsages = []certificates.KeyUsage{ + certificates.UsageKeyEncipherment, + certificates.UsageDigitalSignature, + certificates.UsageClientAuth, +} + +func hasExactUsages(csr *certificates.CertificateSigningRequest, usages []certificates.KeyUsage) bool { + if len(usages) != len(csr.Spec.Usages) { + return false + } + + usageMap := map[certificates.KeyUsage]struct{}{} + for _, u := range usages { + usageMap[u] = struct{}{} + } + + for _, u := range csr.Spec.Usages { + if _, ok := usageMap[u]; !ok { + return false + } + } + + return true +} diff --git a/pkg/controller/certificates/groupapprove_test.go b/pkg/controller/certificates/groupapprove_test.go new file mode 100644 index 00000000000..881349fcea7 --- /dev/null +++ b/pkg/controller/certificates/groupapprove_test.go @@ -0,0 +1,71 @@ +/* +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 certificates + +import ( + "testing" + + certificates "k8s.io/kubernetes/pkg/apis/certificates/v1alpha1" +) + +func TestHasKubeletUsages(t *testing.T) { + cases := []struct { + usages []certificates.KeyUsage + expected bool + }{ + { + usages: nil, + expected: false, + }, + { + usages: []certificates.KeyUsage{}, + expected: false, + }, + { + usages: []certificates.KeyUsage{ + certificates.UsageKeyEncipherment, + certificates.UsageDigitalSignature, + }, + expected: false, + }, + { + usages: []certificates.KeyUsage{ + certificates.UsageKeyEncipherment, + certificates.UsageDigitalSignature, + certificates.UsageServerAuth, + }, + expected: false, + }, + { + usages: []certificates.KeyUsage{ + certificates.UsageKeyEncipherment, + certificates.UsageDigitalSignature, + certificates.UsageClientAuth, + }, + expected: true, + }, + } + for _, c := range cases { + if hasExactUsages(&certificates.CertificateSigningRequest{ + Spec: certificates.CertificateSigningRequestSpec{ + Usages: c.usages, + }, + }, kubeletClientUsages) != c.expected { + t.Errorf("unexpected result of hasKubeletUsages(%v), expecting: %v", c.usages, c.expected) + } + } +} diff --git a/pkg/kubelet/util/csr/csr.go b/pkg/kubelet/util/csr/csr.go index 464efd1c937..cc41bc7b2a8 100644 --- a/pkg/kubelet/util/csr/csr.go +++ b/pkg/kubelet/util/csr/csr.go @@ -54,9 +54,14 @@ func RequestNodeCertificate(client unversionedcertificates.CertificateSigningReq TypeMeta: metav1.TypeMeta{Kind: "CertificateSigningRequest"}, ObjectMeta: v1.ObjectMeta{GenerateName: "csr-"}, - // TODO: For now, this is a request for a certificate with allowed usage of "TLS Web Client Authentication". - // Need to figure out whether/how to surface the allowed usage in the spec. - Spec: certificates.CertificateSigningRequestSpec{Request: csr}, + Spec: certificates.CertificateSigningRequestSpec{ + Request: csr, + Usages: []certificates.KeyUsage{ + certificates.UsageDigitalSignature, + certificates.UsageKeyEncipherment, + certificates.UsageClientAuth, + }, + }, }) if err != nil { return nil, fmt.Errorf("cannot create certificate signing request: %v", err)