Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 29 additions & 2 deletions .github/workflows/on-pr.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -33,15 +33,41 @@ jobs:
[
{ "example": "examples/secretstacks/minimal.yaml" },
{ "example": "examples/secretstacks/standard.yaml" },
{ "example": "examples/secretstacks/vault.yaml" },
{ "example": "examples/secretstacks/vault-external.yaml" },
{ "example": "examples/secretstacks/standard.yaml", "observed_resources": "examples/test/mocks/observed-resources/standard/steps/1/" },
{ "example": "examples/secretstacks/standard.yaml", "observed_resources": "examples/test/mocks/observed-resources/standard/steps/2/" }
{ "example": "examples/secretstacks/standard.yaml", "observed_resources": "examples/test/mocks/observed-resources/standard/steps/2/" },
{ "example": "examples/secretstacks/vault.yaml", "observed_resources": "examples/test/mocks/observed-resources/vault/steps/1/" },
{ "example": "examples/secretstacks/vault.yaml", "observed_resources": "examples/test/mocks/observed-resources/vault/steps/2/" }
]
api_path: apis/secretstacks
error_on_missing_schemas: true

test:
uses: unbounded-tech/workflows-crossplane/.github/workflows/test.yaml@v3.0.0

review-findings:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6
Comment thread
patrickleet marked this conversation as resolved.
with:
persist-credentials: false
- name: Install up
uses: upbound/action-up@v1.0.0
with:
skip-login: true
- name: Build project
uses: upbound/action-up-project@v1.1.2
with:
skip-login-check: true
- name: Install Crossplane CLI
run: |
curl -sL https://cli.crossplane.io/install.sh | XP_VERSION=v2.4.1 sh
sudo mv crossplane /usr/local/bin/crossplane
- name: Verify review findings
run: tests/review-findings.sh

