VYPR
Moderate severityNVD Advisory· Published May 8, 2023· Updated Jan 29, 2025

On a compromised node, the fluid-csi service account can be used to modify node specs

CVE-2023-30840

Description

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.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

Fluid's CSI plugin before v0.8.6 allowed a compromised node to use the fluid-csi service account to modify node specs, enabling cluster-wide privilege escalation.

Vulnerability

Overview In Fluid versions 0.7.0 through 0.8.5, the CSI node plugin (csi-nodeplugin-fluid) runs with a service account that has overly broad RBAC permissions. The patch analysis shows that the service account previously had get, list, watch, create, update, patch on events, plus get, patch on nodes and * on nodes/proxy [3]. After the fix, these were reduced to only create and patch on events, removing node-level permissions entirely. The CSI plugin also previously mounted the host /etc directory, which was replaced with more specific mounts for kubelet configuration and certificates [1][3]. This change prevents the CSI container from accessing sensitive system files across the node.

Exploitation

Prerequisites and Method Exploitation requires two preconditions: an attacker must first compromise a node running the csi-nodeplugin-fluid pod, and must identify all vulnerable nodes through other means (since the service account cannot list nodes). Once these are met, the attacker can use the compromised CSI pod's service account to modify node specs across the cluster. A described attack pattern involves tainting all other nodes to make them unschedulable, then waiting for high-privilege system components to land on the compromised node, which can then be leveraged to access secrets or execute pods elsewhere [2].

Impact

Successful exploitation allows the attacker to escalate from a single compromised node to full cluster compromise. By manipulating node specs and leveraging system-level privileged components that arrive on the compromised node, an attacker could access all secrets in the cluster or execute arbitrary pods on other nodes. The CVSS score could be severe, as the attack chain bridges container escape and lateral movement to achieve privilege escalation across the entire Kubernetes cluster [2].

Mitigation

Fluid version 0.8.6 patches this vulnerability by fixing the CSI plugin's RBAC roles and changing host mounts to use a node-authorized kubelet client instead of the broad node-proxy permissions [1][3][4]. As a workaround, administrators can delete the csi-nodeplugin-fluid DaemonSet and avoid using CSI mode for mounting FUSE filesystems; using sidecar mode is recommended instead [2].

AI Insight generated on May 20, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
github.com/fluid-cloudnative/fluidGo
>= 0.7.0, < 0.8.60.8.6

Affected products

3

Patches

2
77c8110a3d1e

Merge pull request from GHSA-93xx-cvmc-9w3v

