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:
commit
88f3579b2c
5 changed files with 145 additions and 6 deletions
|
@ -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"
|
||||||
}
|
}
|
||||||
|
|
|
@ -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>
|
||||||
|
|
3
init.go
3
init.go
|
@ -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 {
|
||||||
|
|
5
main.go
5
main.go
|
@ -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)
|
||||||
|
|
|
@ -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")
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue