Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

UX: implement rotating keys api in ux-backend server #2469

Merged
merged 2 commits into from
Mar 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -3294,6 +3294,10 @@ spec:
- name: ONBOARDING_TOKEN_LIFETIME
- name: UX_BACKEND_PORT
- name: TLS_ENABLED
- name: POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
image: quay.io/ocs-dev/ocs-operator:latest
imagePullPolicy: IfNotPresent
name: ux-backend-server
Expand Down
8 changes: 8 additions & 0 deletions deploy/ocs-operator/manifests/ux_backend_role.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,11 @@ rules:
verbs:
- get
- list
- apiGroups:
- ""
resources:
- secrets
resourceNames:
- onboarding-ticket-key
verbs:
- delete
8 changes: 8 additions & 0 deletions rbac/ux_backend_role.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,11 @@ rules:
verbs:
- get
- list
- apiGroups:
- ""
resources:
- secrets
resourceNames:
- onboarding-ticket-key
verbs:
- delete
23 changes: 23 additions & 0 deletions services/ux-backend/handlers/common.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package handlers

import (
"os"
)

const (
ContentTypeTextPlain = "text/plain"
)

var namespace string

// returns namespace found in env value, will panic if value is empty
func GetPodNamespace() string {
if namespace != "" {
return namespace
}
if ns := os.Getenv("POD_NAMESPACE"); ns != "" {
namespace = ns
return namespace
}
panic("Value for env var 'POD_NAMESPACE' is empty")
}
8 changes: 4 additions & 4 deletions services/ux-backend/handlers/onboardingtokens/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -37,15 +37,15 @@ 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)
}
} 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)
Expand All @@ -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 {
Expand Down
62 changes: 62 additions & 0 deletions services/ux-backend/handlers/rotatekeys/handler.go
Original file line number Diff line number Diff line change
@@ -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 (
nb-ohad marked this conversation as resolved.
Show resolved Hide resolved
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.GetPodNamespace()
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)
}
}
27 changes: 27 additions & 0 deletions services/ux-backend/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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")
Expand All @@ -61,9 +78,19 @@ func main() {
klog.Info("shutting down!")
os.Exit(-1)
leelavg marked this conversation as resolved.
Show resolved Hide resolved
}

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)

Expand Down
8 changes: 8 additions & 0 deletions tools/csv-merger/csv-merger.go
Original file line number Diff line number Diff line change
Expand Up @@ -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",
},
},
},
},
},
{
Expand Down
Loading