Replace godep with dep
This commit is contained in:
parent
1e7489927c
commit
bf5616c65b
14883 changed files with 3937406 additions and 361781 deletions
66
vendor/k8s.io/kubernetes/pkg/controller/certificates/BUILD
generated
vendored
Normal file
66
vendor/k8s.io/kubernetes/pkg/controller/certificates/BUILD
generated
vendored
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"certificate_controller.go",
|
||||
"certificate_controller_utils.go",
|
||||
],
|
||||
visibility = [
|
||||
":__subpackages__",
|
||||
"//cmd/gke-certificates-controller:__subpackages__",
|
||||
],
|
||||
deps = [
|
||||
"//pkg/controller:go_default_library",
|
||||
"//vendor/github.com/golang/glog:go_default_library",
|
||||
"//vendor/k8s.io/api/certificates/v1beta1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/runtime:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/wait:go_default_library",
|
||||
"//vendor/k8s.io/client-go/informers/certificates/v1beta1:go_default_library",
|
||||
"//vendor/k8s.io/client-go/kubernetes:go_default_library",
|
||||
"//vendor/k8s.io/client-go/kubernetes/typed/core/v1:go_default_library",
|
||||
"//vendor/k8s.io/client-go/listers/certificates/v1beta1:go_default_library",
|
||||
"//vendor/k8s.io/client-go/tools/cache:go_default_library",
|
||||
"//vendor/k8s.io/client-go/tools/record:go_default_library",
|
||||
"//vendor/k8s.io/client-go/util/workqueue:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [
|
||||
":package-srcs",
|
||||
"//pkg/controller/certificates/approver:all-srcs",
|
||||
"//pkg/controller/certificates/signer:all-srcs",
|
||||
],
|
||||
tags = ["automanaged"],
|
||||
visibility = [
|
||||
"//pkg/controller:__pkg__",
|
||||
],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = ["certificate_controller_test.go"],
|
||||
library = ":go_default_library",
|
||||
deps = [
|
||||
"//pkg/controller:go_default_library",
|
||||
"//vendor/k8s.io/api/certificates/v1beta1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/wait:go_default_library",
|
||||
"//vendor/k8s.io/client-go/informers:go_default_library",
|
||||
"//vendor/k8s.io/client-go/kubernetes/fake:go_default_library",
|
||||
],
|
||||
)
|
||||
3
vendor/k8s.io/kubernetes/pkg/controller/certificates/OWNERS
generated
vendored
Executable file
3
vendor/k8s.io/kubernetes/pkg/controller/certificates/OWNERS
generated
vendored
Executable file
|
|
@ -0,0 +1,3 @@
|
|||
reviewers:
|
||||
- deads2k
|
||||
- mikedanese
|
||||
50
vendor/k8s.io/kubernetes/pkg/controller/certificates/approver/BUILD
generated
vendored
Normal file
50
vendor/k8s.io/kubernetes/pkg/controller/certificates/approver/BUILD
generated
vendored
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = ["sarapprove_test.go"],
|
||||
library = ":go_default_library",
|
||||
deps = [
|
||||
"//pkg/apis/certificates/v1beta1:go_default_library",
|
||||
"//vendor/k8s.io/api/authorization/v1beta1:go_default_library",
|
||||
"//vendor/k8s.io/api/certificates/v1beta1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
|
||||
"//vendor/k8s.io/client-go/kubernetes/fake:go_default_library",
|
||||
"//vendor/k8s.io/client-go/testing:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["sarapprove.go"],
|
||||
deps = [
|
||||
"//pkg/apis/certificates/v1beta1:go_default_library",
|
||||
"//pkg/controller/certificates:go_default_library",
|
||||
"//pkg/features:go_default_library",
|
||||
"//vendor/k8s.io/api/authorization/v1beta1:go_default_library",
|
||||
"//vendor/k8s.io/api/certificates/v1beta1:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/util/feature:go_default_library",
|
||||
"//vendor/k8s.io/client-go/informers/certificates/v1beta1:go_default_library",
|
||||
"//vendor/k8s.io/client-go/kubernetes:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
||||
228
vendor/k8s.io/kubernetes/pkg/controller/certificates/approver/sarapprove.go
generated
vendored
Normal file
228
vendor/k8s.io/kubernetes/pkg/controller/certificates/approver/sarapprove.go
generated
vendored
Normal file
|
|
@ -0,0 +1,228 @@
|
|||
/*
|
||||
Copyright 2016 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 approver implements an automated approver for kubelet certificates.
|
||||
package approver
|
||||
|
||||
import (
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
authorization "k8s.io/api/authorization/v1beta1"
|
||||
capi "k8s.io/api/certificates/v1beta1"
|
||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||
certificatesinformers "k8s.io/client-go/informers/certificates/v1beta1"
|
||||
clientset "k8s.io/client-go/kubernetes"
|
||||
k8s_certificates_v1beta1 "k8s.io/kubernetes/pkg/apis/certificates/v1beta1"
|
||||
"k8s.io/kubernetes/pkg/controller/certificates"
|
||||
"k8s.io/kubernetes/pkg/features"
|
||||
)
|
||||
|
||||
type csrRecognizer struct {
|
||||
recognize func(csr *capi.CertificateSigningRequest, x509cr *x509.CertificateRequest) bool
|
||||
permission authorization.ResourceAttributes
|
||||
successMessage string
|
||||
}
|
||||
|
||||
type sarApprover struct {
|
||||
client clientset.Interface
|
||||
recognizers []csrRecognizer
|
||||
}
|
||||
|
||||
func NewCSRApprovingController(client clientset.Interface, csrInformer certificatesinformers.CertificateSigningRequestInformer) (*certificates.CertificateController, error) {
|
||||
approver := &sarApprover{
|
||||
client: client,
|
||||
recognizers: recognizers(),
|
||||
}
|
||||
return certificates.NewCertificateController(
|
||||
client,
|
||||
csrInformer,
|
||||
approver.handle,
|
||||
)
|
||||
}
|
||||
|
||||
func recognizers() []csrRecognizer {
|
||||
recognizers := []csrRecognizer{
|
||||
{
|
||||
recognize: isSelfNodeClientCert,
|
||||
permission: authorization.ResourceAttributes{Group: "certificates.k8s.io", Resource: "certificatesigningrequests", Verb: "create", Subresource: "selfnodeclient"},
|
||||
successMessage: "Auto approving self kubelet client certificate after SubjectAccessReview.",
|
||||
},
|
||||
{
|
||||
recognize: isNodeClientCert,
|
||||
permission: authorization.ResourceAttributes{Group: "certificates.k8s.io", Resource: "certificatesigningrequests", Verb: "create", Subresource: "nodeclient"},
|
||||
successMessage: "Auto approving kubelet client certificate after SubjectAccessReview.",
|
||||
},
|
||||
}
|
||||
if utilfeature.DefaultFeatureGate.Enabled(features.RotateKubeletServerCertificate) {
|
||||
recognizers = append(recognizers, csrRecognizer{
|
||||
recognize: isSelfNodeServerCert,
|
||||
permission: authorization.ResourceAttributes{Group: "certificates.k8s.io", Resource: "certificatesigningrequests", Verb: "create", Subresource: "selfnodeserver"},
|
||||
successMessage: "Auto approving self kubelet server certificate after SubjectAccessReview.",
|
||||
})
|
||||
}
|
||||
return recognizers
|
||||
}
|
||||
|
||||
func (a *sarApprover) handle(csr *capi.CertificateSigningRequest) error {
|
||||
if len(csr.Status.Certificate) != 0 {
|
||||
return nil
|
||||
}
|
||||
if approved, denied := certificates.GetCertApprovalCondition(&csr.Status); approved || denied {
|
||||
return nil
|
||||
}
|
||||
x509cr, err := k8s_certificates_v1beta1.ParseCSR(csr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to parse csr %q: %v", csr.Name, err)
|
||||
}
|
||||
|
||||
tried := []string{}
|
||||
|
||||
for _, r := range a.recognizers {
|
||||
if !r.recognize(csr, x509cr) {
|
||||
continue
|
||||
}
|
||||
|
||||
tried = append(tried, r.permission.Subresource)
|
||||
|
||||
approved, err := a.authorize(csr, r.permission)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if approved {
|
||||
appendApprovalCondition(csr, r.successMessage)
|
||||
_, err = a.client.Certificates().CertificateSigningRequests().UpdateApproval(csr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error updating approval for csr: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
if len(tried) != 0 {
|
||||
return fmt.Errorf("recognized csr %q as %v but subject access review was not approved", csr.Name, tried)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *sarApprover) authorize(csr *capi.CertificateSigningRequest, rattrs authorization.ResourceAttributes) (bool, error) {
|
||||
extra := make(map[string]authorization.ExtraValue)
|
||||
for k, v := range csr.Spec.Extra {
|
||||
extra[k] = authorization.ExtraValue(v)
|
||||
}
|
||||
|
||||
sar := &authorization.SubjectAccessReview{
|
||||
Spec: authorization.SubjectAccessReviewSpec{
|
||||
User: csr.Spec.Username,
|
||||
UID: csr.Spec.UID,
|
||||
Groups: csr.Spec.Groups,
|
||||
Extra: extra,
|
||||
ResourceAttributes: &rattrs,
|
||||
},
|
||||
}
|
||||
sar, err := a.client.AuthorizationV1beta1().SubjectAccessReviews().Create(sar)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return sar.Status.Allowed, nil
|
||||
}
|
||||
|
||||
func appendApprovalCondition(csr *capi.CertificateSigningRequest, message string) {
|
||||
csr.Status.Conditions = append(csr.Status.Conditions, capi.CertificateSigningRequestCondition{
|
||||
Type: capi.CertificateApproved,
|
||||
Reason: "AutoApproved",
|
||||
Message: message,
|
||||
})
|
||||
}
|
||||
|
||||
func hasExactUsages(csr *capi.CertificateSigningRequest, usages []capi.KeyUsage) bool {
|
||||
if len(usages) != len(csr.Spec.Usages) {
|
||||
return false
|
||||
}
|
||||
|
||||
usageMap := map[capi.KeyUsage]struct{}{}
|
||||
for _, u := range usages {
|
||||
usageMap[u] = struct{}{}
|
||||
}
|
||||
|
||||
for _, u := range csr.Spec.Usages {
|
||||
if _, ok := usageMap[u]; !ok {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
var kubeletClientUsages = []capi.KeyUsage{
|
||||
capi.UsageKeyEncipherment,
|
||||
capi.UsageDigitalSignature,
|
||||
capi.UsageClientAuth,
|
||||
}
|
||||
|
||||
func isNodeClientCert(csr *capi.CertificateSigningRequest, x509cr *x509.CertificateRequest) bool {
|
||||
if !reflect.DeepEqual([]string{"system:nodes"}, x509cr.Subject.Organization) {
|
||||
return false
|
||||
}
|
||||
if (len(x509cr.DNSNames) > 0) || (len(x509cr.EmailAddresses) > 0) || (len(x509cr.IPAddresses) > 0) {
|
||||
return false
|
||||
}
|
||||
if !hasExactUsages(csr, kubeletClientUsages) {
|
||||
return false
|
||||
}
|
||||
if !strings.HasPrefix(x509cr.Subject.CommonName, "system:node:") {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func isSelfNodeClientCert(csr *capi.CertificateSigningRequest, x509cr *x509.CertificateRequest) bool {
|
||||
if !isNodeClientCert(csr, x509cr) {
|
||||
return false
|
||||
}
|
||||
if csr.Spec.Username != x509cr.Subject.CommonName {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
var kubeletServerUsages = []capi.KeyUsage{
|
||||
capi.UsageKeyEncipherment,
|
||||
capi.UsageDigitalSignature,
|
||||
capi.UsageServerAuth,
|
||||
}
|
||||
|
||||
func isSelfNodeServerCert(csr *capi.CertificateSigningRequest, x509cr *x509.CertificateRequest) bool {
|
||||
if !reflect.DeepEqual([]string{"system:nodes"}, x509cr.Subject.Organization) {
|
||||
return false
|
||||
}
|
||||
if len(x509cr.DNSNames) == 0 || len(x509cr.IPAddresses) == 0 {
|
||||
return false
|
||||
}
|
||||
if !hasExactUsages(csr, kubeletServerUsages) {
|
||||
return false
|
||||
}
|
||||
if !strings.HasPrefix(x509cr.Subject.CommonName, "system:node:") {
|
||||
return false
|
||||
}
|
||||
if csr.Spec.Username != x509cr.Subject.CommonName {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
370
vendor/k8s.io/kubernetes/pkg/controller/certificates/approver/sarapprove_test.go
generated
vendored
Normal file
370
vendor/k8s.io/kubernetes/pkg/controller/certificates/approver/sarapprove_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,370 @@
|
|||
/*
|
||||
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 approver
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"net"
|
||||
"testing"
|
||||
|
||||
authorization "k8s.io/api/authorization/v1beta1"
|
||||
capi "k8s.io/api/certificates/v1beta1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/client-go/kubernetes/fake"
|
||||
testclient "k8s.io/client-go/testing"
|
||||
k8s_certificates_v1beta1 "k8s.io/kubernetes/pkg/apis/certificates/v1beta1"
|
||||
)
|
||||
|
||||
func TestHasKubeletUsages(t *testing.T) {
|
||||
cases := []struct {
|
||||
usages []capi.KeyUsage
|
||||
expected bool
|
||||
}{
|
||||
{
|
||||
usages: nil,
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
usages: []capi.KeyUsage{},
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
usages: []capi.KeyUsage{
|
||||
capi.UsageKeyEncipherment,
|
||||
capi.UsageDigitalSignature,
|
||||
},
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
usages: []capi.KeyUsage{
|
||||
capi.UsageKeyEncipherment,
|
||||
capi.UsageDigitalSignature,
|
||||
capi.UsageServerAuth,
|
||||
},
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
usages: []capi.KeyUsage{
|
||||
capi.UsageKeyEncipherment,
|
||||
capi.UsageDigitalSignature,
|
||||
capi.UsageClientAuth,
|
||||
},
|
||||
expected: true,
|
||||
},
|
||||
}
|
||||
for _, c := range cases {
|
||||
if hasExactUsages(&capi.CertificateSigningRequest{
|
||||
Spec: capi.CertificateSigningRequestSpec{
|
||||
Usages: c.usages,
|
||||
},
|
||||
}, kubeletClientUsages) != c.expected {
|
||||
t.Errorf("unexpected result of hasKubeletUsages(%v), expecting: %v", c.usages, c.expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandle(t *testing.T) {
|
||||
cases := []struct {
|
||||
message string
|
||||
allowed bool
|
||||
recognized bool
|
||||
err bool
|
||||
verify func(*testing.T, []testclient.Action)
|
||||
}{
|
||||
{
|
||||
recognized: false,
|
||||
allowed: false,
|
||||
verify: func(t *testing.T, as []testclient.Action) {
|
||||
if len(as) != 0 {
|
||||
t.Errorf("expected no client calls but got: %#v", as)
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
recognized: false,
|
||||
allowed: true,
|
||||
verify: func(t *testing.T, as []testclient.Action) {
|
||||
if len(as) != 0 {
|
||||
t.Errorf("expected no client calls but got: %#v", as)
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
recognized: true,
|
||||
allowed: false,
|
||||
verify: func(t *testing.T, as []testclient.Action) {
|
||||
if len(as) != 1 {
|
||||
t.Errorf("expected 1 call but got: %#v", as)
|
||||
return
|
||||
}
|
||||
_ = as[0].(testclient.CreateActionImpl)
|
||||
},
|
||||
err: true,
|
||||
},
|
||||
{
|
||||
recognized: true,
|
||||
allowed: true,
|
||||
verify: func(t *testing.T, as []testclient.Action) {
|
||||
if len(as) != 2 {
|
||||
t.Errorf("expected two calls but got: %#v", as)
|
||||
return
|
||||
}
|
||||
_ = as[0].(testclient.CreateActionImpl)
|
||||
a := as[1].(testclient.UpdateActionImpl)
|
||||
if got, expected := a.Verb, "update"; got != expected {
|
||||
t.Errorf("got: %v, expected: %v", got, expected)
|
||||
}
|
||||
if got, expected := a.Resource, (schema.GroupVersionResource{Group: "certificates.k8s.io", Version: "v1beta1", Resource: "certificatesigningrequests"}); got != expected {
|
||||
t.Errorf("got: %v, expected: %v", got, expected)
|
||||
}
|
||||
if got, expected := a.Subresource, "approval"; got != expected {
|
||||
t.Errorf("got: %v, expected: %v", got, expected)
|
||||
}
|
||||
csr := a.Object.(*capi.CertificateSigningRequest)
|
||||
if len(csr.Status.Conditions) != 1 {
|
||||
t.Errorf("expected CSR to have approved condition: %#v", csr)
|
||||
}
|
||||
c := csr.Status.Conditions[0]
|
||||
if got, expected := c.Type, capi.CertificateApproved; got != expected {
|
||||
t.Errorf("got: %v, expected: %v", got, expected)
|
||||
}
|
||||
if got, expected := c.Reason, "AutoApproved"; got != expected {
|
||||
t.Errorf("got: %v, expected: %v", got, expected)
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(fmt.Sprintf("recognized:%v,allowed: %v,err: %v", c.recognized, c.allowed, c.err), func(t *testing.T) {
|
||||
client := &fake.Clientset{}
|
||||
client.AddReactor("create", "subjectaccessreviews", func(action testclient.Action) (handled bool, ret runtime.Object, err error) {
|
||||
return true, &authorization.SubjectAccessReview{
|
||||
Status: authorization.SubjectAccessReviewStatus{
|
||||
Allowed: c.allowed,
|
||||
},
|
||||
}, nil
|
||||
})
|
||||
approver := sarApprover{
|
||||
client: client,
|
||||
recognizers: []csrRecognizer{
|
||||
{
|
||||
successMessage: "tester",
|
||||
permission: authorization.ResourceAttributes{Group: "foo", Resource: "bar", Subresource: "baz"},
|
||||
recognize: func(csr *capi.CertificateSigningRequest, x509cr *x509.CertificateRequest) bool {
|
||||
return c.recognized
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
csr := makeTestCsr()
|
||||
if err := approver.handle(csr); err != nil && !c.err {
|
||||
t.Errorf("unexpected err: %v", err)
|
||||
}
|
||||
c.verify(t, client.Actions())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSelfNodeServerCertRecognizer(t *testing.T) {
|
||||
defaultCSR := csrBuilder{
|
||||
cn: "system:node:foo",
|
||||
orgs: []string{"system:nodes"},
|
||||
requestor: "system:node:foo",
|
||||
usages: []capi.KeyUsage{
|
||||
capi.UsageKeyEncipherment,
|
||||
capi.UsageDigitalSignature,
|
||||
capi.UsageServerAuth,
|
||||
},
|
||||
dns: []string{"node"},
|
||||
ips: []net.IP{net.ParseIP("192.168.0.1")},
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
description string
|
||||
csrBuilder csrBuilder
|
||||
expectedOutcome bool
|
||||
}{
|
||||
{
|
||||
description: "Success - all requirements met",
|
||||
csrBuilder: defaultCSR,
|
||||
expectedOutcome: true,
|
||||
},
|
||||
{
|
||||
description: "No organization",
|
||||
csrBuilder: func(b csrBuilder) csrBuilder {
|
||||
b.orgs = []string{}
|
||||
return b
|
||||
}(defaultCSR),
|
||||
expectedOutcome: false,
|
||||
},
|
||||
{
|
||||
description: "Wrong organization",
|
||||
csrBuilder: func(b csrBuilder) csrBuilder {
|
||||
b.orgs = append(b.orgs, "new-org")
|
||||
return b
|
||||
}(defaultCSR),
|
||||
expectedOutcome: false,
|
||||
},
|
||||
{
|
||||
description: "Wrong usages",
|
||||
csrBuilder: func(b csrBuilder) csrBuilder {
|
||||
b.usages = []capi.KeyUsage{}
|
||||
return b
|
||||
}(defaultCSR),
|
||||
expectedOutcome: false,
|
||||
},
|
||||
{
|
||||
description: "Wrong common name",
|
||||
csrBuilder: func(b csrBuilder) csrBuilder {
|
||||
b.cn = "wrong-common-name"
|
||||
return b
|
||||
}(defaultCSR),
|
||||
expectedOutcome: false,
|
||||
},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.description, func(t *testing.T) {
|
||||
csr := makeFancyTestCsr(tc.csrBuilder)
|
||||
x509cr, err := k8s_certificates_v1beta1.ParseCSR(csr)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected err: %v", err)
|
||||
}
|
||||
if isSelfNodeServerCert(csr, x509cr) != tc.expectedOutcome {
|
||||
t.Errorf("expected recognized to be %v", tc.expectedOutcome)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRecognizers(t *testing.T) {
|
||||
goodCases := []func(b *csrBuilder){
|
||||
func(b *csrBuilder) {
|
||||
},
|
||||
}
|
||||
|
||||
testRecognizer(t, goodCases, isNodeClientCert, true)
|
||||
testRecognizer(t, goodCases, isSelfNodeClientCert, true)
|
||||
|
||||
badCases := []func(b *csrBuilder){
|
||||
func(b *csrBuilder) {
|
||||
b.cn = "mike"
|
||||
},
|
||||
func(b *csrBuilder) {
|
||||
b.orgs = nil
|
||||
},
|
||||
func(b *csrBuilder) {
|
||||
b.orgs = []string{"system:master"}
|
||||
},
|
||||
func(b *csrBuilder) {
|
||||
b.usages = append(b.usages, capi.UsageServerAuth)
|
||||
},
|
||||
}
|
||||
|
||||
testRecognizer(t, badCases, isNodeClientCert, false)
|
||||
testRecognizer(t, badCases, isSelfNodeClientCert, false)
|
||||
|
||||
// cn different then requestor
|
||||
differentCN := []func(b *csrBuilder){
|
||||
func(b *csrBuilder) {
|
||||
b.requestor = "joe"
|
||||
},
|
||||
func(b *csrBuilder) {
|
||||
b.cn = "system:node:bar"
|
||||
},
|
||||
}
|
||||
|
||||
testRecognizer(t, differentCN, isNodeClientCert, true)
|
||||
testRecognizer(t, differentCN, isSelfNodeClientCert, false)
|
||||
}
|
||||
|
||||
func testRecognizer(t *testing.T, cases []func(b *csrBuilder), recognizeFunc func(csr *capi.CertificateSigningRequest, x509cr *x509.CertificateRequest) bool, shouldRecognize bool) {
|
||||
for _, c := range cases {
|
||||
b := csrBuilder{
|
||||
cn: "system:node:foo",
|
||||
orgs: []string{"system:nodes"},
|
||||
requestor: "system:node:foo",
|
||||
usages: []capi.KeyUsage{
|
||||
capi.UsageKeyEncipherment,
|
||||
capi.UsageDigitalSignature,
|
||||
capi.UsageClientAuth,
|
||||
},
|
||||
}
|
||||
c(&b)
|
||||
t.Run(fmt.Sprintf("csr:%#v", b), func(t *testing.T) {
|
||||
csr := makeFancyTestCsr(b)
|
||||
x509cr, err := k8s_certificates_v1beta1.ParseCSR(csr)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected err: %v", err)
|
||||
}
|
||||
if recognizeFunc(csr, x509cr) != shouldRecognize {
|
||||
t.Errorf("expected recognized to be %v", shouldRecognize)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// noncryptographic for faster testing
|
||||
// DO NOT COPY THIS CODE
|
||||
var insecureRand = rand.New(rand.NewSource(0))
|
||||
|
||||
func makeTestCsr() *capi.CertificateSigningRequest {
|
||||
return makeFancyTestCsr(csrBuilder{cn: "test-cert"})
|
||||
}
|
||||
|
||||
type csrBuilder struct {
|
||||
cn string
|
||||
orgs []string
|
||||
requestor string
|
||||
usages []capi.KeyUsage
|
||||
dns []string
|
||||
emails []string
|
||||
ips []net.IP
|
||||
}
|
||||
|
||||
func makeFancyTestCsr(b csrBuilder) *capi.CertificateSigningRequest {
|
||||
pk, err := ecdsa.GenerateKey(elliptic.P224(), insecureRand)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
csrb, err := x509.CreateCertificateRequest(insecureRand, &x509.CertificateRequest{
|
||||
Subject: pkix.Name{
|
||||
CommonName: b.cn,
|
||||
Organization: b.orgs,
|
||||
},
|
||||
DNSNames: b.dns,
|
||||
EmailAddresses: b.emails,
|
||||
IPAddresses: b.ips,
|
||||
}, pk)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return &capi.CertificateSigningRequest{
|
||||
Spec: capi.CertificateSigningRequestSpec{
|
||||
Username: b.requestor,
|
||||
Usages: b.usages,
|
||||
Request: pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE REQUEST", Bytes: csrb}),
|
||||
},
|
||||
}
|
||||
}
|
||||
184
vendor/k8s.io/kubernetes/pkg/controller/certificates/certificate_controller.go
generated
vendored
Normal file
184
vendor/k8s.io/kubernetes/pkg/controller/certificates/certificate_controller.go
generated
vendored
Normal file
|
|
@ -0,0 +1,184 @@
|
|||
/*
|
||||
Copyright 2016 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 implements an abstract controller that is useful for
|
||||
// building controllers that manage CSRs
|
||||
package certificates
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
certificates "k8s.io/api/certificates/v1beta1"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
certificatesinformers "k8s.io/client-go/informers/certificates/v1beta1"
|
||||
clientset "k8s.io/client-go/kubernetes"
|
||||
v1core "k8s.io/client-go/kubernetes/typed/core/v1"
|
||||
certificateslisters "k8s.io/client-go/listers/certificates/v1beta1"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
"k8s.io/client-go/tools/record"
|
||||
"k8s.io/client-go/util/workqueue"
|
||||
"k8s.io/kubernetes/pkg/controller"
|
||||
|
||||
"github.com/golang/glog"
|
||||
)
|
||||
|
||||
type CertificateController struct {
|
||||
kubeClient clientset.Interface
|
||||
|
||||
csrLister certificateslisters.CertificateSigningRequestLister
|
||||
csrsSynced cache.InformerSynced
|
||||
|
||||
handler func(*certificates.CertificateSigningRequest) error
|
||||
|
||||
queue workqueue.RateLimitingInterface
|
||||
}
|
||||
|
||||
func NewCertificateController(
|
||||
kubeClient clientset.Interface,
|
||||
csrInformer certificatesinformers.CertificateSigningRequestInformer,
|
||||
handler func(*certificates.CertificateSigningRequest) error,
|
||||
) (*CertificateController, error) {
|
||||
// Send events to the apiserver
|
||||
eventBroadcaster := record.NewBroadcaster()
|
||||
eventBroadcaster.StartLogging(glog.Infof)
|
||||
eventBroadcaster.StartRecordingToSink(&v1core.EventSinkImpl{Interface: v1core.New(kubeClient.Core().RESTClient()).Events("")})
|
||||
|
||||
cc := &CertificateController{
|
||||
kubeClient: kubeClient,
|
||||
queue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "certificate"),
|
||||
handler: handler,
|
||||
}
|
||||
|
||||
// Manage the addition/update of certificate requests
|
||||
csrInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
|
||||
AddFunc: func(obj interface{}) {
|
||||
csr := obj.(*certificates.CertificateSigningRequest)
|
||||
glog.V(4).Infof("Adding certificate request %s", csr.Name)
|
||||
cc.enqueueCertificateRequest(obj)
|
||||
},
|
||||
UpdateFunc: func(old, new interface{}) {
|
||||
oldCSR := old.(*certificates.CertificateSigningRequest)
|
||||
glog.V(4).Infof("Updating certificate request %s", oldCSR.Name)
|
||||
cc.enqueueCertificateRequest(new)
|
||||
},
|
||||
DeleteFunc: func(obj interface{}) {
|
||||
csr, ok := obj.(*certificates.CertificateSigningRequest)
|
||||
if !ok {
|
||||
tombstone, ok := obj.(cache.DeletedFinalStateUnknown)
|
||||
if !ok {
|
||||
glog.V(2).Infof("Couldn't get object from tombstone %#v", obj)
|
||||
return
|
||||
}
|
||||
csr, ok = tombstone.Obj.(*certificates.CertificateSigningRequest)
|
||||
if !ok {
|
||||
glog.V(2).Infof("Tombstone contained object that is not a CSR: %#v", obj)
|
||||
return
|
||||
}
|
||||
}
|
||||
glog.V(4).Infof("Deleting certificate request %s", csr.Name)
|
||||
cc.enqueueCertificateRequest(obj)
|
||||
},
|
||||
})
|
||||
cc.csrLister = csrInformer.Lister()
|
||||
cc.csrsSynced = csrInformer.Informer().HasSynced
|
||||
cc.handler = handler
|
||||
return cc, nil
|
||||
}
|
||||
|
||||
// Run the main goroutine responsible for watching and syncing jobs.
|
||||
func (cc *CertificateController) Run(workers int, stopCh <-chan struct{}) {
|
||||
defer utilruntime.HandleCrash()
|
||||
defer cc.queue.ShutDown()
|
||||
|
||||
glog.Infof("Starting certificate controller")
|
||||
defer glog.Infof("Shutting down certificate controller")
|
||||
|
||||
if !controller.WaitForCacheSync("certificate", stopCh, cc.csrsSynced) {
|
||||
return
|
||||
}
|
||||
|
||||
for i := 0; i < workers; i++ {
|
||||
go wait.Until(cc.worker, time.Second, stopCh)
|
||||
}
|
||||
|
||||
<-stopCh
|
||||
}
|
||||
|
||||
// worker runs a thread that dequeues CSRs, handles them, and marks them done.
|
||||
func (cc *CertificateController) worker() {
|
||||
for cc.processNextWorkItem() {
|
||||
}
|
||||
}
|
||||
|
||||
// processNextWorkItem deals with one key off the queue. It returns false when it's time to quit.
|
||||
func (cc *CertificateController) processNextWorkItem() bool {
|
||||
cKey, quit := cc.queue.Get()
|
||||
if quit {
|
||||
return false
|
||||
}
|
||||
defer cc.queue.Done(cKey)
|
||||
|
||||
if err := cc.syncFunc(cKey.(string)); err != nil {
|
||||
cc.queue.AddRateLimited(cKey)
|
||||
utilruntime.HandleError(fmt.Errorf("Sync %v failed with : %v", cKey, err))
|
||||
return true
|
||||
}
|
||||
|
||||
cc.queue.Forget(cKey)
|
||||
return true
|
||||
|
||||
}
|
||||
|
||||
func (cc *CertificateController) enqueueCertificateRequest(obj interface{}) {
|
||||
key, err := controller.KeyFunc(obj)
|
||||
if err != nil {
|
||||
utilruntime.HandleError(fmt.Errorf("Couldn't get key for object %+v: %v", obj, err))
|
||||
return
|
||||
}
|
||||
cc.queue.Add(key)
|
||||
}
|
||||
|
||||
// maybeSignCertificate will inspect the certificate request and, if it has
|
||||
// been approved and meets policy expectations, generate an X509 cert using the
|
||||
// cluster CA assets. If successful it will update the CSR approve subresource
|
||||
// with the signed certificate.
|
||||
func (cc *CertificateController) syncFunc(key string) error {
|
||||
startTime := time.Now()
|
||||
defer func() {
|
||||
glog.V(4).Infof("Finished syncing certificate request %q (%v)", key, time.Now().Sub(startTime))
|
||||
}()
|
||||
csr, err := cc.csrLister.Get(key)
|
||||
if errors.IsNotFound(err) {
|
||||
glog.V(3).Infof("csr has been deleted: %v", key)
|
||||
return nil
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if csr.Status.Certificate != nil {
|
||||
// no need to do anything because it already has a cert
|
||||
return nil
|
||||
}
|
||||
|
||||
// need to operate on a copy so we don't mutate the csr in the shared cache
|
||||
csr = csr.DeepCopy()
|
||||
|
||||
return cc.handler(csr)
|
||||
}
|
||||
86
vendor/k8s.io/kubernetes/pkg/controller/certificates/certificate_controller_test.go
generated
vendored
Normal file
86
vendor/k8s.io/kubernetes/pkg/controller/certificates/certificate_controller_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
/*
|
||||
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"
|
||||
"time"
|
||||
|
||||
certificates "k8s.io/api/certificates/v1beta1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
"k8s.io/client-go/informers"
|
||||
"k8s.io/client-go/kubernetes/fake"
|
||||
"k8s.io/kubernetes/pkg/controller"
|
||||
)
|
||||
|
||||
// TODO flesh this out to cover things like not being able to find the csr in the cache, not
|
||||
// auto-approving, etc.
|
||||
func TestCertificateController(t *testing.T) {
|
||||
|
||||
csr := &certificates.CertificateSigningRequest{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test-csr",
|
||||
},
|
||||
}
|
||||
|
||||
client := fake.NewSimpleClientset(csr)
|
||||
informerFactory := informers.NewSharedInformerFactory(fake.NewSimpleClientset(csr), controller.NoResyncPeriodFunc())
|
||||
|
||||
handler := func(csr *certificates.CertificateSigningRequest) error {
|
||||
csr.Status.Conditions = append(csr.Status.Conditions, certificates.CertificateSigningRequestCondition{
|
||||
Type: certificates.CertificateApproved,
|
||||
Reason: "test reason",
|
||||
Message: "test message",
|
||||
})
|
||||
_, err := client.Certificates().CertificateSigningRequests().UpdateApproval(csr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
controller, err := NewCertificateController(
|
||||
client,
|
||||
informerFactory.Certificates().V1beta1().CertificateSigningRequests(),
|
||||
handler,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("error creating controller: %v", err)
|
||||
}
|
||||
controller.csrsSynced = func() bool { return true }
|
||||
|
||||
stopCh := make(chan struct{})
|
||||
defer close(stopCh)
|
||||
informerFactory.Start(stopCh)
|
||||
informerFactory.WaitForCacheSync(stopCh)
|
||||
wait.PollUntil(10*time.Millisecond, func() (bool, error) {
|
||||
return controller.queue.Len() >= 1, nil
|
||||
}, stopCh)
|
||||
|
||||
controller.processNextWorkItem()
|
||||
|
||||
actions := client.Actions()
|
||||
if len(actions) != 1 {
|
||||
t.Errorf("expected 1 actions")
|
||||
}
|
||||
if a := actions[0]; !a.Matches("update", "certificatesigningrequests") ||
|
||||
a.GetSubresource() != "approval" {
|
||||
t.Errorf("unexpected action: %#v", a)
|
||||
}
|
||||
|
||||
}
|
||||
38
vendor/k8s.io/kubernetes/pkg/controller/certificates/certificate_controller_utils.go
generated
vendored
Normal file
38
vendor/k8s.io/kubernetes/pkg/controller/certificates/certificate_controller_utils.go
generated
vendored
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
Copyright 2016 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 certificates "k8s.io/api/certificates/v1beta1"
|
||||
|
||||
// IsCertificateRequestApproved returns true if a certificate request has the
|
||||
// "Approved" condition and no "Denied" conditions; false otherwise.
|
||||
func IsCertificateRequestApproved(csr *certificates.CertificateSigningRequest) bool {
|
||||
approved, denied := GetCertApprovalCondition(&csr.Status)
|
||||
return approved && !denied
|
||||
}
|
||||
|
||||
func GetCertApprovalCondition(status *certificates.CertificateSigningRequestStatus) (approved bool, denied bool) {
|
||||
for _, c := range status.Conditions {
|
||||
if c.Type == certificates.CertificateApproved {
|
||||
approved = true
|
||||
}
|
||||
if c.Type == certificates.CertificateDenied {
|
||||
denied = true
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
50
vendor/k8s.io/kubernetes/pkg/controller/certificates/signer/BUILD
generated
vendored
Normal file
50
vendor/k8s.io/kubernetes/pkg/controller/certificates/signer/BUILD
generated
vendored
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = ["cfssl_signer_test.go"],
|
||||
data = [
|
||||
"testdata/ca.crt",
|
||||
"testdata/ca.key",
|
||||
"testdata/kubelet.csr",
|
||||
],
|
||||
library = ":go_default_library",
|
||||
deps = [
|
||||
"//vendor/k8s.io/api/certificates/v1beta1:go_default_library",
|
||||
"//vendor/k8s.io/client-go/util/cert:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["cfssl_signer.go"],
|
||||
deps = [
|
||||
"//pkg/controller/certificates:go_default_library",
|
||||
"//vendor/github.com/cloudflare/cfssl/config:go_default_library",
|
||||
"//vendor/github.com/cloudflare/cfssl/helpers:go_default_library",
|
||||
"//vendor/github.com/cloudflare/cfssl/signer:go_default_library",
|
||||
"//vendor/github.com/cloudflare/cfssl/signer/local:go_default_library",
|
||||
"//vendor/k8s.io/api/certificates/v1beta1:go_default_library",
|
||||
"//vendor/k8s.io/client-go/informers/certificates/v1beta1:go_default_library",
|
||||
"//vendor/k8s.io/client-go/kubernetes:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
||||
138
vendor/k8s.io/kubernetes/pkg/controller/certificates/signer/cfssl_signer.go
generated
vendored
Normal file
138
vendor/k8s.io/kubernetes/pkg/controller/certificates/signer/cfssl_signer.go
generated
vendored
Normal file
|
|
@ -0,0 +1,138 @@
|
|||
/*
|
||||
Copyright 2016 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 signer implements a CA signer that uses keys stored on local disk.
|
||||
package signer
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
capi "k8s.io/api/certificates/v1beta1"
|
||||
certificatesinformers "k8s.io/client-go/informers/certificates/v1beta1"
|
||||
clientset "k8s.io/client-go/kubernetes"
|
||||
"k8s.io/kubernetes/pkg/controller/certificates"
|
||||
|
||||
"github.com/cloudflare/cfssl/config"
|
||||
"github.com/cloudflare/cfssl/helpers"
|
||||
"github.com/cloudflare/cfssl/signer"
|
||||
"github.com/cloudflare/cfssl/signer/local"
|
||||
)
|
||||
|
||||
func NewCSRSigningController(
|
||||
client clientset.Interface,
|
||||
csrInformer certificatesinformers.CertificateSigningRequestInformer,
|
||||
caFile, caKeyFile string,
|
||||
certificateDuration time.Duration,
|
||||
) (*certificates.CertificateController, error) {
|
||||
signer, err := newCFSSLSigner(caFile, caKeyFile, client, certificateDuration)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return certificates.NewCertificateController(
|
||||
client,
|
||||
csrInformer,
|
||||
signer.handle,
|
||||
)
|
||||
}
|
||||
|
||||
type cfsslSigner struct {
|
||||
ca *x509.Certificate
|
||||
priv crypto.Signer
|
||||
sigAlgo x509.SignatureAlgorithm
|
||||
client clientset.Interface
|
||||
certificateDuration time.Duration
|
||||
}
|
||||
|
||||
func newCFSSLSigner(caFile, caKeyFile string, client clientset.Interface, certificateDuration time.Duration) (*cfsslSigner, error) {
|
||||
ca, err := ioutil.ReadFile(caFile)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error reading CA cert file %q: %v", caFile, err)
|
||||
}
|
||||
cakey, err := ioutil.ReadFile(caKeyFile)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error reading CA key file %q: %v", caKeyFile, err)
|
||||
}
|
||||
|
||||
parsedCa, err := helpers.ParseCertificatePEM(ca)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error parsing CA cert file %q: %v", caFile, err)
|
||||
}
|
||||
|
||||
strPassword := os.Getenv("CFSSL_CA_PK_PASSWORD")
|
||||
password := []byte(strPassword)
|
||||
if strPassword == "" {
|
||||
password = nil
|
||||
}
|
||||
|
||||
priv, err := helpers.ParsePrivateKeyPEMWithPassword(cakey, password)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Malformed private key %v", err)
|
||||
}
|
||||
return &cfsslSigner{
|
||||
priv: priv,
|
||||
ca: parsedCa,
|
||||
sigAlgo: signer.DefaultSigAlgo(priv),
|
||||
client: client,
|
||||
certificateDuration: certificateDuration,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *cfsslSigner) handle(csr *capi.CertificateSigningRequest) error {
|
||||
if !certificates.IsCertificateRequestApproved(csr) {
|
||||
return nil
|
||||
}
|
||||
csr, err := s.sign(csr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error auto signing csr: %v", err)
|
||||
}
|
||||
_, err = s.client.Certificates().CertificateSigningRequests().UpdateStatus(csr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error updating signature for csr: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *cfsslSigner) sign(csr *capi.CertificateSigningRequest) (*capi.CertificateSigningRequest, error) {
|
||||
var usages []string
|
||||
for _, usage := range csr.Spec.Usages {
|
||||
usages = append(usages, string(usage))
|
||||
}
|
||||
policy := &config.Signing{
|
||||
Default: &config.SigningProfile{
|
||||
Usage: usages,
|
||||
Expiry: s.certificateDuration,
|
||||
ExpiryString: s.certificateDuration.String(),
|
||||
},
|
||||
}
|
||||
cfs, err := local.NewSigner(s.priv, s.ca, s.sigAlgo, policy)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
csr.Status.Certificate, err = cfs.Sign(signer.SignRequest{
|
||||
Request: string(csr.Spec.Request),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return csr, nil
|
||||
}
|
||||
84
vendor/k8s.io/kubernetes/pkg/controller/certificates/signer/cfssl_signer_test.go
generated
vendored
Normal file
84
vendor/k8s.io/kubernetes/pkg/controller/certificates/signer/cfssl_signer_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
/*
|
||||
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 signer
|
||||
|
||||
import (
|
||||
"crypto/x509"
|
||||
"io/ioutil"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
capi "k8s.io/api/certificates/v1beta1"
|
||||
"k8s.io/client-go/util/cert"
|
||||
)
|
||||
|
||||
func TestSigner(t *testing.T) {
|
||||
s, err := newCFSSLSigner("./testdata/ca.crt", "./testdata/ca.key", nil, 1*time.Hour)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create signer: %v", err)
|
||||
}
|
||||
|
||||
csrb, err := ioutil.ReadFile("./testdata/kubelet.csr")
|
||||
if err != nil {
|
||||
t.Fatalf("failed to read CSR: %v", err)
|
||||
}
|
||||
|
||||
csr := &capi.CertificateSigningRequest{
|
||||
Spec: capi.CertificateSigningRequestSpec{
|
||||
Request: []byte(csrb),
|
||||
Usages: []capi.KeyUsage{
|
||||
capi.UsageSigning,
|
||||
capi.UsageKeyEncipherment,
|
||||
capi.UsageServerAuth,
|
||||
capi.UsageClientAuth,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
csr, err = s.sign(csr)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to sign CSR: %v", err)
|
||||
}
|
||||
certData := csr.Status.Certificate
|
||||
if len(certData) == 0 {
|
||||
t.Fatalf("expected a certificate after signing")
|
||||
}
|
||||
|
||||
certs, err := cert.ParseCertsPEM(certData)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to parse certificate: %v", err)
|
||||
}
|
||||
if len(certs) != 1 {
|
||||
t.Fatalf("expected one certificate")
|
||||
}
|
||||
|
||||
crt := certs[0]
|
||||
|
||||
if crt.Subject.CommonName != "system:node:k-a-node-s36b" {
|
||||
t.Errorf("expected common name of 'system:node:k-a-node-s36b', but got: %v", certs[0].Subject.CommonName)
|
||||
}
|
||||
if !reflect.DeepEqual(crt.Subject.Organization, []string{"system:nodes"}) {
|
||||
t.Errorf("expected organization to be [system:nodes] but got: %v", crt.Subject.Organization)
|
||||
}
|
||||
if crt.KeyUsage != x509.KeyUsageDigitalSignature|x509.KeyUsageKeyEncipherment {
|
||||
t.Errorf("bad key usage")
|
||||
}
|
||||
if !reflect.DeepEqual(crt.ExtKeyUsage, []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth}) {
|
||||
t.Errorf("bad extended key usage")
|
||||
}
|
||||
}
|
||||
18
vendor/k8s.io/kubernetes/pkg/controller/certificates/signer/testdata/ca.crt
generated
vendored
Normal file
18
vendor/k8s.io/kubernetes/pkg/controller/certificates/signer/testdata/ca.crt
generated
vendored
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIC9zCCAd+gAwIBAgIJAOWJ8tWNUIsZMA0GCSqGSIb3DQEBCwUAMBIxEDAOBgNV
|
||||
BAMMB2t1YmUtY2EwHhcNMTYxMjIyMDAyNTI5WhcNNDQwNTA5MDAyNTI5WjASMRAw
|
||||
DgYDVQQDDAdrdWJlLWNhMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA
|
||||
1HK1d2p7N7UC6px8lVtABw8jPpVyNYjrJmI+TKTTdCgWGsUTFMCw4t4Q/KQDDlvB
|
||||
P19uPhbfp8aLwOWXBCxOPZzlM2mAEjSUgKjbyGCW/8vaXa2VgQm3tKZdydKiFvIo
|
||||
fEsNA+58w8A0WWEB8wYFcdCt8uPyQ0ws/TxE+WW3u7EPlC0/inIX9JqeZZMpDk3N
|
||||
lHEv/pGEjQmoet/hBwGHq9PKepkN5/V6rrSADJ5I4Uklp2f7G9MCP/zV8xKfs0lK
|
||||
CMoJsIPK3nL9N3C0rqBQPfcyKE2fnEkxC3UVZA8brvLTkBfOgmM2eVg/nauU1ejv
|
||||
zOJL7tDwUioLriw2hiGrFwIDAQABo1AwTjAdBgNVHQ4EFgQUbGJxJeW7BgZ4xSmW
|
||||
d3Aw3gq8YZUwHwYDVR0jBBgwFoAUbGJxJeW7BgZ4xSmWd3Aw3gq8YZUwDAYDVR0T
|
||||
BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAunzpYAxpzguzxG83pK5n3ObsGDwO
|
||||
78d38qX1VRvMLPvioZxYgquqqFPdLI3xe8b8KdZNzb65549tgjAI17tTKGTRgJu5
|
||||
yzLU1tO4vNaAFecMCtPvElYfkrAv2vbGCVJ1bYKTnjdu3083jG3sY9TDj0364A57
|
||||
lNwKEd5uxHGWg4H+NbyHkDqfKmllzLvJ9XjSWBPmNVLSW50hV+h9fUXgz9LN+qVY
|
||||
VEDfAEWqb6PVy9ANw8A8QLnuSRxbd7hAigtlC4MwzYJ6tyFIIH6bCIgfoZuA+brm
|
||||
WGcpIxl4fKEGafSgjsK/6Yhb61mkhHmG16mzEUZNkNsjiYJuF2QxpOlQrw==
|
||||
-----END CERTIFICATE-----
|
||||
27
vendor/k8s.io/kubernetes/pkg/controller/certificates/signer/testdata/ca.key
generated
vendored
Normal file
27
vendor/k8s.io/kubernetes/pkg/controller/certificates/signer/testdata/ca.key
generated
vendored
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEpQIBAAKCAQEA1HK1d2p7N7UC6px8lVtABw8jPpVyNYjrJmI+TKTTdCgWGsUT
|
||||
FMCw4t4Q/KQDDlvBP19uPhbfp8aLwOWXBCxOPZzlM2mAEjSUgKjbyGCW/8vaXa2V
|
||||
gQm3tKZdydKiFvIofEsNA+58w8A0WWEB8wYFcdCt8uPyQ0ws/TxE+WW3u7EPlC0/
|
||||
inIX9JqeZZMpDk3NlHEv/pGEjQmoet/hBwGHq9PKepkN5/V6rrSADJ5I4Uklp2f7
|
||||
G9MCP/zV8xKfs0lKCMoJsIPK3nL9N3C0rqBQPfcyKE2fnEkxC3UVZA8brvLTkBfO
|
||||
gmM2eVg/nauU1ejvzOJL7tDwUioLriw2hiGrFwIDAQABAoIBAFJCmEFE2bEYRajS
|
||||
LusmCgSxt9PjyfUwrtyN7dF/gODZJLX42QqQEe3GTo2EdCp7HLiNGwKvmKo+Fp76
|
||||
Rx82iJUSyyy9DPn/ogCvYWqU++LP7B2ZuOnd+WPZhzc+d8Sqv0JhTQjYrzaclaiG
|
||||
B1syWalYRAJogMXOGR102MA4wovJrlHFuTVSWiDe0uguLxyjoTMIRqbib9ZAMSLX
|
||||
bfcM2abGpXgq10abda3KKAJbZyr2fnBvqKTs4a4zYeHJpQT+NBPMiryb2WnPFg+b
|
||||
93nrjDxUtPsx8NJz6HGkSQLagXkZX2J1JpT8loaNIdyQHab1LNXptc84LR8xxusy
|
||||
bs5NowECgYEA+j+SwVgeC+NCUIfxr3F9zPAD9A0Tk3gD4z+j0opfLIMghX4jtK0e
|
||||
9fQyglecAbojlkEUk/js5IVZ0IIhBNPWXxKtdShZO7EmJ6Z5IEmFrZK1xUomYBa2
|
||||
BfysqSAkxVLsTDIfI0Q4DHQNDOV+iY3j8WoaR51cXr+IY+mYBGSNI80CgYEA2VS5
|
||||
X5QHDxoh3r5ORiyab3ciubEofJ29D3NR1tCe9ZgSYRV5Y7T/4KPpZdpsEX/ydYD6
|
||||
X4DyURuYNK7PUR8DSlX7/VuMzHThqGJMaT0LE+alU4bruiad33X1WXgtcPTGCic0
|
||||
8il50TZTgba0CwxuCO1eVb3IijwgJBX/byM67nMCgYEA7As1KSwtwzbMoVtpa/xY
|
||||
Fgu7HuOKuIn22M55fylH1puk/GXb1huJ3aNGVU2/+J0T3jFq8JxXDsJ90kA8Vupe
|
||||
BXV/qceyS6yv+ax8Cilvbya4T+y+P9qMPR912V1Zccri2ohYeJJrb8uzV5vM/ICb
|
||||
JmbXfP+AVlrBksSOwG37920CgYEAsSi2X6o8QtxLhdZd2ihbz8cu4G4AkezHhAO+
|
||||
T70KBytquAcYR+Xwu38CMEvn0jAZRh3YeueTH/i9jxx81STRutPysSni0Xvpwyg2
|
||||
H4dqM1PNqxQNrlXyVYlDciZb7HsrwHULXOfgbGG7mr6Db4o3XEGap4woID84+BGS
|
||||
glcWn+8CgYEA36uulmZcodfet04qQvlDtr1d7mwLdTR/JAO0ZBIgFH7eGZdEVh8O
|
||||
DoTJTdSSJGiv8J35PwEXfhKHjhgOjDocLYu+yCOwVj7jRdHqlDS1BaE36Hzdw0rb
|
||||
mWkBRMGJtGhzhoRJEFHAnoLXc9danRfnHwVR58drlf7bjR5I9eU9u1I=
|
||||
-----END RSA PRIVATE KEY-----
|
||||
8
vendor/k8s.io/kubernetes/pkg/controller/certificates/signer/testdata/kubelet.csr
generated
vendored
Normal file
8
vendor/k8s.io/kubernetes/pkg/controller/certificates/signer/testdata/kubelet.csr
generated
vendored
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
-----BEGIN CERTIFICATE REQUEST-----
|
||||
MIH1MIGdAgEAMDsxFTATBgNVBAoTDHN5c3RlbTpub2RlczEiMCAGA1UEAxMZc3lz
|
||||
dGVtOm5vZGU6ay1hLW5vZGUtczM2YjBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IA
|
||||
BJbxa5Y8SrUJVHpOoWD5ceqH+5R9mjIhwVP2sqfTcLkjvbitzOiLlxSq/LwJ+qq7
|
||||
kVpf9f3GopZVhRWbYSCg0YGgADAKBggqhkjOPQQDAgNHADBEAiAabb6XFtPOJUCQ
|
||||
+84NhxLEvPANhrtwFq3Q0qFZ9TzH5QIgc/697RTTcbri2lVj+10dLFIC3VYJ7br4
|
||||
QjA7haCYXrA=
|
||||
-----END CERTIFICATE REQUEST-----
|
||||
Loading…
Add table
Add a link
Reference in a new issue