12 files changed · +203 60
  • charts/fluid/fluid/CHANGELOG.md+2 1 modified
    @@ -53,4 +53,5 @@
     * 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
    
  • charts/fluid/fluid/templates/csi/daemonset.yaml+24 4 modified
    @@ -104,8 +104,16 @@ spec:
               - 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:
    @@ -124,6 +132,18 @@ spec:
                 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
    
  • charts/fluid/fluid/templates/role/csi/rbac.yaml+1 7 modified
    @@ -41,13 +41,7 @@ rules:
         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
    
  • charts/fluid/fluid/templates/role/webhook/rabc.yaml+45 10 modified
    @@ -1,16 +1,59 @@
     {{ 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:
    @@ -38,9 +81,7 @@ rules:
       - apiGroups:
           - ""
         resources:
    -      - secrets
           - configmaps
    -      - events
         verbs:
           - get
           - create
    @@ -56,12 +97,6 @@ rules:
           - get
           - list
           - watch
    -  - apiGroups:
    -      - coordination.k8s.io
    -    resources:
    -      - leases
    -    verbs:
    -      - '*'
     ---
     apiVersion: rbac.authorization.k8s.io/v1
     kind: ClusterRoleBinding
    
  • charts/fluid/fluid/templates/webhook/webhook.yaml+2 0 modified
    @@ -16,6 +16,8 @@ spec:
           labels:
             control-plane: fluid-webhook
         spec:
    +      tolerations:
    +        - operator: Exists
           {{- with .Values.image.imagePullSecrets }}
           imagePullSecrets:
             {{- toYaml . | nindent 8 }}
    
  • charts/fluid/fluid/values.yaml+2 0 modified
    @@ -26,6 +26,8 @@ csi:
       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
     
    
  • cmd/csi/app/csi.go+13 10 modified
    @@ -39,12 +39,13 @@ import (
     )
     
     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()
    @@ -81,6 +82,7 @@ func init() {
     	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)
     }
    @@ -109,10 +111,11 @@ func handle() {
     	}
     
     	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 {
    
  • pkg/csi/config/config.go+5 4 modified
    @@ -17,8 +17,9 @@ 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
     }
    
  • pkg/csi/plugins/driver.go+18 14 modified
    @@ -23,6 +23,7 @@ import (
     	"path/filepath"
     	"strings"
     
    +	"k8s.io/client-go/kubernetes"
     	"sigs.k8s.io/controller-runtime/pkg/client"
     	"sigs.k8s.io/controller-runtime/pkg/manager"
     
    @@ -38,15 +39,16 @@ const (
     )
     
     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)
    @@ -68,11 +70,12 @@ func NewDriver(nodeID, endpoint string, client client.Client, apiReader client.R
     	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,
     	}
     }
     
    @@ -84,10 +87,11 @@ 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,
     	}
     }
     
    
  • pkg/csi/plugins/nodeserver.go+60 9 modified
    @@ -17,6 +17,7 @@ limitations under the License.
     package plugins
     
     import (
    +	"encoding/json"
     	"fmt"
     	"os"
     	"os/exec"
    @@ -31,9 +32,11 @@ import (
     	"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"
     
    @@ -52,10 +55,11 @@ 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) {
    @@ -267,7 +271,8 @@ func (ns *nodeServer) NodeUnstageVolume(ctx context.Context, req *csi.NodeUnstag
     		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)
    @@ -315,7 +320,8 @@ func (ns *nodeServer) NodeStageVolume(ctx context.Context, req *csi.NodeStageVol
     		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)
    @@ -373,14 +379,58 @@ func (ns *nodeServer) getNode() (node *v1.Node, err error) {
     		}
     	}
     
    -	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)
    @@ -454,7 +504,8 @@ func (ns *nodeServer) prepareSessMgr(workDir string) error {
     		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)
     	}
    
  • pkg/csi/plugins/register.go+7 1 modified
    @@ -18,12 +18,18 @@ 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
    
  • pkg/utils/kubelet/node_auth_client.go+24 0 added
    @@ -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
    +}
    
91c05c32db13

Merge pull request from GHSA-93xx-cvmc-9w3v

