CVE-2023-30840: Incorrect Authorization (CWE-863)
value: Fluid is an open source Kubernetes-native distributed dataset orchestrator and accelerator for data-intensive applications. Starting in version 0.7.0 and prior to version 0.8.6, if a malicious user gains control of a Kubernetes node running fluid csi pod (controlled by the `csi-nodeplugin-fluid` node-daemonset), they can leverage the fluid-csi service account to modify specs of all the nodes in the cluster. However, since this service account lacks `list node` permissions, the attacker may need to use other techniques to identify vulnerable nodes.

Once the attacker identifies and modifies the node specs, they can manipulate system-level-privileged components to access all secrets in the cluster or execute pods on other nodes. This allows them to elevate privileges beyond the compromised node and potentially gain full privileged access to the whole cluster.

To exploit this vulnerability, the attacker can make all other nodes unschedulable (for example, patch node with taints) and wait for system-critical components with high privilege to appear on the compromised node. However, this attack requires two prerequisites: a compromised node and identifying all vulnerable nodes through other means.

Version 0.8.6 contains a patch for this issue. As a workaround, delete the `csi-nodeplugin-fluid` daemonset in `fluid-system` namespace and avoid using CSI mode to mount FUSE file systems. Alternatively, using sidecar mode to mount FUSE file systems is recommended.
CWE description: The product performs an authorization check when an actor attempts to access a resource or perform an action, but it does not correctly perform the check. This allows attackers to bypass intended access restrictions.
CVSS v3 base score: 7.8
Published: 2023-05-08T18:15Z
Repository: https://github.com/fluid-cloudnative/fluid
Language: Go
Files changed: charts/fluid/fluid/CHANGELOG.md, charts/fluid/fluid/templates/csi/daemonset.yaml, charts/fluid/fluid/templates/role/csi/rbac.yaml, charts/fluid/fluid/templates/role/webhook/rabc.yaml, charts/fluid/fluid/templates/webhook/webhook.yaml, charts/fluid/fluid/values.yaml, cmd/csi/app/csi.go, pkg/csi/config/config.go, pkg/csi/plugins/driver.go, pkg/csi/plugins/nodeserver.go, pkg/csi/plugins/register.go, pkg/utils/kubelet/node_auth_client.go
Commit date: 2023-05-08 14:46:24 +0800
Commit message: Merge pull request from GHSA-93xx-cvmc-9w3v

* Fix rbacs and limit CSI Plugin's node related access

Signed-off-by: trafalgarzzz <trafalgarz@outlook.com>

* Update change log

Signed-off-by: trafalgarzzz <trafalgarz@outlook.com>

---------

Signed-off-by: trafalgarzzz <trafalgarz@outlook.com>
Diff: diff --git a/charts/fluid/fluid/CHANGELOG.md b/charts/fluid/fluid/CHANGELOG.md
index ccf9e96f..7daf32e4 100644
--- a/charts/fluid/fluid/CHANGELOG.md
+++ b/charts/fluid/fluid/CHANGELOG.md
@@ -51,6 +51,7 @@
 * Update mutating webhook configuration rules
 * Support configurable pod metadata of runtimes
 * Scale runtime controllers on demand
 
 ### 0.9.0
-* Support pass image pull secrets from fluid charts to alluxioruntime controller
\ No newline at end of file
+* Support pass image pull secrets from fluid charts to alluxioruntime controller
+* Fix components rbacs and set Fluid CSI Plugin with node-authorized kube-client
\ No newline at end of file
diff --git a/charts/fluid/fluid/templates/csi/daemonset.yaml b/charts/fluid/fluid/templates/csi/daemonset.yaml
index 16abe4d4..a1bfaf39 100644
--- a/charts/fluid/fluid/templates/csi/daemonset.yaml
+++ b/charts/fluid/fluid/templates/csi/daemonset.yaml
@@ -102,12 +102,20 @@ spec:
             mountPath: {{ .Values.csi.kubelet.rootDir }}
             mountPropagation: "Bidirectional"
           - name: fluid-src-dir
             mountPath: {{ .Values.runtime.mountRoot | quote }}
             mountPropagation: "Bidirectional"
-          - name: host-etc-dir
-            mountPath: /host-etc
+          - name: kubelet-kube-config
+            mountPath: /etc/kubernetes/kubelet.conf
+            readOnly: true
+          - name: kubelet-cert-dir
+            mountPath: {{ .Values.csi.kubelet.certDir | quote }}
+            readOnly: true
+          - name: updatedb-conf
+            mountPath: /host-etc/updatedb.conf
+          - name: updatedb-conf-bak
+            mountPath: /host-etc/updatedb.conf.bak
       volumes:
         - name: kubelet-dir
           hostPath:
             path: {{ .Values.csi.kubelet.rootDir }}
             type: Directory
