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

Read secrets for onboarding-token validation #2715

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

Mrudraia
Copy link

This PR reads the secrets instead of reading the secrets from the volume mounts.
whenever the new onboarding secrets are created, it takes more time to read the secrets from the volume mounts,
The user clicks the rotate onboarding keys, the kubernetes still uses the old public, private keys , the new keys are mounted later, So this PR will read the secrets directly from the kubernetes secrets.

@openshift-ci openshift-ci bot added the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Jul 29, 2024
Copy link
Contributor

openshift-ci bot commented Jul 29, 2024

Skipping CI for Draft Pull Request.
If you want CI signal for your change, please convert it to an actual PR.
You can still manually trigger a test run with /test all

@Mrudraia Mrudraia marked this pull request as ready for review July 29, 2024 14:47
@openshift-ci openshift-ci bot removed the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Jul 29, 2024
privateSecret := &corev1.Secret{}
privateSecret.Name = onboardingValidationPrivateKeySecretName
privateSecret.Namespace = operatorNamespace
err = cl.Get(ctx, types.NamespacedName{Name: storageClusterName, Namespace: operatorNamespace}, privateSecret)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do we need to get the storageClusterName?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

storageClusterName is removed


pubKeyBytes := privateSecret.Data["key"]

Block, _ := pem.Decode(pubKeyBytes)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pem.Decode(privateSecret.Data["key"])

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

resolved

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

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think clientgoscheme already registers Kube native APIs. Let's try without the scheme.

@rchikatw
Copy link
Contributor

Do you think it would be a good idea to remove the mounted secret from the ocs-operator? The mounted secret is used to create the default client. @leelavg

@leelavg
Copy link
Contributor

leelavg commented Jul 30, 2024

Do you think it would be a good idea to remove the mounted secret from the ocs-operator? The mounted secret is used to create the default client. @leelavg

  • Sure, I see no point in having two implementations for the same requirement.

@Mrudraia Mrudraia force-pushed the onboarding branch 5 times, most recently from 60022a5 to a98859f Compare August 2, 2024 08:03
@rchikatw
Copy link
Contributor

rchikatw commented Aug 2, 2024

/retest

@rchikatw
Copy link
Contributor

rchikatw commented Aug 2, 2024

/test go test

Copy link
Contributor

openshift-ci bot commented Aug 2, 2024

@rchikatw: The specified target(s) for /test were not found.
The following commands are available to trigger required jobs:

  • /test ci-bundle-ocs-operator-bundle
  • /test images
  • /test ocs-operator-bundle-e2e-aws

Use /test all to run all jobs.

In response to this:

/test go test

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository.

@rchikatw
Copy link
Contributor

rchikatw commented Aug 2, 2024

/test all

Comment on lines 80 to 91
cl, err := newClient()
if err != nil {
return nil, fmt.Errorf("failed to read private key: %v", err)
klog.Exitf("failed to create client: %v", err)
}
ctx := context.Background()
operatorNamespace, err := GetOperatorNamespace()
if err != nil {
klog.Exitf("unable to get operator namespace: %v", err)
}

