-
Notifications
You must be signed in to change notification settings - Fork 1.3k
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
Enable automatic token generation for VirtualMachineExport objects #8418
Enable automatic token generation for VirtualMachineExport objects #8418
Conversation
Skipping CI for Draft Pull Request. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great start!
pkg/storage/export/export/export.go
Outdated
// If no tokenSecretRef has been specified, we use one specific to the current export object | ||
tokenSecretRef := vmExport.Spec.TokenSecretRef | ||
if tokenSecretRef == "" { | ||
tokenSecretRef = getDefaultTokenSecretName(vmExport) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it would be useful to store the token secret name in the VM export status. So refer to that in this function instead of calling getDefaultTokenSecretName
again?
pkg/storage/export/export/export.go
Outdated
// The secret name is constructed so it can be specific to the current vmExport object | ||
tokenSecretName := getDefaultTokenSecretName(vmExport) | ||
|
||
token := rand.String(secretTokenLenght) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we have to use a more cryptographically secure rng for the token. Looks like this package uses "math/rand" which "implements pseudo-random number generators unsuitable for security-sensitive work".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hmmm I'm honestly surprised by that since I thought k8s.io/apimachinery/pkg/util/rand
was our standard for this kind of thing. I see that k8s.io/client-go/util/cert
uses crypto/rand
, so I'll find a way to use that one instead (also in virtctl vmexport).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, math/rand is fine for naming things but not for secrets
token := make([]byte, secretTokenLenght) | ||
if _, err := cryptorand.Read(token); err != nil { | ||
return err | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We still want this to be string. The value has to be included in an http header. So maybe do something like this: https://gist.github.com/dopey/c69559607800d2f2f90b1b1ed4e550fb#file-main-go-L47-L59
Basically whatever the k8s library function was doing but with a better random source
// +optional | ||
// DefaultSecretExportName is the name of an optional secret specifically created for the current VirtualMachineExport object | ||
// in case no other secret name is specified when creating the object | ||
DefaultSecretExportName string `json:"defaultSecretExportName,omitempty"` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would just call this TokenSecretRef
and set it to whatever the user set explicitly or the one we create. One source of truth to get the secret name
f9b40de
to
221d9bf
Compare
d3b721d
to
64dfff5
Compare
TokenSecretRef string `json:"tokenSecretRef"` | ||
// +optional | ||
// TokenSecretRef is the name of the custom-defined secret that contains the token used by the export server pod | ||
TokenSecretRef string `json:"tokenSecretRef,omitempty"` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should probably be a pointer
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Out of curiosity, I'm seeing that we use regular strings for all other fields in the export API (even in the Spec's TokenSecretRef
). Why we'd want this one to be a pointer?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have seen CSI APIs use type LocalObjectReference struct
for SecretRef
but then on the other hand we have CDI's which are strings (type DataVolumeSourceRegistry struct
for example)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Out of curiosity, I'm seeing that we use regular strings for all other fields in the export API (even in the Spec's TokenSecretRef). Why we'd want this one to be a pointer?
we are specifically talking about the spec.tokenSecretRef
Since this field is optional, it should be a pointer per kubernetes api conventions. https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#optional-vs-required
Of course we are not always consistent about this
pkg/storage/export/export/export.go
Outdated
} | ||
|
||
// If not, the secret name is constructed so it can be specific to the current vmExport object | ||
vmExport.Status.TokenSecretRef = getDefaultTokenSecretName(vmExport) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would first check if vmExport.Status.TokenSecretRef
is set and use that value if so. This way if, in the unlikely case that we change the naming scheme, existing exports will still work.
pkg/storage/export/export/export.go
Outdated
@@ -105,6 +106,11 @@ const ( | |||
|
|||
kvm = 107 | |||
|
|||
// secretTokenLenght is the lenght of the randomly generated token | |||
secretTokenLenght = 20 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
typo?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
good catch!
tests/storage/export.go
Outdated
@@ -703,6 +724,7 @@ var _ = SIGDescribe("Export", func() { | |||
Skip("Skip test when Filesystem storage is not present") | |||
} | |||
vmExport := createRunningPVCExport(sc, k8sv1.PersistentVolumeFilesystem) | |||
Expect(vmExport.Spec.TokenSecretRef).To(Equal(vmExport.Status.TokenSecretRef)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
mby should also ensure it has some reasonable length so its not empty?
71424ec
to
4d7e05a
Compare
/retest-required |
This commit introduces a new mechanism in the export controller so, when a VMExport with no tokenSecretRef is created, a corresponding secret is created using a name specific to the current object. This commit also modifies the export API to make the tokenSecretRef field optional. Signed-off-by: Alvaro Romero <alromero@redhat.com>
…nts: * The randomly generated token for the default export secret is now created using the more secure package crypto/rand * The default secret name is now stored in the VirtualMachineExport status Signed-off-by: Alvaro Romero <alromero@redhat.com>
* Fixed error in export pod error handling, so if the pod creation fails, now the controller returns the error appropiately. * The name generation mechanism of the default export secret has been fixed, so no names longer than 63 characters are created. Signed-off-by: Alvaro Romero <alromero@redhat.com>
4d7e05a
to
6f74394
Compare
/retest-required |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks great, just a couple minor comments
pkg/storage/export/export/export.go
Outdated
@@ -599,6 +609,65 @@ func (ctrl *VMExportController) createCertSecretManifest(vmExport *exportv1.Virt | |||
}, nil | |||
} | |||
|
|||
// handleVMExportSecret checks if a secret has been specified for the current export object and, if not, creates one specific to it | |||
func (ctrl *VMExportController) handleVMExportSecret(vmExport *exportv1.VirtualMachineExport) error { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this should be called handleVMExportToken
or handleVMExportTokenGeneration
pkg/storage/export/export/export.go
Outdated
@@ -609,6 +678,11 @@ func (ctrl *VMExportController) getExportSecretName(ownerPod *corev1.Pod) string | |||
return certSecretName | |||
} | |||
|
|||
// getDefaultTokenSecretName returns a secret name specifically created for the current export object | |||
func getDefaultTokenSecretName(vme *exportv1.VirtualMachineExport) string { | |||
return naming.GetName("export-secret", vme.Name, validation.DNS1035LabelMaxLength) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this should be export-token
or vmexport-token
Expect(err).ToNot(HaveOccurred()) | ||
Expect(token.Name).To(Equal(*export.Status.TokenSecretRef)) | ||
Expect(*export.Status.TokenSecretRef).ToNot(BeEmpty()) | ||
}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about downloading something with generated token?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good idea, I'll add a new test in the virtctl vmexport
section.
/retest-required |
/approve |
[APPROVALNOTIFIER] This PR is APPROVED This pull-request has been approved by: mhenriks The full list of commands accepted by this bot can be found here. The pull request process is described here
Needs approval from an approver in each of these files:
Approvers can indicate their approval by writing |
@awels how about giving this a once over? |
Sure let me take a look |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
looks good a few minor things
pkg/storage/export/export/export.go
Outdated
|
||
// If a tokenSecretRef has been specified, we assume that the corresponding | ||
// secret has already been created and managed appropiately by the user | ||
if isTokenSpecified := vmExport.Spec.TokenSecretRef != nil; isTokenSpecified { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can't you just write:
if vmExport.Spec.TokenSecretRef != nil {
vmExport.Status.TokenSecretRef = vmExport.Spec.TokenSecretRef
return nil
}
func (ctrl *VMExportController) handleVMExportToken(vmExport *exportv1.VirtualMachineExport) error { | ||
if vmExport.Status == nil { | ||
vmExport.Status = &exportv1.VirtualMachineExportStatus{ | ||
Phase: exportv1.Pending, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since we are always setting this here, do we still need to set Pending and the conditions in updateCommonVMExportStatusFields
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess we don't, however, I like how we check before dereferencing it. It shouldn't be necessary anyway, so I can remove the one in updateCommonVMExportStatusFields
.
@@ -989,7 +1068,7 @@ func createVMVMExport() *exportv1.VirtualMachineExport { | |||
Kind: "VirtualMachine", | |||
Name: testVmName, | |||
}, | |||
TokenSecretRef: "token", | |||
TokenSecretRef: &token, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What you did is not wrong, this is just an alternative:
TokenSecretRef: pointer.StringPtr("token")
Saves you from having to create the variable at the top.
if vmExport.Status != nil && vmExport.Status.TokenSecretRef != nil { | ||
tokenSecretRef = *vmExport.Status.TokenSecretRef | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is it possible that tokenSecretRef
is blank here? Not sure if we want to create the pod with a blank secret name?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would say it's impossible (handleVMExportToken should always populate vme.Status.TokenSecretRef
), but still wanted to check before dereferencing the pointer.
* This commit introduces a new functional test so we cover the download of an export created with user-defined TokenSecretRef * It also renames several token-related items in the export controller Signed-off-by: Alvaro Romero <alromero@redhat.com>
c54c44b
to
c794f87
Compare
/test pull-kubevirt-e2e-kind-1.23-vgpu-nonroot |
/hold |
/hold cancel |
/retest-required |
/test pull-kubevirt-e2e-k8s-1.24-sig-storage |
@alromeros: The following tests failed, say
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/test-infra repository. I understand the commands that are listed here. |
/retest-required |
What this PR does / why we need it
This PR introduces a new mechanism in the export controller so that when a VMExport with no
tokenSecretRef
is created, a corresponding secret is made using a name specific to the current object.Which issue(s) this PR fixes: https://issues.redhat.com/browse/CNV-20651
Special notes for your reviewer: Work in progress
Release note: