Skip to content

Commit 8d0492b

Browse files
author
Mengqi Yu
committed
initial cert provisioner
1 parent 8cdff9c commit 8d0492b

File tree

13 files changed

+895
-30
lines changed

13 files changed

+895
-30
lines changed

pkg/admission/certprovisioner/certprovisioner.go renamed to pkg/admission/certgenerator/certgenerator.go

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,18 +14,18 @@ See the License for the specific language governing permissions and
1414
limitations under the License.
1515
*/
1616

17-
package certprovisioner
17+
package certgenerator
1818

19-
// Certs hosts a private key, its corresponding serving certificate and
19+
// CertArtifacts hosts a private key, its corresponding serving certificate and
2020
// the CA certificate that signs the serving certificate.
21-
type Certs struct {
21+
type CertArtifacts struct {
2222
Key []byte
2323
Cert []byte
2424
CACert []byte
2525
}
2626

27-
// CertProvisioner is an interface to provision the serving certificate.
28-
type CertProvisioner interface {
29-
// ProvisionServingCert returns a Certs struct.
30-
ProvisionServingCert() (*Certs, error)
27+
// CertGenerator is an interface to provision the serving certificate.
28+
type CertGenerator interface {
29+
// Generate returns a CertArtifacts struct.
30+
Generate(CommonName string) (*CertArtifacts, error)
3131
}

pkg/admission/certprovisioner/certprovisioner_test.go renamed to pkg/admission/certgenerator/certgenerator_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
1414
limitations under the License.
1515
*/
1616

17-
package certprovisioner
17+
package certgenerator
1818

1919
import "fmt"
2020

pkg/admission/certprovisioner/doc.go renamed to pkg/admission/certgenerator/doc.go

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,18 +15,16 @@ limitations under the License.
1515
*/
1616

1717
/*
18-
Package certprovisioner provides an interface and implementation to provision certificates.
18+
Package certgenerator provides an interface and implementation to provision certificates.
1919
20-
Create a implementation instance of certprovisioner.
20+
Create an instance of CertGenerator.
2121
22-
cp := SelfSignedCertProvisioner{
23-
CommonName: "foo.bar.com"
24-
}
22+
cg := SelfSignedCertGenerator{}
2523
26-
Provision the certificates.
27-
certs, err := cp.ProvisionServingCert()
24+
Generate the certificates.
25+
certs, err := cp.Generate("foo.bar.com")
2826
if err != nil {
2927
// handle error
3028
}
3129
*/
32-
package certprovisioner
30+
package certgenerator

pkg/admission/certprovisioner/selfsignedcertprovisioner.go renamed to pkg/admission/certgenerator/selfsigned.go

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
1414
limitations under the License.
1515
*/
1616

17-
package certprovisioner
17+
package certgenerator
1818

1919
import (
2020
"crypto/x509"
@@ -28,21 +28,18 @@ func ServiceToCommonName(serviceNamespace, serviceName string) string {
2828
return fmt.Sprintf("%s.%s.svc", serviceName, serviceNamespace)
2929
}
3030

31-
// SelfSignedCertProvisioner implements the CertProvisioner interface.
31+
// SelfSignedCertGenerator implements the CertGenerator interface.
3232
// It provisions self-signed certificates.
33-
type SelfSignedCertProvisioner struct {
34-
// Required Common Name
35-
CommonName string
36-
}
33+
type SelfSignedCertGenerator struct{}
3734

38-
var _ CertProvisioner = &SelfSignedCertProvisioner{}
35+
var _ CertGenerator = &SelfSignedCertGenerator{}
3936

40-
// ProvisionServingCert creates and returns a CA certificate, certificate and
37+
// Generate creates and returns a CA certificate, certificate and
4138
// key for the server. serverKey and serverCert are used by the server
4239
// to establish trust for clients, CA certificate is used by the
4340
// client to verify the server authentication chain.
4441
// The cert will be valid for 365 days.
45-
func (cp *SelfSignedCertProvisioner) ProvisionServingCert() (*Certs, error) {
42+
func (cp *SelfSignedCertGenerator) Generate(commonName string) (*CertArtifacts, error) {
4643
signingKey, err := cert.NewPrivateKey()
4744
if err != nil {
4845
return nil, fmt.Errorf("failed to create the CA private key: %v", err)
@@ -57,15 +54,15 @@ func (cp *SelfSignedCertProvisioner) ProvisionServingCert() (*Certs, error) {
5754
}
5855
signedCert, err := cert.NewSignedCert(
5956
cert.Config{
60-
CommonName: cp.CommonName,
57+
CommonName: commonName,
6158
Usages: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
6259
},
6360
key, signingCert, signingKey,
6461
)
6562
if err != nil {
6663
return nil, fmt.Errorf("failed to create the cert: %v", err)
6764
}
68-
return &Certs{
65+
return &CertArtifacts{
6966
Key: cert.EncodePrivateKeyPEM(key),
7067
Cert: cert.EncodeCertPEM(signedCert),
7168
CACert: cert.EncodeCertPEM(signingCert),

pkg/admission/certprovisioner/selfsignedcertprovisioner_test.go renamed to pkg/admission/certgenerator/selfsigned_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
1414
limitations under the License.
1515
*/
1616

17-
package certprovisioner
17+
package certgenerator
1818

1919
import (
2020
"crypto/x509"
@@ -24,8 +24,8 @@ import (
2424

2525
func TestProvisionServingCert(t *testing.T) {
2626
cn := "mysvc.myns.svc"
27-
cp := SelfSignedCertProvisioner{CommonName: cn}
28-
certs, _ := cp.ProvisionServingCert()
27+
cp := SelfSignedCertGenerator{}
28+
certs, _ := cp.Generate(cn)
2929

3030
// First, create the set of root certificates. For this example we only
3131
// have one. It's also possible to omit this in order to use the
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
/*
2+
Copyright 2018 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package certwriter
18+
19+
import (
20+
"bytes"
21+
"fmt"
22+
"net/url"
23+
24+
admissionregistrationv1beta1 "k8s.io/api/admissionregistration/v1beta1"
25+
apierrors "k8s.io/apimachinery/pkg/api/errors"
26+
"k8s.io/apimachinery/pkg/runtime"
27+
28+
"github.com/kubernetes-sigs/controller-runtime/pkg/admission/certgenerator"
29+
)
30+
31+
const (
32+
// CACertName is the name of the CA certificate
33+
CACertName = "ca-cert.pem"
34+
// ServerKeyName is the name of the server private key
35+
ServerKeyName = "key.pem"
36+
// ServerCertName is the name of the serving certificate
37+
ServerCertName = "cert.pem"
38+
)
39+
40+
// CertWriter provides method to handle webhooks.
41+
type CertWriter interface {
42+
// EnsureCert ensures that the webhooks it manages have the right certificates.
43+
EnsureCert() error
44+
}
45+
46+
func handleCommon(webhook *admissionregistrationv1beta1.Webhook, ch certReadWriter) error {
47+
// This happens when a webhook name is specified in the annotation, but this webhook doesn't exist.
48+
if webhook == nil {
49+
// TODO: add a log here if useful.
50+
return nil
51+
}
52+
webhookName := webhook.Name
53+
54+
certs, err := ch.read(webhookName)
55+
if apierrors.IsNotFound(err) {
56+
certs, err = ch.write(webhookName)
57+
switch {
58+
case apierrors.IsAlreadyExists(err):
59+
certs, err = ch.read(webhookName)
60+
if err != nil {
61+
return err
62+
}
63+
case err != nil:
64+
return err
65+
}
66+
} else if err != nil {
67+
return err
68+
}
69+
70+
// Recreate the cert if it's invalid.
71+
if !validCert(certs) {
72+
certs, err = ch.overwrite(webhookName)
73+
if err != nil {
74+
return err
75+
}
76+
}
77+
78+
// Ensure the CA bundle in the webhook configuration has the signing CA.
79+
caBundle := webhook.ClientConfig.CABundle
80+
caCert := certs.CACert
81+
if !bytes.Contains(caBundle, caCert) {
82+
webhook.ClientConfig.CABundle = append(caBundle, caCert...)
83+
}
84+
return nil
85+
}
86+
87+
// certReadWriter provides methods for handling certificates for webhook.
88+
type certReadWriter interface {
89+
// read reads a wehbook name and returns the certs for it.
90+
read(webhookName string) (*certgenerator.CertArtifacts, error)
91+
// write writes the certs and return the certs it wrote.
92+
write(webhookName string) (*certgenerator.CertArtifacts, error)
93+
// overwrite overwrites the existing certs and return the certs it wrote.
94+
overwrite(webhookName string) (*certgenerator.CertArtifacts, error)
95+
}
96+
97+
func validCert(certs *certgenerator.CertArtifacts) bool {
98+
// TODO: 1) validate the key and the cert are valid pair e.g. call crypto/tls.X509KeyPair()
99+
// 2) validate the cert with the CA cert
100+
// 3) validate the cert is for a certain DNSName
101+
// e.g.
102+
// c, err := tls.X509KeyPair(cert, key)
103+
// err := c.Verify(options)
104+
105+
return true
106+
}
107+
108+
func getWebhooksFromObject(obj runtime.Object) ([]admissionregistrationv1beta1.Webhook, error) {
109+
switch typed := obj.(type) {
110+
case *admissionregistrationv1beta1.MutatingWebhookConfiguration:
111+
return typed.Webhooks, nil
112+
case *admissionregistrationv1beta1.ValidatingWebhookConfiguration:
113+
return typed.Webhooks, nil
114+
//case *unstructured.Unstructured:
115+
// TODO: implement this if needed
116+
default:
117+
return nil, fmt.Errorf("unsupported type: %T, only support v1beta1.MutatingWebhookConfiguration and v1beta1.ValidatingWebhookConfiguration", typed)
118+
}
119+
}
120+
121+
func webhookClientConfigToCommonName(config *admissionregistrationv1beta1.WebhookClientConfig) (string, error) {
122+
if config.Service != nil && config.URL != nil {
123+
return "", fmt.Errorf("service and URL can't be set at the same time in a webhook: %#v", config)
124+
}
125+
if config.Service == nil && config.URL == nil {
126+
return "", fmt.Errorf("one of service and URL need to be set in a webhook: %#v", config)
127+
}
128+
if config.Service != nil {
129+
return certgenerator.ServiceToCommonName(config.Service.Namespace, config.Service.Name), nil
130+
}
131+
if config.URL != nil {
132+
u, err := url.Parse(*config.URL)
133+
return u.Host, err
134+
}
135+
return "", nil
136+
}

pkg/admission/certwriter/doc.go

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/*
2+
Copyright 2018 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
/*
18+
Package certwriter provide method to handle the CA and certificate for webhooks.
19+
20+
Create a default CertWriterProvider
21+
22+
writerProvider, err := NewProvider(Options{client: client}))
23+
if err != nil {
24+
// handler error
25+
}
26+
27+
Create a SecretCertWriterProvider
28+
29+
writerProvider, err := &SecretCertWriterProvider{
30+
Client: client
31+
}
32+
if err != nil {
33+
// handler error
34+
}
35+
36+
Create a CertWriter from the writerProvider
37+
38+
writer, err := writerProvider.New(webhookConfiguration) // webhookConfiguration is an existing runtime.Object
39+
if err != nil {
40+
// handle error
41+
}
42+
43+
Provision the certificates using the CertWriter.
44+
45+
// writer can be either one of the CertWriters created above
46+
err = writer.EnsureCert()
47+
if err != nil {
48+
// handler error
49+
}
50+
51+
*/
52+
package certwriter

0 commit comments

Comments
 (0)