e2e:
uses: unbounded-tech/workflows-crossplane/.github/workflows/e2e.yaml@v3.0.0
with:
Expand All @@ -59,7 +85,7 @@ jobs:
}
debug-resource-types: |
[
"secretstacks.aws.hops.ops.com.ai"
"secretstacks.hops.ops.com.ai"
]
delete-extra-resources: |
[
Expand All @@ -73,6 +99,7 @@ jobs:
needs:
- validate
- test
- review-findings
uses: unbounded-tech/workflows-crossplane/.github/workflows/publish.yaml@v3.0.0
secrets: inherit
with:
Expand Down
31 changes: 29 additions & 2 deletions .github/workflows/on-push-main.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -29,15 +29,41 @@ jobs:
[
{ "example": "examples/secretstacks/minimal.yaml" },
{ "example": "examples/secretstacks/standard.yaml" },
{ "example": "examples/secretstacks/vault.yaml" },
{ "example": "examples/secretstacks/vault-external.yaml" },
{ "example": "examples/secretstacks/standard.yaml", "observed_resources": "examples/test/mocks/observed-resources/standard/steps/1/" },
{ "example": "examples/secretstacks/standard.yaml", "observed_resources": "examples/test/mocks/observed-resources/standard/steps/2/" }
{ "example": "examples/secretstacks/standard.yaml", "observed_resources": "examples/test/mocks/observed-resources/standard/steps/2/" },
{ "example": "examples/secretstacks/vault.yaml", "observed_resources": "examples/test/mocks/observed-resources/vault/steps/1/" },
{ "example": "examples/secretstacks/vault.yaml", "observed_resources": "examples/test/mocks/observed-resources/vault/steps/2/" }
]
api_path: apis/secretstacks
error_on_missing_schemas: true

test:
uses: unbounded-tech/workflows-crossplane/.github/workflows/test.yaml@v3.0.0

review-findings:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6
with:
persist-credentials: false
- name: Install up
uses: upbound/action-up@v1.0.0
with:
skip-login: true
- name: Build project
uses: upbound/action-up-project@v1.1.2
with:
skip-login-check: true
- name: Install Crossplane CLI
run: |
curl -sL https://cli.crossplane.io/install.sh | XP_VERSION=v2.4.1 sh
sudo mv crossplane /usr/local/bin/crossplane
- name: Verify review findings
run: tests/review-findings.sh

e2e:
uses: unbounded-tech/workflows-crossplane/.github/workflows/e2e.yaml@v3.0.0
with:
Expand All @@ -55,7 +81,7 @@ jobs:
}
debug-resource-types: |
[
"secretstacks.aws.hops.ops.com.ai"
"secretstacks.hops.ops.com.ai"
]
delete-extra-resources: |
[
Expand All @@ -70,6 +96,7 @@ jobs:
needs:
- validate
- test
- review-findings
- e2e
uses: unbounded-tech/workflow-vnext-tag/.github/workflows/workflow.yaml@v1.21.3
secrets:
Expand Down
21 changes: 15 additions & 6 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
SHELL := /bin/bash

PACKAGE ?= aws-secret-stack
PACKAGE ?= secret-stack
XRD_DIR := apis/secretstacks
COMPOSITION := $(XRD_DIR)/composition.yaml
DEFINITION := $(XRD_DIR)/definition.yaml
Expand All @@ -26,8 +26,12 @@ generate-configuration:
EXAMPLES := \
examples/secretstacks/minimal.yaml:: \
examples/secretstacks/standard.yaml:: \
examples/secretstacks/vault.yaml:: \
examples/secretstacks/vault-external.yaml:: \
examples/secretstacks/standard.yaml::examples/test/mocks/observed-resources/standard/steps/1/ \
examples/secretstacks/standard.yaml::examples/test/mocks/observed-resources/standard/steps/2/
examples/secretstacks/standard.yaml::examples/test/mocks/observed-resources/standard/steps/2/ \
examples/secretstacks/vault.yaml::examples/test/mocks/observed-resources/vault/steps/1/ \
examples/secretstacks/vault.yaml::examples/test/mocks/observed-resources/vault/steps/2/

# Render all examples (parallel execution, output shown per-job when complete)
render\:all:
Expand All @@ -38,6 +42,7 @@ render\:all:
observed=$${entry#*::}; \
outfile="$$tmpdir/$$(echo $$entry | tr '/:' '__')"; \
( \
set -euo pipefail; \
if [ -n "$$observed" ]; then \
echo "=== Rendering $$example with observed-resources $$observed ==="; \
up composition render --xrd=$(DEFINITION) $(COMPOSITION) $$example --observed-resources=$$observed; \
Expand Down Expand Up @@ -68,16 +73,17 @@ validate\:all: generate-configuration
observed=$${entry#*::}; \
outfile="$$tmpdir/$$(echo $$entry | tr '/:' '__')"; \
( \
set -euo pipefail; \
if [ -n "$$observed" ]; then \
echo "=== Validating $$example with observed-resources $$observed ==="; \
up composition render --xrd=$(DEFINITION) $(COMPOSITION) $$example \
--observed-resources=$$observed --include-full-xr --quiet | \
crossplane beta validate $(XRD_DIR) --error-on-missing-schemas -; \
crossplane resource validate $(XRD_DIR) --error-on-missing-schemas -; \
else \
echo "=== Validating $$example ==="; \
up composition render --xrd=$(DEFINITION) $(COMPOSITION) $$example \
--include-full-xr --quiet | \
crossplane beta validate $(XRD_DIR) --error-on-missing-schemas -; \
crossplane resource validate $(XRD_DIR) --error-on-missing-schemas -; \
Comment thread
coderabbitai[bot] marked this conversation as resolved.
fi; \
echo "" \
) > "$$outfile" 2>&1 & \
Expand All @@ -94,7 +100,7 @@ validate\:all: generate-configuration
exit $$failed

# Shorthand aliases
.PHONY: render validate generate-configuration
.PHONY: render validate generate-configuration test test-review e2e
render: ; @$(MAKE) 'render:all'
validate: ; @$(MAKE) generate-configuration 'validate:all'

Expand All @@ -107,11 +113,14 @@ validate\:%: generate-configuration
@example="examples/secretstacks/$*.yaml"; \
up composition render --xrd=$(DEFINITION) $(COMPOSITION) $$example \
--include-full-xr --quiet | \
crossplane beta validate $(XRD_DIR) --error-on-missing-schemas -
crossplane resource validate $(XRD_DIR) --error-on-missing-schemas -

test:
up test run $(RENDER_TESTS)

test-review:
./tests/review-findings.sh

e2e:
up test run $(E2E_TESTS) --e2e

Expand Down
108 changes: 58 additions & 50 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,111 +1,119 @@
# aws-secret-stack
# secret-stack

Installs external-secrets with AWS Pod Identity for Secrets Manager and SSM Parameter Store access. Optionally creates a SecretStore.
Installs [External Secrets Operator](https://external-secrets.io/) and wires a
SecretStore to either **AWS Secrets Manager** or **HashiCorp Vault**.

## Overview
**API group:** `hops.ops.com.ai` (platform-neutral; no longer `aws.hops.ops.com.ai`).

Composes a `helm.m.crossplane.io/Release` for external-secrets with `aws.hops.ops.com.ai/PodIdentity`.
Automatically provisions IAM role and Pod Identity association for external-secrets' service account.
## Backend selection

Additionally:
- Creates a **SecretStore** (namespaced by default) or **ClusterSecretStore** (opt-in) pointing to AWS Secrets Manager, so ExternalSecrets can pull secrets immediately
| `spec.backend` | What gets created |
|----------------|-------------------|
| `aws` (default) | ESO Helm + AWS PodIdentity + Secrets Manager SecretStore |
| `vault` | ESO Helm + Vault SecretStore; optional in-cluster Vault Helm (`vault.install`) |

## Usage

Minimal — installs ESO, PodIdentity, and SecretStore:
### AWS (production EKS)

```yaml
apiVersion: aws.hops.ops.com.ai/v1alpha1
apiVersion: hops.ops.com.ai/v1alpha1
kind: SecretStack
metadata:
name: external-secrets
namespace: default
spec:
clusterName: my-cluster
backend: aws
aws:
region: us-east-1
```

With custom values and role prefix:
### Vault in-cluster (local / dory)

```yaml
apiVersion: aws.hops.ops.com.ai/v1alpha1
apiVersion: hops.ops.com.ai/v1alpha1
kind: SecretStack
metadata:
name: external-secrets
namespace: default
spec:
clusterName: production-cluster
namespace: external-secrets
values:
serviceAccount:
create: true
aws:
region: us-west-2
rolePrefix: prod-
tags:
environment: production
```

ClusterSecretStore (cluster-wide access):

```yaml
apiVersion: aws.hops.ops.com.ai/v1alpha1
kind: SecretStack
metadata:
name: external-secrets
namespace: default
spec:
clusterName: my-cluster
clusterName: dory
backend: vault
secretStore:
scope: Cluster
aws:
region: us-east-1
name: vault
vault:
install: true
# server defaults to http://vault.vault.svc.cluster.local:8200
auth:
method: kubernetes
role: external-secrets
```

ESO only — no SecretStore:
The default dev-mode Vault install bootstraps the Kubernetes auth method,
cluster connection, policy, and ESO role before the Vault pod becomes Ready.
If `vault.values` or `vault.overrideAllValues` disables dev mode for an HA or
production deployment, configure Vault authentication externally; Hops does not
assume or synthesize production administrator credentials.

### External Vault (token auth)

```yaml
apiVersion: aws.hops.ops.com.ai/v1alpha1
apiVersion: hops.ops.com.ai/v1alpha1
kind: SecretStack
metadata:
name: external-secrets
namespace: default
spec:
clusterName: my-cluster
secretStore:
enabled: false
aws:
region: us-east-1
backend: vault
vault:
install: false
server: https://vault.example.com:8200
auth:
method: token
tokenSecretRef:
name: vault-token
key: token
```

## What Gets Created

| Resource | Condition | Description |
|----------|-----------|-------------|
| `helm.m.crossplane.io/Release` | Always | external-secrets Helm release (chart v2.2.0) |
| `aws.hops.ops.com.ai/PodIdentity` | Always | IAM role + Pod Identity with Secrets Manager, SSM, and KMS permissions |
| `kubernetes.m.crossplane.io/Object` (SecretStore) | `secretStore.enabled` (default true) | ClusterSecretStore or SecretStore wired to AWS Secrets Manager via PodIdentity JWT auth |
| `helm.m.crossplane.io/Release` (external-secrets) | Always | ESO chart |
| `helm.m.crossplane.io/Release` (vault) | `backend=vault` + `vault.install` | Official Vault chart (dev-friendly defaults) |
| `aws.hops.ops.com.ai/PodIdentity` | `backend=aws` | IAM role + Pod Identity for SM/SSM/KMS |
| `kubernetes.m.crossplane.io/Object` (SecretStore) | `secretStore.enabled` (default true) | Backend-specific SecretStore / ClusterSecretStore |

## SecretStore Options

| Field | Default | Description |
|-------|---------|-------------|
| `secretStore.enabled` | `true` | Create a SecretStore resource |
| `secretStore.scope` | `Namespaced` | `Namespaced` for SecretStore, `Cluster` for ClusterSecretStore |
| `secretStore.scope` | `Namespaced` | `Namespaced` or `Cluster` |
| `secretStore.name` | `default` | Name of the SecretStore resource |

## Status
## Breaking change (from aws-secret-stack)

| Field | Description |
|-------|-------------|
| `ready` | Overall stack readiness |
- API group: `aws.hops.ops.com.ai` → `hops.ops.com.ai`
- Package: `aws-secret-stack` → `secret-stack`
- `spec.aws` is only required when `backend=aws`
- New `spec.backend` and `spec.vault`

## Development

```bash
make render # render all examples
make validate # validate rendered output
make test # run KCL unit tests
make e2e # run E2E tests (requires AWS credentials)
```

### Local install (source)

```bash
# Confirm with the user before targeting a cluster
hops config install --path xrs/stacks/aws/secret
kubectl apply -f local/secretstack.yaml
```
6 changes: 3 additions & 3 deletions apis/secretstacks/composition.yaml
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
apiVersion: apiextensions.crossplane.io/v1
kind: Composition
metadata:
name: secretstacks.aws.hops.ops.com.ai
name: secretstacks.hops.ops.com.ai
spec:
compositeTypeRef:
apiVersion: aws.hops.ops.com.ai/v1alpha1
apiVersion: hops.ops.com.ai/v1alpha1
kind: SecretStack
mode: Pipeline
pipeline:
- functionRef:
name: hops-ops-aws-secret-stackrender
name: hops-ops-secret-stackrender
step: render
- functionRef:
name: crossplane-contrib-function-auto-ready
Expand Down
Loading
Loading