Skip to content

Commit fa1ac99

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

File tree

12 files changed

+837
-26
lines changed

12 files changed

+837
-26
lines changed

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

Lines changed: 5 additions & 5 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
// Certs hosts a private key, its corresponding serving certificate and
2020
// the CA certificate that signs the serving certificate.
@@ -24,8 +24,8 @@ type Certs struct {
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 Certs struct.
30+
Generate(CommonName string) (*Certs, 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: 5 additions & 7 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 a implementation instance of certgenerator.
2121
22-
cp := SelfSignedCertProvisioner{
23-
CommonName: "foo.bar.com"
24-
}
22+
cp := SelfSignedCertGenerator{}
2523
2624
Provision the certificates.
27-
certs, err := cp.ProvisionServingCert()
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: 7 additions & 10 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) (*Certs, 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,7 +54,7 @@ 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,

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: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
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+
"github.com/kubernetes-sigs/controller-runtime/pkg/client"
30+
)
31+
32+
const (
33+
// CACertName is the name of the CA certificate
34+
CACertName = "ca-cert.pem"
35+
// ServerKeyName is the name of the server private key
36+
ServerKeyName = "key.pem"
37+
// ServerCertName is the name of the serving certificate
38+
ServerCertName = "cert.pem"
39+
)
40+
41+
// Options are options for configuring a CertOutput.
42+
type Options struct {
43+
Client client.Client
44+
CertGenerator certgenerator.CertGenerator
45+
}
46+
47+
// CertWriter provides method to handle webhooks.
48+
type CertWriter interface {
49+
// Create a new CertWriter with the given runtime.Object
50+
New(runtime.Object) (CertWriter, error)
51+
// Handle ensures that the webhooks it manages have the right certificates.
52+
Handle() error
53+
}
54+
55+
// New builds a new CertWriter for the provided webhook configuration.
56+
func New(webhookConfig runtime.Object, ops Options) (CertWriter, error) {
57+
if ops.CertGenerator == nil {
58+
ops.CertGenerator = &certgenerator.SelfSignedCertGenerator{}
59+
}
60+
if ops.Client == nil {
61+
// TODO: default the client
62+
}
63+
s := &SecretCertWriter{
64+
Client: ops.Client,
65+
CertGenerator: ops.CertGenerator,
66+
}
67+
sWriter, err := s.New(webhookConfig)
68+
if err != nil {
69+
return nil, err
70+
}
71+
72+
f := &FSCertWriter{
73+
CertGenerator: ops.CertGenerator,
74+
}
75+
fWriter, err := f.New(webhookConfig)
76+
if err != nil {
77+
return nil, err
78+
}
79+
80+
return &MultiCertWriter{
81+
CertWriters: []CertWriter{sWriter, fWriter},
82+
}, nil
83+
}
84+
85+
func handleCommon(webhook *admissionregistrationv1beta1.Webhook, ch certio) error {
86+
// This happens when a webhook name is specified in the annotation, but this webhook doesn't exist.
87+
if webhook == nil {
88+
// TODO: add a log here if useful.
89+
return nil
90+
}
91+
webhookName := webhook.Name
92+
93+
certs, err := ch.read(webhookName)
94+
if apierrors.IsNotFound(err) {
95+
certs, err = ch.write(webhookName)
96+
if err != nil {
97+
return err
98+
}
99+
} else if err != nil {
100+
return err
101+
}
102+
103+
// Recreate the cert if it's invalid.
104+
if !validCert(certs) {
105+
certs, err = ch.write(webhookName)
106+
if err != nil {
107+
return err
108+
}
109+
if err != nil {
110+
return err
111+
}
112+
}
113+
114+
// Ensure the CA bundle in the webhook configuration has the signing CA.
115+
caBundle := webhook.ClientConfig.CABundle
116+
caCert := certs.CACert
117+
if !bytes.Contains(caBundle, caCert) {
118+
webhook.ClientConfig.CABundle = append(caBundle, caCert...)
119+
}
120+
return nil
121+
}
122+
123+
// certio provides methods for handling certificates for webhook.
124+
type certio interface {
125+
// read reads a wehbook name and returns the certs for it.
126+
read(webhookName string) (*certgenerator.Certs, error)
127+
// write writes the certs and return the certs it wrote.
128+
write(webhookName string) (*certgenerator.Certs, error)
129+
}
130+
131+
func validCert(certs *certgenerator.Certs) bool {
132+
// TODO: 1) validate the key and the cert are valid pair e.g. call crypto/tls.X509KeyPair()
133+
// 2) validate the cert with the CA cert
134+
// 3) validate the cert is for a certain DNSName
135+
// e.g.
136+
// c, err := tls.X509KeyPair(cert, key)
137+
// err := c.Verify(options)
138+
139+
return true
140+
}
141+
142+
func getWebhooksFromObject(obj runtime.Object) ([]admissionregistrationv1beta1.Webhook, error) {
143+
switch typed := obj.(type) {
144+
case *admissionregistrationv1beta1.MutatingWebhookConfiguration:
145+
return typed.Webhooks, nil
146+
case *admissionregistrationv1beta1.ValidatingWebhookConfiguration:
147+
return typed.Webhooks, nil
148+
//case *unstructured.Unstructured:
149+
// TODO: implement this if needed
150+
default:
151+
return nil, fmt.Errorf("unsupported type: %T, only support v1beta1.MutatingWebhookConfiguration and v1beta1.ValidatingWebhookConfiguration", typed)
152+
}
153+
}
154+
155+
func webhookClientConfigToCommonName(config *admissionregistrationv1beta1.WebhookClientConfig) (string, error) {
156+
if config.Service != nil && config.URL != nil {
157+
return "", fmt.Errorf("service and URL can't be set at the same time in a webhook: %#v", config)
158+
}
159+
if config.Service == nil && config.URL == nil {
160+
return "", fmt.Errorf("one of service and URL need to be set in a webhook: %#v", config)
161+
}
162+
if config.Service != nil {
163+
return certgenerator.ServiceToCommonName(config.Service.Namespace, config.Service.Name), nil
164+
}
165+
if config.URL != nil {
166+
u, err := url.Parse(*config.URL)
167+
return u.Host, err
168+
}
169+
return "", nil
170+
}

pkg/admission/certwriter/doc.go

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
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 CertWriter
21+
22+
// webhookConfiguration is either a mutatingWebhookConfiguration or a validatingWebhookConfiguration.
23+
writer, err := New(webhookConfiguration, Options{Client: client}))
24+
if err != nil {
25+
// handler error
26+
}
27+
28+
29+
Create a SecretCertWriter
30+
31+
s := &SecretCertWriter{
32+
// Set Client and(or) CertGenerator if needed
33+
}
34+
writer, err := s.New(webhookConfiguration) // webhookConfiguration is an existing runtime.Object
35+
if err != nil {
36+
// handle error
37+
}
38+
39+
Provision the certificates using the initialized CertWriter.
40+
41+
// writer can be either one of the CertWriters created above
42+
err = writer.Handle()
43+
if err != nil {
44+
// handler error
45+
}
46+
47+
*/
48+
package certwriter

0 commit comments

Comments
 (0)