Skip to content

Commit 7f8afd8

Browse files
authored
Merge pull request #2206 from dobsonj/restrict-service-account-role
scope roles/iam.serviceAccountUser to node service accounts
2 parents bf08858 + 7ef4a52 commit 7f8afd8

File tree

6 files changed

+79
-11
lines changed

6 files changed

+79
-11
lines changed

deploy/common.sh

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
readonly KUSTOMIZE_PATH="${PKGDIR}/bin/kustomize"
55
readonly VERBOSITY="${GCE_PD_VERBOSITY:-2}"
66
readonly KUBECTL="${GCE_PD_KUBECTL:-kubectl}"
7+
readonly SA_USER_ROLE="roles/iam.serviceAccountUser"
78

89
# Common functions
910

@@ -17,9 +18,21 @@ function ensure_var(){
1718
fi
1819
}
1920

21+
# Return true if scoping serviceAccountUser role to node service accounts
22+
function use_scoped_sa_role()
23+
{
24+
[ -n "${NODE_SERVICE_ACCOUNTS:-}" ]
25+
}
26+
2027
function get_needed_roles()
2128
{
22-
echo "roles/editor roles/compute.storageAdmin roles/iam.serviceAccountUser projects/${PROJECT}/roles/gcp_compute_persistent_disk_csi_driver_custom_role"
29+
ROLES="roles/editor roles/compute.storageAdmin projects/${PROJECT}/roles/gcp_compute_persistent_disk_csi_driver_custom_role"
30+
# Grant the role at the project level if no node service accounts are provided.
31+
if ! use_scoped_sa_role;
32+
then
33+
ROLES+=" ${SA_USER_ROLE}"
34+
fi
35+
echo "${ROLES}"
2336
}
2437

2538
# Installs kustomize in ${PKGDIR}/bin

deploy/kubernetes/base/controller/controller.yaml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,6 @@ spec:
146146
- "--supports-dynamic-throughput-provisioning=hyperdisk-balanced,hyperdisk-throughput,hyperdisk-ml"
147147
- --enable-data-cache
148148
- --run-node-service=false
149-
- --enable-multitenancy
150149
command:
151150
- /gce-pd-csi-driver
152151
env:

deploy/kubernetes/base/node_linux/node.yaml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,6 @@ spec:
4747
- "--endpoint=unix:/csi/csi.sock"
4848
- "--run-controller-service=false"
4949
- "--enable-data-cache"
50-
- "--enable-multitenancy"
5150
- "--node-name=$(KUBE_NODE_NAME)"
5251
securityContext:
5352
privileged: true

deploy/kubernetes/deploy-driver.sh

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@
1212
# by setup-project.sh). Ignored if GCE_PD_DRIVER_VERSION == noauth.
1313
# GCE_PD_DRIVER_VERSION: The kustomize overlay to deploy. See
1414
# `deploy/kubernetes/overlays` for your choices.
15+
# NODE_SERVICE_ACCOUNTS: (Optional) Comma-separated list of service accounts
16+
# that the CSI driver should be allowed to impersonate. If not specified,
17+
# defaults to project-level serviceAccountUser role.
1518

1619
set -o nounset
1720
set -o errexit
@@ -65,6 +68,23 @@ function check_service_account()
6568
MISSING_ROLES=true
6669
fi
6770
done
71+
72+
# Check each node service account for serviceAccountUser role
73+
if use_scoped_sa_role;
74+
then
75+
IFS=',' read -ra NODE_SA_ARRAY <<< "${NODE_SERVICE_ACCOUNTS}"
76+
for node_sa in "${NODE_SA_ARRAY[@]}";
77+
do
78+
node_sa=$(echo "${node_sa}" | xargs) # trim whitespace
79+
SA_POLICY=$(gcloud iam service-accounts get-iam-policy "${node_sa}" --project="${PROJECT}" --flatten="bindings[].members" --format='table(bindings.role)' --filter="bindings.members:${IAM_NAME}")
80+
if ! grep -q "${SA_USER_ROLE}" <<< "${SA_POLICY}";
81+
then
82+
echo "Missing ${SA_USER_ROLE} for: ${node_sa}"
83+
MISSING_ROLES=true
84+
fi
85+
done
86+
fi
87+
6888
if [ "${MISSING_ROLES}" = true ];
6989
then
7090
echo "Cannot deploy with missing roles in service account, please run setup-project.sh to setup Service Account"

deploy/setup-project.sh

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@
1717
# ENABLE_KMS_ADMIN: Add service account permissions to destroy Cloud KMS keys.
1818
# CREATE_SA_KEY: (Optional) If true, creates a new service account key and
1919
# exports it if creating a new service account
20+
# NODE_SERVICE_ACCOUNTS: (Optional) Comma-separated list of service accounts
21+
# that the CSI driver should be allowed to impersonate. If not specified,
22+
# defaults to project-level serviceAccountUser role.
2023

2124
set -o nounset
2225
set -o errexit
@@ -102,22 +105,36 @@ gcloud iam roles $action gcp_compute_persistent_disk_csi_driver_custom_role --qu
102105
# Bind service account to roles
103106
for role in ${BIND_ROLES}
104107
do
105-
gcloud projects add-iam-policy-binding "${PROJECT}" --member serviceAccount:"${IAM_NAME}" --role "${role}"
108+
gcloud projects add-iam-policy-binding "${PROJECT}" --member serviceAccount:"${IAM_NAME}" --role "${role}" --condition=None
106109
done
107110

