-
Notifications
You must be signed in to change notification settings - Fork 40k
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
Add CSI External Components ClusterRole to bootstrapped roles #61866
Conversation
E2E test that was failing is tested and passing on GKE with this change |
rbac.NewRule("get", "list", "watch", "update").Groups(legacyGroup).Resources("persistentvolumeclaims").RuleOrDie(), | ||
rbac.NewRule("get", "list", "watch", "create", "update", "patch").Groups(legacyGroup).Resources("events").RuleOrDie(), | ||
rbac.NewRule("get", "list").Groups(legacyGroup).Resources("secrets").RuleOrDie(), | ||
rbac.NewRule("get", "list", "watch", "update").Groups(legacyGroup).Resources("nodes").RuleOrDie(), |
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 is a volume provisioner updating Node objects?
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.
it is not, I am splitting up roles now :)
rbac.NewRule("create", "delete", "get", "list", "watch", "update").Groups(legacyGroup).Resources("persistentvolumes").RuleOrDie(), | ||
rbac.NewRule("get", "list", "watch", "update").Groups(legacyGroup).Resources("persistentvolumeclaims").RuleOrDie(), | ||
rbac.NewRule("get", "list", "watch", "create", "update", "patch").Groups(legacyGroup).Resources("events").RuleOrDie(), | ||
rbac.NewRule("get", "list").Groups(legacyGroup).Resources("secrets").RuleOrDie(), |
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 wouldn't expect listing of secrets, and I'd expect secret permissions to be granted specific to an installation in combination with the storage class setup, not globally to all secrets across all namespaces
rbac.NewRule("get", "list", "watch", "update").Groups(storageGroup).Resources("volumeattachments").RuleOrDie(), | ||
rbac.NewRule("get", "list", "watch").Groups(storageGroup).Resources("storageclasses").RuleOrDie(), | ||
}, | ||
}, |
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 grant patch in addition to update and write external components to use patch to minimize chances of dropping fields on version skewed clusters
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.
Right now none of the components use "Patch". It is an open issue and we are aware of it. Should we give "Patch" API access now, or wait until we update the components to support "Patch"ing
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.
go ahead and add patch permission, since we know that is the desired operation long-term
@@ -458,6 +458,19 @@ func ClusterRoles() []rbac.ClusterRole { | |||
eventsRule(), | |||
}, | |||
}, | |||
{ | |||
// a role for csi external components {csi-provisioner,csi-attacher,driver-registrar} | |||
ObjectMeta: metav1.ObjectMeta{Name: "system:csi-external-components"}, |
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.
Role names are typically singular
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.
Where does the driver-registrar run?
If the provisioner, attacher, and driver are run in separate locations with different levels of access to the API, three roles might make more sense. I wouldn't want every kubelet to have the ability to delete any PV object
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.
In the test we are attempting to fix they are all run in the same location, however we wish to leave the flexibility of CSI Driver implementers to split them up if they wish. I will create 3 roles with minimal permissions required for each component
@liggitt looking into splitting into 3 separate roles for the 3 external components. Could you elaborate on "I'd expect secret permissions to be granted specific to an installation in combination with the storage class setup, not globally to all secrets across all namespaces" I'm not exactly sure what that means. You mean a role for secrets permissions would be created on installation of a specific CSI driver that is limited to the namespace it's sitting in? |
I would suggest three different roles: /assign @msau42 |
Rules: []rbac.PolicyRule{ | ||
rbac.NewRule("create", "delete", "list", "watch").Groups(legacyGroup).Resources("persistentvolumes").RuleOrDie(), | ||
rbac.NewRule("get", "list", "watch", "update").Groups(legacyGroup).Resources("persistentvolumeclaims").RuleOrDie(), | ||
rbac.NewRule("get").Groups(legacyGroup).Resources("secrets").RuleOrDie(), |
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.
provisioners don't have a general need to get every secret in the cluster. a provisioner might:
- need no secret-related permissions
- need permissions to view a particular secret for provisioning
- need permissions to view secrets in a particular namespace for provisioning
- need permissions to create attach/mount-related secrets in a particular namespace
rather than grant broad permissions here on the off chance the provisioner needs them, I'd expect specific permissions to be granted as a particular provisioner or storage class requires.
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.
Ok, I will remove the secrets for now. We will have to implement some sort of workaround to allow for creating Role
s in the test framework for GKE once we start adding tests for external CSI components that require secrets.
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.
once we start adding tests for external CSI components that require secrets.
I'd actually expect those to come with recommended setups that put them in a namespace, set up a storage class for them, and granted them appropriate secret permissions within that namespace. Basically, the standard role (which you are defining here) takes care of the "every provisioner will have to do X" things, and they get to grant any additional permissions they want in a manifest specific to their component.
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.
Right, when I remove the secrets it the roles I'm adding here will be consistent with "every provisioner will have to do X". The recommended setups given to users of CSI will have the additional roles to grant whatever additional permissions they need. The only issue is that the test framework on GKE does not allow creation of roles by default so there would just need to be a workaround in the test or a larger framework/GKE change would need to be done.
Live deployment of these plugins was never really the issue. The change was spurred mainly by test failure on GKE.
// a role for csi external components {csi-provisioner,csi-attacher,driver-registrar} | ||
ObjectMeta: metav1.ObjectMeta{Name: "system:csi-driver-registrar"}, | ||
Rules: []rbac.PolicyRule{ | ||
rbac.NewRule("get", "update").Groups(legacyGroup).Resources("nodes").RuleOrDie(), |
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'm not familiar with the registrar component... where does this process run, and what on the Node object is it updating?
/cc @saad-ali
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.
The driver registrar adds an annotation on the node that contains a JSON map of driver names to node names. These node names could be different from what Kubernetes views as the node name. The process will generally be deployed alongside the CSI Plugin in a Pod.
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.
so this is giving every kubelet running the CSI plugin pod access to a credential (the service account credential for the CSI pod) granted a role that lets it modify any node in the cluster?
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 believe so, @saad-ali to confirm
test/e2e/storage/csi_volumes.go
Outdated
clusterRole = csiClusterRole(cs, config, false) | ||
serviceAccount = csiServiceAccount(cs, config, false) | ||
csiClusterRoleBinding(cs, config, false, serviceAccount, clusterRole) | ||
serviceAccount = csiCombinedServiceAccount(cs, config, false) |
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.
this is up to @kubernetes/sig-storage-pr-reviews, but I'd recommend exercising each pair of phases (provision/delete, attach/detach, mount/unmount) using a separate service account with just the specified role. That way, e2e tests make it really clear when a new operation is performed in a phase that isn't covered by the relevant role.
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.
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.
this test is for testing the HostPath CSI Plugin specifically, not the CSI external components
so does that mean it doesn't need the provisioner and attacher roles?
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.
The csi-provisioner, driver-registrar, and csi-attacher are side-car containers necessary for the HostPath CSI Plugin container. The pod containing all 4 containers will need provisioner, attacher, and driver-registrar roles.
thanks, looks a lot better. just a few comments |
For future reference: for tests for plugins that require secrets, the test may have to implement a workaround described here: #43409 (comment) Or we should get it so that the test framework in GKE is allowed to create ClusterRoles. |
Removed "get" secrets from system:csi-external-provisioner cluster role. |
ObjectMeta: metav1.ObjectMeta{Name: "system:csi-external-provisioner"}, | ||
Rules: []rbac.PolicyRule{ | ||
rbac.NewRule("create", "delete", "list", "watch").Groups(legacyGroup).Resources("persistentvolumes").RuleOrDie(), | ||
rbac.NewRule("get", "list", "watch", "update").Groups(legacyGroup).Resources("persistentvolumeclaims").RuleOrDie(), |
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.
Why does the external provisioner need to update PVCs?
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.
It's required for leader election in order "to acquire lease to provision for pvc"
// a role for the csi external attacher | ||
ObjectMeta: metav1.ObjectMeta{Name: "system:csi-external-attacher"}, | ||
Rules: []rbac.PolicyRule{ | ||
rbac.NewRule("get", "list", "watch", "update").Groups(legacyGroup).Resources("persistentvolumes").RuleOrDie(), |
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 does the attacher update in PVs?
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.
It adds a finalizer to the PV
/retest |
CSI allows for the node naming for storage system to be independent of node naming of the Kubernetes cluster. That said, I'm not aware of any cluster where this is actually the case. That said, moving forward there are plans to extend this functionality to allow a storage plugin to report the "topology labels" that apply to a specific node (e.g. zone, rack, etc.), in addition to the node name. These labels would be applied to the node as node labels. So |
That's fine, but it seems like it should be driven by a source of truth, not self-reporting.
letting nodes arbitrarily self-label themselves is problematic (see https://github.com/kubernetes/community/pull/911/files for multiple examples why) and work is in progress to reduce that in the future. |
This is not correct. Technically only 1 instance of provisioner and attacher need to running per cluster. So a CSI deployment would consist of two deployments:
The pod for 1, only needs provisioner and attacher permissions (not driver-registrar) and runs on a single node somewhere on the cluster.
Source of truth in this case is the storage system, and k8s interacts with the storage system via CSI. It queries the storage system and to discover the name of a node according to the storage system (via CSI), and it needs to store the mapping between the storage node name and k8s node name somewhere (the k8s node object was the most obvious place).
Good to know. I'm not sure it can be avoided, but I'll set up some time to sync up on this and see if we can come up with a better solution. |
ff2bcf9
to
60cdc7e
Compare
Instead of whitelisting specific label names or prefixes in NodeRestriction, what if we whitelist regexes? Not sure exactly what the CSI topology labels would look like yet, but just wanted to see what are the options. |
test/e2e/storage/csi_volumes.go
Outdated
clusterRoleBindingClient := client.RbacV1().ClusterRoleBindings() | ||
stdout, stderr, err := framework.RunCmd("gcloud", "config", "get-value", "core/account") |
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.
rather than writing a GCE-specific command, you could do this to get a clientset you could use to define roles (impersonation also used by https://github.com/kubernetes/kubernetes/blob/master/test/e2e/auth/node_authz.go#L65, and the e2e user is presumed to be sufficiently authorized to be allowed to impersonate):
By("Creating an impersonating superuser kubernetes clientset to define RBAC roles")
config, err := framework.LoadConfig()
Expect(err).NotTo(HaveOccurred())
config.Impersonate = restclient.ImpersonationConfig{
UserName: "superuser",
Groups: []string{"system:masters"},
}
superuserClientset, err := clientset.NewForConfig(config)
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.
thats perfect, incorporating now.
@liggitt @davidz627 and I met and discussed the issues with the CSI driver-registrar permissions. The driver-registrar required permission to the Kubernetes node API object in order to persist the mapping of CSI storage node name and, in the future, to allow CSI volume plugins to specify node topology labels. However this would require giving every instance of the driver-registrar on every node permission to edit all Kubernetes node API objects, meaning if one node was compromised, it could modify any other node object. We decided that a better approach would be to remove the logic for applying node labels/annotation from the driver-registrar sidecar container, and move it to the kubelet. This will take advantage of the fact that kubelet's node access is already limited (via node authorizer) to its own node object. The Kubernetes CSI team will track this as an item for the coming quarter. In the meantime, in order to fix the test on GKE, dyzz@ will give the E2E user admin privileges to allow it to create RBAC rules. |
test/e2e/storage/csi_volumes.go
Outdated
clusterRole = csiClusterRole(cs, config, false) | ||
serviceAccount = csiServiceAccount(cs, config, false) | ||
csiClusterRoleBinding(cs, config, false, serviceAccount, clusterRole) | ||
csiDriverRegistrarClusterRole(cs, config, false) |
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.
probably a non-issue for this PR because there's a single test in this section, but if there were multiple tests, teardown of this shared global role at the beginning of every test would be disruptive... as a follow-up, might want to change this to just create the clusterrole if needed and not worry about tearing it down afterward
similar comments about the service account binding... you'd probably want to make it unique per test (include namespace in the clusterrolebinding name)
rbac changes lgtm will let @kubernetes/sig-storage-pr-reviews LGTM the test |
…emoved creation from failing e2e test
/retest |
/lgtm |
[APPROVALNOTIFIER] This PR is APPROVED This pull-request has been approved by: davidz627, liggitt, saad-ali 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 |
Automatic merge from submit-queue (batch tested with PRs 62192, 61866, 62206, 62360). If you want to cherry-pick this change to another branch, please follow the instructions here. |
Automatic merge from submit-queue. Add RBAC policy rules for csi-external-provisioner and csi-external-attacher Adds RBAC Policy rules for `csi-external-provisioner` and `csi-external-attacher` so that CSI drivers can bind to these cluster roles on every version of k8s where CSI is Beta or above. These roles were added in 1.11 but never cherrypicked back to 1.10. The roles originally added as a part of a larger change here: #61866 I could not do a direct cherry-pick because some of the RBAC primitives changed and there was also a fix applied on top with this PR: #65070 The fix has been included in this commit. /kind enhancement /sig storage /cc @msau42 /assign @liggitt @MaciekPytel ```release-note NONE ```
Added CSI External Components ClusterRole to bootstrapped roles and removed creation from failing e2e test
Fixes: #61781
/sig storage
/kind bug
/assign @liggitt @saad-ali