13 files changed · +215 73
  • charts/fluid/fluid/CHANGELOG.md+1 0 modified
    @@ -54,3 +54,4 @@
     
     ### 0.9.0
     * Support pass image pull secrets from fluid charts to alluxioruntime controller
    +* Fix components rbacs and set Fluid CSI Plugin with node-authorized kube-client
    
  • charts/fluid/fluid/Chart.yaml+1 1 modified
    @@ -18,7 +18,7 @@ version: 0.8.6
     
     # This is the version number of the application being deployed. This version number should be
     # incremented each time you make changes to the application.
    -appVersion: 0.8.5-00f609e
    +appVersion: 0.8.6-2131f34
     home: https://github.com/fluid-cloudnative/fluid
     keywords:
       - category:data
    
  • charts/fluid/fluid/templates/csi/daemonset.yaml+24 4 modified
    @@ -104,8 +104,16 @@ spec:
               - 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:
    @@ -124,6 +132,18 @@ spec:
                 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
    
  • charts/fluid/fluid/templates/role/csi/rbac.yaml+1 7 modified
    @@ -37,13 +37,7 @@ rules:
         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
    
  • charts/fluid/fluid/templates/role/webhook/rabc.yaml+45 10 modified
    @@ -1,16 +1,59 @@
     {{ if .Values.webhook.enabled -}}
     apiVersion: rbac.authorization.k8s.io/v1
    +kind: Role
    +metadata:
    +  name: fluid-webhook
    +  namespace: fluid-system
    +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: fluid-system
    +roleRef:
    +  apiGroup: rbac.authorization.k8s.io
    +  kind: Role
    +  name: fluid-webhook
    +subjects:
    +  - kind: ServiceAccount
    +    name: fluid-webhook
    +    namespace: fluid-system
    +---
    +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:
    @@ -36,9 +79,7 @@ rules:
       - apiGroups:
           - ""
         resources:
    -      - secrets
           - configmaps
    -      - events
         verbs:
           - get
           - create
    @@ -54,12 +95,6 @@ rules:
           - get
           - list
           - watch
    -  - apiGroups:
    -      - coordination.k8s.io
    -    resources:
    -      - leases
    -    verbs:
    -      - '*'
     ---
     apiVersion: rbac.authorization.k8s.io/v1
     kind: ClusterRoleBinding
    
  • charts/fluid/fluid/templates/webhook/webhook.yaml+2 1 modified
    @@ -16,6 +16,8 @@ spec:
           labels:
             control-plane: fluid-webhook
         spec:
    +      tolerations:
    +        - operator: Exists
           {{- with .Values.image.imagePullSecrets }}
           imagePullSecrets:
             {{- toYaml . | nindent 8 }}
    @@ -29,7 +31,6 @@ spec:
               - --development=false
               - --full-go-profile=false
               - --pprof-addr=:6060
    -          - --enable-leader-election
             env:
               - name: MY_POD_NAMESPACE
                 valueFrom:
    
  • charts/fluid/fluid/values.yaml+14 12 modified
    @@ -4,15 +4,15 @@
     
     workdir: /tmp
     crdUpgrade:
    -  image: fluidcloudnative/fluid-crd-upgrader:v0.8.5-00f609e
    +  image: fluidcloudnative/fluid-crd-upgrader:v0.8.6-2131f34
     
     image:
       imagePullSecrets: []
     
     dataset:
       replicas: 1
       controller:
    -    image: fluidcloudnative/dataset-controller:v0.8.5-00f609e
    +    image: fluidcloudnative/dataset-controller:v0.8.6-2131f34
     
     csi:
       featureGates: "FuseRecovery=false"
    @@ -21,8 +21,10 @@ csi:
       registrar:
         image: registry.aliyuncs.com/acs/csi-node-driver-registrar:v2.3.0-038aeb6-aliyun
       plugins:
    -    image: fluidcloudnative/fluid-csi:v0.8.5-00f609e
    +    image: fluidcloudnative/fluid-csi:v0.8.6-2131f34
       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
     
    @@ -37,9 +39,9 @@ runtime:
         portAllocatePolicy: random
         enabled: false
         init:
    -      image: fluidcloudnative/init-users:v0.8.5-00f609e
    +      image: fluidcloudnative/init-users:v0.8.6-2131f34
         controller:
    -      image: fluidcloudnative/alluxioruntime-controller:v0.8.5-00f609e
    +      image: fluidcloudnative/alluxioruntime-controller:v0.8.6-2131f34
         runtime:
           # image: fluidcloudnative/alluxio:release-2.7.3-SNAPSHOT-a7154f1
           image: fluidcloudnative/alluxio:release-2.8.1-SNAPSHOT-0433ade
    @@ -59,21 +61,21 @@ runtime:
         fuse:
           image: registry.cn-shanghai.aliyuncs.com/jindofs/jindo-fuse:4.5.1
         controller:
    -      image: fluidcloudnative/jindoruntime-controller:v0.8.5-00f609e
    +      image: fluidcloudnative/jindoruntime-controller:v0.8.6-2131f34
         init:
           portCheck:
             enabled: false
    -      image: fluidcloudnative/init-users:v0.8.5-00f609e
    +      image: fluidcloudnative/init-users:v0.8.6-2131f34
       goosefs:
         replicas: 1
         runtimeWorkers: 3
         portRange: 26000-32000
         portAllocatePolicy: random
         enabled: false
         init:
    -      image: fluidcloudnative/init-users:v0.8.5-00f609e
    +      image: fluidcloudnative/init-users:v0.8.6-2131f34
         controller:
    -      image: fluidcloudnative/goosefsruntime-controller:v0.8.5-00f609e
    +      image: fluidcloudnative/goosefsruntime-controller:v0.8.6-2131f34
         runtime:
           image: ccr.ccs.tencentyun.com/qcloud/goosefs:v1.2.0
         fuse:
    @@ -82,18 +84,18 @@ runtime:
         replicas: 1
         enabled: false
         controller:
    -      image: fluidcloudnative/juicefsruntime-controller:v0.8.5-00f609e
    +      image: fluidcloudnative/juicefsruntime-controller:v0.8.6-2131f34
         fuse:
           image: juicedata/juicefs-fuse:v1.0.0-4.8.0
     
     webhook:
       enabled: true
    -  image: fluidcloudnative/fluid-webhook:v0.8.5-00f609e
    +  image: fluidcloudnative/fluid-webhook:v0.8.6-2131f34
       replicas: 1
       reinvocationPolicy: Never
     
     fluidapp:
       enabled: true
       replicas: 1
       controller:
    -    image: fluidcloudnative/application-controller:v0.8.5-00f609e
    +    image: fluidcloudnative/application-controller:v0.8.6-2131f34
    
  • cmd/csi/app/csi.go+13 10 modified
    @@ -39,12 +39,13 @@ import (
     )
     
     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()
    @@ -81,6 +82,7 @@ func init() {
     	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)
     }
    @@ -109,10 +111,11 @@ func handle() {
     	}
     
     	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 {
    
  • pkg/csi/config/config.go+5 4 modified
    @@ -17,8 +17,9 @@ 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
     }
    
  • pkg/csi/plugins/driver.go+20 15 modified
    @@ -20,9 +20,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"
    -	"strings"
     
     	"github.com/container-storage-interface/spec/lib/go/csi"
     	"github.com/fluid-cloudnative/fluid/pkg/utils"
    @@ -36,15 +38,16 @@ const (
     )
     
     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)
    @@ -66,11 +69,12 @@ func NewDriver(nodeID, endpoint string, client client.Client, apiReader client.R
     	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,
     	}
     }
     
    @@ -82,10 +86,11 @@ 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,
     	}
     }
     
    
  • pkg/csi/plugins/nodeserver.go+58 8 modified
    @@ -16,6 +16,7 @@ limitations under the License.
     package plugins
     
     import (
    +	"encoding/json"
     	"fmt"
     	"os"
     	"os/exec"
    @@ -28,9 +29,11 @@ import (
     	"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"
     
    @@ -49,10 +52,11 @@ 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) {
    @@ -247,7 +251,8 @@ func (ns *nodeServer) NodeUnstageVolume(ctx context.Context, req *csi.NodeUnstag
     		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)
    @@ -286,7 +291,8 @@ func (ns *nodeServer) NodeStageVolume(ctx context.Context, req *csi.NodeStageVol
     		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)
    @@ -344,14 +350,58 @@ func (ns *nodeServer) getNode() (node *v1.Node, err error) {
     		}
     	}
     
    -	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)
    
  • pkg/csi/plugins/register.go+7 1 modified
    @@ -18,12 +18,18 @@ 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
    
  • pkg/utils/kubelet/node_auth_client.go+24 0 added
    @@ -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
    +}
    

Vulnerability mechanics

Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

6

News mentions

0

No linked articles in our index yet.