2019-05-15 07:30:22 -07:00
package client
2019-03-15 19:03:55 +02:00
import (
2020-10-07 14:17:37 -07:00
"encoding/base64"
2019-03-15 19:03:55 +02:00
"fmt"
2019-05-29 12:36:03 -07:00
"net/url"
2019-03-15 19:03:55 +02:00
2020-10-07 11:12:31 -07:00
"github.com/kyverno/kyverno/pkg/config"
tls "github.com/kyverno/kyverno/pkg/tls"
2019-03-15 19:03:55 +02:00
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2020-10-07 14:17:37 -07:00
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
2019-05-29 12:36:03 -07:00
"k8s.io/client-go/rest"
2019-03-15 19:03:55 +02:00
)
2019-11-18 11:41:37 -08:00
// InitTLSPemPair Loads or creates PEM private key and TLS certificate for webhook server.
// Created pair is stored in cluster's secret.
// Returns struct with key/certificate pair.
2020-01-08 16:40:19 -08:00
func ( c * Client ) InitTLSPemPair ( configuration * rest . Config , fqdncn bool ) ( * tls . TlsPemPair , error ) {
2020-03-17 11:05:20 -07:00
logger := c . log
2019-11-18 11:41:37 -08:00
certProps , err := c . GetTLSCertProps ( configuration )
if err != nil {
return nil , err
}
2020-10-07 14:30:00 -07:00
logger . Info ( "Building key/certificate pair for TLS" )
2020-10-07 14:44:36 -07:00
tlsPair , err := c . buildTlsPemPair ( certProps , fqdncn )
2020-10-07 14:30:00 -07:00
if err != nil {
return nil , err
}
2020-10-07 14:44:36 -07:00
if err = c . WriteTlsPairToSecret ( certProps , tlsPair ) ; err != nil {
2020-10-07 14:30:00 -07:00
return nil , fmt . Errorf ( "Unable to save TLS pair to the cluster: %v" , err )
2019-11-18 11:41:37 -08:00
}
2020-10-07 14:30:00 -07:00
2019-11-18 11:41:37 -08:00
return tlsPair , nil
}
2020-10-07 14:44:36 -07:00
//buildTlsPemPair Issues TLS certificate for webhook server using self-signed CA cert
2019-03-15 19:03:55 +02:00
// Returns signed and approved TLS certificate in PEM format
2020-10-07 14:44:36 -07:00
func ( c * Client ) buildTlsPemPair ( props tls . TlsCertificateProps , fqdncn bool ) ( * tls . TlsPemPair , error ) {
2020-10-07 14:17:37 -07:00
caCert , caPEM , err := tls . GenerateCACert ( )
2019-03-15 19:03:55 +02:00
if err != nil {
return nil , err
}
2020-10-07 14:44:36 -07:00
if err := c . WriteCACertToSecret ( caPEM , props ) ; err != nil {
2019-03-15 19:03:55 +02:00
return nil , err
}
2020-10-07 14:44:36 -07:00
return tls . GenerateCertPem ( caCert , props , fqdncn )
2019-03-15 19:03:55 +02:00
}
2019-06-05 17:43:59 -07:00
//ReadRootCASecret returns the RootCA from the pre-defined secret
2019-05-29 12:36:03 -07:00
func ( c * Client ) ReadRootCASecret ( ) ( result [ ] byte ) {
2020-03-17 11:05:20 -07:00
logger := c . log . WithName ( "ReadRootCASecret" )
2019-05-29 12:36:03 -07:00
certProps , err := c . GetTLSCertProps ( c . clientConfig )
2019-05-28 18:16:22 -07:00
if err != nil {
2020-03-17 11:05:20 -07:00
logger . Error ( err , "failed to get TLS Cert Properties" )
2019-05-29 12:36:03 -07:00
return result
2019-05-28 18:16:22 -07:00
}
2019-05-29 12:36:03 -07:00
sname := generateRootCASecretName ( certProps )
2020-08-07 09:47:33 +05:30
stlsca , err := c . GetResource ( "" , Secrets , certProps . Namespace , sname )
2019-05-28 18:16:22 -07:00
if err != nil {
return result
}
tlsca , err := convertToSecret ( stlsca )
if err != nil {
2020-03-17 11:05:20 -07:00
logger . Error ( err , "failed to convert secret" , "name" , sname , "namespace" , certProps . Namespace )
2019-05-28 18:16:22 -07:00
return result
}
2019-05-29 12:36:03 -07:00
result = tlsca . Data [ rootCAKey ]
2019-05-28 18:16:22 -07:00
if len ( result ) == 0 {
2020-03-17 11:05:20 -07:00
logger . Info ( "root CA certificate not found in secret" , "name" , tlsca . Name , "namespace" , certProps . Namespace )
2019-05-28 18:16:22 -07:00
return result
}
2020-03-17 11:05:20 -07:00
logger . V ( 4 ) . Info ( "using CA bundle defined in secret to validate the webhook's server certificate" , "name" , tlsca . Name , "namespace" , certProps . Namespace )
2019-05-28 18:16:22 -07:00
return result
}
2019-05-29 12:36:03 -07:00
const selfSignedAnnotation string = "self-signed-cert"
const rootCAKey string = "rootCA.crt"
2019-03-15 19:03:55 +02:00
2019-06-05 17:43:59 -07:00
//ReadTlsPair Reads the pair of TLS certificate and key from the specified secret.
2019-05-15 11:24:27 -07:00
func ( c * Client ) ReadTlsPair ( props tls . TlsCertificateProps ) * tls . TlsPemPair {
2020-03-17 11:05:20 -07:00
logger := c . log . WithName ( "ReadTlsPair" )
2019-05-29 12:36:03 -07:00
sname := generateTLSPairSecretName ( props )
2020-08-07 09:47:33 +05:30
unstrSecret , err := c . GetResource ( "" , Secrets , props . Namespace , sname )
2019-05-15 07:30:22 -07:00
if err != nil {
2020-03-17 11:05:20 -07:00
logger . Error ( err , "Failed to get secret" , "name" , sname , "namespace" , props . Namespace )
2019-05-15 07:30:22 -07:00
return nil
}
2019-05-29 12:36:03 -07:00
// If secret contains annotation 'self-signed-cert', then it's created using helper scripts to setup self-signed certificates.
// As the root CA used to sign the certificate is required for webhook cnofiguration, check if the corresponding secret is created
annotations := unstrSecret . GetAnnotations ( )
if _ , ok := annotations [ selfSignedAnnotation ] ; ok {
sname := generateRootCASecretName ( props )
2020-08-07 09:47:33 +05:30
_ , err := c . GetResource ( "" , Secrets , props . Namespace , sname )
2019-05-29 12:36:03 -07:00
if err != nil {
2020-03-17 11:05:20 -07:00
logger . Error ( err , "Root CA secret is required while using self-signed certificates TLS pair, defaulting to generating new TLS pair" , "name" , sname , "namespace" , props . Namespace )
2019-05-29 12:36:03 -07:00
return nil
}
}
2019-05-15 07:30:22 -07:00
secret , err := convertToSecret ( unstrSecret )
2019-03-15 19:03:55 +02:00
if err != nil {
return nil
}
2019-05-14 18:10:25 +03:00
pemPair := tls . TlsPemPair {
2019-05-29 12:36:03 -07:00
Certificate : secret . Data [ v1 . TLSCertKey ] ,
PrivateKey : secret . Data [ v1 . TLSPrivateKeyKey ] ,
2019-03-15 19:03:55 +02:00
}
if len ( pemPair . Certificate ) == 0 {
2020-03-17 11:05:20 -07:00
logger . Info ( "TLS Certificate not found in secret" , "name" , sname , "namespace" , props . Namespace )
2019-03-15 19:03:55 +02:00
return nil
}
if len ( pemPair . PrivateKey ) == 0 {
2020-03-17 11:05:20 -07:00
logger . Info ( "TLS PrivateKey not found in secret" , "name" , sname , "namespace" , props . Namespace )
2019-03-15 19:03:55 +02:00
return nil
}
return & pemPair
}
2020-10-07 14:44:36 -07:00
func ( c * Client ) WriteCACertToSecret ( caPEM * tls . TlsPemPair , props tls . TlsCertificateProps ) error {
2020-10-07 14:17:37 -07:00
logger := c . log . WithName ( "CAcert" )
name := generateRootCASecretName ( props )
secretUnstr , err := c . GetResource ( "" , Secrets , props . Namespace , name )
if err != nil {
secret := & v1 . Secret {
TypeMeta : metav1 . TypeMeta {
Kind : "Secret" ,
APIVersion : "v1" ,
} ,
ObjectMeta : metav1 . ObjectMeta {
Name : name ,
Namespace : props . Namespace ,
Annotations : map [ string ] string {
selfSignedAnnotation : "true" ,
} ,
} ,
Data : map [ string ] [ ] byte {
rootCAKey : caPEM . Certificate ,
} ,
Type : v1 . SecretTypeOpaque ,
}
_ , err := c . CreateResource ( "" , Secrets , props . Namespace , secret , false )
if err == nil {
logger . Info ( "secret created" , "name" , name , "namespace" , props . Namespace )
}
return err
}
2020-10-07 14:30:00 -07:00
2020-10-07 14:17:37 -07:00
if _ , ok := secretUnstr . GetAnnotations ( ) [ selfSignedAnnotation ] ; ! ok {
secretUnstr . SetAnnotations ( map [ string ] string { selfSignedAnnotation : "true" } )
}
dataMap := map [ string ] interface { } {
rootCAKey : base64 . StdEncoding . EncodeToString ( caPEM . Certificate ) }
if err := unstructured . SetNestedMap ( secretUnstr . Object , dataMap , "data" ) ; err != nil {
return err
}
_ , err = c . UpdateResource ( "" , Secrets , props . Namespace , secretUnstr , false )
if err != nil {
return err
}
logger . Info ( "secret updated" , "name" , name , "namespace" , props . Namespace )
return nil
}
2020-10-07 14:44:36 -07:00
//WriteTlsPairToSecret Writes the pair of TLS certificate and key to the specified secret.
2019-03-15 19:03:55 +02:00
// Updates existing secret or creates new one.
2020-10-07 14:44:36 -07:00
func ( c * Client ) WriteTlsPairToSecret ( props tls . TlsCertificateProps , pemPair * tls . TlsPemPair ) error {
2020-03-17 11:05:20 -07:00
logger := c . log . WithName ( "WriteTlsPair" )
2019-05-29 12:36:03 -07:00
name := generateTLSPairSecretName ( props )
2020-10-07 14:30:00 -07:00
secretUnstr , err := c . GetResource ( "" , Secrets , props . Namespace , name )
2019-05-23 11:59:30 -07:00
if err != nil {
2019-05-15 07:30:22 -07:00
secret := & v1 . Secret {
2019-03-15 19:03:55 +02:00
TypeMeta : metav1 . TypeMeta {
Kind : "Secret" ,
APIVersion : "v1" ,
} ,
ObjectMeta : metav1 . ObjectMeta {
Name : name ,
Namespace : props . Namespace ,
} ,
Data : map [ string ] [ ] byte {
2019-05-29 12:36:03 -07:00
v1 . TLSCertKey : pemPair . Certificate ,
v1 . TLSPrivateKeyKey : pemPair . PrivateKey ,
2019-03-15 19:03:55 +02:00
} ,
2019-05-29 12:36:03 -07:00
Type : v1 . SecretTypeTLS ,
2019-03-15 19:03:55 +02:00
}
2020-08-07 09:47:33 +05:30
_ , err := c . CreateResource ( "" , Secrets , props . Namespace , secret , false )
2019-03-15 19:03:55 +02:00
if err == nil {
2020-03-17 11:05:20 -07:00
logger . Info ( "secret created" , "name" , name , "namespace" , props . Namespace )
2019-03-15 19:03:55 +02:00
}
2019-05-23 11:59:30 -07:00
return err
}
2020-10-07 14:30:00 -07:00
dataMap := map [ string ] interface { } {
v1 . TLSCertKey : base64 . StdEncoding . EncodeToString ( pemPair . Certificate ) ,
v1 . TLSPrivateKeyKey : base64 . StdEncoding . EncodeToString ( pemPair . PrivateKey ) ,
}
if err := unstructured . SetNestedMap ( secretUnstr . Object , dataMap , "data" ) ; err != nil {
return err
2019-05-23 11:59:30 -07:00
}
2020-10-07 14:30:00 -07:00
_ , err = c . UpdateResource ( "" , Secrets , props . Namespace , secretUnstr , false )
2019-05-23 11:59:30 -07:00
if err != nil {
return err
2019-03-15 19:03:55 +02:00
}
2020-10-07 14:30:00 -07:00
2020-03-17 11:05:20 -07:00
logger . Info ( "secret updated" , "name" , name , "namespace" , props . Namespace )
2019-05-23 11:59:30 -07:00
return nil
2019-03-15 19:03:55 +02:00
}
2019-05-29 12:36:03 -07:00
func generateTLSPairSecretName ( props tls . TlsCertificateProps ) string {
2019-05-28 18:16:22 -07:00
return tls . GenerateInClusterServiceName ( props ) + ".kyverno-tls-pair"
2019-03-15 19:03:55 +02:00
}
2019-05-29 12:36:03 -07:00
func generateRootCASecretName ( props tls . TlsCertificateProps ) string {
return tls . GenerateInClusterServiceName ( props ) + ".kyverno-tls-ca"
}
//GetTLSCertProps provides the TLS Certificate Properties
func ( c * Client ) GetTLSCertProps ( configuration * rest . Config ) ( certProps tls . TlsCertificateProps , err error ) {
apiServerURL , err := url . Parse ( configuration . Host )
if err != nil {
return certProps , err
}
certProps = tls . TlsCertificateProps {
Service : config . WebhookServiceName ,
Namespace : config . KubePolicyNamespace ,
ApiServerHost : apiServerURL . Hostname ( ) ,
}
return certProps , nil
}