diff --git a/KubeArmor/cert/cert.go b/KubeArmor/cert/cert.go new file mode 100644 index 0000000000..f787b3bd4c --- /dev/null +++ b/KubeArmor/cert/cert.go @@ -0,0 +1,209 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2022 Authors of KubeArmor + +package cert + +import ( + "context" + "crypto/rand" + "crypto/rsa" + "crypto/x509" + "crypto/x509/pkix" + "encoding/pem" + "fmt" + "math/big" + "net" + "os" + "path/filepath" + "time" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes" +) + +const ( + // ORG kubearmor + KubeArmor_ORG string = "kubearmor" + KubeArmor_CN string = "kubearmor" +) + +var DefaultKubeArmorServerConfig CertConfig = CertConfig{ + CN: KubeArmor_CN, + Organization: KubeArmor_ORG, + KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, +} + +var DefaultKubeArmorClientConfig CertConfig = CertConfig{ + CN: KubeArmor_CN, + Organization: KubeArmor_ORG, + KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}, +} + +var DefaultKubeArmorCAConfig CertConfig = CertConfig{ + CN: KubeArmor_CN, + Organization: KubeArmor_ORG, + KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign, +} + +type CertConfig struct { + CN string // Common Name + Organization string + DNS []string + IPs []string + KeyUsage x509.KeyUsage + ExtKeyUsage []x509.ExtKeyUsage + NotAfter time.Time +} + +// CertKeyPair type +type CertKeyPair struct { + Crt *x509.Certificate + Key *rsa.PrivateKey +} + +// CertBytes type +type CertBytes struct { + Crt []byte + Key []byte +} + +type CertPath struct { + Base string + CertFile string + KeyFile string // Not Required if CertOnly:true + CertOnly bool // if true read certificate only +} + +func GetCertKeyPairFromCertBytes(certBytes *CertBytes) (*CertKeyPair, error) { + // Parse CA certificate and key + certPem, _ := pem.Decode(certBytes.Crt) + keyPem, _ := pem.Decode(certBytes.Key) + + crt, err := x509.ParseCertificate(certPem.Bytes) + if err != nil { + fmt.Println("Error parsing CA certificate:", err) + return nil, err + } + + key, err := x509.ParsePKCS1PrivateKey(keyPem.Bytes) + if err != nil { + fmt.Println("Error parsing CA private key:", err) + return nil, err + } + + return &CertKeyPair{ + Crt: crt, + Key: key, + }, nil +} + +// ReadCertFromFile func reads certificate key pair from the given path +func ReadCertFromFile(certPath *CertPath) (*CertBytes, error) { + var certBytes, keyBytes []byte + certFile := filepath.Clean(filepath.Join(certPath.Base, certPath.CertFile)) + keyFile := filepath.Clean(filepath.Join(certPath.Base, certPath.KeyFile)) + + // Check if certificate file exists + if _, err := os.Stat(certFile); os.IsNotExist(err) { + fmt.Println("certificate file does not exist:", err) + return nil, err + } + + certBytes, err := os.ReadFile(certFile) + if err != nil { + fmt.Println("Error reading CA certificate file:", err) + return nil, err + } + + if !certPath.CertOnly { + // Check if key file exists + if _, err := os.Stat(keyFile); os.IsNotExist(err) { + fmt.Println("CA key file does not exist:", err) + return nil, err + } + + keyBytes, err = os.ReadFile(keyFile) + if err != nil { + fmt.Println("Error reading CA key file:", err) + return nil, err + } + } + + return &CertBytes{ + Crt: certBytes, + Key: keyBytes, + }, nil +} + +// ReadCertFromK8sSecret func reads cert from the k8s tls secret +// it assumes the cert and key file exists with tls.crt and tls.key names respectively +// that is true in case of kubernetes.io/tls secret type, +// https://kubernetes.io/docs/concepts/configuration/secret/#tls-secrets +func ReadCertFromK8sSecret(client *kubernetes.Clientset, namespace, secret string) (*CertBytes, error) { + + // get secret + cert, err := client.CoreV1().Secrets(namespace).Get(context.Background(), secret, metav1.GetOptions{}) + if err != nil { + return nil, err + } + + return &CertBytes{ + Crt: cert.Data["tls.crt"], + Key: cert.Data["tls.key"], + }, nil +} + +func GenerateCert(cfg *CertConfig) (*CertKeyPair, error) { + // Generate a new RSA private key for the server + key, err := rsa.GenerateKey(rand.Reader, 2048) + if err != nil { + fmt.Println("error generating cert private key:", err) + return nil, err + } + + // Create a template for the certificate + template := x509.Certificate{ + SerialNumber: big.NewInt(1), + Subject: pkix.Name{CommonName: cfg.CN, Organization: []string{cfg.Organization}}, + NotBefore: time.Now(), + NotAfter: cfg.NotAfter, + KeyUsage: cfg.KeyUsage, + ExtKeyUsage: cfg.ExtKeyUsage, + BasicConstraintsValid: true, + } + + for _, ip := range cfg.IPs { + template.IPAddresses = append(template.IPAddresses, net.ParseIP(ip)) + } + + return &CertKeyPair{Crt: &template, Key: key}, nil +} + +// GenerateSelfSignedCert func generates cert and key signed by provided CA +func GenerateSelfSignedCert(ca *CertKeyPair, cfg *CertConfig) (*CertBytes, error) { + certKeyPair, err := GenerateCert(cfg) + if err != nil { + return nil, err + } + + // Create the certificate signed by the CA + certBytes, err := x509.CreateCertificate(rand.Reader, certKeyPair.Crt, ca.Crt, &certKeyPair.Key.PublicKey, ca.Key) + if err != nil { + fmt.Println("Error creating certificate:", err) + return nil, err + } + + certPEM := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: certBytes}) + keyPEM := pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(certKeyPair.Key)}) + + if certPEM == nil || keyPEM == nil { + return nil, fmt.Errorf("error encoding certificate") + } + + return &CertBytes{ + Crt: certPEM, + Key: keyPEM, + }, nil +} diff --git a/KubeArmor/cert/certloader.go b/KubeArmor/cert/certloader.go new file mode 100644 index 0000000000..31e2be17be --- /dev/null +++ b/KubeArmor/cert/certloader.go @@ -0,0 +1,107 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2022 Authors of KubeArmor + +package cert + +import ( + "crypto/tls" + "crypto/x509" + + "k8s.io/client-go/kubernetes" +) + +type CertLoader interface { + GetCertificateAndCaPool() (*tls.Certificate, *x509.CertPool, error) +} + +// generate self sign certificate dynamically +type SelfSignedCertLoader struct { + CaCertPath CertPath + CertConfig CertConfig +} + +func (loader *SelfSignedCertLoader) GetCertificateAndCaPool() (*tls.Certificate, *x509.CertPool, error) { + // load ca certificate and ca key from given path + caCertBytes, err := ReadCertFromFile(&loader.CaCertPath) + if err != nil { + return nil, nil, err + } + caCert, err := GetCertKeyPairFromCertBytes(caCertBytes) + if err != nil { + return nil, nil, err + } + caCertPool := x509.NewCertPool() + caCertPool.AddCert(caCert.Crt) + // create certificate signed with provided ca certificate + certBytes, err := GenerateSelfSignedCert(caCert, &loader.CertConfig) + if err != nil { + return nil, nil, err + } + cert, err := tls.X509KeyPair(certBytes.Crt, certBytes.Key) + if err != nil { + return nil, nil, err + } + return &cert, caCertPool, nil +} + +// load certificates provided by external source using file +type ExternalCertLoader struct { + CaCertPath CertPath + CertPath CertPath +} + +func (loader *ExternalCertLoader) GetCertificateAndCaPool() (*tls.Certificate, *x509.CertPool, error) { + // load ca certificate from cert path, assuming only ca.crt is present + loader.CaCertPath.CertOnly = true + caCertBytes, err := ReadCertFromFile(&loader.CaCertPath) + if err != nil { + return nil, nil, err + } + caCert, err := GetCertKeyPairFromCertBytes(caCertBytes) + if err != nil { + return nil, nil, err + } + caCertPool := x509.NewCertPool() + caCertPool.AddCert(caCert.Crt) + // load server/client certificate from cert path + certBytes, err := ReadCertFromFile(&loader.CertPath) + if err != nil { + return nil, nil, err + } + cert, err := tls.X509KeyPair(certBytes.Crt, certBytes.Key) + if err != nil { + return nil, nil, err + } + return &cert, caCertPool, nil +} + +type K8sCertLoader struct { + CertConfig CertConfig + K8sClient *kubernetes.Clientset + Namespace string + Secret string +} + +func (loader *K8sCertLoader) GetCertificateAndCaPool() (*tls.Certificate, *x509.CertPool, error) { + // load certificate from k8s secret + caCertBytes, err := ReadCertFromK8sSecret(loader.K8sClient, loader.Namespace, loader.Secret) + if err != nil { + return nil, nil, err + } + caCert, err := GetCertKeyPairFromCertBytes(caCertBytes) + if err != nil { + return nil, nil, err + } + caCertPool := x509.NewCertPool() + caCertPool.AddCert(caCert.Crt) + // create certificate signed with provided ca certificate + certBytes, err := GenerateSelfSignedCert(caCert, &loader.CertConfig) + if err != nil { + return nil, nil, err + } + cert, err := tls.X509KeyPair(certBytes.Crt, certBytes.Key) + if err != nil { + return nil, nil, err + } + return &cert, caCertPool, nil +} diff --git a/KubeArmor/cert/tls.go b/KubeArmor/cert/tls.go new file mode 100644 index 0000000000..a51d085f11 --- /dev/null +++ b/KubeArmor/cert/tls.go @@ -0,0 +1,108 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2022 Authors of KubeArmor + +package cert + +import ( + "crypto/tls" + + "google.golang.org/grpc/credentials" + "k8s.io/client-go/kubernetes" +) + +const ( + SelfCertProvider string = "self" + ExternalCertProvider string = "external" +) + +type TlsConfig struct { + // Server/Client Certificate Configurations + CertCfg CertConfig + // If CA is Provided Using a K8s Secret + // Namespace, Secret and K8sClient are Required + ReadCACertFromSecret bool + Secret string + Namespace string + K8sClient *kubernetes.Clientset + + CACertPath CertPath + CertPath CertPath + // Source of Client/Server Certificate, + // "self" : Certificates Will be Generated Dynamically + // "external": Certificates Are Provided Using File + CertProvider string +} + +type TlsCredentialManager struct { + CertLoader CertLoader +} + +func NewTlsCredentialManager(cfg *TlsConfig) *TlsCredentialManager { + switch cfg.CertProvider { + case SelfCertProvider: + if cfg.ReadCACertFromSecret { + cl := K8sCertLoader{ + CertConfig: cfg.CertCfg, + K8sClient: cfg.K8sClient, + Namespace: cfg.Namespace, + Secret: cfg.Secret, + } + return &TlsCredentialManager{ + CertLoader: &cl, + } + } + cl := SelfSignedCertLoader{ + CaCertPath: cfg.CACertPath, + CertConfig: cfg.CertCfg, + } + return &TlsCredentialManager{ + CertLoader: &cl, + } + case ExternalCertProvider: + cl := ExternalCertLoader{ + CaCertPath: cfg.CACertPath, + CertPath: cfg.CertPath, + } + return &TlsCredentialManager{ + CertLoader: &cl, + } + } + return nil +} + +func (manager *TlsCredentialManager) CreateTlsClientCredentials() (credentials.TransportCredentials, error) { + clientCert, caCertPool, err := manager.CertLoader.GetCertificateAndCaPool() + if err != nil { + return nil, err + } + // Create tls client credentials + config := &tls.Config{ + Certificates: []tls.Certificate{*clientCert}, + RootCAs: caCertPool, + } + + return credentials.NewTLS(config), nil +} + +func (manager *TlsCredentialManager) CreateTlsServerCredentials() (credentials.TransportCredentials, error) { + serverCert, caCertPool, err := manager.CertLoader.GetCertificateAndCaPool() + if err != nil { + return nil, err + } + // Create tls server credentials + config := &tls.Config{ + Certificates: []tls.Certificate{*serverCert}, + ClientCAs: caCertPool, + ClientAuth: tls.RequireAndVerifyClientCert, + } + + return credentials.NewTLS(config), nil +} + +func GetX509KeyPairFromCertBytes(certBytes *CertBytes) (*tls.Certificate, error) { + cert, err := tls.X509KeyPair(certBytes.Crt, certBytes.Key) + if err != nil { + return nil, err + } + return &cert, nil +} diff --git a/KubeArmor/config/config.go b/KubeArmor/config/config.go index b6d14e00d8..5ce0d89029 100644 --- a/KubeArmor/config/config.go +++ b/KubeArmor/config/config.go @@ -21,6 +21,9 @@ type KubearmorConfig struct { Host string // Host name to use for feeds GRPC string // gRPC Port to use + TLSEnabled bool // enable tls + TLSCertPath string // tls certification path + TLSCertProvider string // tls certficate provider LogPath string // Log file to use SELinuxProfileDir string // Directory to store SELinux profiles CRISocket string // Container runtime to use @@ -63,6 +66,11 @@ const ( ConfigCluster string = "cluster" ConfigHost string = "host" ConfigGRPC string = "gRPC" + ConfigTLSCertPath string = "tlsCertPath" + ConfigTLSCertProvider string = "tlsCertProvider" + SelfCertProvider string = "self" + ExternalCertProvider string = "external" + ConfigTLS string = "tlsEnabled" ConfigLogPath string = "logPath" ConfigSELinuxProfileDir string = "seLinuxProfileDir" ConfigCRISocket string = "criSocket" @@ -94,6 +102,9 @@ func readCmdLineParams() { hostStr := flag.String(ConfigHost, strings.Split(hostname, ".")[0], "host name") grpcStr := flag.String(ConfigGRPC, "32767", "gRPC port number") + tlsEnabled := flag.Bool(ConfigTLS, true, "enable tls for secure grpc connection") + tlsCertsStr := flag.String(ConfigTLSCertPath, "/var/lib/kubearmor/tls", "path to tls ca certificate files ca.crt, ca.crt") + tlsCertProvider := flag.String(ConfigTLSCertProvider, "self", "source of certificate {self|external}, self: create certificate dynamically, external: provided by some external entity") logStr := flag.String(ConfigLogPath, "none", "log file path, {path|stdout|none}") seLinuxProfileDirStr := flag.String(ConfigSELinuxProfileDir, "/tmp/kubearmor.selinux", "SELinux profile directory") criSocket := flag.String(ConfigCRISocket, "", "path to CRI socket (format: unix:///path/to/file.sock)") @@ -142,6 +153,9 @@ func readCmdLineParams() { viper.SetDefault(ConfigHost, *hostStr) viper.SetDefault(ConfigGRPC, *grpcStr) + viper.SetDefault(ConfigTLS, *tlsEnabled) + viper.SetDefault(ConfigTLSCertPath, *tlsCertsStr) + viper.SetDefault(ConfigTLSCertProvider, *tlsCertProvider) viper.SetDefault(ConfigLogPath, *logStr) viper.SetDefault(ConfigSELinuxProfileDir, *seLinuxProfileDirStr) viper.SetDefault(ConfigCRISocket, *criSocket) @@ -209,6 +223,9 @@ func LoadConfig() error { } GlobalCfg.GRPC = viper.GetString(ConfigGRPC) + GlobalCfg.TLSEnabled = viper.GetBool(ConfigTLS) + GlobalCfg.TLSCertPath = viper.GetString(ConfigTLSCertPath) + GlobalCfg.TLSCertProvider = viper.GetString(ConfigTLSCertProvider) GlobalCfg.LogPath = viper.GetString(ConfigLogPath) GlobalCfg.CRISocket = os.Getenv("CRI_SOCKET") diff --git a/KubeArmor/feeder/feeder.go b/KubeArmor/feeder/feeder.go index 94b8f8a749..e8cef75478 100644 --- a/KubeArmor/feeder/feeder.go +++ b/KubeArmor/feeder/feeder.go @@ -21,8 +21,10 @@ import ( tp "github.com/kubearmor/KubeArmor/KubeArmor/types" "github.com/google/uuid" + "github.com/kubearmor/KubeArmor/KubeArmor/cert" pb "github.com/kubearmor/KubeArmor/protobuf" "google.golang.org/grpc" + "google.golang.org/grpc/credentials" "google.golang.org/grpc/keepalive" ) @@ -32,6 +34,16 @@ import ( // Running flag var Running bool +var CACertPath = cert.CertPath{ + Base: cfg.GlobalCfg.TLSCertPath, + CertFile: "ca.crt", + KeyFile: "ca.key", +} +var ServerCertPath = cert.CertPath{ + Base: cfg.GlobalCfg.TLSCertPath, + CertFile: "server.crt", + KeyFile: "server.key", +} // QueueSize const QueueSize = 1000 @@ -287,8 +299,6 @@ func NewFeeder(node *tp.Node, nodeLock **sync.RWMutex) *Feeder { } } - // create a log server - logService := &LogService{ QueueSize: 1000, Running: &fd.Running, @@ -303,7 +313,22 @@ func NewFeeder(node *tp.Node, nodeLock **sync.RWMutex) *Feeder { Timeout: 5 * time.Second, } - fd.LogServer = grpc.NewServer(grpc.KeepaliveEnforcementPolicy(kaep), grpc.KeepaliveParams(kasp)) + if cfg.GlobalCfg.TLSEnabled { + tlsCredentials, err := loadTLSCredentials(node.NodeIP) + if err != nil { + kg.Errf("cannot load TLS credentials: %s", err) + return nil + } + kg.Print("Server started with tls enabled") + // create a log server + fd.LogServer = grpc.NewServer( + grpc.Creds(tlsCredentials), + grpc.KeepaliveEnforcementPolicy(kaep), + grpc.KeepaliveParams(kasp), + ) + } else { + fd.LogServer = grpc.NewServer(grpc.KeepaliveEnforcementPolicy(kaep), grpc.KeepaliveParams(kasp)) + } pb.RegisterLogServiceServer(fd.LogServer, logService) @@ -711,3 +736,18 @@ func (fd *Feeder) PushLog(log tp.Log) { } } } + +func loadTLSCredentials(ip string) (credentials.TransportCredentials, error) { + // create certificate configurations + serverCertConfig := cert.DefaultKubeArmorServerConfig + serverCertConfig.IPs = []string{ip} + serverCertConfig.NotAfter = time.Now().Add(365 * 24 * time.Hour) //valid for 1 year + // as of now daemonset creates certificates dynamically + tlsConfig := cert.TlsConfig{ + CertCfg: serverCertConfig, + CertProvider: cfg.GlobalCfg.TLSCertProvider, + CACertPath: CACertPath, + } + creds, err := cert.NewTlsCredentialManager(&tlsConfig).CreateTlsServerCredentials() + return creds, err +} diff --git a/deployments/get/defaults.go b/deployments/get/defaults.go index 5b623b32ea..196ed2d8fa 100644 --- a/deployments/get/defaults.go +++ b/deployments/get/defaults.go @@ -12,29 +12,32 @@ var port int32 = 32767 // K8s Object Name Defaults var ( - KubeArmorServiceAccountName = kubearmor - KubeArmorClusterRoleBindingName = "kubearmor-clusterrolebinding" - KubeArmorClusterRoleName = "kubearmor-clusterrole" - RelayServiceName = kubearmor - RelayServiceAccountName = "kubearmor-relay" - RelayClusterRoleName = "kubearmor-relay-clusterrole" - RelayClusterRoleBindingName = "kubearmor-relay-clusterrolebinding" - RelayDeploymentName = "kubearmor-relay" - KubeArmorConfigMapName = "kubearmor-config" - KubeArmorControllerDeploymentName = "kubearmor-controller" - KubeArmorControllerServiceAccountName = KubeArmorControllerDeploymentName - KubeArmorControllerClusterRoleName = "kubearmor-controller-clusterrole" - KubeArmorControllerClusterRoleBindingName = "kubearmor-controller-clusterrolebinding" - KubeArmorControllerLeaderElectionRoleName = "kubearmor-controller-leader-election-role" - KubeArmorControllerLeaderElectionRoleBindingName = "kubearmor-controller-leader-election-rolebinding" - KubeArmorControllerProxyRoleName = "kubearmor-controller-proxy-role" - KubeArmorControllerProxyRoleBindingName = "kubearmor-controller-proxy-rolebinding" - KubeArmorControllerMetricsReaderRoleName = "kubearmor-controller-metrics-reader-role" - KubeArmorControllerMetricsReaderRoleBindingName = "kubearmor-controller-metrics-reader-rolebinding" - KubeArmorControllerMetricsServiceName = "kubearmor-controller-metrics-service" - KubeArmorControllerWebhookServiceName = "kubearmor-controller-webhook-service" - KubeArmorControllerSecretName = "kubearmor-controller-webhook-server-cert" - KubeArmorControllerMutatingWebhookConfiguration = "kubearmor-controller-mutating-webhook-configuration" + KubeArmorServiceAccountName = kubearmor + KubeArmorClusterRoleBindingName = "kubearmor-clusterrolebinding" + KubeArmorClusterRoleName = "kubearmor-clusterrole" + RelayServiceName = kubearmor + RelayServiceAccountName = "kubearmor-relay" + RelayClusterRoleName = "kubearmor-relay-clusterrole" + RelayClusterRoleBindingName = "kubearmor-relay-clusterrolebinding" + RelayDeploymentName = "kubearmor-relay" + KubeArmorConfigMapName = "kubearmor-config" + KubeArmorControllerDeploymentName = "kubearmor-controller" + KubeArmorControllerServiceAccountName = KubeArmorControllerDeploymentName + KubeArmorControllerClusterRoleName = "kubearmor-controller-clusterrole" + KubeArmorControllerClusterRoleBindingName = "kubearmor-controller-clusterrolebinding" + KubeArmorControllerLeaderElectionRoleName = "kubearmor-controller-leader-election-role" + KubeArmorControllerLeaderElectionRoleBindingName = "kubearmor-controller-leader-election-rolebinding" + KubeArmorControllerProxyRoleName = "kubearmor-controller-proxy-role" + KubeArmorControllerProxyRoleBindingName = "kubearmor-controller-proxy-rolebinding" + KubeArmorControllerMetricsReaderRoleName = "kubearmor-controller-metrics-reader-role" + KubeArmorControllerMetricsReaderRoleBindingName = "kubearmor-controller-metrics-reader-rolebinding" + KubeArmorControllerMetricsServiceName = "kubearmor-controller-metrics-service" + KubeArmorControllerWebhookServiceName = "kubearmor-controller-webhook-service" + KubeArmorControllerSecretName = "kubearmor-controller-webhook-server-cert" + KubeArmorControllerMutatingWebhookConfiguration = "kubearmor-controller-mutating-webhook-configuration" + KubeArmorTLSEnabled = true + KubeArmorCASecretName = "kubearmor-ca-certs" + KubeArmorCASecretDefaultMode int32 = 0444 ) // DaemonSetConfig Structure @@ -97,6 +100,32 @@ var apparmorVol = corev1.Volume{ }, } +var kubearmorCACertVolumeMount = corev1.VolumeMount{ + MountPath: "/var/lib/kubearmor/tls", + Name: "kubearmor-ca-secret", + ReadOnly: true, +} + +var kubearmorCACertVolume = corev1.Volume{ + Name: "kubearmor-ca-secret", + VolumeSource: corev1.VolumeSource{ + Secret: &corev1.SecretVolumeSource{ + DefaultMode: &KubeArmorCASecretDefaultMode, + SecretName: KubeArmorCASecretName, + Items: []corev1.KeyToPath{ + { + Key: "tls.crt", + Path: "ca.crt", + }, + { + Key: "tls.key", + Path: "ca.key", + }, + }, + }, + }, +} + var envVar = []corev1.EnvVar{ { Name: "KUBEARMOR_NODENAME", diff --git a/deployments/get/objects.go b/deployments/get/objects.go index 5355276f24..66279d74ed 100644 --- a/deployments/get/objects.go +++ b/deployments/get/objects.go @@ -136,7 +136,7 @@ var envVars = []corev1.EnvVar{ // GetRelayDeployment Function func GetRelayDeployment(namespace string) *appsv1.Deployment { - return &appsv1.Deployment{ + deployment := &appsv1.Deployment{ TypeMeta: metav1.TypeMeta{ Kind: "Deployment", APIVersion: "apps/v1", @@ -180,6 +180,15 @@ func GetRelayDeployment(namespace string) *appsv1.Deployment { }, }, } + + if KubeArmorTLSEnabled { + deployment.Spec.Template.Spec.Containers[0].VolumeMounts = + append(deployment.Spec.Template.Spec.Containers[0].VolumeMounts, kubearmorCACertVolumeMount) + deployment.Spec.Template.Spec.Volumes = + append(deployment.Spec.Template.Spec.Volumes, kubearmorCACertVolume) + } + + return deployment } // GetRelayServiceAccount Function @@ -338,6 +347,11 @@ func GenerateDaemonSet(env, namespace string) *appsv1.DaemonSet { }, } + if KubeArmorTLSEnabled { + containerVolumeMounts = append(containerVolumeMounts, kubearmorCACertVolumeMount) + volumes = append(volumes, kubearmorCACertVolume) + } + if env == "gke" { containerVolumeMounts = append(containerVolumeMounts, gkeHostUsrVolMnt) volumes = append(volumes, gkeHostUsrVol) @@ -953,6 +967,28 @@ func GetKubeArmorControllerMutationAdmissionConfiguration(namespace string, caCe } } +// GetKubeArmorCASecret Func +func GetKubeArmorCASecret(namespace string, caCert string, tlsCrt string, tlsKey string) *corev1.Secret { + data := make(map[string]string) + data["tls.crt"] = tlsCrt + data["tls.key"] = tlsKey + return &corev1.Secret{ + Type: corev1.SecretTypeTLS, + TypeMeta: metav1.TypeMeta{ + Kind: "Secret", + APIVersion: "v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: KubeArmorCASecretName, + Namespace: namespace, + Labels: map[string]string{ + "kubearmor-app": kubearmor, + }, + }, + StringData: data, + } +} + // GetKubeArmorControllerTLSSecret Functionn func GetKubeArmorControllerTLSSecret(namespace string, caCert string, tlsCrt string, tlsKey string) *corev1.Secret { data := make(map[string]string) diff --git a/deployments/helm/KubeArmor/templates/daemonset.yaml b/deployments/helm/KubeArmor/templates/daemonset.yaml index d954717f6d..e8807b4efe 100644 --- a/deployments/helm/KubeArmor/templates/daemonset.yaml +++ b/deployments/helm/KubeArmor/templates/daemonset.yaml @@ -19,6 +19,10 @@ spec: containers: - args: - -gRPC=32767 + {{printf "- -tlsEnabled=%t" .Values.kubearmor.tls.enabled}} + {{- with .Values.kubearmor.args }} + {{- toYaml . | trim | nindent 8 }} + {{- end }} image: {{printf "%s:%s" .Values.kubearmor.image.repository .Values.kubearmor.image.tag}} imagePullPolicy: {{ .Values.kubearmor.imagePullPolicy }} env: @@ -49,6 +53,9 @@ spec: terminationMessagePolicy: File volumeMounts: {{- toYaml .Values.kubearmor.commonMounts | trim | nindent 10 }} + {{- if .Values.kubearmor.tls.enabled -}} + {{- toYaml .Values.kubearmor.tls.kubearmorCACertVolumeMount | trim | nindent 10 }} + {{- end -}} {{- if eq .Values.environment.name "docker" }} {{- toYaml .Values.kubearmor.volumeMountsDocker | trim | nindent 10 }} {{- else if eq .Values.environment.name "crio" }} @@ -105,6 +112,9 @@ spec: - operator: Exists volumes: {{- toYaml .Values.kubearmor.commonVolumes | trim | nindent 8}} + {{- if .Values.kubearmor.tls.enabled -}} + {{- toYaml .Values.kubearmor.tls.kubearmorCACertVolume | trim | nindent 8 }} + {{- end -}} {{- if eq .Values.environment.name "docker" }} {{- toYaml .Values.kubearmor.volumesDocker | trim | nindent 8 }} {{- else if eq .Values.environment.name "crio" }} @@ -125,4 +135,4 @@ spec: {{- toYaml .Values.kubearmor.volumesEKS | trim | nindent 8 }} {{- else }} # generic {{- toYaml .Values.kubearmor.volumesGeneric | trim | nindent 8 }} - {{- end }} + {{- end }} \ No newline at end of file diff --git a/deployments/helm/KubeArmor/templates/deployment.yaml b/deployments/helm/KubeArmor/templates/deployment.yaml index 122a33280b..9eead76774 100644 --- a/deployments/helm/KubeArmor/templates/deployment.yaml +++ b/deployments/helm/KubeArmor/templates/deployment.yaml @@ -1,3 +1,5 @@ +--- +# deployment {{- if .Values.kubearmorRelay.enabled }} apiVersion: apps/v1 kind: Deployment @@ -19,7 +21,9 @@ spec: kubearmor-app: kubearmor-relay spec: containers: - - image: {{printf "%s:%s" .Values.kubearmorRelay.image.repository .Values.kubearmorRelay.image.tag}} + - args: + {{printf "- -tlsEnabled=%t" .Values.kubearmor.tls.enabled}} + image: {{printf "%s:%s" .Values.kubearmorRelay.image.repository .Values.kubearmorRelay.image.tag}} imagePullPolicy: {{ .Values.kubearmorRelay.imagePullPolicy }} name: kubearmor-relay-server env: @@ -31,9 +35,17 @@ spec: value: {{ quote .Values.kubearmorRelay.enableStdoutMsg }} ports: - containerPort: 32767 + {{- if .Values.kubearmor.tls.enabled }} + volumeMounts: + {{- toYaml .Values.kubearmor.tls.kubearmorCACertVolumeMount | trim | nindent 10 }} + {{- end}} nodeSelector: kubernetes.io/os: linux serviceAccountName: kubearmor-relay + {{- if .Values.kubearmor.tls.enabled }} + volumes: + {{- toYaml .Values.kubearmor.tls.kubearmorCACertVolume | trim | nindent 8 }} + {{- end }} {{- end }} --- apiVersion: apps/v1 diff --git a/deployments/helm/KubeArmor/templates/secrets.yaml b/deployments/helm/KubeArmor/templates/secrets.yaml index a580370d2c..b6906b4fc1 100644 --- a/deployments/helm/KubeArmor/templates/secrets.yaml +++ b/deployments/helm/KubeArmor/templates/secrets.yaml @@ -46,3 +46,19 @@ webhooks: - pods scope: '*' sideEffects: NoneOnDryRun +--- +# kubearmor-ca secret +{{- if .Values.kubearmor.tls.enabled }} +{{- $ca := genCA "kubearmor" 1095 -}} +apiVersion: v1 +kind: Secret +type: kubernetes.io/tls +metadata: + name: {{ .Values.kubearmor.tls.caSecretName }} + labels: + kubearmor-app: kubearmor + namespace: {{.Release.Namespace}} +data: + tls.key: {{ $ca.Key | b64enc }} + tls.crt: {{ $ca.Cert | b64enc }} +{{- end}} \ No newline at end of file diff --git a/deployments/helm/KubeArmor/values.yaml b/deployments/helm/KubeArmor/values.yaml index e0641f0c1a..28cf291b78 100644 --- a/deployments/helm/KubeArmor/values.yaml +++ b/deployments/helm/KubeArmor/values.yaml @@ -73,6 +73,27 @@ kubearmor: # kubearmor daemonset arguments. See `kubearmor --help` args: [] + tls: + enabled: true + caSecretName: kubearmor-ca + kubearmorCACertVolumeMount: + - mountPath: /var/lib/kubearmor/tls + name: kubearmor-ca-secret + readOnly: true + kubearmorCACertVolume: + - name: kubearmor-ca-secret + projected: + defaultMode: 0444 + sources: + - secret: + name: kubearmor-ca + items: + - key: tls.crt + path: ca.crt + - key: tls.key + path: ca.key + + capabilities: add: - SETUID diff --git a/tests/go.sum b/tests/go.sum index 9dc42c9940..d32d937556 100644 --- a/tests/go.sum +++ b/tests/go.sum @@ -546,6 +546,7 @@ golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0= golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -583,6 +584,7 @@ golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1 golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo= golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -608,6 +610,7 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE= golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -681,6 +684,7 @@ golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -740,6 +744,7 @@ golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.16.1 h1:TLyB3WofjdOEepBHAU20JdNC1Zbg87elYofWYAY5oZA= golang.org/x/tools v0.16.1/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -771,6 +776,7 @@ google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= @@ -810,10 +816,10 @@ google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20220107163113-42d7afdf6368/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto/googleapis/api v0.0.0-20231002182017-d307bd883b97/go.mod h1:iargEX0SFPm3xcfMI0d1domjg0ZF4Aa0p2awqyxhvF0= -google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:0xJLfVdJqpAPl8tDg1ujOCGzx6LFLttXT5NhllGOXY4= +google.golang.org/genproto v0.0.0-20231002182017-d307bd883b97 h1:SeZZZx0cP0fqUyA+oRzP9k7cSwJlvDFiROO72uwD6i0= +google.golang.org/genproto/googleapis/api v0.0.0-20231002182017-d307bd883b97 h1:W18sezcAYs+3tDZX4F80yctqa12jcP1PUS2gQu1zTPU= +google.golang.org/genproto/googleapis/rpc v0.0.0-20231002182017-d307bd883b97 h1:6GQBEOdGkX6MMTLT9V+TjtIRZCw9VPD5Z+yHY9wMgS0= google.golang.org/genproto/googleapis/rpc v0.0.0-20231002182017-d307bd883b97/go.mod h1:v7nGkzlmW8P3n/bKmWBn2WpBjpOEx8Q6gMueudAmKfY= -google.golang.org/genproto/googleapis/rpc v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:oQ5rr10WTTMvP4A36n8JpR1OrO1BEiV4f78CneXZxkA= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= @@ -833,8 +839,8 @@ google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA5 google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= +google.golang.org/grpc v1.60.1 h1:26+wFr+cNqSGFcOXcabYC0lUVJVRa2Sb2ortSK7VrEU= google.golang.org/grpc v1.60.1/go.mod h1:OlCHIeLYqSSsLi6i49B5QGdzaMZK9+M7LXN2FKz4eGM= -google.golang.org/grpc v1.61.0/go.mod h1:VUbo7IFqmF1QtCAstipjG0GIoq49KvMe9+h1jFLBNJs= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=