From a0bd98a099cc404154468f9027da72344828f796 Mon Sep 17 00:00:00 2001 From: Guillermo Palacio Date: Mon, 11 Dec 2023 14:39:31 +0100 Subject: [PATCH] Adding the possibility to authenticate to elasticSearch using ApiKey (#379) Signed-off-by: guipal --- charts/policy-reporter/config.yaml | 1 + charts/policy-reporter/values.yaml | 4 +++- pkg/config/config.go | 1 + pkg/config/target_factory.go | 5 +++++ pkg/config/target_factory_test.go | 12 ++++++++++++ pkg/kubernetes/secrets/client.go | 5 +++++ pkg/kubernetes/secrets/client_test.go | 5 +++++ pkg/target/elasticsearch/elasticsearch.go | 5 +++++ pkg/target/elasticsearch/elasticsearch_test.go | 1 + 9 files changed, 38 insertions(+), 1 deletion(-) diff --git a/charts/policy-reporter/config.yaml b/charts/policy-reporter/config.yaml index f9393a0e..30277ccd 100644 --- a/charts/policy-reporter/config.yaml +++ b/charts/policy-reporter/config.yaml @@ -30,6 +30,7 @@ elasticsearch: skipTLS: {{ .Values.target.elasticsearch.skipTLS }} username: {{ .Values.target.elasticsearch.username | quote }} password: {{ .Values.target.elasticsearch.password | quote }} + apiKey: {{ .Values.target.elasticsearch.password | quote }} secretRef: {{ .Values.target.elasticsearch.secretRef | quote }} mountedSecret: {{ .Values.target.elasticsearch.mountedSecret | quote }} index: {{ .Values.target.elasticsearch.index | default "policy-reporter" | quote }} diff --git a/charts/policy-reporter/values.yaml b/charts/policy-reporter/values.yaml index c53d2ec0..2acfee29 100644 --- a/charts/policy-reporter/values.yaml +++ b/charts/policy-reporter/values.yaml @@ -362,7 +362,9 @@ target: username: "" # elasticsearch password für HTTP Basic Auth password: "" - # receive the host, username and/or password from an existing secret instead + # elasticsearch apiKey für apiKey authentication + apiKey: "" + # receive the host, username and/or password,apiKey from an existing secret instead secretRef: "" # Mounted secret path by Secrets Controller, secret should be in json format mountedSecret: "" diff --git a/pkg/config/config.go b/pkg/config/config.go index be7e68ce..4d5f8f63 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -108,6 +108,7 @@ type Elasticsearch struct { Rotation string `mapstructure:"rotation"` Username string `mapstructure:"username"` Password string `mapstructure:"password"` + ApiKey string `mapstructure:"apiKey"` Channels []*Elasticsearch `mapstructure:"channels"` } diff --git a/pkg/config/target_factory.go b/pkg/config/target_factory.go index 3953c48c..b45fe379 100644 --- a/pkg/config/target_factory.go +++ b/pkg/config/target_factory.go @@ -408,6 +408,7 @@ func (f *TargetFactory) createElasticsearchClient(config, parent *Elasticsearch) setBool(&config.SkipTLS, parent.SkipTLS) setFallback(&config.Username, parent.Username) setFallback(&config.Password, parent.Password) + setFallback(&config.ApiKey, parent.ApiKey) setFallback(&config.Index, parent.Index, "policy-reporter") setFallback(&config.Rotation, parent.Rotation, elasticsearch.Daily) @@ -420,6 +421,7 @@ func (f *TargetFactory) createElasticsearchClient(config, parent *Elasticsearch) Host: config.Host, Username: config.Username, Password: config.Password, + ApiKey: config.ApiKey, Rotation: config.Rotation, Index: config.Index, CustomFields: config.CustomFields, @@ -822,6 +824,9 @@ func (f *TargetFactory) mapSecretValues(config any, ref, mountedSecret string) { if values.Password != "" { c.Password = values.Password } + if values.ApiKey != "" { + c.ApiKey = values.ApiKey + } case *S3: if values.AccessKeyID != "" { diff --git a/pkg/config/target_factory_test.go b/pkg/config/target_factory_test.go index 2d8287c0..1d772d2c 100644 --- a/pkg/config/target_factory_test.go +++ b/pkg/config/target_factory_test.go @@ -31,6 +31,7 @@ func newFakeClient() v1.SecretInterface { "host": []byte("http://localhost:9200"), "username": []byte("username"), "password": []byte("password"), + "apiKey": []byte("apiKey"), "webhook": []byte("http://localhost:9200/webhook"), "accessKeyID": []byte("accessKeyID"), "secretAccessKey": []byte("secretAccessKey"), @@ -49,6 +50,7 @@ func mountSecret() { Webhook: "http://localhost:9200/webhook", Username: "username", Password: "password", + ApiKey: "apiKey", AccessKeyID: "accessKeyId", SecretAccessKey: "secretAccessKey", KmsKeyID: "kmsKeyId", @@ -332,6 +334,11 @@ func Test_GetValuesFromSecret(t *testing.T) { if password != "password" { t.Errorf("Expected password from secret, got %s", password) } + + apiKey := client.FieldByName("apiKey").String() + if apiKey != "apiKey" { + t.Errorf("Expected apiKey from secret, got %s", apiKey) + } }) t.Run("Get Discord values from Secret", func(t *testing.T) { @@ -639,6 +646,11 @@ func Test_GetValuesFromMountedSecret(t *testing.T) { if password != "password" { t.Errorf("Expected password from mounted secret, got %s", password) } + + apiKey := client.FieldByName("apiKey").String() + if apiKey != "apiKey" { + t.Errorf("Expected apiKey from secret, got %s", apiKey) + } }) t.Run("Get Discord values from MountedSecret", func(t *testing.T) { diff --git a/pkg/kubernetes/secrets/client.go b/pkg/kubernetes/secrets/client.go index d10a304f..8ef329d4 100644 --- a/pkg/kubernetes/secrets/client.go +++ b/pkg/kubernetes/secrets/client.go @@ -16,6 +16,7 @@ type Values struct { Channel string `json:"channel,omitempty"` Username string `json:"username,omitempty"` Password string `json:"password,omitempty"` + ApiKey string `json:"apiKey,omitempty"` AccessKeyID string `json:"accessKeyID,omitempty"` SecretAccessKey string `json:"secretAccessKey,omitempty"` AccountID string `json:"accountID,omitempty"` @@ -87,6 +88,10 @@ func (c *k8sClient) Get(ctx context.Context, name string) (Values, error) { values.Password = string(password) } + if apiKey, ok := secret.Data["apiKey"]; ok { + values.ApiKey = string(apiKey) + } + if database, ok := secret.Data["database"]; ok { values.Database = string(database) } diff --git a/pkg/kubernetes/secrets/client_test.go b/pkg/kubernetes/secrets/client_test.go index c6d5d2fc..38c28df1 100644 --- a/pkg/kubernetes/secrets/client_test.go +++ b/pkg/kubernetes/secrets/client_test.go @@ -25,6 +25,7 @@ func newFakeClient() v1.SecretInterface { "host": []byte("http://localhost:9200"), "username": []byte("username"), "password": []byte("password"), + "apiKey": []byte("apiKey"), "webhook": []byte("http://localhost:9200/webhook"), "accessKeyID": []byte("accessKeyID"), "secretAccessKey": []byte("secretAccessKey"), @@ -62,6 +63,10 @@ func Test_Client(t *testing.T) { t.Errorf("Unexpected Password: %s", values.Password) } + if values.ApiKey != "apiKey" { + t.Errorf("Unexpected ApiKey: %s", values.ApiKey) + } + if values.AccessKeyID != "accessKeyID" { t.Errorf("Unexpected AccessKeyID: %s", values.AccessKeyID) } diff --git a/pkg/target/elasticsearch/elasticsearch.go b/pkg/target/elasticsearch/elasticsearch.go index 616b80d7..f2ab9ed7 100644 --- a/pkg/target/elasticsearch/elasticsearch.go +++ b/pkg/target/elasticsearch/elasticsearch.go @@ -14,6 +14,7 @@ type Options struct { Host string Username string Password string + ApiKey string Index string Rotation string CustomFields map[string]string @@ -37,6 +38,7 @@ type client struct { index string username string password string + apiKey string rotation Rotation customFields map[string]string client http.Client @@ -76,6 +78,8 @@ func (e *client) Send(result v1alpha2.PolicyReportResult) { if e.username != "" { req.SetBasicAuth(e.username, e.password) + } else if e.apiKey != "" { + req.Header.Add("Authorization", "ApiKey "+e.apiKey) } resp, err := e.client.Do(req) @@ -90,6 +94,7 @@ func NewClient(options Options) target.Client { options.Index, options.Username, options.Password, + options.ApiKey, options.Rotation, options.CustomFields, options.HTTPClient, diff --git a/pkg/target/elasticsearch/elasticsearch_test.go b/pkg/target/elasticsearch/elasticsearch_test.go index 4ea2b558..90fd136c 100644 --- a/pkg/target/elasticsearch/elasticsearch_test.go +++ b/pkg/target/elasticsearch/elasticsearch_test.go @@ -50,6 +50,7 @@ func Test_ElasticsearchTarget(t *testing.T) { Host: "http://localhost:9200", Username: "username", Password: "password", + ApiKey: "ApiKey", Index: "policy-reporter", Rotation: elasticsearch.Annually, HTTPClient: testClient{callback, 200},