Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use projected token volume for hostNetwork pods. #428

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

siyanshen
Copy link
Collaborator

@siyanshen siyanshen commented Jan 14, 2025

Why we need this change

Before this change, GCSFuse CSI driver does not support pods with hostNetwork:true. This is because the gcsfuse process runs as part of a sidecar container that is injected into the user pod. GCSFuse uses the ADC workflow to fetch the necessary token to access a GCS bucket. However, with hostNetwork enabled, GKE metadata server cannot intercept the token requests for GET /computeMetadata/v1/instance/service-accounts/default/token endpoint.

What is in this change

  1. For HostNetwork=true user pods, GCSFuse CSI webhook injects a projected SA token volume to the user pod.
  2. gke-gcsfuse-sidecar container prepares a unix domain socket and starts a handler to serve requests on this token; invoke gcsfuse with a config option to point to the socket gcs-auth:token-url:<path to the token>
  3. Token request handler in the gke-gcsfuse-sidecar handles token request from GCSFuse.

Local set up and testing

Test cases covered

  1. pod with hostNetwork=true: can I/O GCS bucket.
  2. pod with hostNetwork=false: is not affected and can I/O GCS bucket.
  3. pod with hostNetwork=true, with GCS bucket A mounted as ephemeral storage and Gcs bucket B mounted as PV: can access bucket A & B without issue.

Setup

  1. Create a cluster with workload identity pool enabled & GCSFuse disabled.
  2. Grant GCS permissions to an SA
kubectl create namespace <ns>
kubectl create serviceaccount <test-ksa-ns> \
    --namespace <ns>

gcloud storage buckets add-iam-policy-binding gs://<bucket> \
    --member "principal://iam.googleapis.com/projects/<project-number>/locations/global/workloadIdentityPools/<project-id>.svc.id.goog/subject/ns/<ns>/sa/<test-ksa-ns>" \
    --role "roles/storage.objectUser"
  1. Build and install your GCSFuse image
make build-image-and-push-multi-arch REGISTRY=<your-registry> STAGINGVERSION=<your-dev-tag>
make install REGISTRY=<your-registry> PROJECT=<project-id> STAGINGVERSION=<your-dev-tag>
  1. Create a user pod with hostNetwork=true, GCS bucket mounted as a volume, and service account set as the one you created in step 2. Check I/O to your bucket from a container in your pod.

@siyanshen siyanshen force-pushed the hostnetwork branch 7 times, most recently from 3b2b99f to 557c193 Compare January 15, 2025 23:56
@siyanshen siyanshen requested review from hime, saikat-royc, mattcary, msau42, kislaykishore and Tulsishah and removed request for saikat-royc January 16, 2025 00:32
deploy/base/webhook/deployment.yaml Outdated Show resolved Hide resolved
cmd/sidecar_mounter/main.go Outdated Show resolved Hide resolved
pkg/sidecar_mounter/sidecar_mounter_config.go Outdated Show resolved Hide resolved
cmd/sidecar_mounter/main.go Outdated Show resolved Hide resolved
@siyanshen siyanshen force-pushed the hostnetwork branch 2 times, most recently from 9afba55 to 2f4c6e4 Compare January 23, 2025 00:47
@siyanshen siyanshen removed the request for review from msau42 January 27, 2025 17:45
cmd/webhook/main.go Outdated Show resolved Hide resolved
@@ -110,6 +113,15 @@ func (si *SidecarInjector) Handle(_ context.Context, req admission.Request) admi

// Inject container.
injectSidecarContainer(pod, config, injectAsNativeSidecar)

if pod.Spec.HostNetwork {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Are there any cases in which the cx would want to keep using the GCE SA? (even though its not very safe)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Is that a common use case? This implementation will not be able to accommodate that

if err != nil {
return admission.Errored(http.StatusInternalServerError, fmt.Errorf("failed to get project id: %w", err))
}
pod.Spec.Volumes = append(pod.Spec.Volumes, GetSATokenVolume(projectID))
Copy link
Collaborator

Choose a reason for hiding this comment

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

do you think we should have the logic that injects volumes in one place?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Could you elaborate?

pkg/webhook/sidecar_spec.go Outdated Show resolved Hide resolved
NobodyGID = 65534
NobodyUID = 65534
NobodyGID = 65534
tokenExpiryDuration = 600
Copy link
Collaborator

Choose a reason for hiding this comment

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

Curious, why 600? Is tokenExpiryDuration something that can be adjusted? if so, should the cx have the ability to adjust?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Prolonged it to 3600-should be a more reasonable token expiry duration. See more about what this value does: https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/#launch-a-pod-using-service-account-token-projection

It's not very useful to make tokenExpiryDuration configurable.

Copy link
Collaborator

Choose a reason for hiding this comment

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

When this expires, we have the ability to renew because of the StartTokenServer call. Do we plan to add e2e tests in a followup PR to test the functionality of the token server? If we have a TokenServer, do we favor a shorter or longer tokenExpiryDuration?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I plan to add an e2e test to test GCS bucket access with hostnetwork enabled pods. That should cover the functionality of the token server.

RetokenExpiryDuration- we will have to weigh in the security benefits of shorter token lifetimes against the performance benefits of longer lifetimes. Kubernetes' approach provides a good starting point- it will refresh when it hits 80% of TTL or 24h (whichever is shorter). I think 3600s(1h) is a nice balance point between security and network traffic overhead.

pkg/sidecar_mounter/sidecar_mounter.go Outdated Show resolved Hide resolved
WriteTimeout: 10 * time.Second,
}

if err := server.Serve(socket); err != nil {

Choose a reason for hiding this comment

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

Is there a way to stop the server gracefully during shutdown?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This implementation follows the standard pattern across the repo. e.g.: metrics server:

if err := server.Serve(socket); err != nil {

Can you give me an example of gracefully shutting down a server? Is there a significant benefit for doing that?

pkg/sidecar_mounter/sidecar_mounter.go Outdated Show resolved Hide resolved
pkg/sidecar_mounter/token_manager.go Outdated Show resolved Hide resolved
pkg/sidecar_mounter/token_manager.go Outdated Show resolved Hide resolved
cmd/webhook/main.go Outdated Show resolved Hide resolved
return audience, nil
}

func StartTokenServer(ctx context.Context) {

Choose a reason for hiding this comment

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

What's the behavior when the server failed to start? Will the GCS calls use node's auth scopes?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

The pod will not have access to GCS bucket in this case

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

And mounting will fail.

pkg/sidecar_mounter/token_source.go Outdated Show resolved Hide resolved
@siyanshen siyanshen force-pushed the hostnetwork branch 4 times, most recently from 4d6f511 to 58f64ae Compare January 30, 2025 00:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants