Skip to content

Commit a778207

Browse files
feat: implement opt-in cloud workload identity for Azure AKS and AWS EKS without static credentials
Signed-off-by: Ankit Kr. Chowdhury <rakesh856100@gmail.com>
1 parent 820046e commit a778207

6 files changed

Lines changed: 104 additions & 3 deletions

File tree

.agents/skills/agentrax-context/SKILL.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ description: Project context and settled architecture decisions for the Agentrax
2020

2121
- **Autoscaling**: native `HorizontalPodAutoscaler` pointed at Prometheus Adapter custom metrics (`queueDepth` or `gpuUtilization`). No custom scaling loop. During active canary, the stable HPA is paused (deleted) and no canary HPA is created — autoscaling resumes only after promotion or rollback.
2222
- **Traffic splitting**: Gateway API `HTTPRoute` weighted backends. Not Istio, not ingress annotations.
23-
- **Network Isolation**: Two-tier Kubernetes `NetworkPolicy` (`allow-metrics-traffic` in `agentrax-system` allowing operator metrics on TCP 8443; `tenant-agent-isolation` rendered into every `tenant-*` namespace selecting agent pods with `agentrax.io/agent: "true"` for scraping on TCP 8080 and egress to API server/CoreDNS). No service mesh.
23+
- **Network Isolation**: Two-tier Kubernetes `NetworkPolicy` (`allow-metrics-traffic` in `agentrax-system` allowing operator metrics on TCP 8443; `tenant-agent-isolation` rendered into every `tenant-*` namespace selecting agent pods with `agentrax.io/agent: "true"` for scraping on TCP 8080 and egress to API server/CoreDNS in `kube-system`). No service mesh.
24+
- **Cloud Workload Identity**: No static cloud credentials ever. Azure deployments use AKS Workload Identity (`azure.workload.identity/client-id` + `/tenant-id` ServiceAccount annotations; `azure.workload.identity/use: "true"` pod label). AWS deployments use IRSA (`eks.amazonaws.com/role-arn` annotation). Both are opt-in via `workloadIdentity.enabled` in `charts/agentrax/values.yaml`; disabled by default for on-premises portability.
2425
- **MCP registry**: embedded HTTP handler inside the operator process, backed by a `ConfigMap`. Not a separate Deployment, not a new database — HA storage is a v2 item.
2526
- **Non-goals**: no model training/fine-tuning, no general-purpose workload management, no service mesh, no UI in v1. Flag any drift toward these rather than quietly implementing them.
2627

charts/agentrax/templates/deployment.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ spec:
1919
{{- end }}
2020
labels:
2121
{{- include "agentrax.selectorLabels" . | nindent 8 }}
22+
{{- if and .Values.workloadIdentity.enabled (eq .Values.workloadIdentity.provider "azure") }}
23+
azure.workload.identity/use: "true"
24+
{{- end }}
2225
spec:
2326
{{- with .Values.imagePullSecrets }}
2427
imagePullSecrets:

charts/agentrax/templates/serviceaccount.yaml

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,15 @@ metadata:
66
namespace: {{ .Release.Namespace }}
77
labels:
88
{{- include "agentrax.labels" . | nindent 4 }}
9-
{{- with .Values.serviceAccount.annotations }}
109
annotations:
10+
{{- with .Values.serviceAccount.annotations }}
1111
{{- toYaml . | nindent 4 }}
12-
{{- end }}
12+
{{- end }}
13+
{{- if and .Values.workloadIdentity.enabled (eq .Values.workloadIdentity.provider "azure") }}
14+
azure.workload.identity/client-id: {{ .Values.workloadIdentity.azureClientId | quote }}
15+
azure.workload.identity/tenant-id: {{ .Values.workloadIdentity.azureTenantId | quote }}
16+
{{- end }}
17+
{{- if and .Values.workloadIdentity.enabled (eq .Values.workloadIdentity.provider "aws") }}
18+
eks.amazonaws.com/role-arn: {{ .Values.workloadIdentity.awsRoleArn | quote }}
19+
{{- end }}
1320
{{- end }}

charts/agentrax/values.yaml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,21 @@ mcp:
104104
# -- Periodic health check interval for registered agents (e.g. "30s")
105105
healthInterval: "30s"
106106

107+
# -- Cloud Workload Identity — secretless pod-level IAM.
108+
# Supported providers: azure (AKS Workload Identity), aws (EKS IRSA).
109+
# When disabled (the default), no annotations or labels are injected.
110+
workloadIdentity:
111+
# -- Set to true to enable workload identity for the controller-manager pod.
112+
enabled: false
113+
# -- Cloud provider. Accepted values: "azure" | "aws".
114+
provider: azure
115+
# -- Azure only: Application (client) ID of the managed identity.
116+
azureClientId: ""
117+
# -- Azure only: Azure AD tenant ID.
118+
azureTenantId: ""
119+
# -- AWS only: Full ARN of the IAM role to assume via IRSA.
120+
awsRoleArn: ""
121+
107122
# -- Environment variables injected into the manager container.
108123
# These values are automatically populated from registry.ttl and mcp.healthInterval above.
109124
env: {}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# Kustomize strategic-merge patch for AWS IRSA (IAM Roles for Service Accounts).
2+
# Apply this patch in your cluster-specific overlay to bind the controller-manager
3+
# ServiceAccount to an IAM role without static AWS credentials.
4+
#
5+
# Usage — in your overlay kustomization.yaml:
6+
#
7+
# resources:
8+
# - ../../config/default
9+
# patches:
10+
# - path: ../../config/workload-identity/irsa-serviceaccount.yaml
11+
# replacements:
12+
# - source:
13+
# kind: ConfigMap # or any source carrying your role ARN
14+
# name: cluster-metadata
15+
# fieldPath: data.roleArn
16+
# targets:
17+
# - select:
18+
# kind: ServiceAccount
19+
# name: agentrax-controller-manager
20+
# fieldPaths:
21+
# - metadata.annotations.[eks.amazonaws.com/role-arn]
22+
#
23+
# The role must have a trust policy allowing the OIDC provider of your EKS cluster
24+
# to assume it on behalf of the agentrax-system/agentrax-controller-manager subject.
25+
apiVersion: v1
26+
kind: ServiceAccount
27+
metadata:
28+
name: agentrax-controller-manager # must match the name rendered by the Helm chart
29+
namespace: agentrax-system
30+
annotations:
31+
# Replace this placeholder with the actual IAM role ARN before applying.
32+
eks.amazonaws.com/role-arn: "arn:aws:iam::ACCOUNT_ID:role/ROLE_NAME"