111+
# Grant scoped serviceAccountUser role for node service accounts
112+
if use_scoped_sa_role;
113+
then
114+
IFS=',' read -ra NODE_SA_ARRAY <<< "${NODE_SERVICE_ACCOUNTS}"
115+
for node_sa in "${NODE_SA_ARRAY[@]}";
116+
do
117+
node_sa=$(echo "${node_sa}" | xargs) # trim whitespace
118+
echo "Granting ${SA_USER_ROLE} for ${node_sa} to serviceAccount:${IAM_NAME}"
119+
gcloud iam service-accounts add-iam-policy-binding "${node_sa}" \
120+
--member="serviceAccount:${IAM_NAME}" --condition=None \
121+
--role="${SA_USER_ROLE}" --project="${PROJECT}"
122+
done
123+
fi
124+
108125
# Authorize GCE to encrypt/decrypt using Cloud KMS encryption keys.
109126
# https://cloud.google.com/compute/docs/disks/customer-managed-encryption#before_you_begin
110127
if [ "${ENABLE_KMS}" = true ];
111128
then
112129
gcloud services enable cloudkms.googleapis.com --project="${PROJECT}"
113-
gcloud projects add-iam-policy-binding "${PROJECT}" --member serviceAccount:"service-${PROJECT_NUMBER}@compute-system.iam.gserviceaccount.com" --role "roles/cloudkms.cryptoKeyEncrypterDecrypter"
130+
gcloud projects add-iam-policy-binding "${PROJECT}" --member serviceAccount:"service-${PROJECT_NUMBER}@compute-system.iam.gserviceaccount.com" --role "roles/cloudkms.cryptoKeyEncrypterDecrypter" --condition=None
114131
fi
115132

116133
# Authorize SA to destroy Cloud KMS encryption keys.
117134
if [ "${ENABLE_KMS_ADMIN}" = true ];
118135
then
119136
gcloud services enable cloudkms.googleapis.com --project="${PROJECT}"
120-
gcloud projects add-iam-policy-binding "${PROJECT}" --member serviceAccount:"${IAM_NAME}" --role "roles/cloudkms.admin"
137+
gcloud projects add-iam-policy-binding "${PROJECT}" --member serviceAccount:"${IAM_NAME}" --role "roles/cloudkms.admin" --condition=None
121138
fi
122139

123140
# Export key if needed

docs/kubernetes/user-guides/driver-install.md

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,13 @@
22

33
## Install Driver
44

5-
1. Clone the driver to your local machine
5+
### 1. Clone the driver to your local machine
66

77
```console
88
$ git clone https://github.com/kubernetes-sigs/gcp-compute-persistent-disk-csi-driver $GOPATH/src/sigs.k8s.io/gcp-compute-persistent-disk-csi-driver
99
```
1010

11-
2. [One-time per project] Set up or use an existing service account:
11+
### 2. [One-time per project] Set up or use an existing service account:
1212

1313
The driver requires a service account that has the following permissions and
1414
roles to function properly:
@@ -18,7 +18,7 @@ compute.instances.get
1818
compute.instances.attachDisk
1919
compute.instances.detachDisk
2020
roles/compute.storageAdmin
21-
roles/iam.serviceAccountUser
21+
roles/iam.serviceAccountUser (see security note below)
2222
```
2323

2424
If there is a pre-existing service account with these roles for use then the
@@ -33,7 +33,24 @@ $ GCE_PD_SA_DIR=/my/safe/credentials/directory
3333
**Note**: The service account key *must* be named `cloud-sa.json` at driver deploy time
3434

3535
However, if there is no pre-existing service account for use the provided script
36-
can be used to create a new service account with all the required permissions:
36+
can be used to create a new service account with all the required permissions.
37+
38+
#### Security Note: Service Account Impersonation
39+
40+
The CSI driver requires the `roles/iam.serviceAccountUser` role to impersonate node service accounts when attaching and detaching disks. This role can be configured in two ways:
41+
42+
* **Recommended (Scoped)**: Grant the role only for specific node service accounts
43+
* **Default (Project-wide)**: Allow project-wide service account impersonation (less secure)
44+
45+
For improved security, specify the node service accounts that the CSI driver needs to impersonate using the `NODE_SERVICE_ACCOUNTS` environment variable. This limits the role to only the specified accounts. Without `NODE_SERVICE_ACCOUNTS`, the CSI driver can impersonate any service account in the project.
46+
47+
```console
48+
$ NODE_SERVICE_ACCOUNTS="[email protected],[email protected]" # Comma-separated list of node service accounts
49+
```
50+
51+
For more details, see [How to remediate over privileged service account users](https://cloud.google.com/security-command-center/docs/how-to-remediate-security-health-analytics-findings#over_privileged_service_account_user).
52+
53+
#### Create service account for the CSI driver
3754

3855
```console
3956
$ PROJECT=your-project-here # GCP project
@@ -46,9 +63,10 @@ $ ./deploy/setup-project.sh
4663
deployment, all actions performed by the driver will be performed as the
4764
specified service account
4865

49-
3. Deploy driver to Kubernetes Cluster
66+
### 3. Deploy driver to Kubernetes Cluster
5067

5168
```console
69+
$ NODE_SERVICE_ACCOUNTS="[email protected],[email protected]" # Same as the setup-project.sh step
5270
$ GCE_PD_SA_DIR=/my/safe/credentials/directory # Directory to get the service account key
5371
$ GCE_PD_DRIVER_VERSION=stable-master # Driver version to deploy
5472
$ ./deploy/kubernetes/deploy-driver.sh
@@ -74,6 +92,8 @@ additional permissions are required in order to create the new service account:
7492
```
7593
resourcemanager.projects.getIamPolicy
7694
resourcemanager.projects.setIamPolicy
95+
iam.serviceAccounts.getIamPolicy
96+
iam.serviceAccounts.setIamPolicy
7797
iam.serviceAccounts.create
7898
iam.serviceAccounts.delete
7999
```

0 commit comments

Comments
 (0)