-
Notifications
You must be signed in to change notification settings - Fork 1.2k
cert provisioner #22
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
cert provisioner #22
Changes from all commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
/* | ||
Copyright 2018 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 fake | ||
|
||
import ( | ||
"fmt" | ||
|
||
"sigs.k8s.io/controller-runtime/pkg/admission/cert/generator" | ||
) | ||
|
||
// CertGenerator is a CertGenerator for testing. | ||
type CertGenerator struct { | ||
DNSNameToCertArtifacts map[string]*generator.Artifacts | ||
} | ||
|
||
var _ generator.CertGenerator = &CertGenerator{} | ||
|
||
// Generate generates certificates by matching a common name. | ||
func (cp *CertGenerator) Generate(commonName string) (*generator.Artifacts, error) { | ||
certs, found := cp.DNSNameToCertArtifacts[commonName] | ||
if !found { | ||
return nil, fmt.Errorf("failed to find common name %q in the CertGenerator", commonName) | ||
} | ||
return certs, nil | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,187 @@ | ||
/* | ||
Copyright 2018 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 writer | ||
|
||
import ( | ||
"bytes" | ||
"crypto/tls" | ||
"errors" | ||
"fmt" | ||
"log" | ||
"net/url" | ||
|
||
admissionregistrationv1beta1 "k8s.io/api/admissionregistration/v1beta1" | ||
apierrors "k8s.io/apimachinery/pkg/api/errors" | ||
"k8s.io/apimachinery/pkg/runtime" | ||
"sigs.k8s.io/controller-runtime/pkg/admission/cert/generator" | ||
"sigs.k8s.io/controller-runtime/pkg/client" | ||
) | ||
|
||
const ( | ||
// CACertName is the name of the CA certificate | ||
CACertName = "ca-cert.pem" | ||
// ServerKeyName is the name of the server private key | ||
ServerKeyName = "key.pem" | ||
// ServerCertName is the name of the serving certificate | ||
ServerCertName = "cert.pem" | ||
) | ||
|
||
// CertWriter provides method to handle webhooks. | ||
type CertWriter interface { | ||
// EnsureCert ensures that the webhooks have proper certificates. | ||
EnsureCerts(runtime.Object) error | ||
} | ||
|
||
// Options are options for configuring a CertWriter. | ||
type Options struct { | ||
Client client.Client | ||
CertGenerator generator.CertGenerator | ||
} | ||
|
||
// NewCertWriter builds a new CertWriter using the provided options. | ||
// By default, it builds a MultiCertWriter that is composed of a SecretCertWriter and a FSCertWriter. | ||
func NewCertWriter(ops Options) (CertWriter, error) { | ||
if ops.CertGenerator == nil { | ||
ops.CertGenerator = &generator.SelfSignedCertGenerator{} | ||
} | ||
if ops.Client == nil { | ||
// TODO: default the client if possible | ||
return nil, errors.New("Options.Client is required") | ||
} | ||
s := &SecretCertWriter{ | ||
Client: ops.Client, | ||
CertGenerator: ops.CertGenerator, | ||
} | ||
//f := &FSCertWriter{ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why is this commented out? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It has not been implemented ans tested completely :) |
||
// CertGenerator: ops.CertGenerator, | ||
//} | ||
return &MultiCertWriter{ | ||
CertWriters: []CertWriter{ | ||
s, | ||
//f, | ||
}, | ||
}, nil | ||
} | ||
|
||
// handleCommon ensures the given webhook has a proper certificate. | ||
// It uses the given certReadWriter to read and (or) write the certificate. | ||
func handleCommon(webhook *admissionregistrationv1beta1.Webhook, ch certReadWriter) error { | ||
if webhook == nil { | ||
return nil | ||
} | ||
if ch == nil { | ||
return errors.New("certReaderWriter should not be nil") | ||
} | ||
|
||
certs, err := createIfNotExists(webhook.Name, ch) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
// Recreate the cert if it's invalid. | ||
if !validCert(certs) { | ||
log.Printf("cert is invalid or expiring, regenerating a new one") | ||
certs, err = ch.overwrite(webhook.Name) | ||
if err != nil { | ||
return err | ||
} | ||
} | ||
|
||
// Ensure the CA bundle in the webhook configuration has the signing CA. | ||
caBundle := webhook.ClientConfig.CABundle | ||
caCert := certs.CACert | ||
if !bytes.Contains(caBundle, caCert) { | ||
webhook.ClientConfig.CABundle = append(caBundle, caCert...) | ||
} | ||
return nil | ||
} | ||
|
||
func createIfNotExists(webhookName string, ch certReadWriter) (*generator.Artifacts, error) { | ||
// Try to read first | ||
certs, err := ch.read(webhookName) | ||
if apierrors.IsNotFound(err) { | ||
// Create if not exists | ||
certs, err = ch.write(webhookName) | ||
switch { | ||
// This may happen if there is another racer. | ||
case apierrors.IsAlreadyExists(err): | ||
certs, err = ch.read(webhookName) | ||
if err != nil { | ||
return certs, err | ||
} | ||
case err != nil: | ||
return certs, err | ||
} | ||
} else if err != nil { | ||
return certs, err | ||
} | ||
return certs, nil | ||
} | ||
|
||
// certReadWriter provides methods for reading and writing certificates. | ||
type certReadWriter interface { | ||
// read reads a wehbook name and returns the certs for it. | ||
read(webhookName string) (*generator.Artifacts, error) | ||
// write writes the certs and return the certs it wrote. | ||
write(webhookName string) (*generator.Artifacts, error) | ||
// overwrite overwrites the existing certs and return the certs it wrote. | ||
overwrite(webhookName string) (*generator.Artifacts, error) | ||
} | ||
|
||
func validCert(certs *generator.Artifacts) bool { | ||
// TODO: | ||
// 1) validate the key and the cert are valid pair e.g. call crypto/tls.X509KeyPair() | ||
// 2) validate the cert with the CA cert | ||
// 3) validate the cert is for a certain DNSName | ||
// e.g. | ||
// c, err := tls.X509KeyPair(cert, key) | ||
// err := c.Verify(options) | ||
if certs == nil { | ||
return false | ||
} | ||
_, err := tls.X509KeyPair(certs.Cert, certs.Key) | ||
return err == nil | ||
} | ||
|
||
func getWebhooksFromObject(obj runtime.Object) ([]admissionregistrationv1beta1.Webhook, error) { | ||
switch typed := obj.(type) { | ||
case *admissionregistrationv1beta1.MutatingWebhookConfiguration: | ||
return typed.Webhooks, nil | ||
case *admissionregistrationv1beta1.ValidatingWebhookConfiguration: | ||
return typed.Webhooks, nil | ||
//case *unstructured.Unstructured: | ||
// TODO: implement this if needed | ||
default: | ||
return nil, fmt.Errorf("unsupported type: %T, only support v1beta1.MutatingWebhookConfiguration and v1beta1.ValidatingWebhookConfiguration", typed) | ||
} | ||
} | ||
|
||
func dnsNameForWebhook(config *admissionregistrationv1beta1.WebhookClientConfig) (string, error) { | ||
if config.Service != nil && config.URL != nil { | ||
return "", fmt.Errorf("service and URL can't be set at the same time in a webhook: %v", config) | ||
} | ||
if config.Service == nil && config.URL == nil { | ||
return "", fmt.Errorf("one of service and URL need to be set in a webhook: %v", config) | ||
} | ||
if config.Service != nil { | ||
return generator.ServiceToCommonName(config.Service.Namespace, config.Service.Name), nil | ||
} | ||
// config.URL != nil | ||
u, err := url.Parse(*config.URL) | ||
return u.Host, err | ||
|
||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This document is not correct, it doesn't do the file writer
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will happen in one week.
I can remove it for now.