2021-03-16 18:31:04 +00:00
package tls
import (
2022-04-26 20:18:14 +00:00
"context"
2021-03-16 18:31:04 +00:00
"fmt"
"net/url"
"github.com/kyverno/kyverno/pkg/config"
"github.com/pkg/errors"
v1 "k8s.io/api/core/v1"
2022-04-26 20:18:14 +00:00
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
2021-03-16 18:31:04 +00:00
"k8s.io/client-go/rest"
)
2021-05-05 05:10:01 +00:00
var ErrorsNotFound = "root CA certificate not found"
2021-03-16 18:31:04 +00:00
// ReadRootCASecret returns the RootCA from the pre-defined secret
2022-04-26 20:18:14 +00:00
func ReadRootCASecret ( restConfig * rest . Config , client kubernetes . Interface ) ( result [ ] byte , err error ) {
2021-03-16 18:31:04 +00:00
certProps , err := GetTLSCertProps ( restConfig )
if err != nil {
return nil , errors . Wrap ( err , "failed to get TLS Cert Properties" )
}
2022-05-11 06:14:30 +00:00
depl , err := client . AppsV1 ( ) . Deployments ( certProps . Namespace ) . Get ( context . TODO ( ) , config . KyvernoDeploymentName ( ) , metav1 . GetOptions { } )
2022-01-06 09:11:16 +00:00
deplHash := ""
if err == nil {
deplHash = fmt . Sprintf ( "%v" , depl . GetUID ( ) )
}
2022-05-10 15:50:04 +00:00
var deplHashSec string
2022-01-06 09:11:16 +00:00
var ok , managedByKyverno bool
2022-01-11 08:47:24 +00:00
sname := GenerateRootCASecretName ( certProps )
2022-04-26 20:18:14 +00:00
stlsca , err := client . CoreV1 ( ) . Secrets ( certProps . Namespace ) . Get ( context . TODO ( ) , sname , metav1 . GetOptions { } )
2021-03-16 18:31:04 +00:00
if err != nil {
return nil , err
}
2022-01-06 09:11:16 +00:00
if label , ok := stlsca . GetLabels ( ) [ ManagedByLabel ] ; ok {
managedByKyverno = label == "kyverno"
}
deplHashSec , ok = stlsca . GetAnnotations ( ) [ MasterDeploymentUID ]
2022-01-07 17:33:01 +00:00
if managedByKyverno && ( ok && deplHashSec != deplHash ) {
2022-01-06 09:11:16 +00:00
return nil , fmt . Errorf ( "outdated secret" )
}
2022-05-11 07:11:50 +00:00
// try "tls.crt"
result = stlsca . Data [ v1 . TLSCertKey ]
// if not there, try old "rootCA.crt"
if len ( result ) == 0 {
result = stlsca . Data [ rootCAKey ]
}
2021-03-16 18:31:04 +00:00
if len ( result ) == 0 {
2022-04-26 20:18:14 +00:00
return nil , errors . Errorf ( "%s in secret %s/%s" , ErrorsNotFound , certProps . Namespace , stlsca . Name )
2021-03-16 18:31:04 +00:00
}
return result , nil
}
// ReadTLSPair returns the pem pair from the pre-defined secret
2022-04-26 20:18:14 +00:00
func ReadTLSPair ( restConfig * rest . Config , client kubernetes . Interface ) ( * PemPair , error ) {
2021-03-16 18:31:04 +00:00
certProps , err := GetTLSCertProps ( restConfig )
if err != nil {
return nil , errors . Wrap ( err , "failed to get TLS Cert Properties" )
}
2022-05-11 06:14:30 +00:00
depl , err := client . AppsV1 ( ) . Deployments ( certProps . Namespace ) . Get ( context . TODO ( ) , config . KyvernoDeploymentName ( ) , metav1 . GetOptions { } )
2022-01-06 09:11:16 +00:00
deplHash := ""
if err == nil {
deplHash = fmt . Sprintf ( "%v" , depl . GetUID ( ) )
}
2022-05-10 15:50:04 +00:00
var deplHashSec string
2022-01-06 09:11:16 +00:00
var ok , managedByKyverno bool
2022-01-11 08:47:24 +00:00
sname := GenerateTLSPairSecretName ( certProps )
2022-04-26 20:18:14 +00:00
secret , err := client . CoreV1 ( ) . Secrets ( certProps . Namespace ) . Get ( context . TODO ( ) , sname , metav1 . GetOptions { } )
2021-03-16 18:31:04 +00:00
if err != nil {
return nil , fmt . Errorf ( "failed to get secret %s/%s: %v" , certProps . Namespace , sname , err )
}
2022-04-26 20:18:14 +00:00
if label , ok := secret . GetLabels ( ) [ ManagedByLabel ] ; ok {
2022-01-06 09:11:16 +00:00
managedByKyverno = label == "kyverno"
}
2022-04-26 20:18:14 +00:00
deplHashSec , ok = secret . GetAnnotations ( ) [ MasterDeploymentUID ]
2022-01-07 17:33:01 +00:00
if managedByKyverno && ( ok && deplHashSec != deplHash ) {
2022-01-06 09:11:16 +00:00
return nil , fmt . Errorf ( "outdated secret" )
}
2021-03-16 18:31:04 +00: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 configuration, check if the corresponding secret is created
2022-05-10 08:58:51 +00:00
{
2022-01-11 08:47:24 +00:00
sname := GenerateRootCASecretName ( certProps )
2022-04-26 20:18:14 +00:00
_ , err := client . CoreV1 ( ) . Secrets ( certProps . Namespace ) . Get ( context . TODO ( ) , sname , metav1 . GetOptions { } )
2021-03-16 18:31:04 +00:00
if err != nil {
return nil , fmt . Errorf ( "rootCA secret is required while using self-signed certificate TLS pair, defaulting to generating new TLS pair %s/%s" , certProps . Namespace , sname )
}
}
pemPair := PemPair {
Certificate : secret . Data [ v1 . TLSCertKey ] ,
PrivateKey : secret . Data [ v1 . TLSPrivateKeyKey ] ,
}
if len ( pemPair . Certificate ) == 0 {
return nil , fmt . Errorf ( "TLS Certificate not found in secret %s/%s" , certProps . Namespace , sname )
}
if len ( pemPair . PrivateKey ) == 0 {
return nil , fmt . Errorf ( "TLS PrivateKey not found in secret %s/%s" , certProps . Namespace , sname )
}
return & pemPair , nil
}
//GetTLSCertProps provides the TLS Certificate Properties
2022-05-10 08:58:51 +00:00
func GetTLSCertProps ( configuration * rest . Config ) ( * CertificateProps , error ) {
2021-03-16 18:31:04 +00:00
apiServerURL , err := url . Parse ( configuration . Host )
if err != nil {
2022-05-10 08:58:51 +00:00
return nil , err
2021-03-16 18:31:04 +00:00
}
2022-05-10 08:58:51 +00:00
return & CertificateProps {
2022-05-11 06:14:30 +00:00
Service : config . KyvernoServiceName ( ) ,
Namespace : config . KyvernoNamespace ( ) ,
2021-03-16 18:31:04 +00:00
APIServerHost : apiServerURL . Hostname ( ) ,
2022-05-10 08:58:51 +00:00
} , nil
2021-03-16 18:31:04 +00:00
}