Block, _ := pem.Decode(pemString)
privateSecret := &corev1.Secret{}
privateSecret.Name = onboardingValidationPrivateKeySecretName
privateSecret.Namespace = operatorNamespace
err1 := cl.Get(ctx, types.NamespacedName{Name: onboardingValidationPrivateKeySecretName, Namespace: operatorNamespace}, privateSecret)
if err1 != nil {
return nil, fmt.Errorf("failed to get private secret: %v", err1)
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. There should be no need to create a new client. In most cases this code is being used there is already a client that is used on the call sire of GenerateOnboardingToken. It is important to use that client instead of the new client for many reasons, some of which are:
  • The client is a cached client and the object is already loaded
  • The client is a mock/testing client used for testing and does not execute to a valid k8s API server
  1. I am not sure you are taking the right approach by trying to load the secret here. This code path was and should be a compute-only code path, this means it will benefit us to not introduce any k8s-specific concerns into it. My approach will be to load the secret outside of the GenerateOnboardingToken util and provide the private key payload an argument

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. Used existing client on the call site on GenerateOnboardingToken,

  2. The secrets are loaded in a separate function and the private key is decoded and passed to the GenerateOnboardingToken

Copy link
Contributor

openshift-ci bot commented Aug 5, 2024

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: Mrudraia
Once this PR has been reviewed and has the lgtm label, please ask for approval from nb-ohad. For more information see the Kubernetes Code Review Process.

The full list of commands accepted by this bot can be found here.

Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@Mrudraia Mrudraia force-pushed the onboarding branch 7 times, most recently from 52d15b8 to c72f700 Compare August 7, 2024 11:52
@rchikatw
Copy link
Contributor

rchikatw commented Aug 9, 2024

i see the rebase is not done properly. Can you rebase correctly? Basically, I see 2 commits and one of the commits is unrelated to your changes.

@@ -36,6 +33,8 @@ func handlePost(w http.ResponseWriter, r *http.Request, tokenLifetimeInHours int
// When ContentLength is 0 that means request body is empty and
// storage quota is unlimited
var err error
var cl client.Client
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

where is the kubeclient initialized?

"sigs.k8s.io/controller-runtime/pkg/reconcile"
"sigs.k8s.io/yaml"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why is this pkg being removed and different pkg added?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change is not mine, It accured due to improper rebasing

Comment on lines 30 to 19
// 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.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pls change or remove irrelevant parts of the comments.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed the irrelevant comments

@@ -64,16 +74,28 @@ func GenerateOnboardingToken(tokenLifetimeInHours int, privateKeyPath string, st
return fmt.Sprintf("%s.%s", encodedPayload, encodedSignature), nil
}

func readAndDecodePrivateKey(privateKeyPath string) (*rsa.PrivateKey, error) {
pemString, err := os.ReadFile(privateKeyPath)
func DecodePemKey(cl client.Client) (*rsa.PrivateKey, error) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does this func needs to be exported?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function not to be exported, changed the function

@Mrudraia Mrudraia force-pushed the onboarding branch 2 times, most recently from e84e5e2 to e9c31d4 Compare August 9, 2024 08:28
Comment on lines 38 to 41
client, err := newClient()
if err != nil {
klog.Errorf("failed to create new client. %v", err)
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you shouldn't be creating kubeclient for every request, create it in main and use that.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Mrudraia I believe main already creates a client and some other endpoints accept that client. Can you please follow that pattern

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Acknowledge

privateSecret.Name = onboardingValidationPrivateKeySecretName
privateSecret.Namespace = operatorNamespace

err = cl.Get(ctx, types.NamespacedName{Name: onboardingValidationPrivateKeySecretName, Namespace: operatorNamespace}, privateSecret)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you have already set ObjectMeta on privateSecret and could do

Suggested change
err = cl.Get(ctx, types.NamespacedName{Name: onboardingValidationPrivateKeySecretName, Namespace: operatorNamespace}, privateSecret)
err = cl.Get(ctx, client.ObjectKeyFromObject(privateSecret), privateSecret)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ack

pemString, err := os.ReadFile(privateKeyPath)
func decodePemKey(cl client.Client) (*rsa.PrivateKey, error) {
klog.Info("Decoding the Pem key")
ctx := context.Background()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

better to get the context from caller rather than creating one here everytime.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

@@ -86,3 +90,19 @@ func handleUnsupportedMethod(w http.ResponseWriter, r *http.Request) {
klog.Errorf("failed write data to response writer: %v", err)
}
}

func newClient() (client.Client, error) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is a handler and possibly creation of client shouldn't be here.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ack, removed the code in handler.go and as suggested called from the main.

Comment on lines 84 to 88
privateSecret := &corev1.Secret{}
privateSecret.Name = onboardingValidationPrivateKeySecretName
privateSecret.Namespace = operatorNamespace

err = cl.Get(ctx, types.NamespacedName{Name: onboardingValidationPrivateKeySecretName, Namespace: operatorNamespace}, privateSecret)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As I stated in my previous review, getting the secret content should be done outside of the flow for GenerateOnboardingToken. The GenerateOnboardingToken (and all sub-calls under it) should not concern themselves with k8s. This means you need to get the private key string outside this flow and pass it instead of passing in a client

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed the GenerateOnboardingToken method, privateKey is passed to the function, no k8s query is done inside GenerateOnboardingToken.

@Mrudraia Mrudraia force-pushed the onboarding branch 3 times, most recently from fdf2e37 to 859cfef Compare August 19, 2024 08:26
return k8sClient, nil
}

func readPrivateKey(cl client.Client) (*rsa.PrivateKey, error) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do not read the private key in the main.go file as it is only called once at the start. It will be a problem if the storage admin rotates the key and the private key changes.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed the reading of private key from main.go, and moved to utils.

@Mrudraia Mrudraia force-pushed the onboarding branch 2 times, most recently from d16fedf to 36a17e7 Compare September 4, 2024 10:59
@openshift-merge-robot openshift-merge-robot added the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Sep 4, 2024
@openshift-merge-robot openshift-merge-robot removed the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Sep 4, 2024
@Mrudraia
Copy link
Author

Mrudraia commented Sep 4, 2024

@Mrudraia: The following test failed, say /retest to rerun all failed tests or /retest-required to rerun all mandatory failed tests:

Test name Commit Details Required Rerun command
ci/prow/ocs-operator-bundle-e2e-aws 496fd5e link unknown /test ocs-operator-bundle-e2e-aws
Full PR test history. Your PR dashboard.

/retest


privateKey, err := util.ReadPrivateKey(cl)
if err != nil {
fmt.Println("Failed to get private key", err)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it should be of type http.Error

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ack

@@ -5,6 +5,8 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

const onboardingValidationPrivateKeySecretName = "onboarding-private-key"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

its also defined in storageclient.go

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed unused code

"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".
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you are updating the parameter type so I would suggest this comment should be update accordingly.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ack

@@ -225,7 +225,7 @@ func (r *StorageClusterReconciler) SetupWithManager(mgr ctrl.Manager) error {
Owns(&appsv1.Deployment{}, builder.WithPredicates(predicate.GenerationChangedPredicate{})).
Owns(&corev1.Service{}, builder.WithPredicates(predicate.GenerationChangedPredicate{})).
Owns(&corev1.ConfigMap{}, builder.MatchEveryOwner, builder.WithPredicates(predicate.GenerationChangedPredicate{})).
Owns(&corev1.Secret{}, builder.WithPredicates(predicate.GenerationChangedPredicate{})).
Owns(&corev1.Secret{}, builder.MatchEveryOwner, builder.WithPredicates(predicate.GenerationChangedPredicate{})).
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why this change is needed.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will trigger reconcilation to all owners whenever their is change in secrets.

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>

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>
Comment on lines +29 to +30
r.Log.Info("Unable to get privatekey:")
return reconcile.Result{}, nil
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If there is an err while reading the private key we should return the err

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants