Skip to content

Commit

Permalink
Read secrets for onboarding-token validation
Browse files Browse the repository at this point in the history
Signed-off-by: mrudraia <mrudraia@redhat.com>

Signed-off-by: mrudraia <mrudraia@redhat.com>

Signed-off-by: mrudraia <mrudraia@redhat.com>

Signed-off-by: mrudraia <mrudraia@redhat.com>

Signed-off-by: mrudraia <mrudraia@redhat.com>

Signed-off-by: mrudraia <mrudraia@redhat.com>

Signed-off-by: mrudraia <mrudraia@redhat.com>

Signed-off-by: mrudraia <mrudraia@redhat.com>

Signed-off-by: mrudraia <mrudraia@redhat.com>

Signed-off-by: mrudraia <mrudraia@redhat.com>

Signed-off-by: mrudraia <mrudraia@redhat.com>

Signed-off-by: mrudraia <mrudraia@redhat.com>

Signed-off-by: mrudraia <mrudraia@redhat.com>

Signed-off-by: mrudraia <mrudraia@redhat.com>

Signed-off-by: mrudraia <mrudraia@redhat.com>

Signed-off-by: mrudraia <mrudraia@redhat.com>

Signed-off-by: mrudraia <mrudraia@redhat.com>

Signed-off-by: mrudraia <mrudraia@redhat.com>

Signed-off-by: mrudraia <mrudraia@redhat.com>

Signed-off-by: mrudraia <mrudraia@redhat.com>

Signed-off-by: mrudraia <mrudraia@redhat.com>

Signed-off-by: mrudraia <mrudraia@redhat.com>
  • Loading branch information
mrudraia1 committed Aug 19, 2024
1 parent a7bd59e commit 859cfef
Show file tree
Hide file tree
Showing 6 changed files with 121 additions and 57 deletions.
45 changes: 42 additions & 3 deletions controllers/storagecluster/storageclient.go
Original file line number Diff line number Diff line change
@@ -1,19 +1,26 @@
package storagecluster

import (
"context"
"crypto/rsa"
"crypto/x509"
"encoding/pem"
"fmt"

ocsclientv1a1 "github.com/red-hat-storage/ocs-client-operator/api/v1alpha1"
ocsv1 "github.com/red-hat-storage/ocs-operator/api/v4/v1"
"github.com/red-hat-storage/ocs-operator/v4/controllers/util"
corev1 "k8s.io/api/core/v1"
kerrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/klog/v2"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
)

const (
tokenLifetimeInHours = 48
onboardingPrivateKeyFilePath = "/etc/private-key/key"
tokenLifetimeInHours = 48
onboardingValidationPrivateKeySecretName = "onboarding-private-key"
)

type storageClient struct{}
Expand All @@ -27,11 +34,17 @@ func (s *storageClient) ensureCreated(r *StorageClusterReconciler, storagecluste
return s.ensureDeleted(r, storagecluster)
}

privateKey, err1 := getPrivateKey(r.Client)
if err1 != nil {
r.Log.Error(err1, "Unable to get privatekey")
return reconcile.Result{}, nil
}

