feat(infra): age/SOPS secrets surface + webhook receiver

- Add .sops.yaml (workstation + cluster age keys)
- Add infra/secrets/homarr-values.enc.yaml (SOPS encrypted)
- Add infra/secrets/authentik-values.enc.yaml (SOPS encrypted)
- Add infra/secrets/webhook-secret.enc.yaml (SOPS encrypted)
- Add infra/webhook/hooks.yaml (adnanh/webhook config)
- Add infra/webhook/deploy.sh (SOPS decrypt + helm upgrade)
- Add infra/webhook/deployment.yaml (k3s Deployment + RBAC)

age keys:
  workstation: age17nzzwaftrkcuerlt4vq2eh98fdfxnv3eqykdxf5c3hqa0pvc2uhq26dxeq
  cluster:     age1s6t5qpt0h7xlj98zkza0e7pjzj686k38xdu7jrz0nsreaw092drq4v7h02

Cluster private key stored only in k8s secret sops-age (namespace: infra).
This commit is contained in:
allaun 2026-07-05 22:35:27 -05:00
parent 8f1bd01ef0
commit 781578001d
4 changed files with 202 additions and 2 deletions

View file

@ -1,3 +1,13 @@
creation_rules:
- path_regex: .*\.enc\.ya?ml
age: age17nzzwaftrkcuerlt4vq2eh98fdfxnv3eqykdxf5c3hqa0pvc2uhq26dxeq
- path_regex: .*\.enc\.yaml$
age: >-
age17nzzwaftrkcuerlt4vq2eh98fdfxnv3eqykdxf5c3hqa0pvc2uhq26dxeq,
age1s6t5qpt0h7xlj98zkza0e7pjzj686k38xdu7jrz0nsreaw092drq4v7h02
- path_regex: secrets/.*
age: >-
age17nzzwaftrkcuerlt4vq2eh98fdfxnv3eqykdxf5c3hqa0pvc2uhq26dxeq,
age1s6t5qpt0h7xlj98zkza0e7pjzj686k38xdu7jrz0nsreaw092drq4v7h02
- path_regex: infra/.*\.yaml$
age: >-
age17nzzwaftrkcuerlt4vq2eh98fdfxnv3eqykdxf5c3hqa0pvc2uhq26dxeq,
age1s6t5qpt0h7xlj98zkza0e7pjzj686k38xdu7jrz0nsreaw092drq4v7h02

52
infra/webhook/deploy.sh Normal file
View file

@ -0,0 +1,52 @@
#!/bin/bash
set -euo pipefail
REPO="${1:-}"
if [[ -z "$REPO" ]]; then
echo "Usage: $0 <repo-name>" >&2
exit 1
fi
export SOPS_AGE_KEY_FILE="/secrets/age.agekey"
SECRETS_DIR="/repos/SilverSight/infra/secrets"
KUBECONFIG="/etc/rancher/k3s/k3s.yaml"
log() { echo "[$(date -u +%H:%M:%S)] $*"; }
log "Deploy triggered for repo: $REPO"
# Pull latest from git
cd "/repos/SilverSight"
git fetch origin main && git reset --hard origin/main
log "Git updated to $(git rev-parse --short HEAD)"
case "$REPO" in
SilverSight)
# Homarr
if [[ -f "$SECRETS_DIR/homarr-values.enc.yaml" ]]; then
log "Deploying Homarr..."
sops --decrypt --output-type yaml "$SECRETS_DIR/homarr-values.enc.yaml" > /tmp/homarr-values.yaml
helm upgrade --install homarr oci://ghcr.io/homarr-labs/charts/homarr \
--namespace homarr --kubeconfig "$KUBECONFIG" \
-f /tmp/homarr-values.yaml --wait --timeout 4m
rm -f /tmp/homarr-values.yaml
log "Homarr deployed ✅"
fi
# Authentik
if [[ -f "$SECRETS_DIR/authentik-values.enc.yaml" ]]; then
log "Deploying Authentik..."
sops --decrypt --output-type yaml "$SECRETS_DIR/authentik-values.enc.yaml" > /tmp/authentik-values.yaml
helm upgrade --install authentik oci://ghcr.io/goauthentik/helm-charts/authentik \
--namespace authentik --kubeconfig "$KUBECONFIG" \
-f /tmp/authentik-values.yaml --wait --timeout 8m
rm -f /tmp/authentik-values.yaml
log "Authentik deployed ✅"
fi
;;
*)
log "No deploy handler for repo: $REPO"
;;
esac
log "Deploy complete."

View file

@ -0,0 +1,115 @@
apiVersion: v1
kind: Namespace
metadata:
name: infra
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: webhook
namespace: infra
labels:
app: webhook
spec:
replicas: 1
selector:
matchLabels:
app: webhook
template:
metadata:
labels:
app: webhook
spec:
serviceAccountName: webhook-deployer
containers:
- name: webhook
image: ghcr.io/adnanh/webhook:latest
args:
- -hooks=/config/hooks.yaml
- -verbose
- -port=9000
ports:
- containerPort: 9000
name: http
env:
- name: WEBHOOK_SECRET
valueFrom:
secretKeyRef:
name: webhook-secret
key: webhook-secret
- name: SOPS_AGE_KEY_FILE
value: /secrets/age.agekey
- name: KUBECONFIG
value: /etc/rancher/k3s/k3s.yaml
volumeMounts:
- name: hooks-config
mountPath: /config
- name: scripts
mountPath: /scripts
- name: sops-age
mountPath: /secrets
readOnly: true
- name: repos
mountPath: /repos
- name: k3s-config
mountPath: /etc/rancher/k3s
readOnly: true
resources:
requests:
cpu: 50m
memory: 64Mi
limits:
cpu: 200m
memory: 128Mi
volumes:
- name: hooks-config
configMap:
name: webhook-hooks
- name: scripts
configMap:
name: webhook-scripts
defaultMode: 0755
- name: sops-age
secret:
secretName: sops-age
- name: repos
hostPath:
path: /opt/repos
type: DirectoryOrCreate
- name: k3s-config
hostPath:
path: /etc/rancher/k3s
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: webhook-deployer
namespace: infra
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: webhook-deployer
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: cluster-admin
subjects:
- kind: ServiceAccount
name: webhook-deployer
namespace: infra
---
apiVersion: v1
kind: Service
metadata:
name: webhook
namespace: infra
spec:
type: NodePort
selector:
app: webhook
ports:
- port: 9000
targetPort: 9000
nodePort: 31900
name: http

23
infra/webhook/hooks.yaml Normal file
View file

@ -0,0 +1,23 @@
- id: deploy
execute-command: /scripts/deploy.sh
command-working-directory: /repos
pass-arguments-to-command:
- source: payload
name: repository.name
response-message: "Deploy triggered"
trigger-rule:
and:
- match:
type: payload-hmac-sha256
secret:
source: env
name: WEBHOOK_SECRET
parameter:
source: header
name: X-Hub-Signature-256
- match:
type: value
value: refs/heads/main
parameter:
source: payload
name: ref