1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2024-12-15 17:51:20 +00:00

support self-signed certificates via secrets

This commit is contained in:
shivdudhani 2019-05-28 18:16:22 -07:00
parent 64b38a5c75
commit 9e0149739f
4 changed files with 137 additions and 5 deletions

View file

@ -9,6 +9,7 @@ import (
certificates "k8s.io/api/certificates/v1beta1"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
)
// Issues TLS certificate for webhook server using given PEM private key
@ -75,7 +76,7 @@ func (c *Client) submitAndApproveCertificateRequest(req *certificates.Certificat
res.Status.Conditions = append(res.Status.Conditions, certificates.CertificateSigningRequestCondition{
Type: certificates.CertificateApproved,
Reason: "NKP-Approve",
Message: "This CSR was approved by Nirmata kube-policy controller",
Message: "This CSR was approved by Nirmata kyverno controller",
})
res, err = certClient.UpdateApproval(res)
if err != nil {
@ -113,6 +114,94 @@ func (c *Client) fetchCertificateFromRequest(req *certificates.CertificateSignin
return nil, errors.New(fmt.Sprintf("Cerificate fetch timeout is reached: %d seconds", maxWaitSeconds))
}
const (
ns string = "kyverno"
tlskeypair string = "tls.kyverno"
tlskeypaircert string = "tls.crt"
tlskeypairkey string = "tls.key"
tlsca string = "tls-ca"
tlscarootca string = "rootCA.crt"
)
// CheckPrePreqSelfSignedCert checks if the required secrets are defined
// if the user is providing self-signed certificates,key pair and CA
func (c *Client) CheckPrePreqSelfSignedCert() bool {
// Check if secrets are defined if user is specifiying self-signed certificates
tlspairfound := true
tlscafound := true
_, err := c.GetResource(Secrets, ns, tlskeypair)
if err != nil {
tlspairfound = false
}
_, err = c.GetResource(Secrets, ns, tlsca)
if err != nil {
tlscafound = false
}
if tlspairfound == tlscafound {
return true
}
// Fail if only one of them is defined
c.logger.Printf("while using self-signed certificates specify both secrets %s/%s & %s/%s for (cert,key) pair & CA respectively", ns, tlskeypair, ns, tlsca)
if !tlspairfound {
c.logger.Printf("secret %s/%s not defined for (cert,key) pair", ns, tlskeypair)
}
if !tlscafound {
c.logger.Printf("secret %s/%s not defined for CA", ns, tlsca)
}
return false
}
func (c *Client) TlsrootCAfromSecret() (result []byte) {
stlsca, err := c.GetResource(Secrets, ns, tlsca)
if err != nil {
return result
}
tlsca, err := convertToSecret(stlsca)
if err != nil {
utilruntime.HandleError(err)
return result
}
result = tlsca.Data[tlscarootca]
if len(result) == 0 {
c.logger.Printf("root CA certificate not found in secret %s/%s", ns, tlsca.Name)
return result
}
c.logger.Printf("using CA bundle defined in secret %s/%s to validate the webhook's server certificate", ns, tlsca.Name)
return result
}
func (c *Client) TlsPairFromSecrets() *tls.TlsPemPair {
// Check if secrets are defined
stlskeypair, err := c.GetResource(Secrets, ns, tlskeypair)
if err != nil {
return nil
}
tlskeypair, err := convertToSecret(stlskeypair)
if err != nil {
utilruntime.HandleError(err)
return nil
}
pemPair := tls.TlsPemPair{
Certificate: tlskeypair.Data[tlskeypaircert],
PrivateKey: tlskeypair.Data[tlskeypairkey],
}
if len(pemPair.Certificate) == 0 {
c.logger.Printf("TLS Certificate not found in secret %s/%s", ns, tlskeypair.Name)
return nil
}
if len(pemPair.PrivateKey) == 0 {
c.logger.Printf("TLS PrivateKey not found in secret %s/%s", ns, tlskeypair.Name)
return nil
}
c.logger.Printf("using TLS pair defined in secret %s/%s for webhook's server tls configuration", ns, tlskeypair.Name)
return &pemPair
}
const privateKeyField string = "privateKey"
const certificateField string = "certificate"
@ -187,5 +276,5 @@ func (c *Client) WriteTlsPair(props tls.TlsCertificateProps, pemPair *tls.TlsPem
}
func generateSecretName(props tls.TlsCertificateProps) string {
return tls.GenerateInClusterServiceName(props) + ".kube-policy-tls-pair"
return tls.GenerateInClusterServiceName(props) + ".kyverno-tls-pair"
}

View file

@ -41,5 +41,32 @@ The [Kyverno CLI](documentation/testing-policies-cli.md) allows you to write and
When the cluster ca & key is not passed as arguments(mostly for in-cluster mode), the TLS communicate between admission webhook and api-server a certificate signer configured to issue certificate to a certificate signing request(CSR) generated by Kyverno.
The Kubernetes controller manager provides a default implementation of a signer. To verify if it is enabled, check if the command args --cluster-signing-cert-file and --cluster-signing-key-file are passed to the controller manager with paths to your Certificate Authoritys keypair.
## Use self-signed certificates to test kyverno.
To create root CA and generate certificate & key pair using it via openssl:
1. `openssl genrsa -out rootCA.key 4096`
2. `openssl req -x509 -new -nodes -key rootCA.key -sha256 -days 1024 -out rootCA.crt -subj "/C=US/ST=test/L=test /O=test /OU=PIB/CN=*.kyverno.svc/emailAddress=test@test.com”`
3. `openssl genrsa -out webhook.key 4096`
4. `openssl req -new -key webhook.key -out webhook.csr -subj "/C=US/ST=test /L=test /O=test /OU=PIB/CN=kyverno-svc.kyverno.svc/emailAddress=test@test.com"`
5. `openssl x509 -req -in webhook.csr -CA rootCA.crt -CAkey rootCA.key -CAcreateserial -out webhook.crt -days 1024 -sha256`
the following generated files are used to create secrets:
- rootCA.crt
- webhooks.crt
- webhooks.key
To create the required secrets:
1. `kubectl create ns kyverno`
2. `kubectl -n kyverno create secret tls tls.kyverno --cert=webhook.crt --key=webhook.key `
3. `kubectl -n kyverno create secret generic tls-ca --from-file=rootCA.crt`
Secret | Data | Content
------------ | ------------- | -------------
`tls.ca` | rootCA.crt | root CA used to sign the certificate
`tls.kyverno` | tls.key & tls.crt | key and signed certificate
Here, we create the project namespace kyverno, followed by secrets for CA and TLS pair(cert,key). If the above secrets are defined then the kyverno Webhooks would use these to define the TLS pair for web server and CA bundle used to validate the webhook's server certificate in the Mutating/Validating Webhooks configuration.
To deploy the kyverno project, run `kubectl create -f definitions/install.yaml`. You can ignore the error 'namespaces "kyverno" already exists', as we have already created the namespace 'kyverno' while defining the secrets the previous step.
---
<small>*Read Next >> [Writing Policies](/documentation/writing-policies.md)*</small>

View file

@ -22,7 +22,6 @@ var (
func main() {
clientConfig, err := createClientConfig(kubeconfig)
if err != nil {
log.Fatalf("Error building kubeconfig: %v\n", err)
}
@ -31,6 +30,10 @@ func main() {
log.Fatalf("Error creating client: %v\n", err)
}
if !client.CheckPrePreqSelfSignedCert() {
log.Fatalf("Error loading the pre-requisites\n")
}
policyInformerFactory, err := sharedinformer.NewSharedInformerFactory(clientConfig)
if err != nil {
log.Fatalf("Error creating policy sharedinformer: %v\n", err)

View file

@ -71,7 +71,14 @@ func (wrc *WebhookRegistrationClient) Deregister() {
}
func (wrc *WebhookRegistrationClient) constructMutatingWebhookConfig(configuration *rest.Config) (*admregapi.MutatingWebhookConfiguration, error) {
caData := extractCA(configuration)
var caData []byte
// Check if ca is defined in the secret tls-ca
// assume the key and signed cert have been defined in secret tls.kyverno
caData = wrc.client.TlsrootCAfromSecret()
if len(caData) == 0 {
// load the CA from kubeconfig
caData = extractCA(configuration)
}
if len(caData) == 0 {
return nil, errors.New("Unable to extract CA data from configuration")
}
@ -94,7 +101,13 @@ func (wrc *WebhookRegistrationClient) constructMutatingWebhookConfig(configurati
}
func (wrc *WebhookRegistrationClient) constructValidatingWebhookConfig(configuration *rest.Config) (*admregapi.ValidatingWebhookConfiguration, error) {
caData := extractCA(configuration)
// Check if ca is defined in the secret tls-ca
// assume the key and signed cert have been defined in secret tls.kyverno
caData := wrc.client.TlsrootCAfromSecret()
if len(caData) == 0 {
// load the CA from kubeconfig
caData = extractCA(configuration)
}
if len(caData) == 0 {
return nil, errors.New("Unable to extract CA data from configuration")
}