Adds Google Cloud Storage as a cache backend for the Job Cacher plugin, implementing its jenkins.plugins.itemstorage.ItemStorage extension point.
Unlike routing GCS through the S3 extension's interoperability endpoint (which requires HMAC keys), this plugin uses the native GCS client and authenticates with Application Default Credentials (ADC) — so on GKE it works with Workload Identity and no keys.
The S3 storage extension can target storage.googleapis.com, but the AWS SDK path only speaks SigV4/SigV2 with HMAC access key/secret. There is no way to feed Google OAuth / ADC through it. This plugin talks to GCS natively instead.
Credentials resolve via GoogleCredentials.getApplicationDefault(), in order:
GOOGLE_APPLICATION_CREDENTIALS(service-account JSON file)- gcloud user credentials (
gcloud auth application-default login) — local dev - GKE Workload Identity / metadata server — recommended for production
No HMAC keys and no service-account JSON are stored in Jenkins configuration.
Cache transfers run on the build agent. To keep the IAM grant on the controller only, the plugin mints a short-lived OAuth access token from ADC on the controller and ships it to the agent for the transfer. The token is never written to config.xml. If the controller has no ADC, the agent falls back to resolving ADC itself (e.g. Workload Identity bound to the agent pod).
The shipped token is downscoped with a Credential Access Boundary to roles/storage.objectAdmin on the single configured bucket, so a build cannot use it against other buckets the controller identity can reach.
- Cache-key isolation. Caches for different jobs/branches are separated only by the object-key prefix. Attacker-influenceable components (
branch, cachepath) are validated to reject empty,.,.., leading/trailing/, and control characters, so a build cannot climb out of its namespace to poison another job's cache. - Known limitation — bucket-scoped token. The downscoped token is
objectAdminon the whole bucket. Untrusted build code on the agent could bypass the plugin and use that token directly against any object in the bucket. The next hardening step is to add an object-prefix condition to the Credential Access Boundary so the token only grants access under the current job's prefix. That needs integration testing against real GCP (CAB conditions are enforced server-side) and is intentionally deferred rather than shipped unverified.
Manage Jenkins → System → Cache Storage → Google Cloud Storage:
| Field | Required | Notes |
|---|---|---|
| GCS Bucket Name | yes | Must exist; identity needs roles/storage.objectAdmin on it |
| Base Prefix | no | Object-name prefix, e.g. jenkins/caches/ |
| GCP Project ID | no | Inferred from ADC/metadata when blank |
unclassified:
globalItemStorage:
storage:
googleCloudStorage:
bucketName: "my-jenkins-caches"
prefix: "jenkins/caches/"
projectId: "my-gcp-project"
Grant to a Google group or the controller's Kubernetes service account — never an individual — and scope the role to the bucket:
gcloud storage buckets add-iam-policy-binding gs://my-jenkins-caches \
--member="principal://iam.googleapis.com/projects/PROJECT_NUMBER/locations/global/workloadIdentityPools/PROJECT.svc.id.goog/subject/ns/NAMESPACE/sa/KSA_NAME" \
--role="roles/storage.objectAdmin"
mvn -ntp clean verify # unit tests + emulator tests + spotless + CasC compatibility
mvn -ntp hpi:run # run a local Jenkins with the plugin at http://localhost:8080/jenkins
The emulator-backed tests need a Docker daemon. They are skipped automatically when none is available, so mvn verify still passes without one.
Working, and covered end-to-end. GCSProfile is exercised against the fake-gcs-server emulator over Testcontainers — resumable upload, existence check, download, rename on job move, and delete on job removal (including that a sibling job sharing a name prefix is not swept up). Unit tests cover cache-key validation and JCasC round-tripping.
The GCS client and ADC credentials come from the gcp-java-sdk-storage API plugin rather than a direct google-cloud-storage dependency, so this plugin bundles no third-party jars of its own.
Remaining follow-ups:
- Object-prefix condition on the downscoped token's Credential Access Boundary (see Security notes); needs verification against real GCP.
- Optional resumable-download tuning / parallelism for large caches.
MIT