diff --git a/Makefile b/Makefile index 43ef1c52..0465d4a3 100644 --- a/Makefile +++ b/Makefile @@ -154,7 +154,7 @@ ENVTEST ?= $(LOCALBIN)/setup-envtest ## Tool Versions KUSTOMIZE_VERSION ?= v3.8.7 -CONTROLLER_TOOLS_VERSION ?= v0.9.0 +CONTROLLER_TOOLS_VERSION ?= v0.14.0 KUSTOMIZE_INSTALL_SCRIPT ?= "https://raw.githubusercontent.com/kubernetes-sigs/kustomize/master/hack/install_kustomize.sh" .PHONY: kustomize diff --git a/api/v1alpha1/endpointmonitor_types.go b/api/v1alpha1/endpointmonitor_types.go index c8a2f70b..9fbda8cf 100644 --- a/api/v1alpha1/endpointmonitor_types.go +++ b/api/v1alpha1/endpointmonitor_types.go @@ -163,6 +163,10 @@ type StatusCakeConfig struct { // +optional BasicAuthUser string `json:"basicAuthUser,omitempty"` + // Basic Auth Secret Name + // +optional + BasicAuthSecret string `json:"basicAuthSecret,omitempty"` + // Set Check Rate for the monitor // +optional CheckRate int `json:"checkRate,omitempty"` diff --git a/charts/ingressmonitorcontroller/crds/endpointmonitor.stakater.com_endpointmonitors.yaml b/charts/ingressmonitorcontroller/crds/endpointmonitor.stakater.com_endpointmonitors.yaml index 0f984d44..8ba49a54 100644 --- a/charts/ingressmonitorcontroller/crds/endpointmonitor.stakater.com_endpointmonitors.yaml +++ b/charts/ingressmonitorcontroller/crds/endpointmonitor.stakater.com_endpointmonitors.yaml @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.9.0 + controller-gen.kubebuilder.io/version: v0.14.0 creationTimestamp: null name: endpointmonitors.endpointmonitor.stakater.com spec: @@ -255,6 +255,9 @@ spec: statusCakeConfig: description: Configuration for StatusCake Monitor Provider properties: + basicAuthSecret: + description: Basic Auth Secret Name + type: string basicAuthUser: description: Basic Auth User type: string diff --git a/config/crd/bases/endpointmonitor.stakater.com_endpointmonitors.yaml b/config/crd/bases/endpointmonitor.stakater.com_endpointmonitors.yaml index 0f984d44..8ba49a54 100644 --- a/config/crd/bases/endpointmonitor.stakater.com_endpointmonitors.yaml +++ b/config/crd/bases/endpointmonitor.stakater.com_endpointmonitors.yaml @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.9.0 + controller-gen.kubebuilder.io/version: v0.14.0 creationTimestamp: null name: endpointmonitors.endpointmonitor.stakater.com spec: @@ -255,6 +255,9 @@ spec: statusCakeConfig: description: Configuration for StatusCake Monitor Provider properties: + basicAuthSecret: + description: Basic Auth Secret Name + type: string basicAuthUser: description: Basic Auth User type: string diff --git a/docs/statuscake-configuration.md b/docs/statuscake-configuration.md index bd0c62bf..93490aba 100644 --- a/docs/statuscake-configuration.md +++ b/docs/statuscake-configuration.md @@ -16,15 +16,16 @@ Currently additional Statuscake configurations can be added through these fields |:--------------------------------------------------------:|:------------------------------------------------:| | CheckRate | Set Check Rate for the monitor (default: 300) | | TestType | Set Test type - HTTP, TCP, PING (default: HTTP) | -| Paused | Pause the service | +| Paused | Pause the service | | PingURL | Webhook for alerts | | FollowRedirect | Enable ingress redirects | -| Port | TCP Port | +| Port | TCP Port | | TriggerRate | Minutes to wait before sending an alert | | ContactGroup | Contact Group to be alerted. | | TestTags | Comma separated list of tags | | FindString | String to look for within the response | -| BasicAuthUser | Required for [basic-authenticationchecks](#basic-auth-checks) | +| BasicAuthUser | Required for [basic-authenticationchecks](#basic-auth-checks) | +| BasicAuthSecret | Allows for an alternate method of adding basic-auth to checks | | Regions | Regions to execute the check from | @@ -34,6 +35,10 @@ Statuscake supports checks completing basic auth requirements. In `EndpointMonit For example; setting the field like `basic-auth-user: 'my-service-username'` will set the username field to the value `my-service-username` and will retrieve the password via `os.Getenv('my-service-username')` and set this appropriately. +In addition to the previous method, you can use the `basicAuthSecret` field to define a secret that should be read by the monitor which contains the basic-auth data. This secret should only contain the keys `username` and `password`. It expects the values for those keys to be strings. NOT base64 encoded strings. Furthermore, the secret must be present in the same namespace as the IngressMonitorController operator. This ensures that we can keep the permissions of the operator to be as small as possible. + +So for example, if you have a secret called `my-deployment-secret` it should contain the data `username: my-user` and `password: MyPassword1!` and you should set to `basicAuthSecret: my-deployment-secret`. This will ensure that the monitor can read the basic-auth data correctly. + ## Example: ```yaml diff --git a/examples/endpointMonitor/statuscake-config.yaml b/examples/endpointMonitor/statuscake-config.yaml index bf4f64eb..54ab4994 100644 --- a/examples/endpointMonitor/statuscake-config.yaml +++ b/examples/endpointMonitor/statuscake-config.yaml @@ -7,6 +7,7 @@ spec: statusCakeConfig: port: 123 basicAuthUser: my-service-username + basicAuthSecret: my-basicauth-secret checkRate: 300 realBrowser: true testTags: 'abc,def' diff --git a/pkg/monitors/statuscake/statuscake-monitor.go b/pkg/monitors/statuscake/statuscake-monitor.go index 0ed9b13e..88f6d071 100644 --- a/pkg/monitors/statuscake/statuscake-monitor.go +++ b/pkg/monitors/statuscake/statuscake-monitor.go @@ -17,7 +17,9 @@ import ( statuscake "github.com/StatusCakeDev/statuscake-go" endpointmonitorv1alpha1 "github.com/stakater/IngressMonitorController/v2/api/v1alpha1" "github.com/stakater/IngressMonitorController/v2/pkg/config" + "github.com/stakater/IngressMonitorController/v2/pkg/kube" "github.com/stakater/IngressMonitorController/v2/pkg/models" + "github.com/stakater/IngressMonitorController/v2/pkg/secret" ) var log = logf.Log.WithName("statuscake-monitor") @@ -100,6 +102,24 @@ func buildUpsertForm(m models.Monitor, cgroup string) url.Values { } } + if providerConfig != nil && len(providerConfig.BasicAuthSecret) > 0 { + k8sClient, err := kube.GetClient() + if err != nil { + panic(err) + } + + namespace := kube.GetCurrentKubernetesNamespace() + username, password, err := secret.ReadBasicAuthSecret(k8sClient.CoreV1().Secrets(namespace), providerConfig.BasicAuthSecret) + + if err != nil { + log.Error(err, "Could not read the secret") + } else { + f.Add("basic_username", username) + f.Add("basic_password", password) + log.Info("Basic auth requirement detected. Setting username and password") + } + } + if providerConfig != nil && len(providerConfig.StatusCodes) > 0 { f.Add("status_codes_csv", providerConfig.StatusCodes) diff --git a/pkg/secret/secrets.go b/pkg/secret/secrets.go index 704a7167..872cf1be 100644 --- a/pkg/secret/secrets.go +++ b/pkg/secret/secrets.go @@ -5,7 +5,9 @@ import ( "fmt" corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" + clientv1 "k8s.io/client-go/kubernetes/typed/core/v1" "sigs.k8s.io/controller-runtime/pkg/client" ) @@ -23,3 +25,32 @@ func LoadSecretData(apiReader client.Reader, secretName, namespace, dataKey stri } return string(retStr), nil } + +func ReadBasicAuthSecret(apiReader clientv1.SecretInterface, secretName string) (string, string, error) { + secret, err := apiReader.Get(context.TODO(), secretName, metav1.GetOptions{}) + username, password := "", "" + if err != nil { + return "", "", err + } + + for key, value := range secret.Data { + switch key { + case "username": + username = string(value) + case "password": + password = string(value) + default: + return "", "", fmt.Errorf("secret %s contained unkown key %s", secretName, key) + } + } + + if username == "" { + return "", "", fmt.Errorf("secret %s does not contain expected key '%s'", secretName, "username") + } else if password == "" { + return "", "", fmt.Errorf("secret %s does not contain expected key '%s'", secretName, "password") + } else if username == "" && password == "" { + return "", "", fmt.Errorf("secret %s does not contain expected keys '%s','%s'", secretName, "username", "password") + } + + return username, password, err +}