@@ -122,8 +130,20 @@ spec:
         - hostPath:
             path: {{ .Values.runtime.mountRoot | quote }}
             type: DirectoryOrCreate
           name: fluid-src-dir
         - hostPath:
-            path: /etc
+            path: {{ .Values.csi.kubelet.kubeConfigFile | quote }}
+            type: File
+          name: kubelet-kube-config
+        - hostPath:
+            path: {{ .Values.csi.kubelet.certDir | quote }}
             type: Directory
-          name: host-etc-dir
+          name: kubelet-cert-dir
+        - hostPath:
+            path: /etc/updatedb.conf
+            type: FileOrCreate
+          name: updatedb-conf
+        - hostPath:
+            path: /etc/updatedb.conf.backup
+            type: FileOrCreate
+          name: updatedb-conf-bak
diff --git a/charts/fluid/fluid/templates/role/csi/rbac.yaml b/charts/fluid/fluid/templates/role/csi/rbac.yaml
index c27f4adc..5354a76f 100644
--- a/charts/fluid/fluid/templates/role/csi/rbac.yaml
+++ b/charts/fluid/fluid/templates/role/csi/rbac.yaml
@@ -39,17 +39,11 @@ rules:
   - apiGroups: [""]
     resources: ["persistentvolumeclaims/status"]
     verbs: ["get"]
   - apiGroups: [""]
     resources: ["events"]
-    verbs: ["get", "list", "watch", "create", "update", "patch"]
-  - apiGroups: [""]
-    resources: ["nodes"]
-    verbs: ["get", "patch"]
-  - apiGroups: [""]
-    resources: ["nodes/proxy"]
-    verbs: ["*"]
+    verbs: ["create", "patch"]
 ---
 kind: ClusterRoleBinding
 apiVersion: rbac.authorization.k8s.io/v1
 metadata:
   name: fluid-csi-plugin
diff --git a/charts/fluid/fluid/templates/role/webhook/rabc.yaml b/charts/fluid/fluid/templates/role/webhook/rabc.yaml
index a2558ec2..c7e46795 100644
--- a/charts/fluid/fluid/templates/role/webhook/rabc.yaml
+++ b/charts/fluid/fluid/templates/role/webhook/rabc.yaml
@@ -1,18 +1,61 @@
 {{ if .Values.webhook.enabled -}}
 apiVersion: rbac.authorization.k8s.io/v1
+kind: Role
+metadata:
+  name: fluid-webhook
+  namespace: {{ include "fluid.namespace" . }}
+rules:
+  - apiGroups:
+      - ""
+    resources:
+      - secrets
+    verbs:
+      - get
+      - update
+    resourceNames:
+      - fluid-webhook-certs
+  # resourceNames won't protect create verb, so individually specify it for readability
+  - apiGroups:
+      - ""
+    resources:
+      - secrets
+    verbs:
+      - create
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: RoleBinding
+metadata:
+  name: fluid-webhook-rolebinding
+  namespace: {{ include "fluid.namespace" . }}
+roleRef:
+  apiGroup: rbac.authorization.k8s.io
+  kind: Role
+  name: fluid-webhook
+subjects:
+  - kind: ServiceAccount
+    name: fluid-webhook
+    namespace: {{ include "fluid.namespace" . }}
+---
+apiVersion: rbac.authorization.k8s.io/v1
 kind: ClusterRole
 metadata:
   name: fluid-webhook
 rules:
+  # Can only list and watch secret `mutatingwebhookconfiguration` with a metadata.name field selector
+  # See https://kubernetes.io/docs/reference/access-authn-authz/rbac/#referring-to-resources
   - apiGroups:
       - admissionregistration.k8s.io
     resources:
-      - validatingwebhookconfigurations
       - mutatingwebhookconfigurations
+    resourceNames:
+      - fluid-pod-admission-webhook
     verbs:
-      - '*'
+      - get
+      - patch
+      - list
+      - watch
   - apiGroups:
       - data.fluid.io
     resources:
       - datasets
       - alluxioruntimes
@@ -36,13 +79,11 @@ rules:
       - list
       - watch
   - apiGroups:
       - ""
     resources:
-      - secrets
       - configmaps
-      - events
     verbs:
       - get
       - create
       - update
       - list
@@ -54,16 +95,10 @@ rules:
       - daemonsets/status
     verbs:
       - get
       - list
       - watch
-  - apiGroups:
-      - coordination.k8s.io
-    resources:
-      - leases
-    verbs:
-      - '*'
 ---
 apiVersion: rbac.authorization.k8s.io/v1
 kind: ClusterRoleBinding
 metadata:
   name: fluid-webhook-clusterrolebinding
