Update README and remove script sources from the README body.

This commit is contained in:
slawek committed 2026-10-17 11:18:46 +02:00
1 parent f50bd984fb
commit fa3c612668
2 files changed
+16 -214

No files matched your search

+14 -212
View File
@@ -10,7 +10,7 @@ This short guide provides step by step instructions how to replace a JSON key wi
## Configuration ## Configuration
The scripts read the names of the resources from `env.sh`. Replace the example values with your own before running them. The scripts read the names of the resources from `env.sh`. Replace the example values with your own before running them. Run all scripts from the repository directory.
`env.sh`: `env.sh`:
@@ -32,69 +32,23 @@ BUCKET=my-project-wif-test
BUCKET_LOCATION=europe-central2 BUCKET_LOCATION=europe-central2
``` ```
## About the shell scripts
The `.sh` files are meant as references for copying and pasting the commands one by one, so that each step can be inspected and adapted. Running them directly with `bash` also works.
> **Note:** The scripts are not idempotent. Running a whole script with `bash` a second time may fail, because the resources created by the first run already exist, for example the workload identity pool, the provider, the service account, the bucket and the `wif-test` gcloud configuration.
## 1. Create the certificates ## 1. Create the certificates
The script creates a root CA, an issuing CA and a client certificate. The client certificate subject CN (`test-client`) becomes the WiF subject. `make_certs.sh` creates a root CA, an issuing CA and a client certificate. The client certificate subject CN (`test-client`) becomes the WiF subject.
`make_certs.sh`:
```bash ```bash
source ./env.sh bash make_certs.sh
# Root CA certificate
echo "Generating Root CA certificate..."
openssl req \
-x509 \
-newkey rsa:4096 \
-keyout ca_key.pem \
-out ca_cert.pem \
-days 3650 \
-noenc \
-subj "/CN=Test CA $(date +"%Y-%m-%d")" \
-addext "basicConstraints=critical,CA:TRUE,pathlen:1" \
-addext "keyUsage=critical,keyCertSign,cRLSign"
# Issuing CA certificate
echo "Generating Issuing CA certificate..."
openssl req \
-newkey rsa:4096 \
-keyout issuing_ca_key.pem \
-noenc \
-subj "/CN=Issuing Test CA $(date +"%Y-%m-%d")" \
-addext "basicConstraints=critical,CA:TRUE,pathlen:0" \
-addext "keyUsage=critical,keyCertSign,cRLSign" |
openssl x509 \
-req \
-CA ca_cert.pem \
-CAkey ca_key.pem \
-copy_extensions copyall \
-days 3650 \
-out issuing_ca_cert.pem
# Client certificate
echo "Generating Client certificate..."
openssl req \
-newkey rsa:4096 \
-keyout client_key.pem \
-noenc \
-subj "/CN=test-client" \
-addext "basicConstraints=critical,CA:FALSE" \
-addext "keyUsage=critical,digitalSignature,keyEncipherment" \
-addext "extendedKeyUsage=clientAuth" |
openssl x509 \
-req \
-CA issuing_ca_cert.pem \
-CAkey issuing_ca_key.pem \
-copy_extensions copyall \
-days 365 \
-out client_cert.pem
``` ```
## 2. Create the trust store ## 2. Create the trust store
The trust store is a YAML file that holds the root CA as the trust anchor and the issuing CA as an intermediate CA. The generator uses the `yaml` npm package. `make_trust_store.mjs` creates `trust_store.yaml`, a YAML file that holds the root CA as the trust anchor and the issuing CA as an intermediate CA. It uses the `yaml` npm package, declared in `package.json`:
`package.json`:
```json ```json
{ {
@@ -104,38 +58,6 @@ The trust store is a YAML file that holds the root CA as the trust anchor and th
} }
``` ```
`make_trust_store.mjs`:
```javascript
#!/usr/bin/env node
import { existsSync, readFileSync, writeFileSync } from "node:fs";
import { parseArgs } from "node:util";
import YAML from "yaml";
const { values } = parseArgs({
options: {
"ca-cert": { type: "string", default: "ca_cert.pem" },
"issuing-ca-cert": { type: "string", default: "issuing_ca_cert.pem" },
output: { type: "string", default: "trust_store.yaml" },
},
});
// IMPORTANT: Strip the trailing newline from the PEM files,
// otherwise gcloud will reject the trust store with a cryptic error message.
const trustStore = {
trustAnchors: [{ pemCertificate: readFileSync(values["ca-cert"], "utf8").trimEnd() }],
};
// The issuing CA is optional; it is included only when its file exists.
if (existsSync(values["issuing-ca-cert"])) {
trustStore.intermediateCas = [
{ pemCertificate: readFileSync(values["issuing-ca-cert"], "utf8").trimEnd() },
];
}
writeFileSync(values.output, YAML.stringify({ trustStore }, { indentSeq: false }));
```
Install the dependency and generate `trust_store.yaml`: Install the dependency and generate `trust_store.yaml`:
```bash ```bash
@@ -147,134 +69,14 @@ The PEM files must contain only the certificate block. A trailing newline after
## 3. Create the pool, provider and credentials ## 3. Create the pool, provider and credentials
The script creates the workload identity pool, the X.509 provider and a service account. It allows the client certificate subject to impersonate the service account, grants the service account read access to the project, creates a storage bucket with read and write access to its objects for the service account, enables Data Access audit logs for Cloud Storage, and writes `cred.json`. It then logs in with `cred.json` in a separate gcloud configuration, checks the access, uploads and downloads a file, makes the public folder of the bucket publicly readable, and lists the audit log of the service account operations. `make_wif.sh` creates the workload identity pool, the X.509 provider and a service account. It allows the client certificate subject to impersonate the service account, grants the service account read access to the project, creates a storage bucket with read and write access to its objects for the service account, enables Data Access audit logs for Cloud Storage, and writes `cred.json`. It then logs in with `cred.json` in a separate gcloud configuration, checks the access, uploads and downloads a file, makes the public folder of the bucket publicly readable, and lists the audit log of the service account operations.
Data Access audit logs are enabled for the whole project, not per bucket, by editing the project IAM policy. `add_audit_config.mjs` adds the `auditConfigs` section to the policy file fetched by gcloud. The policy must be fetched as YAML, because a different default gcloud output format makes `set-iam-policy` reject the file.
`add_audit_config.mjs`:
```javascript
#!/usr/bin/env node
import { readFileSync, writeFileSync } from "node:fs";
import YAML from "yaml";
const policy = YAML.parse(readFileSync("iam_policy.yaml", "utf8"));
// Replace the Cloud Storage entry, if any, and keep the other audit configs.
policy.auditConfigs = (policy.auditConfigs ?? []).filter(
(c) => c.service !== "storage.googleapis.com",
);
policy.auditConfigs.push({
service: "storage.googleapis.com",
auditLogConfigs: [{ logType: "DATA_READ" }, { logType: "DATA_WRITE" }],
});
writeFileSync("iam_policy.yaml", YAML.stringify(policy, { indentSeq: false }));
```
`make_wif.sh`:
```bash ```bash
source ./env.sh bash make_wif.sh
# Create a workload identity pool and a X.509 provider
gcloud iam workload-identity-pools create "${POOL_ID}" \
--project="${PROJECT_ID}" \
--location=global \
--display-name="${POOL_DISPLAY_NAME}"
# Create X.509 provider
gcloud iam workload-identity-pools providers create-x509 "${PROVIDER_ID}" \
--project="${PROJECT_ID}" \
--location=global \
--workload-identity-pool="${POOL_ID}" \
--trust-store-config-path=trust_store.yaml \
--attribute-mapping="google.subject=assertion.subject.dn.cn"
# Create the service account
gcloud iam service-accounts create sa-test \
--project="${PROJECT_ID}" \
--display-name="SA Test"
# Allow impersonation of the service account
gcloud iam service-accounts add-iam-policy-binding \
"${SA_EMAIL}" \
--role=roles/iam.workloadIdentityUser \
--member="principal://iam.googleapis.com/projects/${PROJECT_NUMBER}/locations/global/workloadIdentityPools/${POOL_ID}/subject/test-client"
# Grant the service account read access to the project
gcloud projects add-iam-policy-binding "${PROJECT_ID}" \
--member="serviceAccount:${SA_EMAIL}" \
--role=roles/browser
# Create credentials configuration files
gcloud iam workload-identity-pools create-cred-config \
"projects/${PROJECT_NUMBER}/locations/global/workloadIdentityPools/${POOL_ID}/providers/${PROVIDER_ID}" \
--service-account="${SA_EMAIL}" \
--credential-cert-path=client_cert.pem \
--credential-cert-private-key-path=client_key.pem \
--output-file=cred.json
# Create a storage bucket
gcloud storage buckets create "gs://${BUCKET}" \
--project="${PROJECT_ID}" \
--location="${BUCKET_LOCATION}" \
--uniform-bucket-level-access
# Allow the service account to read and write objects in the bucket
gcloud storage buckets add-iam-policy-binding "gs://${BUCKET}" \
--member="serviceAccount:${SA_EMAIL}" \
--role=roles/storage.objectUser
# Enable Data Access audit logs for Cloud Storage (project-wide, not per bucket)
gcloud projects get-iam-policy "${PROJECT_ID}" --format=yaml > iam_policy.yaml
node add_audit_config.mjs
gcloud projects set-iam-policy "${PROJECT_ID}" iam_policy.yaml
# Create a new gcloud configuration for the workload identity federation test
gcloud config configurations create wif-test
# Authenticate gcloud with the credentials configuration file
gcloud auth login --cred-file=cred.json
# Set the project
gcloud config set project "${PROJECT_ID}"
# Test the authentication by listing the project details
gcloud auth list
gcloud projects describe "${PROJECT_ID}"
gcloud projects get-iam-policy "${PROJECT_ID}" \
--flatten="bindings[].members" \
--format="table(bindings.role, bindings.members)"
# Test the bucket access by uploading and downloading a file
gcloud storage ls "gs://${BUCKET}"
gcloud storage cp README.md "gs://${BUCKET}/README.md"
gcloud storage ls "gs://${BUCKET}"
gcloud storage cp "gs://${BUCKET}/README.md" README.downloaded.md
cmp README.md README.downloaded.md
# Then, switch back to your original account
gcloud config configurations activate default
# Create a managed folder and make the objects in it publicly readable
gcloud storage managed-folders create "gs://${BUCKET}/public/"
gcloud storage managed-folders add-iam-policy-binding "gs://${BUCKET}/public/" \
--member=allUsers \
--role=roles/storage.objectViewer
# List the audit log of the operations made by the service account
gcloud logging read \
"protoPayload.authenticationInfo.principalEmail=\"${SA_EMAIL}\"" \
--project="${PROJECT_ID}" \
--freshness=1h \
--order=asc \
--format="table(timestamp, protoPayload.serviceName, protoPayload.methodName, protoPayload.authenticationInfo.principalEmail:label=EMAIL, protoPayload.authenticationInfo.principalSubject:label=SUBJECT, protoPayload.authenticationInfo.serviceAccountDelegationInfo[0].principalSubject:label=DELEGATED_BY)"
# and again switch back to the service account
gcloud config configurations activate wif-test
``` ```
Data Access audit logs are enabled for the whole project, not per bucket, by editing the project IAM policy. `add_audit_config.mjs` adds the `auditConfigs` section to the policy file fetched by gcloud. The policy must be fetched as YAML, because a different default gcloud output format makes `set-iam-policy` reject the file.
The audit log lists only Cloud Storage operations of the service account. Resource Manager calls such as `projects describe` are not covered by the Data Access configuration. The audit log lists only Cloud Storage operations of the service account. Resource Manager calls such as `projects describe` are not covered by the Data Access configuration.
## Result ## Result
+2 -2
View File
@@ -94,5 +94,5 @@ gcloud logging read \
--order=asc \ --order=asc \
--format="table(timestamp, protoPayload.serviceName, protoPayload.methodName, protoPayload.authenticationInfo.principalEmail:label=EMAIL, protoPayload.authenticationInfo.principalSubject:label=SUBJECT, protoPayload.authenticationInfo.serviceAccountDelegationInfo[0].principalSubject:label=DELEGATED_BY)" --format="table(timestamp, protoPayload.serviceName, protoPayload.methodName, protoPayload.authenticationInfo.principalEmail:label=EMAIL, protoPayload.authenticationInfo.principalSubject:label=SUBJECT, protoPayload.authenticationInfo.serviceAccountDelegationInfo[0].principalSubject:label=DELEGATED_BY)"
# and again switch back to the service account # optionally switch back to the service account for further testing
gcloud config configurations activate wif-test # gcloud config configurations activate wif-test