From 94e65db6073c7996748b5740972bcabc98d459f5 Mon Sep 17 00:00:00 2001 From: Leela Venkaiah G Date: Tue, 20 Feb 2024 17:53:46 +0530 Subject: [PATCH] ux: implement rotating keys api Signed-off-by: Leela Venkaiah G --- rbac/ux_backend_role.yaml | 8 +++ services/ux-backend/handlers/constants.go | 10 +++ .../handlers/onboardingtokens/handler.go | 8 +-- .../ux-backend/handlers/rotatekeys/handler.go | 62 +++++++++++++++++++ services/ux-backend/handlers/utils.go | 20 ++++++ services/ux-backend/main.go | 27 ++++++++ tools/csv-merger/csv-merger.go | 8 +++ 7 files changed, 139 insertions(+), 4 deletions(-) create mode 100644 services/ux-backend/handlers/constants.go create mode 100644 services/ux-backend/handlers/rotatekeys/handler.go create mode 100644 services/ux-backend/handlers/utils.go diff --git a/rbac/ux_backend_role.yaml b/rbac/ux_backend_role.yaml index f89b32672e..5f1bbd7f9d 100644 --- a/rbac/ux_backend_role.yaml +++ b/rbac/ux_backend_role.yaml @@ -14,3 +14,11 @@ rules: verbs: - get - list +- apiGroups: + - "" + resources: + - secrets + resourceNames: + - onboarding-ticket-key + verbs: + - delete diff --git a/services/ux-backend/handlers/constants.go b/services/ux-backend/handlers/constants.go new file mode 100644 index 0000000000..65ec2276e1 --- /dev/null +++ b/services/ux-backend/handlers/constants.go @@ -0,0 +1,10 @@ +package handlers + +// this file contains that constants that are common across all handlers and utils + +const ( + ContentTypeTextPlain = "text/plain" + + // local to the package + podNamespaceEnvVar = "POD_NAMESPACE" +) diff --git a/services/ux-backend/handlers/onboardingtokens/handler.go b/services/ux-backend/handlers/onboardingtokens/handler.go index bef1c5e044..8560b8428c 100644 --- a/services/ux-backend/handlers/onboardingtokens/handler.go +++ b/services/ux-backend/handlers/onboardingtokens/handler.go @@ -16,12 +16,12 @@ import ( "github.com/google/uuid" "github.com/red-hat-storage/ocs-operator/v4/services" + "github.com/red-hat-storage/ocs-operator/v4/services/ux-backend/handlers" "k8s.io/klog/v2" ) const ( onboardingPrivateKeyFilePath = "/etc/private-key/key" - ContentTypeTextPlain = "text/plain" ) func HandleMessage(w http.ResponseWriter, r *http.Request, tokenLifetimeInHours int) { @@ -37,7 +37,7 @@ func handlePost(w http.ResponseWriter, tokenLifetimeInHours int) { if onboardingToken, err := generateOnboardingToken(tokenLifetimeInHours); err != nil { klog.Errorf("failed to get onboardig token: %v", err) w.WriteHeader(http.StatusInternalServerError) - w.Header().Set("Content-Type", ContentTypeTextPlain) + w.Header().Set("Content-Type", handlers.ContentTypeTextPlain) if _, err := w.Write([]byte("Failed to generate token")); err != nil { klog.Errorf("failed write data to response writer, %v", err) @@ -45,7 +45,7 @@ func handlePost(w http.ResponseWriter, tokenLifetimeInHours int) { } else { klog.Info("onboarding token generated successfully") w.WriteHeader(http.StatusOK) - w.Header().Set("Content-Type", ContentTypeTextPlain) + w.Header().Set("Content-Type", handlers.ContentTypeTextPlain) if _, err = w.Write([]byte(onboardingToken)); err != nil { klog.Errorf("failed write data to response writer: %v", err) @@ -56,7 +56,7 @@ func handlePost(w http.ResponseWriter, tokenLifetimeInHours int) { func handleUnsupportedMethod(w http.ResponseWriter, r *http.Request) { klog.Info("Only POST method should be used to send data to this endpoint /onboarding-tokens") w.WriteHeader(http.StatusMethodNotAllowed) - w.Header().Set("Content-Type", ContentTypeTextPlain) + w.Header().Set("Content-Type", handlers.ContentTypeTextPlain) w.Header().Set("Allow", "POST") if _, err := w.Write([]byte(fmt.Sprintf("Unsupported method : %s", r.Method))); err != nil { diff --git a/services/ux-backend/handlers/rotatekeys/handler.go b/services/ux-backend/handlers/rotatekeys/handler.go new file mode 100644 index 0000000000..fe87ebd9c6 --- /dev/null +++ b/services/ux-backend/handlers/rotatekeys/handler.go @@ -0,0 +1,62 @@ +package rotatekeys + +import ( + "context" + "fmt" + "net/http" + + "github.com/red-hat-storage/ocs-operator/v4/services/ux-backend/handlers" + corev1 "k8s.io/api/core/v1" + "k8s.io/klog/v2" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +const ( + onboardingValidationPublicKeySecretName = "onboarding-ticket-key" +) + +func HandleMessage(w http.ResponseWriter, r *http.Request, cl client.Client) { + switch r.Method { + case "POST": + handlePost(r.Context(), w, cl) + default: + handleUnsupportedMethod(w, r) + } +} + +func handlePost(ctx context.Context, w http.ResponseWriter, cl client.Client) { + klog.Info("POST method on /rotate-keys endpoint is invoked") + w.Header().Set("Content-Type", handlers.ContentTypeTextPlain) + + publicKeySecret := &corev1.Secret{} + publicKeySecret.Name = onboardingValidationPublicKeySecretName + publicKeySecret.Namespace = handlers.GetOperatorNamespace() + err := cl.Delete(ctx, publicKeySecret) + if err != nil { + klog.Errorf("failed to delete public key secret: %v", err) + w.WriteHeader(http.StatusInternalServerError) + + // TODO: should we differentiate b/n secret not found and remaining errors? + if _, err = w.Write([]byte("Failed to rotate keys")); err != nil { + klog.Errorf("failed to write data to response writer, %v", err) + } + return + } + + klog.Info("onboarding validation keys are rotated successfully") + w.WriteHeader(http.StatusOK) + if _, err = w.Write([]byte("Successfully rotated keys")); err != nil { + klog.Errorf("failed to write data to response writer, %v", err) + } +} + +func handleUnsupportedMethod(w http.ResponseWriter, r *http.Request) { + klog.Info("Only POST method should be used to send data to this endpoint /rotate-keys") + w.WriteHeader(http.StatusMethodNotAllowed) + w.Header().Set("Content-Type", handlers.ContentTypeTextPlain) + w.Header().Set("Allow", "POST") + + if _, err := w.Write([]byte(fmt.Sprintf("Unsupported method : %s", r.Method))); err != nil { + klog.Errorf("failed to write data to response writer: %v", err) + } +} diff --git a/services/ux-backend/handlers/utils.go b/services/ux-backend/handlers/utils.go new file mode 100644 index 0000000000..30c5729075 --- /dev/null +++ b/services/ux-backend/handlers/utils.go @@ -0,0 +1,20 @@ +package handlers + +import ( + "fmt" + "os" +) + +var namespace string + +// returns namespace found in env value, will panic if value is empty +func GetOperatorNamespace() string { + if namespace != "" { + return namespace + } + if ns := os.Getenv(podNamespaceEnvVar); ns != "" { + namespace = ns + return namespace + } + panic(fmt.Sprintf("Value for env var %q is empty", podNamespaceEnvVar)) +} \ No newline at end of file diff --git a/services/ux-backend/main.go b/services/ux-backend/main.go index 5651379225..2ff778d812 100644 --- a/services/ux-backend/main.go +++ b/services/ux-backend/main.go @@ -10,6 +10,9 @@ import ( "k8s.io/klog/v2" "github.com/red-hat-storage/ocs-operator/v4/services/ux-backend/handlers/onboardingtokens" + "github.com/red-hat-storage/ocs-operator/v4/services/ux-backend/handlers/rotatekeys" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/client/config" ) type serverConfig struct { @@ -51,6 +54,20 @@ func loadAndValidateServerConfig() (*serverConfig, error) { return &config, nil } +func newKubeClient() (client.Client, error) { + cfg, err := config.GetConfig() + if err != nil { + return nil, err + } + + newClient, err := client.New(cfg, client.Options{}) + if err != nil { + return nil, err + } + + return newClient, nil +} + func main() { klog.Info("Starting ux backend server") @@ -61,9 +78,19 @@ func main() { klog.Info("shutting down!") os.Exit(-1) } + + cl, err := newKubeClient() + if err != nil { + klog.Errorf("failed to create kubernetes api client: %v", err) + klog.Exit("shutting down!") + } + http.HandleFunc("/onboarding-tokens", func(w http.ResponseWriter, r *http.Request) { onboardingtokens.HandleMessage(w, r, config.tokenLifetimeInHours) }) + http.HandleFunc("/rotate-keys", func(w http.ResponseWriter, r *http.Request) { + rotatekeys.HandleMessage(w, r, cl) + }) klog.Info("ux backend server listening on port ", config.listenPort) diff --git a/tools/csv-merger/csv-merger.go b/tools/csv-merger/csv-merger.go index 11539086f4..1e2222b219 100644 --- a/tools/csv-merger/csv-merger.go +++ b/tools/csv-merger/csv-merger.go @@ -980,6 +980,14 @@ func getUXBackendServerDeployment() appsv1.DeploymentSpec { Name: "TLS_ENABLED", Value: os.Getenv("TLS_ENABLED"), }, + { + Name: "POD_NAMESPACE", + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "metadata.namespace", + }, + }, + }, }, }, {