diff --git a/charts/fluid/fluid/templates/webhook/webhook.yaml b/charts/fluid/fluid/templates/webhook/webhook.yaml
index 994020bf..6b9c48e7 100644
--- a/charts/fluid/fluid/templates/webhook/webhook.yaml
+++ b/charts/fluid/fluid/templates/webhook/webhook.yaml
@@ -14,10 +14,12 @@ spec:
   template:
     metadata:
       labels:
         control-plane: fluid-webhook
     spec:
+      tolerations:
+        - operator: Exists
       {{- with .Values.image.imagePullSecrets }}
       imagePullSecrets:
         {{- toYaml . | nindent 8 }}
       {{- end }}
       serviceAccountName: fluid-webhook
diff --git a/charts/fluid/fluid/values.yaml b/charts/fluid/fluid/values.yaml
index 1b29b5d3..431aca34 100644
--- a/charts/fluid/fluid/values.yaml
+++ b/charts/fluid/fluid/values.yaml
@@ -24,10 +24,12 @@ csi:
   registrar:
     image: fluidcloudnative/csi-node-driver-registrar:v2.3.0
   plugins:
     image: fluidcloudnative/fluid-csi:v0.9.0-085b23e
   kubelet:
+    kubeConfigFile: /etc/kubernetes/kubelet.conf
+    certDir: /var/lib/kubelet/pki
     rootDir: /var/lib/kubelet
   pruneFs: fuse.alluxio-fuse,fuse.jindofs-fuse,fuse.juicefs,fuse.goosefs-fuse,ossfs,alifuse.aliyun-alinas-efc
 
 runtime:
   criticalFusePod: true
