Skip to content

Commit

Permalink
ux: implement rotating keys api
Browse files Browse the repository at this point in the history
Implement rotating keys by deleting the secret containing onboarding
public key.

The expectation is this new api endpoint be called via CLI or UI and
get the existing private & public key pairs rotated.

Signed-off-by: Leela Venkaiah G <[email protected]>
  • Loading branch information
leelavg committed Mar 7, 2024
1 parent 2d082fc commit 529643b
Show file tree
Hide file tree
Showing 6 changed files with 132 additions and 4 deletions.
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 (
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)
}
}
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)
}

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

0 comments on commit 529643b

Please sign in to comment.