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

Merge pull request #114 from nirmata/98_bug

update error msg and documentation+ support self-signed certificate for testing
This commit is contained in:
Shivkumar Dudhani 2019-05-28 20:18:42 -07:00 committed by GitHub
commit 88f3579b2c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 145 additions and 6 deletions

View file

@ -9,6 +9,7 @@ import (
certificates "k8s.io/api/certificates/v1beta1" certificates "k8s.io/api/certificates/v1beta1"
v1 "k8s.io/api/core/v1" v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/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 // Issues TLS certificate for webhook server using given PEM private key
@ -31,7 +32,7 @@ func (c *Client) GenerateTlsPemPair(props tls.TlsCertificateProps) (*tls.TlsPemP
tlsCert, err := c.fetchCertificateFromRequest(certRequest, 10) tlsCert, err := c.fetchCertificateFromRequest(certRequest, 10)
if err != nil { if err != nil {
return nil, errors.New(fmt.Sprintf("Unable to fetch certificate from request: %v", err)) return nil, errors.New(fmt.Sprintf("Failed to configure a certificate for the Kyverno controller. A CA certificate is required to allow the Kubernetes API Server to communicate with Kyverno. You can either provide a certificate or configure your cluster to allow certificate signing. Please refer to https://github.com/nirmata/kyverno/installation.md.: %v", err))
} }
return &tls.TlsPemPair{ return &tls.TlsPemPair{
@ -75,7 +76,7 @@ func (c *Client) submitAndApproveCertificateRequest(req *certificates.Certificat
res.Status.Conditions = append(res.Status.Conditions, certificates.CertificateSigningRequestCondition{ res.Status.Conditions = append(res.Status.Conditions, certificates.CertificateSigningRequestCondition{
Type: certificates.CertificateApproved, Type: certificates.CertificateApproved,
Reason: "NKP-Approve", 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) res, err = certClient.UpdateApproval(res)
if err != nil { 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)) 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 privateKeyField string = "privateKey"
const certificateField string = "certificate" const certificateField string = "certificate"
@ -187,5 +276,5 @@ func (c *Client) WriteTlsPair(props tls.TlsCertificateProps, pemPair *tls.TlsPem
} }
func generateSecretName(props tls.TlsCertificateProps) string { func generateSecretName(props tls.TlsCertificateProps) string {
return tls.GenerateInClusterServiceName(props) + ".kube-policy-tls-pair" return tls.GenerateInClusterServiceName(props) + ".kyverno-tls-pair"
} }

View file

@ -36,6 +36,37 @@ To check if the controller is working, find it in the list of kyverno pods:
The [Kyverno CLI](documentation/testing-policies-cli.md) allows you to write and test policies without installing Kyverno in a Kubernetes cluster. The [Kyverno CLI](documentation/testing-policies-cli.md) allows you to write and test policies without installing Kyverno in a Kubernetes cluster.
# Pre-Requisites
Kyverno installs an admission webhook that requires a CA-signed certificate and key to setup TLS communication with the kube-apiserver. In-cluster mode, there are 2 ways to configure the admission webhook TLS configuration:
* Kyverno generates certificate and key pair for user, and a signed certificate is issued against the certificate signing request generated by Kyverno. This setup requires a 'certificate signer' configured in the cluster. The kube-controller-manager provides a default implementation of a signer which can be used to issue certificates. 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 CA's key-pair.
* Use self-signed certificates.
## Use self-signed certificates
To create a root CA, generate signed certificate and key using 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 files are generated and are used to create kubernetes 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
Kyverno uses secrets created above to define the TLS configuration for the webserver hook and specify the CA bundle used to validate the webhook's server certificate in the admission webhook configurations.
To deploy the Kyverno project, run `kubectl create -f definitions/install.yaml`. You can ignore the error 'namespaces "kyverno" already exists', which occurs as we previously created the namespace while creating the secrets.
--- ---
<small>*Read Next >> [Writing Policies](/documentation/writing-policies.md)*</small> <small>*Read Next >> [Writing Policies](/documentation/writing-policies.md)*</small>

View file

@ -27,6 +27,9 @@ func initTlsPemPair(certFile, keyFile string, clientConfig *rest.Config, client
if certFile != "" || keyFile != "" { if certFile != "" || keyFile != "" {
tlsPair = tlsPairFromFiles(certFile, keyFile) tlsPair = tlsPairFromFiles(certFile, keyFile)
} }
// if cert & key defined in secret(tls.kyverno) use it,
// the CA used to sign the cert is expected in secret (tls-ca)
tlsPair = client.TlsPairFromSecrets()
var err error var err error
if tlsPair != nil { if tlsPair != nil {

View file

@ -22,7 +22,6 @@ var (
func main() { func main() {
clientConfig, err := createClientConfig(kubeconfig) clientConfig, err := createClientConfig(kubeconfig)
if err != nil { if err != nil {
log.Fatalf("Error building kubeconfig: %v\n", err) log.Fatalf("Error building kubeconfig: %v\n", err)
} }
@ -31,6 +30,10 @@ func main() {
log.Fatalf("Error creating client: %v\n", err) log.Fatalf("Error creating client: %v\n", err)
} }
if !client.CheckPrePreqSelfSignedCert() {
log.Fatalf("Error loading the pre-requisites\n")
}
policyInformerFactory, err := sharedinformer.NewSharedInformerFactory(clientConfig) policyInformerFactory, err := sharedinformer.NewSharedInformerFactory(clientConfig)
if err != nil { if err != nil {
log.Fatalf("Error creating policy sharedinformer: %v\n", err) 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) { 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 { if len(caData) == 0 {
return nil, errors.New("Unable to extract CA data from configuration") 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) { 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 { if len(caData) == 0 {
return nil, errors.New("Unable to extract CA data from configuration") return nil, errors.New("Unable to extract CA data from configuration")
} }