diff --git a/cmd/csi/app/csi.go b/cmd/csi/app/csi.go
index 0188f11a..d5b553e7 100644
--- a/cmd/csi/app/csi.go
+++ b/cmd/csi/app/csi.go
@@ -37,16 +37,17 @@ import (
 	clientgoscheme "k8s.io/client-go/kubernetes/scheme"
 	ctrl "sigs.k8s.io/controller-runtime"
 )
 
 var (
-	endpoint    string
-	nodeID      string
-	metricsAddr string
-	pprofAddr   string
-	pruneFs     []string
-	prunePath   string
+	endpoint              string
+	nodeID                string
+	metricsAddr           string
+	pprofAddr             string
+	pruneFs               []string
+	prunePath             string
+	kubeletKubeConfigPath string
 )
 
 var scheme = runtime.NewScheme()
 
 var startCmd = &cobra.Command{
@@ -79,10 +80,11 @@ func init() {
 
 	startCmd.Flags().StringSliceVarP(&pruneFs, "prune-fs", "", []string{"fuse.alluxio-fuse", "fuse.jindofs-fuse", "fuse.juicefs", "fuse.goosefs-fuse", "ossfs"}, "Prune fs to add in /etc/updatedb.conf, separated by comma")
 	startCmd.Flags().StringVarP(&prunePath, "prune-path", "", "/runtime-mnt", "Prune path to add in /etc/updatedb.conf")
 	startCmd.Flags().StringVarP(&metricsAddr, "metrics-addr", "", ":8080", "The address the metrics endpoint binds to.")
 	startCmd.Flags().StringVarP(&pprofAddr, "pprof-addr", "", "", "The address for pprof to use while exporting profiling results")
+	startCmd.Flags().StringVarP(&kubeletKubeConfigPath, "kubelet-kube-config", "", "/etc/kubernetes/kubelet.conf", "The file path to kubelet kube config")
 	utilfeature.DefaultMutableFeatureGate.AddFlag(startCmd.Flags())
 	startCmd.Flags().AddGoFlagSet(flag.CommandLine)
 }
 
 func ErrorAndExit(err error) {
@@ -107,14 +109,15 @@ func handle() {
 	if err != nil {
 		panic(fmt.Sprintf("csi: unable to create controller manager due to error %v", err))
 	}
 
 	config := config.Config{
-		NodeId:    nodeID,
-		Endpoint:  endpoint,
-		PruneFs:   pruneFs,
-		PrunePath: prunePath,
+		NodeId:            nodeID,
+		Endpoint:          endpoint,
+		PruneFs:           pruneFs,
+		PrunePath:         prunePath,
+		KubeletConfigPath: kubeletKubeConfigPath,
 	}
 
 	if err = csi.SetupWithManager(mgr, config); err != nil {
 		panic(fmt.Sprintf("unable to set up manager due to error %v", err))
 	}
diff --git a/pkg/csi/config/config.go b/pkg/csi/config/config.go
index 2b606872..3be35acd 100644
--- a/pkg/csi/config/config.go
+++ b/pkg/csi/config/config.go
@@ -15,10 +15,11 @@ limitations under the License.
 */
 
 package config
 
 type Config struct {
-	NodeId    string
-	Endpoint  string
-	PruneFs   []string
-	PrunePath string
+	NodeId            string
+	Endpoint          string
+	PruneFs           []string
+	PrunePath         string
+	KubeletConfigPath string
 }
diff --git a/pkg/csi/plugins/driver.go b/pkg/csi/plugins/driver.go
index f66c8b7f..5681ea05 100644
--- a/pkg/csi/plugins/driver.go
+++ b/pkg/csi/plugins/driver.go
@@ -21,10 +21,11 @@ import (
 	"fmt"
 	"os"
 	"path/filepath"
 	"strings"
 
+	"k8s.io/client-go/kubernetes"
 	"sigs.k8s.io/controller-runtime/pkg/client"
 	"sigs.k8s.io/controller-runtime/pkg/manager"
 
 	"github.com/container-storage-interface/spec/lib/go/csi"
 	"github.com/fluid-cloudnative/fluid/pkg/utils"
@@ -36,19 +37,20 @@ const (
 	driverName = "fuse.csi.fluid.io"
 	version    = "1.0.0"
 )
 
 type driver struct {
-	client           client.Client
-	apiReader        client.Reader
-	csiDriver        *csicommon.CSIDriver
-	nodeId, endpoint string
+	client               client.Client
+	apiReader            client.Reader
+	nodeAuthorizedClient *kubernetes.Clientset
+	csiDriver            *csicommon.CSIDriver
+	nodeId, endpoint     string
 }
 
 var _ manager.Runnable = &driver{}
 
-func NewDriver(nodeID, endpoint string, client client.Client, apiReader client.Reader) *driver {
+func NewDriver(nodeID, endpoint string, client client.Client, apiReader client.Reader, nodeAuthorizedClient *kubernetes.Clientset) *driver {
 	glog.Infof("Driver: %v version: %v", driverName, version)
 
 	proto, addr := utils.SplitSchemaAddr(endpoint)
 	glog.Infof("protocol: %v addr: %v", proto, addr)
 
@@ -66,15 +68,16 @@ func NewDriver(nodeID, endpoint string, client client.Client, apiReader client.R
 	csiDriver := csicommon.NewCSIDriver(driverName, version, nodeID)
 	csiDriver.AddControllerServiceCapabilities([]csi.ControllerServiceCapability_RPC_Type{csi.ControllerServiceCapability_RPC_CREATE_DELETE_VOLUME})
 	csiDriver.AddVolumeCapabilityAccessModes([]csi.VolumeCapability_AccessMode_Mode{csi.VolumeCapability_AccessMode_MULTI_NODE_MULTI_WRITER})
 
 	return &driver{
-		nodeId:    nodeID,
-		endpoint:  endpoint,
-		csiDriver: csiDriver,
-		client:    client,
-		apiReader: apiReader,
+		nodeId:               nodeID,
+		endpoint:             endpoint,
+		csiDriver:            csiDriver,
+		client:               client,
+		nodeAuthorizedClient: nodeAuthorizedClient,
+		apiReader:            apiReader,
 	}
 }
 
 func (d *driver) newControllerServer() *controllerServer {
 	return &controllerServer{
@@ -82,14 +85,15 @@ func (d *driver) newControllerServer() *controllerServer {
 	}
 }
 
 func (d *driver) newNodeServer() *nodeServer {
 	return &nodeServer{
-		nodeId:            d.nodeId,
-		DefaultNodeServer: csicommon.NewDefaultNodeServer(d.csiDriver),
-		client:            d.client,
-		apiReader:         d.apiReader,
+		nodeId:               d.nodeId,
+		DefaultNodeServer:    csicommon.NewDefaultNodeServer(d.csiDriver),
+		client:               d.client,
+		apiReader:            d.apiReader,
+		nodeAuthorizedClient: d.nodeAuthorizedClient,
 	}
 }
 
 func (d *driver) run() {
 	s := csicommon.NewNonBlockingGRPCServer()
diff --git a/pkg/csi/plugins/nodeserver.go b/pkg/csi/plugins/nodeserver.go
index fa5e94d7..dabf800b 100644
--- a/pkg/csi/plugins/nodeserver.go
+++ b/pkg/csi/plugins/nodeserver.go
@@ -15,10 +15,11 @@ limitations under the License.
 */
 
 package plugins
 
 import (
+	"encoding/json"
 	"fmt"
 	"os"
 	"os/exec"
 	"path/filepath"
 	"strings"
@@ -29,13 +30,15 @@ import (
 	"github.com/fluid-cloudnative/fluid/api/v1alpha1"
 	"github.com/fluid-cloudnative/fluid/pkg/common"
 	"github.com/fluid-cloudnative/fluid/pkg/ddc/base"
 	"github.com/fluid-cloudnative/fluid/pkg/utils"
 	"github.com/fluid-cloudnative/fluid/pkg/utils/dataset/volume"
-	"github.com/fluid-cloudnative/fluid/pkg/utils/kubeclient"
 	"github.com/pkg/errors"
 	v1 "k8s.io/api/core/v1"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/types"
+	"k8s.io/client-go/kubernetes"
 	"k8s.io/utils/mount"
 	"sigs.k8s.io/controller-runtime/pkg/client"
 
 	"github.com/container-storage-interface/spec/lib/go/csi"
 	"github.com/golang/glog"
@@ -50,14 +53,15 @@ const (
 )
 
 type nodeServer struct {
 	nodeId string
 	*csicommon.DefaultNodeServer
-	client    client.Client
-	apiReader client.Reader
-	mutex     sync.Mutex
-	node      *v1.Node
+	client               client.Client
+	apiReader            client.Reader
+	nodeAuthorizedClient *kubernetes.Clientset
+	mutex                sync.Mutex
+	node                 *v1.Node
 }
 
 func (ns *nodeServer) NodePublishVolume(ctx context.Context, req *csi.NodePublishVolumeRequest) (*csi.NodePublishVolumeResponse, error) {
 
 	glog.Infof("NodePublishVolumeRequest is %v", req)
@@ -265,11 +269,12 @@ func (ns *nodeServer) NodeUnstageVolume(ctx context.Context, req *csi.NodeUnstag
 	if err != nil {
 		glog.Errorf("NodeUnstageVolume: can't get node %s: %v", ns.nodeId, err)
 		return nil, errors.Wrapf(err, "NodeUnstageVolume: can't get node %s", ns.nodeId)
 	}
 
-	_, err = utils.ChangeNodeLabelWithPatchMode(ns.client, node, labelsToModify)
+	// _, err = utils.ChangeNodeLabelWithPatchMode(ns.client, node, labelsToModify)
+	err = ns.patchNodeWithLabel(node, labelsToModify)
 	if err != nil {
 		glog.Errorf("NodeUnstageVolume: error when patching labels on node %s: %v", ns.nodeId, err)
 		return nil, errors.Wrapf(err, "NodeUnstageVolume: error when patching labels on node %s", ns.nodeId)
 	}
 
@@ -313,11 +318,12 @@ func (ns *nodeServer) NodeStageVolume(ctx context.Context, req *csi.NodeStageVol
 	if err != nil {
 		glog.Errorf("NodeStageVolume: can't get node %s: %v", ns.nodeId, err)
 		return nil, errors.Wrapf(err, "NodeStageVolume: can't get node %s", ns.nodeId)
 	}
 
-	_, err = utils.ChangeNodeLabelWithPatchMode(ns.client, node, labelsToModify)
+	// _, err = utils.ChangeNodeLabelWithPatchMode(ns.client, node, labelsToModify)
+	err = ns.patchNodeWithLabel(node, labelsToModify)
 	if err != nil {
 		glog.Errorf("NodeStageVolume: error when patching labels on node %s: %v", ns.nodeId, err)
 		return nil, errors.Wrapf(err, "NodeStageVolume: error when patching labels on node %s", ns.nodeId)
 	}
 
@@ -371,18 +377,62 @@ func (ns *nodeServer) getNode() (node *v1.Node, err error) {
 			glog.V(3).Infof("Found cached node %s", ns.node.Name)
 			return ns.node, nil
 		}
 	}
 
-	if node, err = kubeclient.GetNode(ns.apiReader, ns.nodeId); err != nil {
+	if node, err = ns.nodeAuthorizedClient.CoreV1().Nodes().Get(context.TODO(), ns.nodeId, metav1.GetOptions{}); err != nil {
 		return nil, err
 	}
+
+	// if node, err = kubeclient.GetNode(ns.apiReader, ns.nodeId); err != nil {
+	// return nil, err
+	// }
+
 	glog.V(1).Infof("Got node %s from api server", node.Name)
 	ns.node = node
 	return ns.node, nil
 }
 
+func (ns *nodeServer) patchNodeWithLabel(node *v1.Node, labelsToModify common.LabelsToModify) error {
+	labels := labelsToModify.GetLabels()
+	labelValuePair := map[string]interface{}{}
+
+	for _, labelToModify := range labels {
+		operationType := labelToModify.GetOperationType()
+		labelToModifyKey := labelToModify.GetLabelKey()
+		labelToModifyValue := labelToModify.GetLabelValue()
+
+		switch operationType {
+		case common.AddLabel, common.UpdateLabel:
+			labelValuePair[labelToModifyKey] = labelToModifyValue
+		case common.DeleteLabel:
+			labelValuePair[labelToModifyKey] = nil
+		default:
+			err := fmt.Errorf("fail to update the label due to the wrong operation: %s", operationType)
+			return err
+		}
+	}
+
+	metadata := map[string]interface{}{
+		"metadata": map[string]interface{}{
+			"labels": labelValuePair,
+		},
+	}
+
+	patchByteData, err := json.Marshal(metadata)
+	if err != nil {
+		return err
+	}
+
+	_, err = ns.nodeAuthorizedClient.CoreV1().Nodes().Patch(context.TODO(), node.Name, types.StrategicMergePatchType, patchByteData, metav1.PatchOptions{})
+	if err != nil {
+		return err
+	}
+
+	return nil
+}
+
 func checkMountInUse(volumeName string) (bool, error) {
 	var inUse bool
 	glog.Infof("Try to check if the volume %s is being used", volumeName)
 	if volumeName == "" {
 		return inUse, errors.New("volumeName is not specified")
@@ -452,11 +502,12 @@ func (ns *nodeServer) prepareSessMgr(workDir string) error {
 	node, err := ns.getNode()
 	if err != nil {
 		return errors.Wrapf(err, "can't get node %s", ns.nodeId)
 	}
 
-	_, err = utils.ChangeNodeLabelWithPatchMode(ns.client, node, labelsToModify)
+	// _, err = utils.ChangeNodeLabelWithPatchMode(ns.client, node, labelsToModify)
+	err = ns.patchNodeWithLabel(node, labelsToModify)
 	if err != nil {
 		return errors.Wrapf(err, "error when patching labels on node %s", ns.nodeId)
 	}
 
 	// check sessmgrd.sock file existence
diff --git a/pkg/csi/plugins/register.go b/pkg/csi/plugins/register.go
index e15b09f0..45406626 100644
--- a/pkg/csi/plugins/register.go
+++ b/pkg/csi/plugins/register.go
@@ -16,16 +16,22 @@ limitations under the License.
 
 package plugins
 
 import (
 	"github.com/fluid-cloudnative/fluid/pkg/csi/config"
+	"github.com/fluid-cloudnative/fluid/pkg/utils/kubelet"
 	"sigs.k8s.io/controller-runtime/pkg/manager"
 )
 
 // Register initializes the csi driver and registers it to the controller manager.
 func Register(mgr manager.Manager, cfg config.Config) error {
-	csiDriver := NewDriver(cfg.NodeId, cfg.Endpoint, mgr.GetClient(), mgr.GetAPIReader())
+	client, err := kubelet.InitNodeAuthorizedClient(cfg.KubeletConfigPath)
+	if err != nil {
+		return err
+	}
+
+	csiDriver := NewDriver(cfg.NodeId, cfg.Endpoint, mgr.GetClient(), mgr.GetAPIReader(), client)
 
 	if err := mgr.Add(csiDriver); err != nil {
 		return err
 	}
 
diff --git a/pkg/utils/kubelet/node_auth_client.go b/pkg/utils/kubelet/node_auth_client.go
new file mode 100644
index 00000000..a7aeb58f
--- /dev/null
+++ b/pkg/utils/kubelet/node_auth_client.go
@@ -0,0 +1,24 @@
+package kubelet
+
+import (
+	"github.com/pkg/errors"
+	"k8s.io/client-go/kubernetes"
+	"k8s.io/client-go/tools/clientcmd"
+)
+
+// InitNodeAuthorizedClient initializes node authorized client with kubelet's kube config.
+// This is now an available workaround to implement a node-scoped daemonset.
+// See discussion https://github.com/kubernetes/enhancements/pull/944#issuecomment-490242290
+func InitNodeAuthorizedClient(kubeletKubeConfigPath string) (*kubernetes.Clientset, error) {
+	config, err := clientcmd.BuildConfigFromFlags("", kubeletKubeConfigPath)
+	if err != nil {
+		return nil, errors.Wrapf(err, "fail to build kubelet config")
+	}
+
+	client, err := kubernetes.NewForConfig(config)
+	if err != nil {
+		return nil, errors.Wrap(err, "fail to build client-go client from kubelet kubeconfig")
+	}
+
+	return client, nil
+}

Vulnerable code: * Support pass image pull secrets from fluid charts to alluxioruntime controller
          - name: host-etc-dir
            mountPath: /host-etc
            path: /etc
          name: host-etc-dir
    verbs: ["get", "list", "watch", "create", "update", "patch"]
  - apiGroups: [""]
    resources: ["nodes"]
    verbs: ["get", "patch"]
  - apiGroups: [""]
    resources: ["nodes/proxy"]
    verbs: ["*"]
      - validatingwebhookconfigurations
      - '*'
      - secrets
      - events
  - apiGroups:
      - coordination.k8s.io
    resources:
      - leases
    verbs:
      - '*'
	endpoint    string
	nodeID      string
	metricsAddr string
	pprofAddr   string
	pruneFs     []string
	prunePath   string
		NodeId:    nodeID,
		Endpoint:  endpoint,
		PruneFs:   pruneFs,
		PrunePath: prunePath,
	NodeId    string
	Endpoint  string
	PruneFs   []string
	PrunePath string
	client           client.Client
	apiReader        client.Reader
	csiDriver        *csicommon.CSIDriver
	nodeId, endpoint string
func NewDriver(nodeID, endpoint string, client client.Client, apiReader client.Reader) *driver {
		nodeId:    nodeID,
		endpoint:  endpoint,
		csiDriver: csiDriver,
		client:    client,
		apiReader: apiReader,
		nodeId:            d.nodeId,
		DefaultNodeServer: csicommon.NewDefaultNodeServer(d.csiDriver),
		client:            d.client,
		apiReader:         d.apiReader,
	"github.com/fluid-cloudnative/fluid/pkg/utils/kubeclient"
	client    client.Client
	apiReader client.Reader
	mutex     sync.Mutex
	node      *v1.Node
	_, err = utils.ChangeNodeLabelWithPatchMode(ns.client, node, labelsToModify)
	_, err = utils.ChangeNodeLabelWithPatchMode(ns.client, node, labelsToModify)
	if node, err = kubeclient.GetNode(ns.apiReader, ns.nodeId); err != nil {
	_, err = utils.ChangeNodeLabelWithPatchMode(ns.client, node, labelsToModify)
	csiDriver := NewDriver(cfg.NodeId, cfg.Endpoint, mgr.GetClient(), mgr.GetAPIReader())
Fixed code: * Support pass image pull secrets from fluid charts to alluxioruntime controller
* Fix components rbacs and set Fluid CSI Plugin with node-authorized kube-client
          - name: kubelet-kube-config
            mountPath: /etc/kubernetes/kubelet.conf
            readOnly: true
          - name: kubelet-cert-dir
            mountPath: {{ .Values.csi.kubelet.certDir | quote }}
            readOnly: true
          - name: updatedb-conf
            mountPath: /host-etc/updatedb.conf
          - name: updatedb-conf-bak
            mountPath: /host-etc/updatedb.conf.bak
            path: {{ .Values.csi.kubelet.kubeConfigFile | quote }}
            type: File
          name: kubelet-kube-config
        - hostPath:
            path: {{ .Values.csi.kubelet.certDir | quote }}
          name: kubelet-cert-dir
        - hostPath:
            path: /etc/updatedb.conf
            type: FileOrCreate
          name: updatedb-conf
        - hostPath:
            path: /etc/updatedb.conf.backup
            type: FileOrCreate
          name: updatedb-conf-bak
    verbs: ["create", "patch"]
kind: Role
metadata:
  name: fluid-webhook
  namespace: {{ include "fluid.namespace" . }}
rules:
  - apiGroups:
      - ""
    resources:
      - secrets
    verbs:
      - get
      - update
    resourceNames:
      - fluid-webhook-certs
  # resourceNames won't protect create verb, so individually specify it for readability
  - apiGroups:
      - ""
    resources:
      - secrets
    verbs:
      - create
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: fluid-webhook-rolebinding
  namespace: {{ include "fluid.namespace" . }}
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: fluid-webhook
subjects:
  - kind: ServiceAccount
    name: fluid-webhook
    namespace: {{ include "fluid.namespace" . }}
---
apiVersion: rbac.authorization.k8s.io/v1
  # Can only list and watch secret `mutatingwebhookconfiguration` with a metadata.name field selector
  # See https://kubernetes.io/docs/reference/access-authn-authz/rbac/#referring-to-resources
    resourceNames:
      - fluid-pod-admission-webhook
      - get
      - patch
      - list
      - watch
      tolerations:
        - operator: Exists
    kubeConfigFile: /etc/kubernetes/kubelet.conf
    certDir: /var/lib/kubelet/pki
	endpoint              string
	nodeID                string
	metricsAddr           string
	pprofAddr             string
	pruneFs               []string
	prunePath             string
	kubeletKubeConfigPath string
	startCmd.Flags().StringVarP(&kubeletKubeConfigPath, "kubelet-kube-config", "", "/etc/kubernetes/kubelet.conf", "The file path to kubelet kube config")
		NodeId:            nodeID,
		Endpoint:          endpoint,
		PruneFs:           pruneFs,
		PrunePath:         prunePath,
		KubeletConfigPath: kubeletKubeConfigPath,
	NodeId            string
	Endpoint          string
	PruneFs           []string
	PrunePath         string
	KubeletConfigPath string
	"k8s.io/client-go/kubernetes"
	client               client.Client
	apiReader            client.Reader
	nodeAuthorizedClient *kubernetes.Clientset
	csiDriver            *csicommon.CSIDriver
	nodeId, endpoint     string
func NewDriver(nodeID, endpoint string, client client.Client, apiReader client.Reader, nodeAuthorizedClient *kubernetes.Clientset) *driver {
		nodeId:               nodeID,
		endpoint:             endpoint,
		csiDriver:            csiDriver,
		client:               client,
		nodeAuthorizedClient: nodeAuthorizedClient,
		apiReader:            apiReader,
		nodeId:               d.nodeId,
		DefaultNodeServer:    csicommon.NewDefaultNodeServer(d.csiDriver),
		client:               d.client,
		apiReader:            d.apiReader,
		nodeAuthorizedClient: d.nodeAuthorizedClient,
	"encoding/json"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/types"
	"k8s.io/client-go/kubernetes"
	client               client.Client
	apiReader            client.Reader
	nodeAuthorizedClient *kubernetes.Clientset
	mutex                sync.Mutex
	node                 *v1.Node
	// _, err = utils.ChangeNodeLabelWithPatchMode(ns.client, node, labelsToModify)
	err = ns.patchNodeWithLabel(node, labelsToModify)
	// _, err = utils.ChangeNodeLabelWithPatchMode(ns.client, node, labelsToModify)
	err = ns.patchNodeWithLabel(node, labelsToModify)
	if node, err = ns.nodeAuthorizedClient.CoreV1().Nodes().Get(context.TODO(), ns.nodeId, metav1.GetOptions{}); err != nil {

	// if node, err = kubeclient.GetNode(ns.apiReader, ns.nodeId); err != nil {
	// return nil, err
	// }

func (ns *nodeServer) patchNodeWithLabel(node *v1.Node, labelsToModify common.LabelsToModify) error {
	labels := labelsToModify.GetLabels()
	labelValuePair := map[string]interface{}{}

	for _, labelToModify := range labels {
		operationType := labelToModify.GetOperationType()
		labelToModifyKey := labelToModify.GetLabelKey()
		labelToModifyValue := labelToModify.GetLabelValue()

		switch operationType {
		case common.AddLabel, common.UpdateLabel:
			labelValuePair[labelToModifyKey] = labelToModifyValue
		case common.DeleteLabel:
			labelValuePair[labelToModifyKey] = nil
		default:
			err := fmt.Errorf("fail to update the label due to the wrong operation: %s", operationType)
			return err
		}
	}

	metadata := map[string]interface{}{
		"metadata": map[string]interface{}{
			"labels": labelValuePair,
		},
	}

	patchByteData, err := json.Marshal(metadata)
	if err != nil {
		return err
	}

	_, err = ns.nodeAuthorizedClient.CoreV1().Nodes().Patch(context.TODO(), node.Name, types.StrategicMergePatchType, patchByteData, metav1.PatchOptions{})
	if err != nil {
		return err
	}

	return nil
}

	// _, err = utils.ChangeNodeLabelWithPatchMode(ns.client, node, labelsToModify)
	err = ns.patchNodeWithLabel(node, labelsToModify)
	"github.com/fluid-cloudnative/fluid/pkg/utils/kubelet"
	client, err := kubelet.InitNodeAuthorizedClient(cfg.KubeletConfigPath)
	if err != nil {
		return err
	}

	csiDriver := NewDriver(cfg.NodeId, cfg.Endpoint, mgr.GetClient(), mgr.GetAPIReader(), client)
package kubelet

import (
	"github.com/pkg/errors"
	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/tools/clientcmd"
)

// InitNodeAuthorizedClient initializes node authorized client with kubelet's kube config.
// This is now an available workaround to implement a node-scoped daemonset.
// See discussion https://github.com/kubernetes/enhancements/pull/944#issuecomment-490242290
func InitNodeAuthorizedClient(kubeletKubeConfigPath string) (*kubernetes.Clientset, error) {
	config, err := clientcmd.BuildConfigFromFlags("", kubeletKubeConfigPath)
	if err != nil {
		return nil, errors.Wrapf(err, "fail to build kubelet config")
	}

	client, err := kubernetes.NewForConfig(config)
	if err != nil {
		return nil, errors.Wrap(err, "fail to build client-go client from kubelet kubeconfig")
	}

	return client, nil
}