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
9 changes: 9 additions & 0 deletions .github/workflows/publish-image.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ jobs:
permissions:
contents: read
packages: write
id-token: write
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: docker/setup-buildx-action@f87e5991a6d7451dcb8d9637bfbc97413f497069 # v4.4.1
Expand Down Expand Up @@ -57,3 +58,11 @@ jobs:
cache-from: type=gha
provenance: true
sbom: true
id: push

# Keyless signature tied to this workflow's OIDC identity. The Kyverno
# policy in deploy/aks/policies only admits images signed by it.
- uses: sigstore/cosign-installer@6f9f17788090df1f26f669e9d70d6ae9567deba6 # v4.1.2

- name: Sign the pushed image
run: cosign sign --yes "${IMAGE}@${{ steps.push.outputs.digest }}"
9 changes: 7 additions & 2 deletions deploy/aks/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,22 +10,27 @@ Git holds the desired state:

- `k8s/base` and `k8s/overlays/demo`: a one-replica StatefulSet on a 1Gi volume, a Service, and the image tag.
- `argocd/application.yaml`: the Argo CD Application that syncs the demo overlay from `master`.
- `.github/workflows/publish-image.yml`: builds, scans and pushes `ghcr.io/sharedcode/joltrin:sha-<commit>`.
- `.github/workflows/publish-image.yml`: builds, scans, pushes `ghcr.io/sharedcode/joltrin:sha-<commit>` and signs it with keyless cosign.
- `argocd/kyverno.yaml` and `policies/verify-joltrin-image.yaml`: Kyverno, plus a policy that rejects any pod in `joltrin-demo` whose image was not signed by `publish-image` on `master`.

The cluster and Argo CD come from [gitops-aks-demo](https://github.com/gerardrecinto/gitops-aks-demo) (`make up`, about $0.13 an hour, `make down` removes it).

## Run it

1. Run the `publish-image` workflow. The repository is public and the image holds only binaries built from it, with no secrets, so the GHCR package can be made public once, which lets the cluster pull without a pull secret.
2. Put the new tag in `k8s/overlays/demo/kustomization.yaml` through a pull request.
3. From gitops-aks-demo, `make up`, then apply the Application:
3. From gitops-aks-demo, `make up`, then apply the Applications. Kyverno goes first so the policy CRD exists:

```
kubectl apply -f deploy/aks/argocd/kyverno.yaml
kubectl apply -f deploy/aks/argocd/policies.yaml
kubectl apply -f deploy/aks/argocd/application.yaml
kubectl -n argocd get applications
kubectl -n joltrin-demo port-forward svc/joltrin 8080:80
```

4. When done, `make down` in gitops-aks-demo. The volume is deleted with the resource group.

The policy is in Enforce mode, so an image that was not signed by `publish-image` (including any tag built before signing was added) is refused at admission. The tag in the overlay must come from a run of the updated workflow.

One replica is deliberate. The B-Tree engine has no guarantee of safe multi-process writes to one data path, so scaling out needs the Redis-backed lock cache first.
21 changes: 21 additions & 0 deletions deploy/aks/argocd/kyverno.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: kyverno
namespace: argocd
spec:
project: default
source:
repoURL: https://kyverno.github.io/kyverno/
chart: kyverno
targetRevision: 3.9.2
destination:
server: https://kubernetes.default.svc
namespace: kyverno
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
- ServerSideApply=true
21 changes: 21 additions & 0 deletions deploy/aks/argocd/policies.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: joltrin-policies
namespace: argocd
spec:
project: default
source:
repoURL: https://github.com/SharedCode/joltrin.git
targetRevision: master
path: deploy/aks/policies
destination:
server: https://kubernetes.default.svc
namespace: kyverno
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
# The ClusterPolicy CRD only exists once the kyverno app has synced.
- SkipDryRunOnMissingResource=true
29 changes: 29 additions & 0 deletions deploy/aks/policies/verify-joltrin-image.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: verify-joltrin-image
spec:
validationFailureAction: Enforce
webhookTimeoutSeconds: 30
rules:
- name: signed-by-publish-image
match:
any:
- resources:
kinds:
- Pod
namespaces:
- joltrin-demo
verifyImages:
- imageReferences:
- ghcr.io/sharedcode/joltrin*
mutateDigest: true
verifyDigest: true
required: true
attestors:
- entries:
- keyless:
issuer: https://token.actions.githubusercontent.com
subject: https://github.com/SharedCode/joltrin/.github/workflows/publish-image.yml@refs/heads/master
rekor:
url: https://rekor.sigstore.dev
Loading