# Workload Federation with GCP

Namespace relies on Workload Identity Federation to allow Namespace to interact with different systems, instead of relying on pre-shared keys which can be more easily compromised.

## Accessing GCP resources from Namespace

Identity Federation with GCP allows your Namespace workloads to identify themselves to GCP using short-lived secure credentials.

To enable this federation, create a Workload Identity Pool and Provider for Namespace federation in GCP and configure service account impersonation.

### 1. Create a Workload Identity Pool and Provider

1. Ensure [IAM](https://console.cloud.google.com/apis/api/iamcredentials.googleapis.com/metrics) is enabled for your GCP project.
2. Create a Workload Identity Pool using `gcloud` CLI:

```bash
d  export POOL_ID=namespace-workload-pool

gcloud iam workload-identity-pools create ${POOL_ID} \
       --location="global"
   ```
3. Create an Identity Provider:

```bash
   export PROVIDER_ID=namespace-id-provider

gcloud iam workload-identity-pools providers create-oidc \
       ${PROVIDER_ID} \
       --location="global" \
       --workload-identity-pool=${POOL_ID} \
       --issuer-uri="https://federation.namespaceapis.com" \
       --attribute-mapping="google.subject=assertion.tenant_id"
   ```

### 2. Configure service account impersonation

1. Discover your [GCP Project number](https://cloud.google.com/resource-manager/docs/creating-managing-projects). Note, this is different from the [GCP Project ID](https://support.google.com/googleapi/answer/7014113):

```bash
   export PROJECT_NUMBER=$(gcloud projects list \
        --filter="$(gcloud config get-value project)" \
        --format="value(PROJECT_NUMBER)")

echo ${PROJECT_NUMBER}
   ```
2. Open the [Dashboard](https://cloud.namespace.so/workspace/settings) and copy your Workspace ID.
3. Allow external workloads to impersonate the service account by granting the Workload Identity User role `roles/iam.workloadIdentityUser` on the service account:

```bash
   export SERVICE_ACCOUNT=<NAME>@${PROJECT_ID}.iam.gserviceaccount.com

gcloud iam service-accounts add-iam-policy-binding \
       ${SERVICE_ACCOUNT} \
       --project="${PROJECT_ID}" \
       --role="roles/iam.workloadIdentityUser" \
       --member="principal://iam.googleapis.com/projects/${PROJECT_NUMBER}/locations/global/workloadIdentityPools/${POOL_ID}/subject/<your-workspace-id>"
   ```

### 3. Access GCP resources from a Namespace workload

1. Obtain GCP credentials:

```bash
   nsc gcp impersonate --service_account $SERVICE_ACCOUNT \
       --workload_identity_provider /projects/${PROJECT_NUMBER}/locations/global/workloadIdentityPools/${POOL_ID}/providers/${PROVIDER_ID} \\
       --write_creds=gcp-creds.json
   ```
2. Apply the obtained credentials:

```bash
   export GOOGLE_APPLICATION_CREDENTIALS=gcp-creds.json
   ```
3. Access GCP resources:

```bash
   gcloud storage cp test.txt gs://your-bucket-name/test2.txt
   ```

## Pulling private Artifact Registry images on instances

When creating an instance through the Compute API, you can pull private images from [Google Artifact Registry](https://cloud.google.com/artifact-registry) by having Namespace federate with GCP Workload Identity Federation to obtain a short-lived access token automatically.

The resolved token is used both to pull the instance's own container images and is written into the guest's Docker/nerdctl configuration (`/root/.docker/config.json`), so subsequent `docker pull` and `nerdctl pull` commands from inside the instance authenticate to the same registry.

### 1. Grant read access to Artifact Registry

Namespace maps each workspace to the federated subject `assertion.tenant_id`. Grant that principal the `roles/artifactregistry.reader` role on the repository, using your [Workspace ID](https://cloud.namespace.so/workspace/settings) as `<your-workspace-id>`:

```bash
gcloud artifacts repositories add-iam-policy-binding <REPOSITORY> \
    --project=${PROJECT_ID} \
    --location=<region> \
    --role="roles/artifactregistry.reader" \
    --member="principal://iam.googleapis.com/projects/${PROJECT_NUMBER}/locations/global/workloadIdentityPools/${POOL_ID}/subject/<your-workspace-id>"
```

### 2. Reference the registry when creating an instance

Set `additional_registries` on the `CreateInstance` request, keyed by your Artifact Registry host (`<region>-docker.pkg.dev`). Namespace performs the STS token-exchange against the provider `audience` and uses the resulting token to pull the image:

```json
{
	"containers": [
		{
			"name": "app",
			"image_ref": "<region>-docker.pkg.dev/<project-id>/<repository>/my-image:latest"
		}
	],
	"additional_registries": {
		"<region>-docker.pkg.dev": {
			"authentication": {
				"gcp_workload_identity_federation": {
					"audience": "/iam.googleapis.com/projects/<project-number>/locations/global/workloadIdentityPools/<pool-id>/providers/<provider-id>",
					"username": "oauth2accesstoken"
				}
			}
		}
	}
}
```

## Accessing Namespace resources from GCP

Identity Federation with GCP allows your GCP-based workloads (e.g. running in GKE) to identify themselves to Namespace using short-lived secure credentials.
To enable this federation, we rely on GCP's ability to generate OIDC tokens that can be exchanged for Namespace credentials.

* **Issuer**: `https://accounts.google.com`
* **Subject Format**: The full resource path of the Google Service Account your workload runs as (format: `projects/PROJECT_NUMBER/serviceAccounts/EMAIL`)

### 1. Ensure your workload has a Google Service Account

Your GCP workload needs a Google Service Account (GSA) that can mint OIDC tokens. If you're running in GKE with [Workload Identity](https://cloud.google.com/kubernetes-engine/docs/concepts/workload-identity), bind your Kubernetes Service Account (KSA) to a GSA:

1. Create a GSA (if you don't already have one):

```bash
   gcloud iam service-accounts create my-nsc-sa --project=${PROJECT_ID}
   ```
2. Bind the KSA to the GSA:

```bash
   gcloud iam service-accounts add-iam-policy-binding \
       my-nsc-sa@${PROJECT_ID}.iam.gserviceaccount.com \
       --role roles/iam.workloadIdentityUser \
       --member "serviceAccount:${PROJECT_ID}.svc.id.goog[${K8S_NAMESPACE}/${KSA_NAME}]"
   ```
3. Annotate the KSA:

```bash
   kubectl annotate serviceaccount ${KSA_NAME} -n ${K8S_NAMESPACE} \
       iam.gke.io/gcp-service-account=my-nsc-sa@${PROJECT_ID}.iam.gserviceaccount.com
   ```

### 2. Configure Trust Relationship

Use the Namespace CLI to establish a trust relationship with your GCP service account:

**For a specific service account:**

```bash
nsc auth trust-relationships add \
    --issuer "https://accounts.google.com" \
    --subject-match "projects/${PROJECT_NUMBER}/serviceAccounts/my-nsc-sa@${PROJECT_ID}.iam.gserviceaccount.com"
```

**For all service accounts in a GCP project:**

```bash
nsc auth trust-relationships add \
    --issuer "https://accounts.google.com" \
    --subject-match "projects/${PROJECT_NUMBER}/serviceAccounts/*"
```

### 3. Verify Trust Relationship

List your configured trust relationships to confirm the setup:

```bash
nsc auth trust-relationships list
```

### 4. Obtain Namespace credentials from your GCP workload

From your GCP workload (e.g. a GKE pod), obtain and exchange an OIDC token for Namespace credentials:

1. Generate a GCP OIDC token targeting Namespace:

```bash
   export OIDC_TOKEN=$(gcloud auth print-identity-token --audiences=federation.namespaceapis.com)
   ```
2. Exchange the OIDC token for Namespace credentials:

```bash
   nsc auth exchange-oidc-token --token $OIDC_TOKEN --tenant_id <your-workspace-id>
   ```

This command should succeed with the name of workspace you've signed in to.
It stores a short-lived token that will be used automatically in subsequent `nsc` calls.
