From 4e63fcbfa05cae01e13b1c3500ebd9c6e46d4384 Mon Sep 17 00:00:00 2001 From: belyshevdenis Date: Fri, 15 Mar 2019 19:03:55 +0200 Subject: [PATCH 01/12] NK-31: Implemented creation TLS certificate Implemented storing the certificates in secret within the cluster. Implemented the cheking certificate's expiration date. Implemented initialization of server with certs data instead of files. --- kubeclient/certificates.go | 181 ++++++++++++++++++++++++++++++++++++ kubeclient/kubeclient.go | 9 +- main.go | 96 ++++++++++++++----- server/server.go | 17 ++-- utils/certificates_utils.go | 141 ++++++++++++++++++++++++++++ 5 files changed, 414 insertions(+), 30 deletions(-) create mode 100644 kubeclient/certificates.go create mode 100644 utils/certificates_utils.go diff --git a/kubeclient/certificates.go b/kubeclient/certificates.go new file mode 100644 index 0000000000..199111ab63 --- /dev/null +++ b/kubeclient/certificates.go @@ -0,0 +1,181 @@ +package kubeclient + +import ( + "errors" + "fmt" + "time" + + "github.com/nirmata/kube-policy/utils" + + certificates "k8s.io/api/certificates/v1beta1" + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// Issues TLS certificate for webhook server using given PEM private key +// Returns signed and approved TLS certificate in PEM format +func (kc *KubeClient) GenerateTlsPemPair(props utils.TlsCertificateProps) (*utils.TlsPemPair, error) { + privateKey, err := utils.TlsGeneratePrivateKey() + if err != nil { + return nil, err + } + + certRequest, err := utils.TlsCertificateGenerateRequest(privateKey, props) + if err != nil { + return nil, errors.New(fmt.Sprintf("Unable to create certificate request: %v", err)) + } + + certRequest, err = kc.submitAndApproveCertificateRequest(certRequest) + if err != nil { + return nil, errors.New(fmt.Sprintf("Unable to submit and approve certificate request: %v", err)) + } + + tlsCert, err := kc.fetchCertificateFromRequest(certRequest, 10) + if err != nil { + return nil, errors.New(fmt.Sprintf("Unable to fetch certificate from request: %v", err)) + } + + return &utils.TlsPemPair{ + Certificate: tlsCert, + PrivateKey: utils.TlsPrivateKeyToPem(privateKey), + }, nil +} + +// Submits and approves certificate request, returns request which need to be fetched +func (kc *KubeClient) submitAndApproveCertificateRequest(req *certificates.CertificateSigningRequest) (*certificates.CertificateSigningRequest, error) { + certClient := kc.client.CertificatesV1beta1().CertificateSigningRequests() + + csrList, err := certClient.List(metav1.ListOptions{}) + if err != nil { + return nil, errors.New(fmt.Sprintf("Unable to list existing certificate requests: %v", err)) + } + + for _, csr := range csrList.Items { + if csr.ObjectMeta.Name == req.ObjectMeta.Name { + err := certClient.Delete(csr.ObjectMeta.Name, defaultDeleteOptions()) + if err != nil { + return nil, errors.New(fmt.Sprintf("Unable to delete existing certificate request: %v", err)) + } + kc.logger.Printf("Old certificate request is deleted") + break + } + } + + res, err := certClient.Create(req) + if err != nil { + return nil, err + } + kc.logger.Printf("Certificate request %s is created", req.ObjectMeta.Name) + + res.Status.Conditions = append(res.Status.Conditions, certificates.CertificateSigningRequestCondition{ + Type: certificates.CertificateApproved, + Reason: "NKP-Approve", + Message: "This CSR was approved by Nirmata kube-policy controller", + }) + res, err = certClient.UpdateApproval(res) + if err != nil { + return nil, errors.New(fmt.Sprintf("Unable to approve certificate request: %v", err)) + } + kc.logger.Printf("Certificate request %s is approved", res.ObjectMeta.Name) + + return res, nil +} + +const certificateFetchWaitInterval time.Duration = 200 * time.Millisecond + +// Fetches certificate from given request. Tries to obtain certificate for maxWaitSeconds +func (kc *KubeClient) fetchCertificateFromRequest(req *certificates.CertificateSigningRequest, maxWaitSeconds uint8) ([]byte, error) { + // TODO: react of SIGINT and SIGTERM + timeStart := time.Now() + certClient := kc.client.CertificatesV1beta1().CertificateSigningRequests() + for time.Now().Sub(timeStart) < time.Duration(maxWaitSeconds)*time.Second { + r, err := certClient.Get(req.ObjectMeta.Name, defaultGetOptions()) + if err != nil { + return nil, err + } + + if r.Status.Certificate != nil { + return r.Status.Certificate, nil + } + + for _, condition := range r.Status.Conditions { + if condition.Type == certificates.CertificateDenied { + return nil, errors.New(condition.String()) + } + } + } + return nil, errors.New(fmt.Sprintf("Cerificate fetch timeout is reached: %d seconds", maxWaitSeconds)) +} + +const privateKeyField string = "privateKey" +const certificateField string = "certificate" + +// Reads the pair of TLS certificate and key from the specified secret. +func (kc *KubeClient) ReadTlsPair(props utils.TlsCertificateProps) *utils.TlsPemPair { + name := generateSecretName(props) + secret, err := kc.client.CoreV1().Secrets(props.Namespace).Get(name, defaultGetOptions()) + if err != nil { + kc.logger.Printf("Unable to get secret %s/%s: %s", props.Namespace, name, err) + return nil + } + + pemPair := utils.TlsPemPair{ + Certificate: secret.Data[certificateField], + PrivateKey: secret.Data[privateKeyField], + } + if len(pemPair.Certificate) == 0 { + kc.logger.Printf("TLS Certificate not found in secret %s/%s", props.Namespace, name) + return nil + } + if len(pemPair.PrivateKey) == 0 { + kc.logger.Printf("TLS PrivateKey not found in secret %s/%s", props.Namespace, name) + return nil + } + return &pemPair +} + +// Writes the pair of TLS certificate and key to the specified secret. +// Updates existing secret or creates new one. +func (kc *KubeClient) WriteTlsPair(props utils.TlsCertificateProps, pemPair *utils.TlsPemPair) error { + name := generateSecretName(props) + secret, err := kc.client.CoreV1().Secrets(props.Namespace).Get(name, defaultGetOptions()) + + if err == nil { // Update existing secret + if secret.Data == nil { + secret.Data = make(map[string][]byte) + } + secret.Data[certificateField] = pemPair.Certificate + secret.Data[privateKeyField] = pemPair.PrivateKey + + secret, err = kc.client.CoreV1().Secrets(props.Namespace).Update(secret) + if err == nil { + kc.logger.Printf("Secret %s is updated", name) + } + + } else { // Create new secret + secret = &v1.Secret{ + TypeMeta: metav1.TypeMeta{ + Kind: "Secret", + APIVersion: "v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: props.Namespace, + }, + Data: map[string][]byte{ + certificateField: pemPair.Certificate, + privateKeyField: pemPair.PrivateKey, + }, + } + + secret, err = kc.client.CoreV1().Secrets(props.Namespace).Create(secret) + if err == nil { + kc.logger.Printf("Secret %s is created", name) + } + } + return err +} + +func generateSecretName(props utils.TlsCertificateProps) string { + return utils.GenerateInClusterServiceName(props) + ".kube-policy-tls-pair" +} diff --git a/kubeclient/kubeclient.go b/kubeclient/kubeclient.go index 23cd53ffdc..9a5cf8001c 100644 --- a/kubeclient/kubeclient.go +++ b/kubeclient/kubeclient.go @@ -22,7 +22,7 @@ type KubeClient struct { // Checks parameters and creates new instance of KubeClient func NewKubeClient(config *rest.Config, logger *log.Logger) (*KubeClient, error) { if logger == nil { - logger = log.New(os.Stdout, "Policy Controller: ", log.LstdFlags|log.Lshortfile) + logger = log.New(os.Stdout, "Kubernetes client: ", log.LstdFlags|log.Lshortfile) } client, err := kubernetes.NewForConfig(config) @@ -113,6 +113,13 @@ func defaultGetOptions() metav1.GetOptions { } } +func defaultDeleteOptions() *metav1.DeleteOptions { + var deletePeriod int64 = 0 + return &metav1.DeleteOptions{ + GracePeriodSeconds: &deletePeriod, + } +} + const namespaceCreationMaxWaitTime time.Duration = 30 * time.Second const namespaceCreationWaitInterval time.Duration = 100 * time.Millisecond diff --git a/main.go b/main.go index a15c8a1d22..02e0fd31b3 100644 --- a/main.go +++ b/main.go @@ -2,12 +2,14 @@ package main import ( "flag" - "fmt" + "io/ioutil" "log" + "net/url" "github.com/nirmata/kube-policy/controller" "github.com/nirmata/kube-policy/kubeclient" "github.com/nirmata/kube-policy/server" + "github.com/nirmata/kube-policy/utils" rest "k8s.io/client-go/rest" clientcmd "k8s.io/client-go/tools/clientcmd" @@ -15,38 +17,84 @@ import ( ) var ( - masterURL string kubeconfig string cert string key string ) -func createClientConfig(masterURL, kubeconfig string) (*rest.Config, error) { - // TODO: make possible to create config within a cluster with proper rights - config, err := clientcmd.BuildConfigFromFlags(masterURL, kubeconfig) +func createClientConfig(kubeconfig string) (*rest.Config, error) { + if kubeconfig == "" { + log.Printf("Using in-cluster configuration") + return rest.InClusterConfig() + } else { + log.Printf("Using configuration from '%s'", kubeconfig) + return clientcmd.BuildConfigFromFlags("", kubeconfig) + } +} + +func readTlsPairFromFiles() *utils.TlsPemPair { + certContent, err := ioutil.ReadFile(cert) + if err != nil { + log.Printf("Unable to read file with TLS certificate: %v", err) + return nil + } + + keyContent, err := ioutil.ReadFile(key) + if err != nil { + log.Printf("Unable to read file with TLS private key: %v", err) + return nil + } + + return &utils.TlsPemPair{ + Certificate: certContent, + PrivateKey: keyContent, + } +} + +// Loads or creates PEM private key and TLS certificate for webhook server +// Returns struct with key/certificate pair +func initTlsPemsPair(config *rest.Config, client *kubeclient.KubeClient) (*utils.TlsPemPair, error) { + tlsPair := readTlsPairFromFiles() + if tlsPair != nil { + log.Print("Using given TLS key/certificate pair") + return tlsPair, nil + } + + apiServerUrl, err := url.Parse(config.Host) if err != nil { return nil, err } - return config, nil + certProps := utils.TlsCertificateProps{ + Service: "localhost", + Namespace: "default", + ApiServerHost: apiServerUrl.Hostname(), + } + + tlsPair = client.ReadTlsPair(certProps) + if utils.IsTlsPairShouldBeUpdated(tlsPair) { + log.Printf("Generating new key/certificate pair for TLS") + tlsPair, err = client.GenerateTlsPemPair(certProps) + if err != nil { + return nil, err + } + err = client.WriteTlsPair(certProps, tlsPair) + if err != nil { + log.Printf("Unable to save TLS pair to the cluster: %v", err) + } + } + + return tlsPair, nil } func main() { - flag.Parse() - - if cert == "" || key == "" { - log.Fatal("TLS certificate or/and key is not set") - } - - clientConfig, err := createClientConfig(masterURL, kubeconfig) + clientConfig, err := createClientConfig(kubeconfig) if err != nil { log.Fatalf("Error building kubeconfig: %v\n", err) - return } controller, err := controller.NewPolicyController(clientConfig, nil) if err != nil { log.Fatalf("Error creating PolicyController! Error: %s\n", err) - return } kubeclient, err := kubeclient.NewKubeClient(clientConfig, nil) @@ -54,14 +102,20 @@ func main() { log.Fatalf("Error creating kubeclient: %v\n", err) } + tlsPair, err := initTlsPemsPair(clientConfig, kubeclient) + if err != nil { + log.Fatalf("Failed to initialize TLS key/certificate pair: %v\n", err) + } + serverConfig := server.WebhookServerConfig{ - CertFile: cert, - KeyFile: key, + TlsPemPair: tlsPair, Controller: controller, Kubeclient: kubeclient, } - server, err := server.NewWebhookServer(serverConfig, nil) + if err != nil { + log.Fatalf("Unable to create webhook server: %v\n", err) + } server.RunAsync() stopCh := signals.SetupSignalHandler() @@ -72,15 +126,15 @@ func main() { return } - fmt.Println("Policy Controller has started") + log.Println("Policy Controller has started") <-stopCh server.Stop() - fmt.Println("Policy Controller has stopped") + log.Println("Policy Controller has stopped") } func init() { flag.StringVar(&kubeconfig, "kubeconfig", "", "Path to a kubeconfig. Only required if out-of-cluster.") - flag.StringVar(&masterURL, "master", "", "The address of the Kubernetes API server. Overrides any value in kubeconfig. Only required if out-of-cluster.") flag.StringVar(&cert, "cert", "", "TLS certificate used in connection with cluster.") flag.StringVar(&key, "key", "", "Key, used in TLS connection.") + flag.Parse() } diff --git a/server/server.go b/server/server.go index 8495b0844e..d16e09c6a5 100644 --- a/server/server.go +++ b/server/server.go @@ -12,9 +12,11 @@ import ( "os" "time" - controller "github.com/nirmata/kube-policy/controller" - kubeclient "github.com/nirmata/kube-policy/kubeclient" - webhooks "github.com/nirmata/kube-policy/webhooks" + "github.com/nirmata/kube-policy/controller" + "github.com/nirmata/kube-policy/kubeclient" + "github.com/nirmata/kube-policy/utils" + "github.com/nirmata/kube-policy/webhooks" + v1beta1 "k8s.io/api/admission/v1beta1" ) @@ -30,8 +32,7 @@ type WebhookServer struct { // Configuration struct for WebhookServer used in NewWebhookServer // Controller and Kubeclient should be initialized and valid type WebhookServerConfig struct { - CertFile string - KeyFile string + TlsPemPair *utils.TlsPemPair Controller *controller.PolicyController Kubeclient *kubeclient.KubeClient } @@ -43,12 +44,12 @@ func NewWebhookServer(config WebhookServerConfig, logger *log.Logger) (*WebhookS logger = log.New(os.Stdout, "HTTPS Server: ", log.LstdFlags|log.Lshortfile) } - if config.Controller == nil || config.Kubeclient == nil { - return nil, errors.New("WebHook server requires initialized Policy Controller and Kubernetes Client") + if config.TlsPemPair == nil || config.Controller == nil || config.Kubeclient == nil { + return nil, errors.New("WebhookServerConfig is not initialized properly") } var tlsConfig tls.Config - pair, err := tls.LoadX509KeyPair(config.CertFile, config.KeyFile) + pair, err := tls.X509KeyPair(config.TlsPemPair.Certificate, config.TlsPemPair.PrivateKey) if err != nil { return nil, err } diff --git a/utils/certificates_utils.go b/utils/certificates_utils.go new file mode 100644 index 0000000000..1508c34326 --- /dev/null +++ b/utils/certificates_utils.go @@ -0,0 +1,141 @@ +package utils + +import ( + "crypto/rand" + "crypto/rsa" + "crypto/x509" + "crypto/x509/pkix" + "encoding/pem" + "errors" + "net" + "time" + + certificates "k8s.io/api/certificates/v1beta1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// Properties of TLS certificate which should be issued for webhook server +type TlsCertificateProps struct { + Service string + Namespace string + ApiServerHost string +} + +// The pair of TLS certificate corresponding private key, both in PEM format +type TlsPemPair struct { + Certificate []byte + PrivateKey []byte +} + +// Generates RSA private key +func TlsGeneratePrivateKey() (*rsa.PrivateKey, error) { + return rsa.GenerateKey(rand.Reader, 2048) +} + +// Creates PEM block from private key object +func TlsPrivateKeyToPem(rsaKey *rsa.PrivateKey) []byte { + privateKey := &pem.Block{ + Type: "PRIVATE KEY", + Bytes: x509.MarshalPKCS1PrivateKey(rsaKey), + } + + return pem.EncodeToMemory(privateKey) +} + +// Creates PEM block from raw certificate request +func TlsCertificateRequestToPem(csrRaw []byte) []byte { + csrBlock := &pem.Block{ + Type: "CERTIFICATE REQUEST", + Bytes: csrRaw, + } + + return pem.EncodeToMemory(csrBlock) +} + +// Generates raw certificate signing request +func TlsCertificateGenerateRequest(privateKey *rsa.PrivateKey, props TlsCertificateProps) (*certificates.CertificateSigningRequest, error) { + dnsNames := make([]string, 3) + dnsNames[0] = props.Service + dnsNames[1] = props.Service + "." + props.Namespace + // The full service name is the CommonName for the certificate + commonName := GenerateInClusterServiceName(props) + dnsNames[2] = commonName + + var ips []net.IP + apiServerIp := net.ParseIP(props.ApiServerHost) + if apiServerIp != nil { + ips = append(ips, apiServerIp) + } else { + dnsNames = append(dnsNames, props.ApiServerHost) + } + + csrTemplate := x509.CertificateRequest{ + Subject: pkix.Name{ + CommonName: props.Service, //commonName, + }, + SignatureAlgorithm: x509.SHA256WithRSA, + DNSNames: dnsNames, + IPAddresses: ips, + } + + csrBytes, err := x509.CreateCertificateRequest(rand.Reader, &csrTemplate, privateKey) + if err != nil { + return nil, err + } + + return &certificates.CertificateSigningRequest{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "certificates.k8s.io/v1beta1", + Kind: "CertificateSigningRequest", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: props.Service + "." + props.Namespace + ".cert-request", + }, + Spec: certificates.CertificateSigningRequestSpec{ + Request: TlsCertificateRequestToPem(csrBytes), + Groups: []string{"system:masters", "system:authenticated"}, + Usages: []certificates.KeyUsage{ + certificates.UsageDigitalSignature, + certificates.UsageKeyEncipherment, + certificates.UsageServerAuth, + certificates.UsageClientAuth, + }, + }, + }, nil +} + +// The generated service name should be the common name for TLS certificate +func GenerateInClusterServiceName(props TlsCertificateProps) string { + return props.Service + "." + props.Namespace + ".svc" +} + +//Gets NotAfter property from raw certificate +func TlsCertificateGetExpirationDate(certData []byte) (*time.Time, error) { + block, _ := pem.Decode(certData) + if block == nil { + return nil, errors.New("Failed to decode PEM") + } + + cert, err := x509.ParseCertificate(block.Bytes) + if err != nil { + return nil, errors.New("Failed to parse certificate: %v" + err.Error()) + } + return &cert.NotAfter, nil +} + +// The certificate is valid for a year, but we update it earlier to avoid using +// an expired certificate in a controller that has been running for a long time +const timeReserveBeforeCertificateExpiration time.Duration = time.Hour * 24 * 30 * 6 // About half a year + +func IsTlsPairShouldBeUpdated(tlsPair *TlsPemPair) bool { + if tlsPair == nil { + return true + } + + expirationDate, err := TlsCertificateGetExpirationDate(tlsPair.Certificate) + if err != nil { + return true + } + + return expirationDate.Sub(time.Now()) < timeReserveBeforeCertificateExpiration +} From 0597c3ec4aaa66db937d4c0ef443d83d4d15817c Mon Sep 17 00:00:00 2001 From: belyshevdenis Date: Fri, 15 Mar 2019 19:22:06 +0200 Subject: [PATCH 02/12] NK-31: Decomposed controller initialization functions. --- init.go | 80 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ main.go | 80 ++++++--------------------------------------------------- 2 files changed, 88 insertions(+), 72 deletions(-) create mode 100644 init.go diff --git a/init.go b/init.go new file mode 100644 index 0000000000..cd729e91dc --- /dev/null +++ b/init.go @@ -0,0 +1,80 @@ +package main + +import ( + "io/ioutil" + "log" + "net/url" + + "github.com/nirmata/kube-policy/kubeclient" + "github.com/nirmata/kube-policy/utils" + + rest "k8s.io/client-go/rest" + clientcmd "k8s.io/client-go/tools/clientcmd" +) + +// These constants MUST be equal to the corresponding names in service definition in definitions/install.yaml +const serviceName string = "kube-policy-svc" +const namespace string = "default" + +func createClientConfig(kubeconfig string) (*rest.Config, error) { + if kubeconfig == "" { + log.Printf("Using in-cluster configuration") + return rest.InClusterConfig() + } else { + log.Printf("Using configuration from '%s'", kubeconfig) + return clientcmd.BuildConfigFromFlags("", kubeconfig) + } +} + +func readTlsPairFromFiles(certFile, keyFile string) *utils.TlsPemPair { + certContent, err := ioutil.ReadFile(certFile) + if err != nil { + log.Printf("Unable to read file with TLS certificate: %v", err) + return nil + } + + keyContent, err := ioutil.ReadFile(keyFile) + if err != nil { + log.Printf("Unable to read file with TLS private key: %v", err) + return nil + } + + return &utils.TlsPemPair{ + Certificate: certContent, + PrivateKey: keyContent, + } +} + +// Loads or creates PEM private key and TLS certificate for webhook server +// Returns struct with key/certificate pair +func initTlsPemsPair(config *rest.Config, client *kubeclient.KubeClient) (*utils.TlsPemPair, error) { + apiServerUrl, err := url.Parse(config.Host) + if err != nil { + return nil, err + } + certProps := utils.TlsCertificateProps{ + Service: serviceName, + Namespace: namespace, + ApiServerHost: apiServerUrl.Hostname(), + } + + tlsPair := client.ReadTlsPair(certProps) + if utils.IsTlsPairShouldBeUpdated(tlsPair) { + log.Printf("Generating new key/certificate pair for TLS") + tlsPair, err = client.GenerateTlsPemPair(certProps) + if err != nil { + return nil, err + } + err = client.WriteTlsPair(certProps, tlsPair) + if err != nil { + log.Printf("Unable to save TLS pair to the cluster: %v", err) + } + } + + return tlsPair, nil +} + +func registerWebhook(client *kubeclient.KubeClient) error { + // TODO + return nil +} diff --git a/main.go b/main.go index 02e0fd31b3..1cf206a68d 100644 --- a/main.go +++ b/main.go @@ -2,17 +2,12 @@ package main import ( "flag" - "io/ioutil" "log" - "net/url" "github.com/nirmata/kube-policy/controller" "github.com/nirmata/kube-policy/kubeclient" "github.com/nirmata/kube-policy/server" - "github.com/nirmata/kube-policy/utils" - rest "k8s.io/client-go/rest" - clientcmd "k8s.io/client-go/tools/clientcmd" signals "k8s.io/sample-controller/pkg/signals" ) @@ -22,70 +17,6 @@ var ( key string ) -func createClientConfig(kubeconfig string) (*rest.Config, error) { - if kubeconfig == "" { - log.Printf("Using in-cluster configuration") - return rest.InClusterConfig() - } else { - log.Printf("Using configuration from '%s'", kubeconfig) - return clientcmd.BuildConfigFromFlags("", kubeconfig) - } -} - -func readTlsPairFromFiles() *utils.TlsPemPair { - certContent, err := ioutil.ReadFile(cert) - if err != nil { - log.Printf("Unable to read file with TLS certificate: %v", err) - return nil - } - - keyContent, err := ioutil.ReadFile(key) - if err != nil { - log.Printf("Unable to read file with TLS private key: %v", err) - return nil - } - - return &utils.TlsPemPair{ - Certificate: certContent, - PrivateKey: keyContent, - } -} - -// Loads or creates PEM private key and TLS certificate for webhook server -// Returns struct with key/certificate pair -func initTlsPemsPair(config *rest.Config, client *kubeclient.KubeClient) (*utils.TlsPemPair, error) { - tlsPair := readTlsPairFromFiles() - if tlsPair != nil { - log.Print("Using given TLS key/certificate pair") - return tlsPair, nil - } - - apiServerUrl, err := url.Parse(config.Host) - if err != nil { - return nil, err - } - certProps := utils.TlsCertificateProps{ - Service: "localhost", - Namespace: "default", - ApiServerHost: apiServerUrl.Hostname(), - } - - tlsPair = client.ReadTlsPair(certProps) - if utils.IsTlsPairShouldBeUpdated(tlsPair) { - log.Printf("Generating new key/certificate pair for TLS") - tlsPair, err = client.GenerateTlsPemPair(certProps) - if err != nil { - return nil, err - } - err = client.WriteTlsPair(certProps, tlsPair) - if err != nil { - log.Printf("Unable to save TLS pair to the cluster: %v", err) - } - } - - return tlsPair, nil -} - func main() { clientConfig, err := createClientConfig(kubeconfig) if err != nil { @@ -102,9 +33,14 @@ func main() { log.Fatalf("Error creating kubeclient: %v\n", err) } - tlsPair, err := initTlsPemsPair(clientConfig, kubeclient) - if err != nil { - log.Fatalf("Failed to initialize TLS key/certificate pair: %v\n", err) + tlsPair := readTlsPairFromFiles(cert, key) + if tlsPair != nil { + log.Print("Using given TLS key/certificate pair") + } else { + tlsPair, err = initTlsPemsPair(clientConfig, kubeclient) + if err != nil { + log.Fatalf("Failed to initialize TLS key/certificate pair: %v\n", err) + } } serverConfig := server.WebhookServerConfig{ From 89b211d1f951664cefd148daf2341f5634019fde Mon Sep 17 00:00:00 2001 From: belyshevdenis Date: Tue, 19 Mar 2019 21:32:31 +0200 Subject: [PATCH 03/12] NK-31: Implemented webhook registration logic. --- definitions/examples/selector-policy.yaml | 21 +++++ init.go | 7 +- main.go | 8 +- webhooks/registration.go | 98 +++++++++++++++++++++++ 4 files changed, 127 insertions(+), 7 deletions(-) create mode 100644 definitions/examples/selector-policy.yaml create mode 100644 webhooks/registration.go diff --git a/definitions/examples/selector-policy.yaml b/definitions/examples/selector-policy.yaml new file mode 100644 index 0000000000..d15312b76d --- /dev/null +++ b/definitions/examples/selector-policy.yaml @@ -0,0 +1,21 @@ +apiVersion: policy.nirmata.io/v1alpha1 +kind : Policy +metadata: + name: selector-policy +spec: + failurePolicy: continueOnError + rules: + - resource: + kind: ConfigMap + selector: + matchLabels: + label1: test1 + matchExpressions: + - key: label2 + operator: In + values: + - test2 + patch: + - path: / + op : add + value : "20" diff --git a/init.go b/init.go index cd729e91dc..8940b39c8c 100644 --- a/init.go +++ b/init.go @@ -72,9 +72,4 @@ func initTlsPemsPair(config *rest.Config, client *kubeclient.KubeClient) (*utils } return tlsPair, nil -} - -func registerWebhook(client *kubeclient.KubeClient) error { - // TODO - return nil -} +} \ No newline at end of file diff --git a/main.go b/main.go index 1cf206a68d..d3dea750e1 100644 --- a/main.go +++ b/main.go @@ -6,6 +6,7 @@ import ( "github.com/nirmata/kube-policy/controller" "github.com/nirmata/kube-policy/kubeclient" + "github.com/nirmata/kube-policy/webhooks" "github.com/nirmata/kube-policy/server" signals "k8s.io/sample-controller/pkg/signals" @@ -23,9 +24,14 @@ func main() { log.Fatalf("Error building kubeconfig: %v\n", err) } + _, err = webhooks.RegisterMutationWebhook(clientConfig) + if err != nil { + log.Fatalf("Error registering mutation webhook server: %v\n", err) + } + controller, err := controller.NewPolicyController(clientConfig, nil) if err != nil { - log.Fatalf("Error creating PolicyController! Error: %s\n", err) + log.Fatalf("Error creating PolicyController: %s\n", err) } kubeclient, err := kubeclient.NewKubeClient(clientConfig, nil) diff --git a/webhooks/registration.go b/webhooks/registration.go new file mode 100644 index 0000000000..db8912bf45 --- /dev/null +++ b/webhooks/registration.go @@ -0,0 +1,98 @@ +package webhooks +import ( + "io/ioutil" + "encoding/base64" + + rest "k8s.io/client-go/rest" + meta "k8s.io/apimachinery/pkg/apis/meta/v1" + adm "k8s.io/api/admissionregistration/v1beta1" + types "k8s.io/api/admissionregistration/v1beta1" + admreg "k8s.io/client-go/kubernetes/typed/admissionregistration/v1beta1" +) + +const ( + webhookName = "nirmata-kube-policy-webhook-cfg" + mutationWebhookName = "webhook.nirmata.kube-policy" + webhookServiceNamespace = "default" + webhookServiceName = "kube-policy-svc" +) + +var ( + webhookPath = "mutate" + webhookLabels = map[string]string { + "app": "kube-policy", + } +) + +func RegisterMutationWebhook(config *rest.Config) (*types.MutatingWebhookConfiguration, error) { +var result *types.MutatingWebhookConfiguration = nil + + registrationClient, err := admreg.NewForConfig(config) + if err != nil { + return nil, err + } + + result, err = registrationClient.MutatingWebhookConfigurations().Create(constructWebhookConfig(config)) + if err != nil { + return nil, err + } + + return result, nil +} + +func constructWebhookConfig(config *rest.Config) *adm.MutatingWebhookConfiguration { + return &adm.MutatingWebhookConfiguration { + ObjectMeta: meta.ObjectMeta { + Name: webhookName, + Labels: webhookLabels, + }, + Webhooks: []adm.Webhook { + adm.Webhook { + Name: mutationWebhookName, + ClientConfig: adm.WebhookClientConfig { + Service: &adm.ServiceReference { + Namespace: webhookServiceNamespace, + Name: webhookServiceName, + Path: &webhookPath, + }, + CABundle: extractCA(config), + }, + Rules: []adm.RuleWithOperations { + adm.RuleWithOperations { + Operations: []adm.OperationType { + adm.Create, + }, + Rule: adm.Rule { + APIGroups: []string { + "*", + }, + APIVersions: []string { + "*", + }, + Resources: []string { + "*/*", + }, + }, + }, + }, + }, + }, + } +} + +func extractCA(config *rest.Config) (result []byte) { + + if config.TLSClientConfig.CAData != nil { + return config.TLSClientConfig.CAData + } else { + fileName := config.TLSClientConfig.CAFile + bytes, err := ioutil.ReadFile(fileName) + + if err != nil { + return nil + } + + base64.StdEncoding.Encode(result, bytes) + return + } +} \ No newline at end of file From 0afd1c279fcade0227b8f53e87824377ef4ea325 Mon Sep 17 00:00:00 2001 From: belyshevdenis Date: Wed, 20 Mar 2019 12:37:05 +0200 Subject: [PATCH 04/12] NK-31: Added tests for CA extraction from clientset --- webhooks/registration.go | 20 +++++------ webhooks/registration_test.go | 65 +++++++++++++++++++++++++++++++++++ webhooks/resources/CAFile | 17 +++++++++ 3 files changed, 91 insertions(+), 11 deletions(-) create mode 100644 webhooks/registration_test.go create mode 100644 webhooks/resources/CAFile diff --git a/webhooks/registration.go b/webhooks/registration.go index db8912bf45..434e825c42 100644 --- a/webhooks/registration.go +++ b/webhooks/registration.go @@ -1,7 +1,6 @@ package webhooks import ( "io/ioutil" - "encoding/base64" rest "k8s.io/client-go/rest" meta "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -55,7 +54,7 @@ func constructWebhookConfig(config *rest.Config) *adm.MutatingWebhookConfigurati Name: webhookServiceName, Path: &webhookPath, }, - CABundle: extractCA(config), + CABundle: ExtractCA(config), }, Rules: []adm.RuleWithOperations { adm.RuleWithOperations { @@ -80,19 +79,18 @@ func constructWebhookConfig(config *rest.Config) *adm.MutatingWebhookConfigurati } } -func extractCA(config *rest.Config) (result []byte) { - - if config.TLSClientConfig.CAData != nil { - return config.TLSClientConfig.CAData - } else { - fileName := config.TLSClientConfig.CAFile - bytes, err := ioutil.ReadFile(fileName) +func ExtractCA(config *rest.Config) (result []byte) { + fileName := config.TLSClientConfig.CAFile + if fileName != "" { + result, err := ioutil.ReadFile(fileName) + if err != nil { return nil } - base64.StdEncoding.Encode(result, bytes) - return + return result + } else { + return config.TLSClientConfig.CAData } } \ No newline at end of file diff --git a/webhooks/registration_test.go b/webhooks/registration_test.go new file mode 100644 index 0000000000..7a6cbaffb8 --- /dev/null +++ b/webhooks/registration_test.go @@ -0,0 +1,65 @@ +package webhooks_test +import ( + "gotest.tools/assert" + "io/ioutil" + "testing" + "bytes" + + "github.com/nirmata/kube-policy/webhooks" + + rest "k8s.io/client-go/rest" +) + +func TestExtractCA_EmptyBundle(t *testing.T) { + CAFile := "resources/CAFile" + + config := &rest.Config { + TLSClientConfig: rest.TLSClientConfig { + CAData: nil, + CAFile: CAFile, + }, + } + + expected, err := ioutil.ReadFile(CAFile) + assert.Assert(t, err == nil) + actual := webhooks.ExtractCA(config) + assert.Assert(t, bytes.Equal(expected, actual)) +} + +func TestExtractCA_EmptyCAFile(t *testing.T) { + CABundle := []byte(`LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUN5RENDQWJDZ0F3SUJBZ0lCQURBTkJna3Foa2lHOXcwQkFRc0ZBREFWTVJNd0VRWURWUVFERXdwcmRXSmwKY201bGRHVnpNQjRYRFRFNU1ETXhPVEUwTURjd05Gb1hEVEk1TURNeE5qRTBNRGN3TkZvd0ZURVRNQkVHQTFVRQpBeE1LYTNWaVpYSnVaWFJsY3pDQ0FTSXdEUVlKS29aSWh2Y05BUUVCQlFBRGdnRVBBRENDQVFvQ2dnRUJBTStQClVLVmExcm9tQndOZzdqNnBBSGo5TDQ4RVJpdEplRzRXM1pUYmNMNWNKbnVTQmFsc1h1TWpQTGZmbUV1VEZIdVAKenRqUlBEUHcreEg1d3VTWFF2U0tIaXF2VE1pUm9DSlJFa09sQXpIa1dQM0VrdnUzNzRqZDVGV3Q3NEhnRk91cApIZ1ZwdUxPblczK2NDVE5iQ3VkeDFMVldRbGgwQzJKbm1Lam5uS1YrTkxzNFJVaVk1dk91ekpuNHl6QldLRjM2CmJLZ3ZDOVpMWlFSM3dZcnJNZWllYzBnWVY2VlJtaGgxSjRDV3V1UWd0ckM2d2NJanFWZFdEUlJyNHFMdEtDcDIKQVNIZmNieitwcEdHblJ5Z2FzcWNJdnpiNUVwV3NIRGtHRStUUW5WQ0JmTmsxN0NEOTZBQ1pmRWVybzEvWE16MgpRbzZvcUE0dnF5ZkdWWVU5RVZFQ0F3RUFBYU1qTUNFd0RnWURWUjBQQVFIL0JBUURBZ0trTUE4R0ExVWRFd0VCCi93UUZNQU1CQWY4d0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dFQkFNWFVpUVJpdUc4cGdzcHMrZTdGZWdCdEJOZEcKZlFUdHVLRWFUZ0U0RjQwamJ3UmdrN25DTHlsSHgvRG04aVRRQmsyWjR4WnNuY0huRys4SkwrckRLdlJBSE5iVQpsYnpReXA1V3FwdjdPcThwZ01wU0o5bTdVY3BGZmRVZkorNW43aXFnTGdMb3lhNmtRVTR2Rk0yTE1rWjI5NVpxCmVId0hnREo5Z3IwWGNyOWM1L2tRdkxFc2Z2WU5QZVhuamNyWXlDb2JNcVduSElxeVd3cHM1VTJOaGgraXhSZEIKbzRRL3RJS04xOU93WGZBaVc5SENhNzZMb3ZXaUhPU2UxVnFzK1h1N1A5ckx4eW1vQm91aFcxVmZ0bUo5Qy9vTAp3cFVuNnlXRCttY0tkZ3J5QTFjTWJ4Q281bUd6YTNLaFk1QTd5eDQ1cThkSEIzTWU4d0FCam1wWEs0ST0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=`) + + config := &rest.Config { + TLSClientConfig: rest.TLSClientConfig { + CAData: CABundle, + CAFile: "", + }, + } + + actual := webhooks.ExtractCA(config) + assert.Assert(t, bytes.Equal(CABundle, actual)) +} + +func TestExtractCA_EmptyConfig(t *testing.T) { + config := &rest.Config { + TLSClientConfig: rest.TLSClientConfig { + CAData: nil, + CAFile: "", + }, + } + + actual := webhooks.ExtractCA(config) + assert.Assert(t, actual == nil) +} + +func TestExtractCA_InvalidFile(t *testing.T) { + config := &rest.Config { + TLSClientConfig: rest.TLSClientConfig { + CAData: nil, + CAFile: "somenonexistingfile", + }, + } + + actual := webhooks.ExtractCA(config) + assert.Assert(t, actual == nil) +} \ No newline at end of file diff --git a/webhooks/resources/CAFile b/webhooks/resources/CAFile new file mode 100644 index 0000000000..d3700b2b42 --- /dev/null +++ b/webhooks/resources/CAFile @@ -0,0 +1,17 @@ +-----BEGIN CERTIFICATE----- +MIICyDCCAbCgAwIBAgIBADANBgkqhkiG9w0BAQsFADAVMRMwEQYDVQQDEwprdWJl +cm5ldGVzMB4XDTE5MDMxOTE0MDcwNFoXDTI5MDMxNjE0MDcwNFowFTETMBEGA1UE +AxMKa3ViZXJuZXRlczCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAM+P +UKVa1romBwNg7j6pAHj9L48ERitJeG4W3ZTbcL5cJnuSBalsXuMjPLffmEuTFHuP +ztjRPDPw+xH5wuSXQvSKHiqvTMiRoCJREkOlAzHkWP3Ekvu374jd5FWt74HgFOup +HgVpuLOnW3+cCTNbCudx1LVWQlh0C2JnmKjnnKV+NLs4RUiY5vOuzJn4yzBWKF36 +bKgvC9ZLZQR3wYrrMeiec0gYV6VRmhh1J4CWuuQgtrC6wcIjqVdWDRRr4qLtKCp2 +ASHfcbz+ppGGnRygasqcIvzb5EpWsHDkGE+TQnVCBfNk17CD96ACZfEero1/XMz2 +Qo6oqA4vqyfGVYU9EVECAwEAAaMjMCEwDgYDVR0PAQH/BAQDAgKkMA8GA1UdEwEB +/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAMXUiQRiuG8pgsps+e7FegBtBNdG +fQTtuKEaTgE4F40jbwRgk7nCLylHx/Dm8iTQBk2Z4xZsncHnG+8JL+rDKvRAHNbU +lbzQyp5Wqpv7Oq8pgMpSJ9m7UcpFfdUfJ+5n7iqgLgLoya6kQU4vFM2LMkZ295Zq +eHwHgDJ9gr0Xcr9c5/kQvLEsfvYNPeXnjcrYyCobMqWnHIqyWwps5U2Nhh+ixRdB +o4Q/tIKN19OwXfAiW9HCa76LovWiHOSe1Vqs+Xu7P9rLxymoBouhW1VftmJ9C/oL +wpUn6yWD+mcKdgryA1cMbxCo5mGza3KhY5A7yx45q8dHB3Me8wABjmpXK4I= +-----END CERTIFICATE----- From 92c97a92e9e9f77b4d9ed3b693586b707a52339b Mon Sep 17 00:00:00 2001 From: belyshevdenis Date: Thu, 21 Mar 2019 15:57:30 +0200 Subject: [PATCH 05/12] NK-31: Put constants in separate file. Updated install.yaml definition to create Service and DaemonSet. Fixed bug with webhook registration. --- constants/constants.go | 17 ++++++ definitions/examples/selector-policy.yaml | 21 ------- definitions/install.yaml | 69 +++++++++++++++++++++++ init.go | 13 ++--- main.go | 2 +- server/server.go | 7 ++- webhooks/registration.go | 42 +++++--------- 7 files changed, 110 insertions(+), 61 deletions(-) create mode 100644 constants/constants.go delete mode 100644 definitions/examples/selector-policy.yaml diff --git a/constants/constants.go b/constants/constants.go new file mode 100644 index 0000000000..7bfd24e07c --- /dev/null +++ b/constants/constants.go @@ -0,0 +1,17 @@ +package constants + +const ( + // These constants MUST be equal to the corresponding names in service definition in definitions/install.yaml + WebhookServiceNamespace = "kube-system" + WebhookServiceName = "kube-policy-svc" + + WebhookConfigName = "nirmata-kube-policy-webhook-cfg" + MutationWebhookName = "webhook.nirmata.kube-policy" +) + +var ( + WebhookServicePath = "/mutate" + WebhookConfigLabels = map[string]string { + "app": "kube-policy", + } +) \ No newline at end of file diff --git a/definitions/examples/selector-policy.yaml b/definitions/examples/selector-policy.yaml deleted file mode 100644 index d15312b76d..0000000000 --- a/definitions/examples/selector-policy.yaml +++ /dev/null @@ -1,21 +0,0 @@ -apiVersion: policy.nirmata.io/v1alpha1 -kind : Policy -metadata: - name: selector-policy -spec: - failurePolicy: continueOnError - rules: - - resource: - kind: ConfigMap - selector: - matchLabels: - label1: test1 - matchExpressions: - - key: label2 - operator: In - values: - - test2 - patch: - - path: / - op : add - value : "20" diff --git a/definitions/install.yaml b/definitions/install.yaml index 19273f4f08..6a1caea2d6 100644 --- a/definitions/install.yaml +++ b/definitions/install.yaml @@ -145,3 +145,72 @@ spec: type: object additionalProperties: type: string +--- +apiVersion: v1 +kind: Service +metadata: + namespace: kube-system + name: kube-policy-svc + labels: + app: kube-policy +spec: + ports: + - port: 443 + targetPort: 443 + selector: + app: kube-policy +#--- +#apiVersion: v1 +#kind: ServiceAccount +#metadata: +# name: kube-policy-service-account +# namespace: kube-system +--- +kind: ClusterRoleBinding +apiVersion: rbac.authorization.k8s.io/v1beta1 +metadata: + name: kube-policy-admin +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: cluster-admin +subjects: +- kind: ServiceAccount + name: default + namespace: kube-system +--- +apiVersion: extensions/v1beta1 +kind: DaemonSet +metadata: + labels: + app: kube-policy + tier: node + name: kube-policy-daemon + namespace: kube-system +spec: + template: + metadata: + labels: + app: kube-policy + tier: node + spec: + #serviceAccountName: kube-policy-service-account + #serviceAccount: kube-policy-service-account + containers: + - name: kube-policy + image: nirmata/kube-policy:latest + imagePullPolicy: IfNotPresent + ports: + - containerPort: 443 + securityContext: + privileged: true + hostNetwork: true + tolerations: + - key: CriticalAddonsOnly + operator: Exists + - effect: NoSchedule + key: node-role.kubernetes.io/master + operator: Exists + - effect: NoSchedule + key: node.kubernetes.io/not-ready + operator: Exists diff --git a/init.go b/init.go index 8940b39c8c..d8ec4cd7c6 100644 --- a/init.go +++ b/init.go @@ -6,16 +6,13 @@ import ( "net/url" "github.com/nirmata/kube-policy/kubeclient" + "github.com/nirmata/kube-policy/constants" "github.com/nirmata/kube-policy/utils" rest "k8s.io/client-go/rest" clientcmd "k8s.io/client-go/tools/clientcmd" ) -// These constants MUST be equal to the corresponding names in service definition in definitions/install.yaml -const serviceName string = "kube-policy-svc" -const namespace string = "default" - func createClientConfig(kubeconfig string) (*rest.Config, error) { if kubeconfig == "" { log.Printf("Using in-cluster configuration") @@ -29,13 +26,13 @@ func createClientConfig(kubeconfig string) (*rest.Config, error) { func readTlsPairFromFiles(certFile, keyFile string) *utils.TlsPemPair { certContent, err := ioutil.ReadFile(certFile) if err != nil { - log.Printf("Unable to read file with TLS certificate: %v", err) + log.Printf("Unable to read file with TLS certificate: path - %s, error - %v", certFile, err) return nil } keyContent, err := ioutil.ReadFile(keyFile) if err != nil { - log.Printf("Unable to read file with TLS private key: %v", err) + log.Printf("Unable to read file with TLS private key: path - %s, error - %v", keyFile, err) return nil } @@ -53,8 +50,8 @@ func initTlsPemsPair(config *rest.Config, client *kubeclient.KubeClient) (*utils return nil, err } certProps := utils.TlsCertificateProps{ - Service: serviceName, - Namespace: namespace, + Service: constants.WebhookServiceName, + Namespace: constants.WebhookServiceNamespace, ApiServerHost: apiServerUrl.Hostname(), } diff --git a/main.go b/main.go index d3dea750e1..cb728a4c57 100644 --- a/main.go +++ b/main.go @@ -24,7 +24,7 @@ func main() { log.Fatalf("Error building kubeconfig: %v\n", err) } - _, err = webhooks.RegisterMutationWebhook(clientConfig) + err = webhooks.RegisterMutationWebhook(clientConfig) if err != nil { log.Fatalf("Error registering mutation webhook server: %v\n", err) } diff --git a/server/server.go b/server/server.go index d16e09c6a5..0c1d3957e8 100644 --- a/server/server.go +++ b/server/server.go @@ -14,8 +14,9 @@ import ( "github.com/nirmata/kube-policy/controller" "github.com/nirmata/kube-policy/kubeclient" - "github.com/nirmata/kube-policy/utils" + "github.com/nirmata/kube-policy/constants" "github.com/nirmata/kube-policy/webhooks" + "github.com/nirmata/kube-policy/utils" v1beta1 "k8s.io/api/admission/v1beta1" ) @@ -66,7 +67,7 @@ func NewWebhookServer(config WebhookServerConfig, logger *log.Logger) (*WebhookS } mux := http.NewServeMux() - mux.HandleFunc("/mutate", ws.serve) + mux.HandleFunc(constants.WebhookServicePath, ws.serve) ws.server = http.Server{ Addr: ":443", // Listen on port for HTTPS requests @@ -82,7 +83,7 @@ func NewWebhookServer(config WebhookServerConfig, logger *log.Logger) (*WebhookS // Main server endpoint for all requests func (ws *WebhookServer) serve(w http.ResponseWriter, r *http.Request) { - if r.URL.Path == "/mutate" { + if r.URL.Path == constants.WebhookServicePath { admissionReview := ws.parseAdmissionReview(r, w) if admissionReview == nil { return diff --git a/webhooks/registration.go b/webhooks/registration.go index 434e825c42..ecbf548170 100644 --- a/webhooks/registration.go +++ b/webhooks/registration.go @@ -1,58 +1,44 @@ package webhooks + import ( "io/ioutil" + "github.com/nirmata/kube-policy/constants" + rest "k8s.io/client-go/rest" meta "k8s.io/apimachinery/pkg/apis/meta/v1" adm "k8s.io/api/admissionregistration/v1beta1" - types "k8s.io/api/admissionregistration/v1beta1" admreg "k8s.io/client-go/kubernetes/typed/admissionregistration/v1beta1" ) -const ( - webhookName = "nirmata-kube-policy-webhook-cfg" - mutationWebhookName = "webhook.nirmata.kube-policy" - webhookServiceNamespace = "default" - webhookServiceName = "kube-policy-svc" -) - -var ( - webhookPath = "mutate" - webhookLabels = map[string]string { - "app": "kube-policy", - } -) - -func RegisterMutationWebhook(config *rest.Config) (*types.MutatingWebhookConfiguration, error) { -var result *types.MutatingWebhookConfiguration = nil - +func RegisterMutationWebhook(config *rest.Config) error { registrationClient, err := admreg.NewForConfig(config) if err != nil { - return nil, err + return err } - result, err = registrationClient.MutatingWebhookConfigurations().Create(constructWebhookConfig(config)) + _, err = registrationClient.MutatingWebhookConfigurations().Create(constructWebhookConfig(config)) if err != nil { - return nil, err + return err } - return result, nil + return nil } func constructWebhookConfig(config *rest.Config) *adm.MutatingWebhookConfiguration { return &adm.MutatingWebhookConfiguration { ObjectMeta: meta.ObjectMeta { - Name: webhookName, - Labels: webhookLabels, + Name: constants.WebhookConfigName, + Labels: constants.WebhookConfigLabels, }, Webhooks: []adm.Webhook { adm.Webhook { - Name: mutationWebhookName, + Name: constants.MutationWebhookName, ClientConfig: adm.WebhookClientConfig { Service: &adm.ServiceReference { - Namespace: webhookServiceNamespace, - Name: webhookServiceName, - Path: &webhookPath, + Namespace: constants.WebhookServiceNamespace, + Name: constants.WebhookServiceName, + Path: &constants.WebhookServicePath, }, CABundle: ExtractCA(config), }, From 20d9fcd56346f3a5309eb6c8126e510ab092966a Mon Sep 17 00:00:00 2001 From: belyshevdenis Date: Thu, 21 Mar 2019 16:56:03 +0200 Subject: [PATCH 06/12] NK-31: Fixed indentation --- webhooks/registration.go | 4 ++-- webhooks/registration_test.go | 21 +++++++++++---------- webhooks/resources/CAFile | 27 ++++++++++++--------------- 3 files changed, 25 insertions(+), 27 deletions(-) diff --git a/webhooks/registration.go b/webhooks/registration.go index ecbf548170..ab3f546ead 100644 --- a/webhooks/registration.go +++ b/webhooks/registration.go @@ -35,7 +35,7 @@ func constructWebhookConfig(config *rest.Config) *adm.MutatingWebhookConfigurati adm.Webhook { Name: constants.MutationWebhookName, ClientConfig: adm.WebhookClientConfig { - Service: &adm.ServiceReference { + Service: &adm.ServiceReference { Namespace: constants.WebhookServiceNamespace, Name: constants.WebhookServiceName, Path: &constants.WebhookServicePath, @@ -68,7 +68,7 @@ func constructWebhookConfig(config *rest.Config) *adm.MutatingWebhookConfigurati func ExtractCA(config *rest.Config) (result []byte) { fileName := config.TLSClientConfig.CAFile - if fileName != "" { + if fileName != "" { result, err := ioutil.ReadFile(fileName) if err != nil { diff --git a/webhooks/registration_test.go b/webhooks/registration_test.go index 7a6cbaffb8..a095756838 100644 --- a/webhooks/registration_test.go +++ b/webhooks/registration_test.go @@ -1,4 +1,5 @@ package webhooks_test + import ( "gotest.tools/assert" "io/ioutil" @@ -11,55 +12,55 @@ import ( ) func TestExtractCA_EmptyBundle(t *testing.T) { - CAFile := "resources/CAFile" + CAFile := "resources/CAFile" config := &rest.Config { TLSClientConfig: rest.TLSClientConfig { CAData: nil, - CAFile: CAFile, + CAFile: CAFile, }, } expected, err := ioutil.ReadFile(CAFile) assert.Assert(t, err == nil) actual := webhooks.ExtractCA(config) - assert.Assert(t, bytes.Equal(expected, actual)) + assert.Assert(t, bytes.Equal(expected, actual)) } func TestExtractCA_EmptyCAFile(t *testing.T) { - CABundle := []byte(`LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUN5RENDQWJDZ0F3SUJBZ0lCQURBTkJna3Foa2lHOXcwQkFRc0ZBREFWTVJNd0VRWURWUVFERXdwcmRXSmwKY201bGRHVnpNQjRYRFRFNU1ETXhPVEUwTURjd05Gb1hEVEk1TURNeE5qRTBNRGN3TkZvd0ZURVRNQkVHQTFVRQpBeE1LYTNWaVpYSnVaWFJsY3pDQ0FTSXdEUVlKS29aSWh2Y05BUUVCQlFBRGdnRVBBRENDQVFvQ2dnRUJBTStQClVLVmExcm9tQndOZzdqNnBBSGo5TDQ4RVJpdEplRzRXM1pUYmNMNWNKbnVTQmFsc1h1TWpQTGZmbUV1VEZIdVAKenRqUlBEUHcreEg1d3VTWFF2U0tIaXF2VE1pUm9DSlJFa09sQXpIa1dQM0VrdnUzNzRqZDVGV3Q3NEhnRk91cApIZ1ZwdUxPblczK2NDVE5iQ3VkeDFMVldRbGgwQzJKbm1Lam5uS1YrTkxzNFJVaVk1dk91ekpuNHl6QldLRjM2CmJLZ3ZDOVpMWlFSM3dZcnJNZWllYzBnWVY2VlJtaGgxSjRDV3V1UWd0ckM2d2NJanFWZFdEUlJyNHFMdEtDcDIKQVNIZmNieitwcEdHblJ5Z2FzcWNJdnpiNUVwV3NIRGtHRStUUW5WQ0JmTmsxN0NEOTZBQ1pmRWVybzEvWE16MgpRbzZvcUE0dnF5ZkdWWVU5RVZFQ0F3RUFBYU1qTUNFd0RnWURWUjBQQVFIL0JBUURBZ0trTUE4R0ExVWRFd0VCCi93UUZNQU1CQWY4d0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dFQkFNWFVpUVJpdUc4cGdzcHMrZTdGZWdCdEJOZEcKZlFUdHVLRWFUZ0U0RjQwamJ3UmdrN25DTHlsSHgvRG04aVRRQmsyWjR4WnNuY0huRys4SkwrckRLdlJBSE5iVQpsYnpReXA1V3FwdjdPcThwZ01wU0o5bTdVY3BGZmRVZkorNW43aXFnTGdMb3lhNmtRVTR2Rk0yTE1rWjI5NVpxCmVId0hnREo5Z3IwWGNyOWM1L2tRdkxFc2Z2WU5QZVhuamNyWXlDb2JNcVduSElxeVd3cHM1VTJOaGgraXhSZEIKbzRRL3RJS04xOU93WGZBaVc5SENhNzZMb3ZXaUhPU2UxVnFzK1h1N1A5ckx4eW1vQm91aFcxVmZ0bUo5Qy9vTAp3cFVuNnlXRCttY0tkZ3J5QTFjTWJ4Q281bUd6YTNLaFk1QTd5eDQ1cThkSEIzTWU4d0FCam1wWEs0ST0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=`) + CABundle := []byte(`LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUN5RENDQWJDZ0F3SUJBZ0lCQURBTkJna3Foa2lHOXcwQkFRc0ZBREFWTVJNd0VRWURWUVFERXdwcmRXSmwKY201bGRHVnpNQjRYRFRFNU1ETXhPVEUwTURjd05Gb1hEVEk1TURNeE5qRTBNRGN3TkZvd0ZURVRNQkVHQTFVRQpBeE1LYTNWaVpYSnVaWFJsY3pDQ0FTSXdEUVlKS29aSWh2Y05BUUVCQlFBRGdnRVBBRENDQVFvQ2dnRUJBTStQClVLVmExcm9tQndOZzdqNnBBSGo5TDQ4RVJpdEplRzRXM1pUYmNMNWNKbnVTQmFsc1h1TWpQTGZmbUV1VEZIdVAKenRqUlBEUHcreEg1d3VTWFF2U0tIaXF2VE1pUm9DSlJFa09sQXpIa1dQM0VrdnUzNzRqZDVGV3Q3NEhnRk91cApIZ1ZwdUxPblczK2NDVE5iQ3VkeDFMVldRbGgwQzJKbm1Lam5uS1YrTkxzNFJVaVk1dk91ekpuNHl6QldLRjM2CmJLZ3ZDOVpMWlFSM3dZcnJNZWllYzBnWVY2VlJtaGgxSjRDV3V1UWd0ckM2d2NJanFWZFdEUlJyNHFMdEtDcDIKQVNIZmNieitwcEdHblJ5Z2FzcWNJdnpiNUVwV3NIRGtHRStUUW5WQ0JmTmsxN0NEOTZBQ1pmRWVybzEvWE16MgpRbzZvcUE0dnF5ZkdWWVU5RVZFQ0F3RUFBYU1qTUNFd0RnWURWUjBQQVFIL0JBUURBZ0trTUE4R0ExVWRFd0VCCi93UUZNQU1CQWY4d0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dFQkFNWFVpUVJpdUc4cGdzcHMrZTdGZWdCdEJOZEcKZlFUdHVLRWFUZ0U0RjQwamJ3UmdrN25DTHlsSHgvRG04aVRRQmsyWjR4WnNuY0huRys4SkwrckRLdlJBSE5iVQpsYnpReXA1V3FwdjdPcThwZ01wU0o5bTdVY3BGZmRVZkorNW43aXFnTGdMb3lhNmtRVTR2Rk0yTE1rWjI5NVpxCmVId0hnREo5Z3IwWGNyOWM1L2tRdkxFc2Z2WU5QZVhuamNyWXlDb2JNcVduSElxeVd3cHM1VTJOaGgraXhSZEIKbzRRL3RJS04xOU93WGZBaVc5SENhNzZMb3ZXaUhPU2UxVnFzK1h1N1A5ckx4eW1vQm91aFcxVmZ0bUo5Qy9vTAp3cFVuNnlXRCttY0tkZ3J5QTFjTWJ4Q281bUd6YTNLaFk1QTd5eDQ1cThkSEIzTWU4d0FCam1wWEs0ST0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=`) config := &rest.Config { TLSClientConfig: rest.TLSClientConfig { CAData: CABundle, - CAFile: "", + CAFile: "", }, } actual := webhooks.ExtractCA(config) - assert.Assert(t, bytes.Equal(CABundle, actual)) + assert.Assert(t, bytes.Equal(CABundle, actual)) } func TestExtractCA_EmptyConfig(t *testing.T) { config := &rest.Config { TLSClientConfig: rest.TLSClientConfig { CAData: nil, - CAFile: "", + CAFile: "", }, } actual := webhooks.ExtractCA(config) - assert.Assert(t, actual == nil) + assert.Assert(t, actual == nil) } func TestExtractCA_InvalidFile(t *testing.T) { config := &rest.Config { TLSClientConfig: rest.TLSClientConfig { CAData: nil, - CAFile: "somenonexistingfile", + CAFile: "somenonexistingfile", }, } actual := webhooks.ExtractCA(config) - assert.Assert(t, actual == nil) + assert.Assert(t, actual == nil) } \ No newline at end of file diff --git a/webhooks/resources/CAFile b/webhooks/resources/CAFile index d3700b2b42..8e3c60a196 100644 --- a/webhooks/resources/CAFile +++ b/webhooks/resources/CAFile @@ -1,17 +1,14 @@ -----BEGIN CERTIFICATE----- -MIICyDCCAbCgAwIBAgIBADANBgkqhkiG9w0BAQsFADAVMRMwEQYDVQQDEwprdWJl -cm5ldGVzMB4XDTE5MDMxOTE0MDcwNFoXDTI5MDMxNjE0MDcwNFowFTETMBEGA1UE -AxMKa3ViZXJuZXRlczCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAM+P -UKVa1romBwNg7j6pAHj9L48ERitJeG4W3ZTbcL5cJnuSBalsXuMjPLffmEuTFHuP -ztjRPDPw+xH5wuSXQvSKHiqvTMiRoCJREkOlAzHkWP3Ekvu374jd5FWt74HgFOup -HgVpuLOnW3+cCTNbCudx1LVWQlh0C2JnmKjnnKV+NLs4RUiY5vOuzJn4yzBWKF36 -bKgvC9ZLZQR3wYrrMeiec0gYV6VRmhh1J4CWuuQgtrC6wcIjqVdWDRRr4qLtKCp2 -ASHfcbz+ppGGnRygasqcIvzb5EpWsHDkGE+TQnVCBfNk17CD96ACZfEero1/XMz2 -Qo6oqA4vqyfGVYU9EVECAwEAAaMjMCEwDgYDVR0PAQH/BAQDAgKkMA8GA1UdEwEB -/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAMXUiQRiuG8pgsps+e7FegBtBNdG -fQTtuKEaTgE4F40jbwRgk7nCLylHx/Dm8iTQBk2Z4xZsncHnG+8JL+rDKvRAHNbU -lbzQyp5Wqpv7Oq8pgMpSJ9m7UcpFfdUfJ+5n7iqgLgLoya6kQU4vFM2LMkZ295Zq -eHwHgDJ9gr0Xcr9c5/kQvLEsfvYNPeXnjcrYyCobMqWnHIqyWwps5U2Nhh+ixRdB -o4Q/tIKN19OwXfAiW9HCa76LovWiHOSe1Vqs+Xu7P9rLxymoBouhW1VftmJ9C/oL -wpUn6yWD+mcKdgryA1cMbxCo5mGza3KhY5A7yx45q8dHB3Me8wABjmpXK4I= +V2VsY29tZSB0byBUaGUgUnVzdCBQcm9ncmFtbWluZyBMYW5ndWFnZSwgY +W4gaW50cm9kdWN0b3J5IGJvb2sgYWJvdXQgUnVzdC4gVGhlIFJ1c3QgcH +JvZ3JhbW1pbmcgbGFuZ3VhZ2UgaGVscHMgeW91IHdyaXRlIGZhc3Rlciw +gbW9yZSByZWxpYWJsZSBzb2Z0d2FyZS4gSGlnaC1sZXZlbCBlcmdvbm9t +aWNzIGFuZCBsb3ctbGV2ZWwgY29udHJvbCBhcmUgb2Z0ZW4gYXQgb2Rkc +yBpbiBwcm9ncmFtbWluZyBsYW5ndWFnZSBkZXNpZ247IFJ1c3QgY2hhbG +xlbmdlcyB0aGF0IGNvbmZsaWN0LiBUaHJvdWdoIGJhbGFuY2luZyBwb3d +lcmZ1bCB0ZWNobmljYWwgY2FwYWNpdHkgYW5kIGEgZ3JlYXQgZGV2ZWxv +cGVyIGV4cGVyaWVuY2UsIFJ1c3QgZ2l2ZXMgeW91IHRoZSBvcHRpb24gd +G8gY29udHJvbCBsb3ctbGV2ZWwgZGV0YWlscyAoc3VjaCBhcyBtZW1vcn +kgdXNhZ2UpIHdpdGhvdXQgYWxsIHRoZSBoYXNzbGUgdHJhZGl0aW9uYWx +seSBhc3NvY2lhdGVkIHdpdGggc3VjaCBjb250cm9sLgyzmqp31l8rqr1== -----END CERTIFICATE----- From dccb9e6f6e1f48ca4485d729c9f57772e9f27303 Mon Sep 17 00:00:00 2001 From: belyshevdenis Date: Thu, 21 Mar 2019 17:25:36 +0200 Subject: [PATCH 07/12] NK-31: Changed DaemonSet to Deployment for kube-policy image --- definitions/install.yaml | 50 +++++++++++++++------------------------- 1 file changed, 19 insertions(+), 31 deletions(-) diff --git a/definitions/install.yaml b/definitions/install.yaml index 6a1caea2d6..28cbbc797b 100644 --- a/definitions/install.yaml +++ b/definitions/install.yaml @@ -159,12 +159,12 @@ spec: targetPort: 443 selector: app: kube-policy -#--- -#apiVersion: v1 -#kind: ServiceAccount -#metadata: -# name: kube-policy-service-account -# namespace: kube-system +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: kube-policy-service-account + namespace: kube-system --- kind: ClusterRoleBinding apiVersion: rbac.authorization.k8s.io/v1beta1 @@ -176,41 +176,29 @@ roleRef: name: cluster-admin subjects: - kind: ServiceAccount - name: default + name: kube-policy-service-account namespace: kube-system --- apiVersion: extensions/v1beta1 -kind: DaemonSet +kind: Deployment metadata: + namespace: kube-system + name: kube-policy-deployment labels: app: kube-policy - tier: node - name: kube-policy-daemon - namespace: kube-system spec: + replicas: 1 template: metadata: labels: app: kube-policy - tier: node spec: - #serviceAccountName: kube-policy-service-account - #serviceAccount: kube-policy-service-account + serviceAccountName: kube-policy-service-account containers: - - name: kube-policy - image: nirmata/kube-policy:latest - imagePullPolicy: IfNotPresent - ports: - - containerPort: 443 - securityContext: - privileged: true - hostNetwork: true - tolerations: - - key: CriticalAddonsOnly - operator: Exists - - effect: NoSchedule - key: node-role.kubernetes.io/master - operator: Exists - - effect: NoSchedule - key: node.kubernetes.io/not-ready - operator: Exists + - name: kube-policy + image: nirmata/kube-policy:latest + imagePullPolicy: IfNotPresent + ports: + - containerPort: 443 + securityContext: + privileged: true From c662f1c9dbead353a52df58f1a83a8e402323df0 Mon Sep 17 00:00:00 2001 From: belyshevdenis Date: Thu, 21 Mar 2019 18:09:14 +0200 Subject: [PATCH 08/12] NK-31: Renamed constants package to config --- constants/constants.go => config/config.go | 2 +- init.go | 10 +++++----- main.go | 3 +-- server/server.go | 14 +++++++------- webhooks/registration.go | 18 +++++++++--------- 5 files changed, 23 insertions(+), 24 deletions(-) rename constants/constants.go => config/config.go (95%) diff --git a/constants/constants.go b/config/config.go similarity index 95% rename from constants/constants.go rename to config/config.go index 7bfd24e07c..4ca7879709 100644 --- a/constants/constants.go +++ b/config/config.go @@ -1,4 +1,4 @@ -package constants +package config const ( // These constants MUST be equal to the corresponding names in service definition in definitions/install.yaml diff --git a/init.go b/init.go index d8ec4cd7c6..f7cff8345a 100644 --- a/init.go +++ b/init.go @@ -6,7 +6,7 @@ import ( "net/url" "github.com/nirmata/kube-policy/kubeclient" - "github.com/nirmata/kube-policy/constants" + "github.com/nirmata/kube-policy/config" "github.com/nirmata/kube-policy/utils" rest "k8s.io/client-go/rest" @@ -44,14 +44,14 @@ func readTlsPairFromFiles(certFile, keyFile string) *utils.TlsPemPair { // Loads or creates PEM private key and TLS certificate for webhook server // Returns struct with key/certificate pair -func initTlsPemsPair(config *rest.Config, client *kubeclient.KubeClient) (*utils.TlsPemPair, error) { - apiServerUrl, err := url.Parse(config.Host) +func initTlsPemsPair(configuration *rest.Config, client *kubeclient.KubeClient) (*utils.TlsPemPair, error) { + apiServerUrl, err := url.Parse(configuration.Host) if err != nil { return nil, err } certProps := utils.TlsCertificateProps{ - Service: constants.WebhookServiceName, - Namespace: constants.WebhookServiceNamespace, + Service: config.WebhookServiceName, + Namespace: config.WebhookServiceNamespace, ApiServerHost: apiServerUrl.Hostname(), } diff --git a/main.go b/main.go index cb728a4c57..0e93ed2192 100644 --- a/main.go +++ b/main.go @@ -64,8 +64,7 @@ func main() { controller.Run(stopCh) if err != nil { - log.Fatalf("Error running PolicyController! Error: %s\n", err) - return + log.Fatalf("Error running PolicyController: %s\n", err) } log.Println("Policy Controller has started") diff --git a/server/server.go b/server/server.go index 0c1d3957e8..64b0c153be 100644 --- a/server/server.go +++ b/server/server.go @@ -14,7 +14,7 @@ import ( "github.com/nirmata/kube-policy/controller" "github.com/nirmata/kube-policy/kubeclient" - "github.com/nirmata/kube-policy/constants" + "github.com/nirmata/kube-policy/config" "github.com/nirmata/kube-policy/webhooks" "github.com/nirmata/kube-policy/utils" @@ -40,23 +40,23 @@ type WebhookServerConfig struct { // NewWebhookServer creates new instance of WebhookServer accordingly to given configuration // Policy Controller and Kubernetes Client should be initialized in configuration -func NewWebhookServer(config WebhookServerConfig, logger *log.Logger) (*WebhookServer, error) { +func NewWebhookServer(configuration WebhookServerConfig, logger *log.Logger) (*WebhookServer, error) { if logger == nil { logger = log.New(os.Stdout, "HTTPS Server: ", log.LstdFlags|log.Lshortfile) } - if config.TlsPemPair == nil || config.Controller == nil || config.Kubeclient == nil { + if configuration.TlsPemPair == nil || configuration.Controller == nil || configuration.Kubeclient == nil { return nil, errors.New("WebhookServerConfig is not initialized properly") } var tlsConfig tls.Config - pair, err := tls.X509KeyPair(config.TlsPemPair.Certificate, config.TlsPemPair.PrivateKey) + pair, err := tls.X509KeyPair(configuration.TlsPemPair.Certificate, configuration.TlsPemPair.PrivateKey) if err != nil { return nil, err } tlsConfig.Certificates = []tls.Certificate{pair} - mw, err := webhooks.NewMutationWebhook(config.Kubeclient, config.Controller, logger) + mw, err := webhooks.NewMutationWebhook(configuration.Kubeclient, configuration.Controller, logger) if err != nil { return nil, err } @@ -67,7 +67,7 @@ func NewWebhookServer(config WebhookServerConfig, logger *log.Logger) (*WebhookS } mux := http.NewServeMux() - mux.HandleFunc(constants.WebhookServicePath, ws.serve) + mux.HandleFunc(config.WebhookServicePath, ws.serve) ws.server = http.Server{ Addr: ":443", // Listen on port for HTTPS requests @@ -83,7 +83,7 @@ func NewWebhookServer(config WebhookServerConfig, logger *log.Logger) (*WebhookS // Main server endpoint for all requests func (ws *WebhookServer) serve(w http.ResponseWriter, r *http.Request) { - if r.URL.Path == constants.WebhookServicePath { + if r.URL.Path == config.WebhookServicePath { admissionReview := ws.parseAdmissionReview(r, w) if admissionReview == nil { return diff --git a/webhooks/registration.go b/webhooks/registration.go index ab3f546ead..63cbdb9ecf 100644 --- a/webhooks/registration.go +++ b/webhooks/registration.go @@ -3,7 +3,7 @@ package webhooks import ( "io/ioutil" - "github.com/nirmata/kube-policy/constants" + "github.com/nirmata/kube-policy/config" rest "k8s.io/client-go/rest" meta "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -25,22 +25,22 @@ func RegisterMutationWebhook(config *rest.Config) error { return nil } -func constructWebhookConfig(config *rest.Config) *adm.MutatingWebhookConfiguration { +func constructWebhookConfig(configuration *rest.Config) *adm.MutatingWebhookConfiguration { return &adm.MutatingWebhookConfiguration { ObjectMeta: meta.ObjectMeta { - Name: constants.WebhookConfigName, - Labels: constants.WebhookConfigLabels, + Name: config.WebhookConfigName, + Labels: config.WebhookConfigLabels, }, Webhooks: []adm.Webhook { adm.Webhook { - Name: constants.MutationWebhookName, + Name: config.MutationWebhookName, ClientConfig: adm.WebhookClientConfig { Service: &adm.ServiceReference { - Namespace: constants.WebhookServiceNamespace, - Name: constants.WebhookServiceName, - Path: &constants.WebhookServicePath, + Namespace: config.WebhookServiceNamespace, + Name: config.WebhookServiceName, + Path: &config.WebhookServicePath, }, - CABundle: ExtractCA(config), + CABundle: ExtractCA(configuration), }, Rules: []adm.RuleWithOperations { adm.RuleWithOperations { From b160fabcbacd6e517feaa730df154463155a9741 Mon Sep 17 00:00:00 2001 From: belyshevdenis Date: Thu, 21 Mar 2019 15:57:30 +0200 Subject: [PATCH 09/12] NK-31: Put constants in separate file. Updated install.yaml definition to create Service and DaemonSet. Fixed bug with webhook registration. --- constants/constants.go | 17 ++++++ definitions/examples/selector-policy.yaml | 21 ------- definitions/install.yaml | 69 +++++++++++++++++++++++ init.go | 13 ++--- main.go | 2 +- server/server.go | 7 ++- webhooks/registration.go | 42 +++++--------- 7 files changed, 110 insertions(+), 61 deletions(-) create mode 100644 constants/constants.go delete mode 100644 definitions/examples/selector-policy.yaml diff --git a/constants/constants.go b/constants/constants.go new file mode 100644 index 0000000000..7bfd24e07c --- /dev/null +++ b/constants/constants.go @@ -0,0 +1,17 @@ +package constants + +const ( + // These constants MUST be equal to the corresponding names in service definition in definitions/install.yaml + WebhookServiceNamespace = "kube-system" + WebhookServiceName = "kube-policy-svc" + + WebhookConfigName = "nirmata-kube-policy-webhook-cfg" + MutationWebhookName = "webhook.nirmata.kube-policy" +) + +var ( + WebhookServicePath = "/mutate" + WebhookConfigLabels = map[string]string { + "app": "kube-policy", + } +) \ No newline at end of file diff --git a/definitions/examples/selector-policy.yaml b/definitions/examples/selector-policy.yaml deleted file mode 100644 index d15312b76d..0000000000 --- a/definitions/examples/selector-policy.yaml +++ /dev/null @@ -1,21 +0,0 @@ -apiVersion: policy.nirmata.io/v1alpha1 -kind : Policy -metadata: - name: selector-policy -spec: - failurePolicy: continueOnError - rules: - - resource: - kind: ConfigMap - selector: - matchLabels: - label1: test1 - matchExpressions: - - key: label2 - operator: In - values: - - test2 - patch: - - path: / - op : add - value : "20" diff --git a/definitions/install.yaml b/definitions/install.yaml index 19273f4f08..6a1caea2d6 100644 --- a/definitions/install.yaml +++ b/definitions/install.yaml @@ -145,3 +145,72 @@ spec: type: object additionalProperties: type: string +--- +apiVersion: v1 +kind: Service +metadata: + namespace: kube-system + name: kube-policy-svc + labels: + app: kube-policy +spec: + ports: + - port: 443 + targetPort: 443 + selector: + app: kube-policy +#--- +#apiVersion: v1 +#kind: ServiceAccount +#metadata: +# name: kube-policy-service-account +# namespace: kube-system +--- +kind: ClusterRoleBinding +apiVersion: rbac.authorization.k8s.io/v1beta1 +metadata: + name: kube-policy-admin +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: cluster-admin +subjects: +- kind: ServiceAccount + name: default + namespace: kube-system +--- +apiVersion: extensions/v1beta1 +kind: DaemonSet +metadata: + labels: + app: kube-policy + tier: node + name: kube-policy-daemon + namespace: kube-system +spec: + template: + metadata: + labels: + app: kube-policy + tier: node + spec: + #serviceAccountName: kube-policy-service-account + #serviceAccount: kube-policy-service-account + containers: + - name: kube-policy + image: nirmata/kube-policy:latest + imagePullPolicy: IfNotPresent + ports: + - containerPort: 443 + securityContext: + privileged: true + hostNetwork: true + tolerations: + - key: CriticalAddonsOnly + operator: Exists + - effect: NoSchedule + key: node-role.kubernetes.io/master + operator: Exists + - effect: NoSchedule + key: node.kubernetes.io/not-ready + operator: Exists diff --git a/init.go b/init.go index 8940b39c8c..d8ec4cd7c6 100644 --- a/init.go +++ b/init.go @@ -6,16 +6,13 @@ import ( "net/url" "github.com/nirmata/kube-policy/kubeclient" + "github.com/nirmata/kube-policy/constants" "github.com/nirmata/kube-policy/utils" rest "k8s.io/client-go/rest" clientcmd "k8s.io/client-go/tools/clientcmd" ) -// These constants MUST be equal to the corresponding names in service definition in definitions/install.yaml -const serviceName string = "kube-policy-svc" -const namespace string = "default" - func createClientConfig(kubeconfig string) (*rest.Config, error) { if kubeconfig == "" { log.Printf("Using in-cluster configuration") @@ -29,13 +26,13 @@ func createClientConfig(kubeconfig string) (*rest.Config, error) { func readTlsPairFromFiles(certFile, keyFile string) *utils.TlsPemPair { certContent, err := ioutil.ReadFile(certFile) if err != nil { - log.Printf("Unable to read file with TLS certificate: %v", err) + log.Printf("Unable to read file with TLS certificate: path - %s, error - %v", certFile, err) return nil } keyContent, err := ioutil.ReadFile(keyFile) if err != nil { - log.Printf("Unable to read file with TLS private key: %v", err) + log.Printf("Unable to read file with TLS private key: path - %s, error - %v", keyFile, err) return nil } @@ -53,8 +50,8 @@ func initTlsPemsPair(config *rest.Config, client *kubeclient.KubeClient) (*utils return nil, err } certProps := utils.TlsCertificateProps{ - Service: serviceName, - Namespace: namespace, + Service: constants.WebhookServiceName, + Namespace: constants.WebhookServiceNamespace, ApiServerHost: apiServerUrl.Hostname(), } diff --git a/main.go b/main.go index d3dea750e1..cb728a4c57 100644 --- a/main.go +++ b/main.go @@ -24,7 +24,7 @@ func main() { log.Fatalf("Error building kubeconfig: %v\n", err) } - _, err = webhooks.RegisterMutationWebhook(clientConfig) + err = webhooks.RegisterMutationWebhook(clientConfig) if err != nil { log.Fatalf("Error registering mutation webhook server: %v\n", err) } diff --git a/server/server.go b/server/server.go index d16e09c6a5..0c1d3957e8 100644 --- a/server/server.go +++ b/server/server.go @@ -14,8 +14,9 @@ import ( "github.com/nirmata/kube-policy/controller" "github.com/nirmata/kube-policy/kubeclient" - "github.com/nirmata/kube-policy/utils" + "github.com/nirmata/kube-policy/constants" "github.com/nirmata/kube-policy/webhooks" + "github.com/nirmata/kube-policy/utils" v1beta1 "k8s.io/api/admission/v1beta1" ) @@ -66,7 +67,7 @@ func NewWebhookServer(config WebhookServerConfig, logger *log.Logger) (*WebhookS } mux := http.NewServeMux() - mux.HandleFunc("/mutate", ws.serve) + mux.HandleFunc(constants.WebhookServicePath, ws.serve) ws.server = http.Server{ Addr: ":443", // Listen on port for HTTPS requests @@ -82,7 +83,7 @@ func NewWebhookServer(config WebhookServerConfig, logger *log.Logger) (*WebhookS // Main server endpoint for all requests func (ws *WebhookServer) serve(w http.ResponseWriter, r *http.Request) { - if r.URL.Path == "/mutate" { + if r.URL.Path == constants.WebhookServicePath { admissionReview := ws.parseAdmissionReview(r, w) if admissionReview == nil { return diff --git a/webhooks/registration.go b/webhooks/registration.go index 434e825c42..ecbf548170 100644 --- a/webhooks/registration.go +++ b/webhooks/registration.go @@ -1,58 +1,44 @@ package webhooks + import ( "io/ioutil" + "github.com/nirmata/kube-policy/constants" + rest "k8s.io/client-go/rest" meta "k8s.io/apimachinery/pkg/apis/meta/v1" adm "k8s.io/api/admissionregistration/v1beta1" - types "k8s.io/api/admissionregistration/v1beta1" admreg "k8s.io/client-go/kubernetes/typed/admissionregistration/v1beta1" ) -const ( - webhookName = "nirmata-kube-policy-webhook-cfg" - mutationWebhookName = "webhook.nirmata.kube-policy" - webhookServiceNamespace = "default" - webhookServiceName = "kube-policy-svc" -) - -var ( - webhookPath = "mutate" - webhookLabels = map[string]string { - "app": "kube-policy", - } -) - -func RegisterMutationWebhook(config *rest.Config) (*types.MutatingWebhookConfiguration, error) { -var result *types.MutatingWebhookConfiguration = nil - +func RegisterMutationWebhook(config *rest.Config) error { registrationClient, err := admreg.NewForConfig(config) if err != nil { - return nil, err + return err } - result, err = registrationClient.MutatingWebhookConfigurations().Create(constructWebhookConfig(config)) + _, err = registrationClient.MutatingWebhookConfigurations().Create(constructWebhookConfig(config)) if err != nil { - return nil, err + return err } - return result, nil + return nil } func constructWebhookConfig(config *rest.Config) *adm.MutatingWebhookConfiguration { return &adm.MutatingWebhookConfiguration { ObjectMeta: meta.ObjectMeta { - Name: webhookName, - Labels: webhookLabels, + Name: constants.WebhookConfigName, + Labels: constants.WebhookConfigLabels, }, Webhooks: []adm.Webhook { adm.Webhook { - Name: mutationWebhookName, + Name: constants.MutationWebhookName, ClientConfig: adm.WebhookClientConfig { Service: &adm.ServiceReference { - Namespace: webhookServiceNamespace, - Name: webhookServiceName, - Path: &webhookPath, + Namespace: constants.WebhookServiceNamespace, + Name: constants.WebhookServiceName, + Path: &constants.WebhookServicePath, }, CABundle: ExtractCA(config), }, From 8f7a0dba952fccdd2ac9fdaf5b963cf1233145b5 Mon Sep 17 00:00:00 2001 From: belyshevdenis Date: Thu, 21 Mar 2019 16:56:03 +0200 Subject: [PATCH 10/12] NK-31: Fixed indentation --- webhooks/registration.go | 4 ++-- webhooks/registration_test.go | 21 +++++++++++---------- webhooks/resources/CAFile | 27 ++++++++++++--------------- 3 files changed, 25 insertions(+), 27 deletions(-) diff --git a/webhooks/registration.go b/webhooks/registration.go index ecbf548170..ab3f546ead 100644 --- a/webhooks/registration.go +++ b/webhooks/registration.go @@ -35,7 +35,7 @@ func constructWebhookConfig(config *rest.Config) *adm.MutatingWebhookConfigurati adm.Webhook { Name: constants.MutationWebhookName, ClientConfig: adm.WebhookClientConfig { - Service: &adm.ServiceReference { + Service: &adm.ServiceReference { Namespace: constants.WebhookServiceNamespace, Name: constants.WebhookServiceName, Path: &constants.WebhookServicePath, @@ -68,7 +68,7 @@ func constructWebhookConfig(config *rest.Config) *adm.MutatingWebhookConfigurati func ExtractCA(config *rest.Config) (result []byte) { fileName := config.TLSClientConfig.CAFile - if fileName != "" { + if fileName != "" { result, err := ioutil.ReadFile(fileName) if err != nil { diff --git a/webhooks/registration_test.go b/webhooks/registration_test.go index 7a6cbaffb8..a095756838 100644 --- a/webhooks/registration_test.go +++ b/webhooks/registration_test.go @@ -1,4 +1,5 @@ package webhooks_test + import ( "gotest.tools/assert" "io/ioutil" @@ -11,55 +12,55 @@ import ( ) func TestExtractCA_EmptyBundle(t *testing.T) { - CAFile := "resources/CAFile" + CAFile := "resources/CAFile" config := &rest.Config { TLSClientConfig: rest.TLSClientConfig { CAData: nil, - CAFile: CAFile, + CAFile: CAFile, }, } expected, err := ioutil.ReadFile(CAFile) assert.Assert(t, err == nil) actual := webhooks.ExtractCA(config) - assert.Assert(t, bytes.Equal(expected, actual)) + assert.Assert(t, bytes.Equal(expected, actual)) } func TestExtractCA_EmptyCAFile(t *testing.T) { - CABundle := []byte(`LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUN5RENDQWJDZ0F3SUJBZ0lCQURBTkJna3Foa2lHOXcwQkFRc0ZBREFWTVJNd0VRWURWUVFERXdwcmRXSmwKY201bGRHVnpNQjRYRFRFNU1ETXhPVEUwTURjd05Gb1hEVEk1TURNeE5qRTBNRGN3TkZvd0ZURVRNQkVHQTFVRQpBeE1LYTNWaVpYSnVaWFJsY3pDQ0FTSXdEUVlKS29aSWh2Y05BUUVCQlFBRGdnRVBBRENDQVFvQ2dnRUJBTStQClVLVmExcm9tQndOZzdqNnBBSGo5TDQ4RVJpdEplRzRXM1pUYmNMNWNKbnVTQmFsc1h1TWpQTGZmbUV1VEZIdVAKenRqUlBEUHcreEg1d3VTWFF2U0tIaXF2VE1pUm9DSlJFa09sQXpIa1dQM0VrdnUzNzRqZDVGV3Q3NEhnRk91cApIZ1ZwdUxPblczK2NDVE5iQ3VkeDFMVldRbGgwQzJKbm1Lam5uS1YrTkxzNFJVaVk1dk91ekpuNHl6QldLRjM2CmJLZ3ZDOVpMWlFSM3dZcnJNZWllYzBnWVY2VlJtaGgxSjRDV3V1UWd0ckM2d2NJanFWZFdEUlJyNHFMdEtDcDIKQVNIZmNieitwcEdHblJ5Z2FzcWNJdnpiNUVwV3NIRGtHRStUUW5WQ0JmTmsxN0NEOTZBQ1pmRWVybzEvWE16MgpRbzZvcUE0dnF5ZkdWWVU5RVZFQ0F3RUFBYU1qTUNFd0RnWURWUjBQQVFIL0JBUURBZ0trTUE4R0ExVWRFd0VCCi93UUZNQU1CQWY4d0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dFQkFNWFVpUVJpdUc4cGdzcHMrZTdGZWdCdEJOZEcKZlFUdHVLRWFUZ0U0RjQwamJ3UmdrN25DTHlsSHgvRG04aVRRQmsyWjR4WnNuY0huRys4SkwrckRLdlJBSE5iVQpsYnpReXA1V3FwdjdPcThwZ01wU0o5bTdVY3BGZmRVZkorNW43aXFnTGdMb3lhNmtRVTR2Rk0yTE1rWjI5NVpxCmVId0hnREo5Z3IwWGNyOWM1L2tRdkxFc2Z2WU5QZVhuamNyWXlDb2JNcVduSElxeVd3cHM1VTJOaGgraXhSZEIKbzRRL3RJS04xOU93WGZBaVc5SENhNzZMb3ZXaUhPU2UxVnFzK1h1N1A5ckx4eW1vQm91aFcxVmZ0bUo5Qy9vTAp3cFVuNnlXRCttY0tkZ3J5QTFjTWJ4Q281bUd6YTNLaFk1QTd5eDQ1cThkSEIzTWU4d0FCam1wWEs0ST0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=`) + CABundle := []byte(`LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUN5RENDQWJDZ0F3SUJBZ0lCQURBTkJna3Foa2lHOXcwQkFRc0ZBREFWTVJNd0VRWURWUVFERXdwcmRXSmwKY201bGRHVnpNQjRYRFRFNU1ETXhPVEUwTURjd05Gb1hEVEk1TURNeE5qRTBNRGN3TkZvd0ZURVRNQkVHQTFVRQpBeE1LYTNWaVpYSnVaWFJsY3pDQ0FTSXdEUVlKS29aSWh2Y05BUUVCQlFBRGdnRVBBRENDQVFvQ2dnRUJBTStQClVLVmExcm9tQndOZzdqNnBBSGo5TDQ4RVJpdEplRzRXM1pUYmNMNWNKbnVTQmFsc1h1TWpQTGZmbUV1VEZIdVAKenRqUlBEUHcreEg1d3VTWFF2U0tIaXF2VE1pUm9DSlJFa09sQXpIa1dQM0VrdnUzNzRqZDVGV3Q3NEhnRk91cApIZ1ZwdUxPblczK2NDVE5iQ3VkeDFMVldRbGgwQzJKbm1Lam5uS1YrTkxzNFJVaVk1dk91ekpuNHl6QldLRjM2CmJLZ3ZDOVpMWlFSM3dZcnJNZWllYzBnWVY2VlJtaGgxSjRDV3V1UWd0ckM2d2NJanFWZFdEUlJyNHFMdEtDcDIKQVNIZmNieitwcEdHblJ5Z2FzcWNJdnpiNUVwV3NIRGtHRStUUW5WQ0JmTmsxN0NEOTZBQ1pmRWVybzEvWE16MgpRbzZvcUE0dnF5ZkdWWVU5RVZFQ0F3RUFBYU1qTUNFd0RnWURWUjBQQVFIL0JBUURBZ0trTUE4R0ExVWRFd0VCCi93UUZNQU1CQWY4d0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dFQkFNWFVpUVJpdUc4cGdzcHMrZTdGZWdCdEJOZEcKZlFUdHVLRWFUZ0U0RjQwamJ3UmdrN25DTHlsSHgvRG04aVRRQmsyWjR4WnNuY0huRys4SkwrckRLdlJBSE5iVQpsYnpReXA1V3FwdjdPcThwZ01wU0o5bTdVY3BGZmRVZkorNW43aXFnTGdMb3lhNmtRVTR2Rk0yTE1rWjI5NVpxCmVId0hnREo5Z3IwWGNyOWM1L2tRdkxFc2Z2WU5QZVhuamNyWXlDb2JNcVduSElxeVd3cHM1VTJOaGgraXhSZEIKbzRRL3RJS04xOU93WGZBaVc5SENhNzZMb3ZXaUhPU2UxVnFzK1h1N1A5ckx4eW1vQm91aFcxVmZ0bUo5Qy9vTAp3cFVuNnlXRCttY0tkZ3J5QTFjTWJ4Q281bUd6YTNLaFk1QTd5eDQ1cThkSEIzTWU4d0FCam1wWEs0ST0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=`) config := &rest.Config { TLSClientConfig: rest.TLSClientConfig { CAData: CABundle, - CAFile: "", + CAFile: "", }, } actual := webhooks.ExtractCA(config) - assert.Assert(t, bytes.Equal(CABundle, actual)) + assert.Assert(t, bytes.Equal(CABundle, actual)) } func TestExtractCA_EmptyConfig(t *testing.T) { config := &rest.Config { TLSClientConfig: rest.TLSClientConfig { CAData: nil, - CAFile: "", + CAFile: "", }, } actual := webhooks.ExtractCA(config) - assert.Assert(t, actual == nil) + assert.Assert(t, actual == nil) } func TestExtractCA_InvalidFile(t *testing.T) { config := &rest.Config { TLSClientConfig: rest.TLSClientConfig { CAData: nil, - CAFile: "somenonexistingfile", + CAFile: "somenonexistingfile", }, } actual := webhooks.ExtractCA(config) - assert.Assert(t, actual == nil) + assert.Assert(t, actual == nil) } \ No newline at end of file diff --git a/webhooks/resources/CAFile b/webhooks/resources/CAFile index d3700b2b42..8e3c60a196 100644 --- a/webhooks/resources/CAFile +++ b/webhooks/resources/CAFile @@ -1,17 +1,14 @@ -----BEGIN CERTIFICATE----- -MIICyDCCAbCgAwIBAgIBADANBgkqhkiG9w0BAQsFADAVMRMwEQYDVQQDEwprdWJl -cm5ldGVzMB4XDTE5MDMxOTE0MDcwNFoXDTI5MDMxNjE0MDcwNFowFTETMBEGA1UE -AxMKa3ViZXJuZXRlczCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAM+P -UKVa1romBwNg7j6pAHj9L48ERitJeG4W3ZTbcL5cJnuSBalsXuMjPLffmEuTFHuP -ztjRPDPw+xH5wuSXQvSKHiqvTMiRoCJREkOlAzHkWP3Ekvu374jd5FWt74HgFOup -HgVpuLOnW3+cCTNbCudx1LVWQlh0C2JnmKjnnKV+NLs4RUiY5vOuzJn4yzBWKF36 -bKgvC9ZLZQR3wYrrMeiec0gYV6VRmhh1J4CWuuQgtrC6wcIjqVdWDRRr4qLtKCp2 -ASHfcbz+ppGGnRygasqcIvzb5EpWsHDkGE+TQnVCBfNk17CD96ACZfEero1/XMz2 -Qo6oqA4vqyfGVYU9EVECAwEAAaMjMCEwDgYDVR0PAQH/BAQDAgKkMA8GA1UdEwEB -/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAMXUiQRiuG8pgsps+e7FegBtBNdG -fQTtuKEaTgE4F40jbwRgk7nCLylHx/Dm8iTQBk2Z4xZsncHnG+8JL+rDKvRAHNbU -lbzQyp5Wqpv7Oq8pgMpSJ9m7UcpFfdUfJ+5n7iqgLgLoya6kQU4vFM2LMkZ295Zq -eHwHgDJ9gr0Xcr9c5/kQvLEsfvYNPeXnjcrYyCobMqWnHIqyWwps5U2Nhh+ixRdB -o4Q/tIKN19OwXfAiW9HCa76LovWiHOSe1Vqs+Xu7P9rLxymoBouhW1VftmJ9C/oL -wpUn6yWD+mcKdgryA1cMbxCo5mGza3KhY5A7yx45q8dHB3Me8wABjmpXK4I= +V2VsY29tZSB0byBUaGUgUnVzdCBQcm9ncmFtbWluZyBMYW5ndWFnZSwgY +W4gaW50cm9kdWN0b3J5IGJvb2sgYWJvdXQgUnVzdC4gVGhlIFJ1c3QgcH +JvZ3JhbW1pbmcgbGFuZ3VhZ2UgaGVscHMgeW91IHdyaXRlIGZhc3Rlciw +gbW9yZSByZWxpYWJsZSBzb2Z0d2FyZS4gSGlnaC1sZXZlbCBlcmdvbm9t +aWNzIGFuZCBsb3ctbGV2ZWwgY29udHJvbCBhcmUgb2Z0ZW4gYXQgb2Rkc +yBpbiBwcm9ncmFtbWluZyBsYW5ndWFnZSBkZXNpZ247IFJ1c3QgY2hhbG +xlbmdlcyB0aGF0IGNvbmZsaWN0LiBUaHJvdWdoIGJhbGFuY2luZyBwb3d +lcmZ1bCB0ZWNobmljYWwgY2FwYWNpdHkgYW5kIGEgZ3JlYXQgZGV2ZWxv +cGVyIGV4cGVyaWVuY2UsIFJ1c3QgZ2l2ZXMgeW91IHRoZSBvcHRpb24gd +G8gY29udHJvbCBsb3ctbGV2ZWwgZGV0YWlscyAoc3VjaCBhcyBtZW1vcn +kgdXNhZ2UpIHdpdGhvdXQgYWxsIHRoZSBoYXNzbGUgdHJhZGl0aW9uYWx +seSBhc3NvY2lhdGVkIHdpdGggc3VjaCBjb250cm9sLgyzmqp31l8rqr1== -----END CERTIFICATE----- From 3c3befe8cad63f09354a30902fbf775a4074a272 Mon Sep 17 00:00:00 2001 From: belyshevdenis Date: Thu, 21 Mar 2019 17:25:36 +0200 Subject: [PATCH 11/12] NK-31: Changed DaemonSet to Deployment for kube-policy image --- definitions/install.yaml | 50 +++++++++++++++------------------------- 1 file changed, 19 insertions(+), 31 deletions(-) diff --git a/definitions/install.yaml b/definitions/install.yaml index 6a1caea2d6..28cbbc797b 100644 --- a/definitions/install.yaml +++ b/definitions/install.yaml @@ -159,12 +159,12 @@ spec: targetPort: 443 selector: app: kube-policy -#--- -#apiVersion: v1 -#kind: ServiceAccount -#metadata: -# name: kube-policy-service-account -# namespace: kube-system +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: kube-policy-service-account + namespace: kube-system --- kind: ClusterRoleBinding apiVersion: rbac.authorization.k8s.io/v1beta1 @@ -176,41 +176,29 @@ roleRef: name: cluster-admin subjects: - kind: ServiceAccount - name: default + name: kube-policy-service-account namespace: kube-system --- apiVersion: extensions/v1beta1 -kind: DaemonSet +kind: Deployment metadata: + namespace: kube-system + name: kube-policy-deployment labels: app: kube-policy - tier: node - name: kube-policy-daemon - namespace: kube-system spec: + replicas: 1 template: metadata: labels: app: kube-policy - tier: node spec: - #serviceAccountName: kube-policy-service-account - #serviceAccount: kube-policy-service-account + serviceAccountName: kube-policy-service-account containers: - - name: kube-policy - image: nirmata/kube-policy:latest - imagePullPolicy: IfNotPresent - ports: - - containerPort: 443 - securityContext: - privileged: true - hostNetwork: true - tolerations: - - key: CriticalAddonsOnly - operator: Exists - - effect: NoSchedule - key: node-role.kubernetes.io/master - operator: Exists - - effect: NoSchedule - key: node.kubernetes.io/not-ready - operator: Exists + - name: kube-policy + image: nirmata/kube-policy:latest + imagePullPolicy: IfNotPresent + ports: + - containerPort: 443 + securityContext: + privileged: true From 0ef675f3c17de7f99717b27090731e47e64384d9 Mon Sep 17 00:00:00 2001 From: belyshevdenis Date: Thu, 21 Mar 2019 18:09:58 +0200 Subject: [PATCH 12/12] NK-31: Implemnted loggin about success to policy. Also fixed showing of error on initialization. --- init.go | 6 ++- webhooks/mutation.go | 96 ++++++++++++++++++++++++-------------------- webhooks/utils.go | 7 ++++ 3 files changed, 65 insertions(+), 44 deletions(-) diff --git a/init.go b/init.go index d8ec4cd7c6..e945988bd8 100644 --- a/init.go +++ b/init.go @@ -24,6 +24,10 @@ func createClientConfig(kubeconfig string) (*rest.Config, error) { } func readTlsPairFromFiles(certFile, keyFile string) *utils.TlsPemPair { + if certFile == "" || keyFile == "" { + return nil + } + certContent, err := ioutil.ReadFile(certFile) if err != nil { log.Printf("Unable to read file with TLS certificate: path - %s, error - %v", certFile, err) @@ -69,4 +73,4 @@ func initTlsPemsPair(config *rest.Config, client *kubeclient.KubeClient) (*utils } return tlsPair, nil -} \ No newline at end of file +} diff --git a/webhooks/mutation.go b/webhooks/mutation.go index 4b6033565b..d060f56a25 100644 --- a/webhooks/mutation.go +++ b/webhooks/mutation.go @@ -41,42 +41,32 @@ func (mw *MutationWebhook) Mutate(request *v1beta1.AdmissionRequest) *v1beta1.Ad var allPatches []PatchBytes for _, policy := range policies { - patchingSets := getPolicyPatchingSets(policy) + mw.logger.Printf("Applying policy %s with %d rules", policy.ObjectMeta.Name, len(policy.Spec.Rules)) - for ruleIdx, rule := range policy.Spec.Rules { - err := rule.Validate() - if err != nil { - mw.logger.Printf("Invalid rule detected: #%d in policy %s", ruleIdx, policy.ObjectMeta.Name) - continue - } + policyPatches, err := mw.applyPolicyRules(request, policy) + if err != nil { + mw.controller.LogPolicyError(policy.Name, err.Error()) - mw.logger.Printf("Applying policy %s, rule #%d", policy.ObjectMeta.Name, ruleIdx) - rulePatches, err := mw.applyRule(request, rule, patchingSets) - if err != nil { - return mw.denyResourceCreation(policy.Name, fmt.Sprintf("Unable to apply rule #%d: %s", ruleIdx, err)) - } + errStr := fmt.Sprintf("Unable to apply policy %s: %v", policy.Name, err) + mw.logger.Printf("Denying the request because of error: %s", errStr) + return mw.denyResourceCreation(errStr) + } - rulePatchesProcessed, err := ProcessPatches(rulePatches, request.Object.Raw, patchingSets) - if err != nil { - return mw.denyResourceCreation(policy.Name, fmt.Sprintf("Unable to apply rule #%d: %s", ruleIdx, err)) - } + if len(policyPatches) > 0 { + meta := parseMetadataFromObject(request.Object.Raw) + namespace := parseNamespaceFromMetadata(meta) + name := parseNameFromMetadata(meta) + mw.controller.LogPolicyInfo(policy.Name, fmt.Sprintf("Applied to %s %s/%s", request.Kind.Kind, namespace, name)) - if rulePatches != nil { - allPatches = append(allPatches, rulePatchesProcessed...) - mw.logger.Printf("Prepared %d patches", len(rulePatchesProcessed)) - } else { - mw.logger.Print("No patches prepared") - } + allPatches = append(allPatches, policyPatches...) } } + patchType := v1beta1.PatchTypeJSONPatch return &v1beta1.AdmissionResponse{ - Allowed: true, - Patch: JoinPatches(allPatches), - PatchType: func() *v1beta1.PatchType { - pt := v1beta1.PatchTypeJSONPatch - return &pt - }(), + Allowed: true, + Patch: JoinPatches(allPatches), + PatchType: &patchType, } } @@ -89,20 +79,43 @@ func getPolicyPatchingSets(policy types.Policy) PatchingSets { return PatchingSetsDefault } -// Applies all rule to the created object and returns list of JSON patches. +// Applies all policy rules to the created object and returns list of processed JSON patches. // May return nil patches if it is not necessary to create patches for requested object. // Returns error ONLY in case when creation of resource should be denied. -func (mw *MutationWebhook) applyRule(request *v1beta1.AdmissionRequest, rule types.PolicyRule, errorBehavior PatchingSets) ([]types.PolicyPatch, error) { - if !IsRuleApplicableToRequest(rule.Resource, request) { - return nil, nil +func (mw *MutationWebhook) applyPolicyRules(request *v1beta1.AdmissionRequest, policy types.Policy) ([]PatchBytes, error) { + patchingSets := getPolicyPatchingSets(policy) + var policyPatches []PatchBytes + + for ruleIdx, rule := range policy.Spec.Rules { + err := rule.Validate() + if err != nil { + mw.logger.Printf("Invalid rule detected: #%d in policy %s", ruleIdx, policy.ObjectMeta.Name) + continue + } + + if !IsRuleApplicableToRequest(rule.Resource, request) { + return nil, nil + } + + err = mw.applyRuleGenerators(request, rule) + if err != nil && patchingSets == PatchingSetsStopOnError { + return nil, errors.New(fmt.Sprintf("Failed to apply generators from rule #%d: %s", ruleIdx, err)) + } + + rulePatchesProcessed, err := ProcessPatches(rule.Patches, request.Object.Raw, patchingSets) + if err != nil { + return nil, errors.New(fmt.Sprintf("Failed to process patches from rule #%d: %s", ruleIdx, err)) + } + + if rulePatchesProcessed != nil { + policyPatches = append(policyPatches, rulePatchesProcessed...) + mw.logger.Printf("Rule %d: prepared %d patches", ruleIdx, len(rulePatchesProcessed)) + } else { + mw.logger.Print("Rule %d: no patches prepared") + } } - err := mw.applyRuleGenerators(request, rule) - if err != nil && errorBehavior == PatchingSetsStopOnError { - return nil, err - } else { - return rule.Patches, nil - } + return policyPatches, nil } // Applies "configMapGenerator" and "secretGenerator" described in PolicyRule @@ -121,7 +134,7 @@ func (mw *MutationWebhook) applyRuleGenerators(request *v1beta1.AdmissionRequest return nil } -// Creates resourceKind (ConfigMap or Secret) with parameters specified in generator in cluster specified in request +// Creates resourceKind (ConfigMap or Secret) with parameters specified in generator in cluster specified in request. func (mw *MutationWebhook) applyConfigGenerator(generator *types.PolicyConfigGenerator, namespace string, configKind string) error { if generator == nil { return nil @@ -149,10 +162,7 @@ func (mw *MutationWebhook) applyConfigGenerator(generator *types.PolicyConfigGen } // Forms AdmissionResponse with denial of resource creation and error message -func (mw *MutationWebhook) denyResourceCreation(policyName, errStr string) *v1beta1.AdmissionResponse { - mw.logger.Printf("Denying the request because of error: %s", errStr) - mw.controller.LogPolicyError(policyName, errStr) - +func (mw *MutationWebhook) denyResourceCreation(errStr string) *v1beta1.AdmissionResponse { return &v1beta1.AdmissionResponse{ Result: &metav1.Status{ Message: errStr, diff --git a/webhooks/utils.go b/webhooks/utils.go index 029975b952..c96a14db77 100644 --- a/webhooks/utils.go +++ b/webhooks/utils.go @@ -31,3 +31,10 @@ func parseNameFromMetadata(meta map[string]interface{}) string { } return "" } + +func parseNamespaceFromMetadata(meta map[string]interface{}) string { + if namespace, ok := meta["namespace"].(string); ok { + return namespace + } + return "" +}