storageClient := &ocsclientv1a1.StorageClient{}
storageClient.Name = storagecluster.Name
_, err := controllerutil.CreateOrUpdate(r.ctx, r.Client, storageClient, func() error {
if storageClient.Status.ConsumerID == "" {
token, err := util.GenerateOnboardingToken(tokenLifetimeInHours, onboardingPrivateKeyFilePath, nil)
token, err := util.GenerateOnboardingToken(tokenLifetimeInHours, privateKey, nil)
if err != nil {
return fmt.Errorf("unable to generate onboarding token: %v", err)
}
Expand Down Expand Up @@ -59,3 +72,29 @@ func (s *storageClient) ensureDeleted(r *StorageClusterReconciler, storagecluste
}
return reconcile.Result{}, nil
}

func getPrivateKey(cl client.Client) (*rsa.PrivateKey, error) {
klog.Info("Getting the Pem key")
ctx := context.Background()
operatorNamespace, err := util.GetOperatorNamespace()
if err != nil {
return nil, fmt.Errorf("unable to get operator namespace: %v", err)
}

privateSecret := &corev1.Secret{}
privateSecret.Name = onboardingValidationPrivateKeySecretName
privateSecret.Namespace = operatorNamespace

err = cl.Get(ctx, client.ObjectKeyFromObject(privateSecret), privateSecret)
if err != nil {
return nil, fmt.Errorf("failed to get private secret: %v", err)
}

Block, _ := pem.Decode(privateSecret.Data["key"])
privateKey, err := x509.ParsePKCS1PrivateKey(Block.Bytes)
if err != nil {
return nil, fmt.Errorf("failed to parse private key: %v", err)
}

return privateKey, nil
}
25 changes: 1 addition & 24 deletions controllers/util/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,18 @@ import (
"crypto/rand"
"crypto/rsa"
"crypto/sha256"
"crypto/x509"
"encoding/base64"
"encoding/json"
"encoding/pem"
"fmt"
"os"
"time"

"github.com/google/uuid"
"github.com/red-hat-storage/ocs-operator/v4/services"
)

// GenerateOnboardingToken generates a token valid for a duration of "tokenLifetimeInHours".
// The token content is predefined and signed by the private key which'll be read from supplied "privateKeyPath".
// The storageQuotaInGiB is optional, and it is used to limit the storage of PVC in the application cluster.
func GenerateOnboardingToken(tokenLifetimeInHours int, privateKeyPath string, storageQuotaInGiB *uint) (string, error) {
func GenerateOnboardingToken(tokenLifetimeInHours int, privateKey *rsa.PrivateKey, storageQuotaInGiB *uint) (string, error) {
tokenExpirationDate := time.Now().
Add(time.Duration(tokenLifetimeInHours) * time.Hour).
Unix()
Expand All @@ -46,11 +42,6 @@ func GenerateOnboardingToken(tokenLifetimeInHours int, privateKeyPath string, st
return "", fmt.Errorf("failed to hash onboarding token payload: %v", err)
}

privateKey, err := readAndDecodePrivateKey(privateKeyPath)
if err != nil {
return "", fmt.Errorf("failed to read and decode private key: %v", err)
}

msgHashSum := msgHash.Sum(nil)
// In order to generate the signature, we provide a random number generator,
// our private key, the hashing algorithm that we used, and the hash sum
Expand All @@ -63,17 +54,3 @@ func GenerateOnboardingToken(tokenLifetimeInHours int, privateKeyPath string, st
encodedSignature := base64.StdEncoding.EncodeToString(signature)
return fmt.Sprintf("%s.%s", encodedPayload, encodedSignature), nil
}

func readAndDecodePrivateKey(privateKeyPath string) (*rsa.PrivateKey, error) {
pemString, err := os.ReadFile(privateKeyPath)
if err != nil {
return nil, fmt.Errorf("failed to read private key: %v", err)
}

Block, _ := pem.Decode(pemString)
privateKey, err := x509.ParsePKCS1PrivateKey(Block.Bytes)
if err != nil {
return nil, fmt.Errorf("failed to parse private key: %v", err)
}
return privateKey, nil
}
Original file line number Diff line number Diff line change
Expand Up @@ -717,8 +717,6 @@ spec:
readOnlyRootFilesystem: true
runAsNonRoot: true
volumeMounts:
- mountPath: /etc/private-key
name: onboarding-private-key
- mountPath: /etc/tls/private
name: ux-cert-secret
- args:
Expand Down Expand Up @@ -754,10 +752,6 @@ spec:
operator: Equal
value: "true"
volumes:
- name: onboarding-private-key
secret:
optional: true
secretName: onboarding-private-key
- name: ux-proxy-secret
secret:
secretName: ux-backend-proxy
Expand Down
15 changes: 7 additions & 8 deletions services/ux-backend/handlers/onboardingtokens/handler.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package onboardingtokens

import (
"crypto/rsa"
"encoding/json"
"fmt"
"math"
Expand All @@ -12,30 +13,27 @@ import (
"k8s.io/utils/ptr"
)

const (
onboardingPrivateKeyFilePath = "/etc/private-key/key"
)

var unitToGib = map[string]uint{
"Gi": 1,
"Ti": 1024,
"Pi": 1024 * 1024,
}

func HandleMessage(w http.ResponseWriter, r *http.Request, tokenLifetimeInHours int) {
func HandleMessage(w http.ResponseWriter, r *http.Request, tokenLifetimeInHours int, privateKey *rsa.PrivateKey) {
switch r.Method {
case "POST":
handlePost(w, r, tokenLifetimeInHours)
handlePost(w, r, tokenLifetimeInHours, privateKey)
default:
handleUnsupportedMethod(w, r)
}
}

func handlePost(w http.ResponseWriter, r *http.Request, tokenLifetimeInHours int) {
func handlePost(w http.ResponseWriter, r *http.Request, tokenLifetimeInHours int, privateKey *rsa.PrivateKey) {
var storageQuotaInGiB *uint
// When ContentLength is 0 that means request body is empty and
// storage quota is unlimited
var err error

if r.ContentLength != 0 {
var quota = struct {
Value uint `json:"value"`
Expand All @@ -57,7 +55,8 @@ func handlePost(w http.ResponseWriter, r *http.Request, tokenLifetimeInHours int
}
storageQuotaInGiB = ptr.To(unitAsGiB * quota.Value)
}
if onboardingToken, err := util.GenerateOnboardingToken(tokenLifetimeInHours, onboardingPrivateKeyFilePath, storageQuotaInGiB); err != nil {

if onboardingToken, err := util.GenerateOnboardingToken(tokenLifetimeInHours, privateKey, storageQuotaInGiB); err != nil {
klog.Errorf("failed to get onboardig token: %v", err)
w.WriteHeader(http.StatusInternalServerError)
w.Header().Set("Content-Type", handlers.ContentTypeTextPlain)
Expand Down
74 changes: 71 additions & 3 deletions services/ux-backend/main.go
Original file line number Diff line number Diff line change
@@ -1,18 +1,30 @@
package main

import (
"context"
"crypto/rsa"
"crypto/x509"
"encoding/pem"
"fmt"
"log"
"net/http"
"os"
"strconv"

"k8s.io/klog/v2"

"github.com/red-hat-storage/ocs-operator/v4/controllers/util"
"github.com/red-hat-storage/ocs-operator/v4/services/ux-backend/handlers/onboardingtokens"
v1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/klog/v2"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/client/config"
)

const onboardingValidationPrivateKeySecretName = "onboarding-private-key"

type serverConfig struct {
client.Client
listenPort int
tokenLifetimeInHours int
tlsEnabled bool
Expand Down Expand Up @@ -61,8 +73,19 @@ func main() {
klog.Info("shutting down!")
os.Exit(-1)
}

cl, err := newClient()
if err != nil {
klog.Exitf("failed to create client: %v", err)
}

privateKey, err := readPrivateKey(cl)
if err != nil {
klog.Exitf("failed to get privatekey: %v", err)
}

http.HandleFunc("/onboarding-tokens", func(w http.ResponseWriter, r *http.Request) {
onboardingtokens.HandleMessage(w, r, config.tokenLifetimeInHours)
onboardingtokens.HandleMessage(w, r, config.tokenLifetimeInHours, privateKey)
})

klog.Info("ux backend server listening on port ", config.listenPort)
Expand All @@ -82,3 +105,48 @@ func main() {
log.Fatal(err)

}

func newClient() (client.Client, error) {
klog.Info("Setting up k8s client")
scheme := runtime.NewScheme()
if err := v1.AddToScheme(scheme); err != nil {
return nil, err
}

config, err := config.GetConfig()
if err != nil {
return nil, err
}
k8sClient, err := client.New(config, client.Options{Scheme: scheme})
if err != nil {
return nil, err
}

return k8sClient, nil
}

func readPrivateKey(cl client.Client) (*rsa.PrivateKey, error) {
klog.Info("Getting the Pem key")
ctx := context.Background()
operatorNamespace, err := util.GetOperatorNamespace()
if err != nil {
return nil, fmt.Errorf("unable to get operator namespace: %v", err)
}

privateSecret := &corev1.Secret{}
privateSecret.Name = onboardingValidationPrivateKeySecretName
privateSecret.Namespace = operatorNamespace

err = cl.Get(ctx, client.ObjectKeyFromObject(privateSecret), privateSecret)
if err != nil {
return nil, fmt.Errorf("failed to get private secret: %v", err)
}

Block, _ := pem.Decode(privateSecret.Data["key"])
privateKey, err := x509.ParsePKCS1PrivateKey(Block.Bytes)
if err != nil {
return nil, fmt.Errorf("failed to parse private key: %v", err)
}

return privateKey, nil
}
13 changes: 0 additions & 13 deletions tools/csv-merger/csv-merger.go
Original file line number Diff line number Diff line change
Expand Up @@ -644,10 +644,6 @@ func getUXBackendServerDeployment() appsv1.DeploymentSpec {
{
Name: "ux-backend-server",
VolumeMounts: []corev1.VolumeMount{
{
Name: "onboarding-private-key",
MountPath: "/etc/private-key",
},
{
Name: "ux-cert-secret",
MountPath: "/etc/tls/private",
Expand Down Expand Up @@ -716,15 +712,6 @@ func getUXBackendServerDeployment() appsv1.DeploymentSpec {
},
},
Volumes: []corev1.Volume{
{
Name: "onboarding-private-key",
VolumeSource: corev1.VolumeSource{
Secret: &corev1.SecretVolumeSource{
SecretName: "onboarding-private-key",
Optional: ptr.To(true),
},
},
},
{
Name: "ux-proxy-secret",
VolumeSource: corev1.VolumeSource{
Expand Down

0 comments on commit 859cfef

Please sign in to comment.