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

Adding credentials support for k8s core CSI #60118

Merged
merged 3 commits into from
Feb 24, 2018
Merged

Adding credentials support for k8s core CSI #60118

merged 3 commits into from
Feb 24, 2018

Conversation

sbezverk
Copy link
Contributor

@sbezverk sbezverk commented Feb 21, 2018

PR implements changes proposed in: kubernetes/community#1816

CSI now allows credentials to be specified on CreateVolume/DeleteVolume, ControllerPublishVolume/ControllerUnpublishVolume, and NodePublishVolume/NodeUnpublishVolume operations

@k8s-ci-robot k8s-ci-robot added release-note Denotes a PR that will be considered when it comes time to generate release notes. size/L Denotes a PR that changes 100-499 lines, ignoring generated files. cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. labels Feb 21, 2018
@sbezverk
Copy link
Contributor Author

/sig storage
/kind feature

@k8s-ci-robot k8s-ci-robot added sig/storage Categorizes an issue or PR as relevant to SIG Storage. kind/feature Categorizes issue or PR as related to a new feature. labels Feb 21, 2018
@k8s-github-robot k8s-github-robot added the kind/api-change Categorizes issue or PR as related to adding, removing, or otherwise changing an API label Feb 21, 2018
@sbezverk
Copy link
Contributor Author

@vladimirvivien @saad-ali Please review.
This implementation adds two new objects and modifies code only for NodePublish and NodeUnpublish, since these operations are executed by csi_mounter.go and csi_client.go
Changes for ControllerCreate/Delete/Publish/Unpublish are outside of k8s core and will be done after merging this change.

@david-mcmahon david-mcmahon removed their request for review February 21, 2018 22:59
@k8s-ci-robot k8s-ci-robot added needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. and removed needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. labels Feb 21, 2018
@saad-ali saad-ali self-assigned this Feb 22, 2018
@saad-ali saad-ali requested review from saad-ali and removed request for lavalamp, nikhiljindal and wojtek-t February 22, 2018 19:05
@sbezverk
Copy link
Contributor Author

@saad-ali addressed changes in the proposal, PTAL.

// ControllerPublishVolume and ControllerUnpublishVolume calls.
// This may be empty if no secret is required. If the secret object contains
// more than one secret, all secrets are passed.
// +optional
Copy link
Member

Choose a reason for hiding this comment

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

nit: Update comment

  // ControllerPublishSecretRef is a reference to the secret object containing
  // sensitive information to pass to the CSI driver to complete the CSI
  // ControllerPublishVolume and ControllerUnpublishVolume calls.
  // This field is optional, and  may be empty if no secret is required. If the
  // secret object contains more than one secret, all secrets are passed.
  // +optional

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

// NodeStageSecretRef is a reference to the secret object containing sensitive
// information to pass to the CSI driver to complete the CSI NodeStageVolume
// and NodeStageVolume and NodeUnstageVolume calls.
// This secret will be fetched by the kubelet.
Copy link
Member

Choose a reason for hiding this comment

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

You can remove this line.

// NodePublishSecretRef is a reference to the secret object containing
// sensitive information to pass to the CSI driver to complete the CSI
// NodePublishVolume and NodeUnpublishVolume calls.
// This secret will be fetched by the kubelet.
Copy link
Member

Choose a reason for hiding this comment

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

You can remove this line.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done