docs/ARCHITECTURE.md

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -338,6 +338,48 @@ Agentrax maintains a **two-tier network policy model**:
338338

339339
---
340340

341+
### 4.7 Keyless Cloud IAM — Workload Identity
342+
343+
Agentrax requires cloud API access (e.g., Azure Container Registry pulls, AWS Secrets Manager reads) in production. Static credentials baked into `Secret` objects rotate manually, are visible in etcd, and create a long-lived blast radius if leaked.
344+
345+
The operator is instead bound to a cloud-managed identity at the pod level:
346+
347+
| Cloud | Mechanism | How it works |
348+
| :------ | :------------------------------ | :------------------------------------------------------------------------------------------------------------------------------------- |
349+
| **Azure** (AKS) | Azure Workload Identity | An OIDC-projected service account token is exchanged for a short-lived Azure AD access token by the Azure Identity SDK. The AKS admission webhook injects the projected volume and `AZURE_*` env vars when the pod carries `azure.workload.identity/use: "true"`. |
350+
| **AWS** (EKS) | IRSA (IAM Roles for SA) | EKS projects a signed OIDC token into the pod; the AWS SDK exchanges it for temporary STS credentials scoped to the bound IAM role via `eks.amazonaws.com/role-arn` annotation. |
351+
352+
#### Helm Configuration
353+
354+
Workload identity is off by default. Enable it via `values.yaml` or `--set`:
355+
356+
```yaml
357+
# Azure AKS
358+
workloadIdentity:
359+
enabled: true
360+
provider: azure
361+
azureClientId: "<managed-identity-client-id>"
362+
azureTenantId: "<azure-ad-tenant-id>"
363+
364+
# AWS EKS
365+
workloadIdentity:
366+
enabled: true
367+
provider: aws
368+
awsRoleArn: "arn:aws:iam::<account>:role/<role-name>"
369+
```
370+
371+
When `workloadIdentity.enabled=true`:
372+
373+
- **Azure**: The `ServiceAccount` gains `azure.workload.identity/client-id` and `azure.workload.identity/tenant-id` annotations; the manager pod gains the `azure.workload.identity/use: "true"` label required by the AKS mutating webhook.
374+
- **AWS**: The `ServiceAccount` gains the `eks.amazonaws.com/role-arn` annotation consumed by the EKS pod identity webhook. For Kustomize-based cluster overlays, use `config/workload-identity/irsa-serviceaccount.yaml` as a strategic-merge patch.
375+
376+
#### Invariants
377+
378+
- No static cloud credentials (`client_secret`, `AWS_SECRET_ACCESS_KEY`) are ever stored in cluster `Secret` objects.
379+
- `workloadIdentity.enabled=false` (the default) renders no identity annotations or pod labels — the chart remains fully portable to on-premises or non-cloud environments.
380+
381+
---
382+
341383
## 5. Architectural Decision Records (ADRs) & Trade-Offs
342384

343385
| Decision | Alternative Considered | Trade-Off & Rationale for Agentrax |
@@ -347,6 +389,7 @@ Agentrax maintains a **two-tier network policy model**:
347389
| **Native HPA via Custom Metrics** | KEDA (`ScaledObject`) | KEDA is powerful but adds external CRD dependencies. Generating native Kubernetes `HorizontalPodAutoscaler` objects tied to the Prometheus Adapter custom metrics pipeline minimized dependencies while giving full control over stabilization windows. |
348390
| **Embedded Registry + ConfigMap Store** | Dedicated etcd / Redis / Database | Adding a dedicated database for service discovery increases operator operational complexity. The in-operator HTTP server with ConfigMap write-through store provides simple, robust storage for hundreds of agent services with cold-restart recovery. |
349391
| **Two-Tier NetworkPolicy** | Istio / Linkerd Service Mesh | Service mesh requires sidecar injection and significant control plane memory overhead. Native Kubernetes NetworkPolicy with label-selector binding (`agentrax.io/agent: "true"`) provides lightweight, CNI-enforced zero-trust tenant isolation with default-deny rules. |
392+
| **Workload Identity (no static secrets)** | Kubernetes `Secret` with cloud credentials | Static credentials require manual rotation, are stored in etcd, and present a wide blast radius on leak. OIDC-projected pod tokens (Azure Workload Identity / AWS IRSA) are short-lived, auto-rotated, and scoped to a single identity. |
350393
| **Go (`controller-runtime`)** | Python (`Kopf`) | Go provides native compile-time safety, seamless alignment with Kubernetes upstream libraries, and access to `setup-envtest` for isolated in-process integration testing. |
351394

352395
---

0 commit comments

Comments
 (0)