func getCredentialsFromSecret(k8s kubernetes.Interface, sn, ns string) map[string]string {
credentials := map[string]string{}
if len(sn) == 0 || len(ns) == 0 {
Copy link
Member

Choose a reason for hiding this comment

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

I think empty namespace maybe a valid case (default namespace?).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done, in reality it will not be ever empty as I take it from pod's spec. Removed ns check.

@@ -195,7 +195,12 @@ func (c *csiMountMgr) SetUpAt(dir string, fsGroup *int64) error {
if len(fsType) == 0 {
fsType = defaultFSType
}

nodePublishCredentials := map[string]string{}
Copy link
Member

Choose a reason for hiding this comment

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

Not your code, but, can you add a commit to remove probe related code in this PR too:

...
 	if err := csi.NodeProbe(ctx, csiVersion); err != nil {
...

and all related comments, e.g.:

// TODO (vladimirvivien) use this method to probe controller using CSI.NodeProbe() call

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done


nodePublishCredentials := map[string]string{}
if c.spec.PersistentVolume.Spec.CSI.NodePublishSecretRef != nil {
sn := c.spec.PersistentVolume.Spec.CSI.NodePublishSecretRef.Name
Copy link
Member

Choose a reason for hiding this comment

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

Just use csiSource.NodePublishSecretRef.Name everywhere instead.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

@@ -195,7 +195,12 @@ func (c *csiMountMgr) SetUpAt(dir string, fsGroup *int64) error {
if len(fsType) == 0 {
fsType = defaultFSType
}

nodePublishCredentials := map[string]string{}
if c.spec.PersistentVolume.Spec.CSI.NodePublishSecretRef != nil {
Copy link
Member

Choose a reason for hiding this comment

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

I'm not 100% sure, but I think an empty namespace is valid. So maybe just check if NodePublishSecretRef.Name != nil

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Here I check for the presence of Secret Object, Name is string, it could be not nil but "". If you do not mind I would like to keep this check for object not nil and secret name validation in getCredentialsFromSecret

@sbezverk
Copy link
Contributor Author

@saad-ali addressed your comments, PTAL.

@saad-ali
Copy link
Member

I forgot one more set of changes: The Kubernetes API server's node authorizer must be updated to allow kubelet to access the secrets referenced by CSIPersistentVolumeSource.

@saad-ali
Copy link
Member

I forgot one more set of changes: The Kubernetes API server's node authorizer must be updated to allow kubelet to access the secrets referenced by CSIPersistentVolumeSource.

Actually I think this might be handled by VisitPVSecretNames.
@liggitt to confirm and review.

/assign @liggitt

@saad-ali
Copy link
Member

verify gofmt is failing, please run gofmt

@liggitt
Copy link
Member

liggitt commented Feb 23, 2018

I forgot one more set of changes: The Kubernetes API server's node authorizer must be updated to allow kubelet to access the secrets referenced by CSIPersistentVolumeSource.

Actually I think this might be handled by VisitPVSecretNames.
@liggitt to confirm and review.

Close, though the node authorizer today allows the kubelet to read any secret referenced by a PV. As of this PR, only some secret references should be kubelet-visible (specifically, ControllerPublishSecretRef should not be). You can make that a parameter to the visitor (func(namespace, name string, kubeletVisible bool)) and omit appropriately in the node authorizer visitor

@k8s-ci-robot k8s-ci-robot added the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Feb 23, 2018
@@ -111,6 +111,32 @@ func VisitPVSecretNames(pv *api.PersistentVolume, visitor Visitor) bool {
if source.StorageOS.SecretRef != nil && !visitor(source.StorageOS.SecretRef.Namespace, source.StorageOS.SecretRef.Name) {
return false
}
case source.CSI != nil:
if source.CSI.ControllerPublishSecretRef != nil {
if len(source.CSI.ControllerPublishSecretRef.Name) == 0 || len(source.CSI.ControllerPublishSecretRef.Namespace) == 0 {
Copy link
Member

@liggitt liggitt Feb 23, 2018

Choose a reason for hiding this comment

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

api validation should be added to prevent these from being empty... no need to recheck here (or in the other blocks)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@liggitt could you please point me where the validation you mentioned should be added.

Copy link
Member

Choose a reason for hiding this comment

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

func validateCSIPersistentVolumeSource(csi *core.CSIPersistentVolumeSource, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
if !utilfeature.DefaultFeatureGate.Enabled(features.CSIPersistentVolume) {
allErrs = append(allErrs, field.Forbidden(fldPath, "CSIPersistentVolume disabled by feature-gate"))
}
if len(csi.Driver) == 0 {
allErrs = append(allErrs, field.Required(fldPath.Child("driver"), ""))
}
if len(csi.Driver) > 63 {
allErrs = append(allErrs, field.TooLong(fldPath.Child("driver"), csi.Driver, 63))
}
if !csiDriverNameRexp.MatchString(csi.Driver) {
allErrs = append(allErrs, field.Invalid(fldPath.Child("driver"), csi.Driver, validation.RegexError(csiDriverNameRexpErrMsg, csiDriverNameRexpFmt, "csi-hostpath")))
}
if len(csi.VolumeHandle) == 0 {
allErrs = append(allErrs, field.Required(fldPath.Child("volumeHandle"), ""))
}
return allErrs
}

@k8s-ci-robot k8s-ci-robot removed the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Feb 23, 2018
@sbezverk
Copy link
Contributor Author

/test pull-kubernetes-unit

@@ -1452,6 +1452,33 @@ func validateCSIPersistentVolumeSource(csi *core.CSIPersistentVolumeSource, fldP
allErrs = append(allErrs, field.Required(fldPath.Child("volumeHandle"), ""))
}

if csi.ControllerPublishSecretRef != nil {
Copy link
Member

Choose a reason for hiding this comment

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

for all of these, ensure name is a valid secret name and namespace is a valid namespace name

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

nodePublishCredentials := map[string]string{}
if csiSource.NodePublishSecretRef != nil {
sn := csiSource.NodePublishSecretRef.Name
ns := c.pod.ObjectMeta.Namespace
Copy link
Member

Choose a reason for hiding this comment

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

this should be csiSource.NodePublishSecretRef.Namespace

Copy link
Member

Choose a reason for hiding this comment

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

to protect against this type of mistake, consider passing the secret ref to getCredentialsFromSecret instead of loose name/namespace fields

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

nodeUnpublishCredentials := map[string]string{}
if csiSource.NodePublishSecretRef != nil {
sn := csiSource.NodePublishSecretRef.Name
ns := c.pod.ObjectMeta.Namespace
Copy link
Member

Choose a reason for hiding this comment

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

NodePublishSecretRef.Namespace

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Will be passing SecreteReference to getCredentialsFromSecret.

@sbezverk
Copy link
Contributor Author

@saad-ali @liggitt addressed latest comments, PTAL

Copy link
Member

@saad-ali saad-ali left a comment

Choose a reason for hiding this comment

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

LGTM mod nit

@liggitt for node authorizer review

@@ -40,11 +40,11 @@ func VisitPVSecretNames(pv *api.PersistentVolume, visitor Visitor) bool {
switch {
case source.AzureFile != nil:
if source.AzureFile.SecretNamespace != nil && len(*source.AzureFile.SecretNamespace) > 0 {
if len(source.AzureFile.SecretName) > 0 && !visitor(*source.AzureFile.SecretNamespace, source.AzureFile.SecretName) {
if len(source.AzureFile.SecretName) > 0 && !visitor(*source.AzureFile.SecretNamespace, source.AzureFile.SecretName, true) {
Copy link
Member

Choose a reason for hiding this comment

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

nit: when it is unclear what the value means, it's nice to have a comment, for example:

!visitor(
    *source.AzureFile.SecretNamespace,
    source.AzureFile.SecretName,
    true /* kubeletVisible */) {

But it's a personal preference, up to you if you want to do that.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

agree, it is more next developer friendly :)

credentials := map[string]string{}
secret, err := k8s.CoreV1().Secrets(secretRef.Namespace).Get(secretRef.Name, meta.GetOptions{})
if err != nil {
glog.Warningf("failed to find the secret %s in the namespace %s with error: %v\n", secretRef.Name, secretRef.Namespace, err)
Copy link
Member

Choose a reason for hiding this comment

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

I expected this to return the error and avoid making the CSI call

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I am not sure, credentials are not mandatory, so if not specified I do not think we should fail CSI call. @saad-ali what do you think?

Copy link
Member

@liggitt liggitt Feb 23, 2018

Choose a reason for hiding this comment

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

if they were specified by the PV, then it seems like an error resolving them should be considered an error in input gathering and short-circuit


nodePublishCredentials := map[string]string{}
if csiSource.NodePublishSecretRef != nil {
nodePublishCredentials = getCredentialsFromSecret(c.k8s, csiSource.NodePublishSecretRef)
Copy link
Member

Choose a reason for hiding this comment

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

here and below, I expected to handle an error return from getCredentialsFromSecret by short-circuiting and returning the error, rather than making the csi call with an empty credentials map

@liggitt
Copy link
Member

liggitt commented Feb 23, 2018

API and node authorizer bits LGTM
one comment on error handling on secret lookup

@liggitt
Copy link
Member

liggitt commented Feb 23, 2018

/approve

@k8s-ci-robot k8s-ci-robot added the lgtm "Looks good to me", indicates that a PR is ready to be merged. label Feb 23, 2018
Copy link
Member

@saad-ali saad-ali left a comment

Choose a reason for hiding this comment

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

/lgtm
/approve

@saad-ali
Copy link
Member

For vendored dep update:
/assign @thockin

@brendandburns
Copy link
Contributor

/approve

@k8s-ci-robot
Copy link
Contributor

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: brendandburns, liggitt, saad-ali, sbezverk

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 /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@k8s-ci-robot k8s-ci-robot added the approved Indicates a PR has been approved by an approver from all required OWNERS files. label Feb 23, 2018
@k8s-github-robot
Copy link

/test all [submit-queue is verifying that this PR is safe to merge]

@sbezverk
Copy link
Contributor Author

/test pull-kubernetes-unit

@k8s-github-robot
Copy link

Automatic merge from submit-queue. If you want to cherry-pick this change to another branch, please follow the instructions here.

@k8s-github-robot k8s-github-robot merged commit 8e8601a into kubernetes:master Feb 24, 2018
@sbezverk sbezverk deleted the csi_core_credentials branch March 20, 2018 21:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
approved Indicates a PR has been approved by an approver from all required OWNERS files. cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. kind/api-change Categorizes issue or PR as related to adding, removing, or otherwise changing an API kind/feature Categorizes issue or PR as related to a new feature. lgtm "Looks good to me", indicates that a PR is ready to be merged. release-note Denotes a PR that will be considered when it comes time to generate release notes. sig/storage Categorizes an issue or PR as relevant to SIG Storage. size/L Denotes a PR that changes 100-499 lines, ignoring generated files.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

7 participants