feat: replace secret-init Jobs with mittwald operator + cert-manager

Migrate ~180 LOC of openssl/kubectl init Jobs to declarative Secret
manifests reconciled by mittwald/kubernetes-secret-generator (random
strings, SSH keypair) and cert-manager Certificates (RSA private key +
self-signed CA chain). mittwald only fills empty fields, so existing
populated Secrets keep their current values across the migration.

Changes:

- New prototype kubernetes-secret-generator (chart 3.4.1, mittwald helm
  repo). Cluster-wide informer reconciler, no webhook -> cold-bootstrap
  safe via ArgoCD retries.
- New cert-manager selfsigned ClusterIssuer (in-cluster trust root).
  letsencrypt remains for public-DNS endpoints.
- forgejo: admin-secret Job replaced with a mittwald-annotated Secret
  (hex-encoded 24-char password). Deploy-key Job split: mittwald
  ssh-keypair Secret + slim Job that uploads pubkey to Forgejo and
  copies privkey into the argocd repo Secret.
- ocis: 13 Secrets / 16 random fields now mittwald-managed (UUIDs
  replaced with opaque random hex; ocis treats user-id as opaque). IDP
  RSA signing key, LDAP self-signed CA, and LDAP server cert produced
  by cert-manager. Per-Deployment ytt overlay remaps volume key paths
  (tls.crt -> ldap-ca.crt, tls.key -> private-key.pem, etc.) since the
  ocis chart mounts Secrets raw without items support. Old multi-secret
  s3-secret-job replaced with a slim external-secret precheck Job that
  only validates pre-created Hetzner S3/Storage Box credentials.
- Application sync-wave -10 on cert-manager and kubernetes-secret-
  generator so they install before consumers. ArgoCD selfHeal handles
  any residual races.

CLAUDE.md: remove the "all namespaces use privileged PodSecurity"
convention. Existing namespaces still carry the label and will be
audited separately.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Felix Wolf 2026-05-03 00:00:07 +02:00
parent 9112153e8a
commit 85b8fec6b3
68 changed files with 1191 additions and 506 deletions

View file

@ -7,7 +7,6 @@ GitOps-managed Kubernetes cluster on Hetzner Cloud running Talos Linux. Uses [my
- **3 Talos control-plane nodes** (CAX11 ARM64, Hetzner Cloud Nuremberg) - **3 Talos control-plane nodes** (CAX11 ARM64, Hetzner Cloud Nuremberg)
- Node IPs: `195.201.219.111`, `195.201.140.75`, `195.201.219.17` - Node IPs: `195.201.219.111`, `195.201.140.75`, `195.201.219.17`
- `allowSchedulingOnControlPlanes: true` (no dedicated workers) - `allowSchedulingOnControlPlanes: true` (no dedicated workers)
- All namespaces use `pod-security.kubernetes.io/enforce: privileged`
## Domain & DNS ## Domain & DNS
- **Domain**: `tr1ceracop.de` (registered at INWX) - **Domain**: `tr1ceracop.de` (registered at INWX)

View file

@ -0,0 +1,9 @@
#@ load("@ytt:overlay", "overlay")
#@overlay/match by=overlay.all, expects="1+"
---
metadata:
#@overlay/match missing_ok=True
#@overlay/match-child-defaults missing_ok=True
annotations:
argocd.argoproj.io/sync-wave: "-10"

View file

@ -0,0 +1,5 @@
#@data/values-schema
---
#@overlay/match-child-defaults missing_ok=True
application:
namespace: kubernetes-secret-generator

View file

@ -0,0 +1,9 @@
#@ load("@ytt:overlay", "overlay")
#@overlay/match by=overlay.all, expects="1+"
---
metadata:
#@overlay/match missing_ok=True
#@overlay/match-child-defaults missing_ok=True
annotations:
argocd.argoproj.io/sync-wave: "-10"

View file

@ -6,6 +6,7 @@ environment:
- proto: argocd - proto: argocd
- proto: traefik - proto: traefik
- proto: cert-manager - proto: cert-manager
- proto: kubernetes-secret-generator
- proto: forgejo - proto: forgejo
- proto: victoria-metrics-single - proto: victoria-metrics-single
- proto: grafana - proto: grafana

View file

@ -0,0 +1,7 @@
---
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: selfsigned
spec:
selfSigned: {}

View file

@ -1,73 +0,0 @@
#@ load("@ytt:data", "data")
#@ ns = data.values.application.namespace
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: forgejo-admin-secret-init
namespace: #@ ns
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: forgejo-admin-secret-init
namespace: #@ ns
rules:
- apiGroups: [""]
resources: ["secrets"]
verbs: ["get", "create"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: forgejo-admin-secret-init
namespace: #@ ns
subjects:
- kind: ServiceAccount
name: forgejo-admin-secret-init
namespace: #@ ns
roleRef:
kind: Role
name: forgejo-admin-secret-init
apiGroup: rbac.authorization.k8s.io
---
apiVersion: batch/v1
kind: Job
metadata:
name: forgejo-admin-secret-init
namespace: #@ ns
annotations:
argocd.argoproj.io/sync-options: Replace=true
spec:
ttlSecondsAfterFinished: 300
template:
spec:
serviceAccountName: forgejo-admin-secret-init
restartPolicy: OnFailure
containers:
- name: init
image: alpine/k8s:1.32.3
command:
- sh
- -c
- |
if kubectl get secret forgejo-admin-secret -n ${NAMESPACE} >/dev/null 2>&1; then
echo "Secret already exists, skipping"
exit 0
fi
PASSWORD=$(head -c 32 /dev/urandom | base64 | tr -dc 'a-zA-Z0-9' | head -c 24)
kubectl create secret generic forgejo-admin-secret \
-n ${NAMESPACE} \
--from-literal=username=gitea_admin \
--from-literal=password="${PASSWORD}"
echo "Created forgejo-admin-secret with random password"
env:
- name: NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace

View file

@ -0,0 +1,17 @@
#@ load("@ytt:data", "data")
#@ ns = data.values.application.namespace
---
apiVersion: v1
kind: Secret
metadata:
name: forgejo-admin-secret
namespace: #@ ns
annotations:
secret-generator.v1.mittwald.de/autogenerate: password
secret-generator.v1.mittwald.de/encoding: hex
secret-generator.v1.mittwald.de/length: "24"
type: Opaque
stringData:
username: gitea_admin

View file

@ -17,7 +17,7 @@ metadata:
rules: rules:
- apiGroups: [""] - apiGroups: [""]
resources: ["secrets"] resources: ["secrets"]
verbs: ["get", "create"] verbs: ["get", "create", "patch", "update"]
--- ---
apiVersion: rbac.authorization.k8s.io/v1 apiVersion: rbac.authorization.k8s.io/v1
@ -40,6 +40,7 @@ metadata:
name: argocd-deploy-key-init name: argocd-deploy-key-init
namespace: #@ ns namespace: #@ ns
annotations: annotations:
argocd.argoproj.io/sync-wave: "1"
argocd.argoproj.io/sync-options: Replace=true argocd.argoproj.io/sync-options: Replace=true
spec: spec:
ttlSecondsAfterFinished: 300 ttlSecondsAfterFinished: 300
@ -56,8 +57,6 @@ spec:
- | - |
set -e set -e
apk add --no-cache openssh-keygen > /dev/null 2>&1
ARGOCD_NS="argocd" ARGOCD_NS="argocd"
REPO_SECRET="forgejo-repo" REPO_SECRET="forgejo-repo"
REPO_URL="ssh://git@git.tr1ceracop.de:222/gitea_admin/k8s-and-chill.git" REPO_URL="ssh://git@git.tr1ceracop.de:222/gitea_admin/k8s-and-chill.git"
@ -65,6 +64,27 @@ spec:
REPO_OWNER="gitea_admin" REPO_OWNER="gitea_admin"
REPO_NAME="k8s-and-chill" REPO_NAME="k8s-and-chill"
# Check if ArgoCD repo secret already exists
if kubectl get secret "${REPO_SECRET}" -n "${ARGOCD_NS}" >/dev/null 2>&1; then
echo "Secret ${REPO_SECRET} already exists in ${ARGOCD_NS}, skipping"
exit 0
fi
# Wait for mittwald to populate the keypair and admin secrets
echo "Waiting for forgejo-repo-keypair to be populated..."
for i in $(seq 1 60); do
PRIV_B64=$(kubectl get secret forgejo-repo-keypair -n "${NAMESPACE}" -o jsonpath='{.data.ssh-privatekey}' 2>/dev/null || true)
PUB_B64=$(kubectl get secret forgejo-repo-keypair -n "${NAMESPACE}" -o jsonpath='{.data.ssh-publickey}' 2>/dev/null || true)
if [ -n "${PRIV_B64}" ] && [ -n "${PUB_B64}" ]; then
break
fi
if [ "$i" -eq 60 ]; then
echo "forgejo-repo-keypair was not populated in time"
exit 1
fi
sleep 5
done
# Wait for Forgejo to be ready # Wait for Forgejo to be ready
echo "Waiting for Forgejo to be ready..." echo "Waiting for Forgejo to be ready..."
for i in $(seq 1 60); do for i in $(seq 1 60); do
@ -79,22 +99,12 @@ spec:
sleep 5 sleep 5
done done
# Check if ArgoCD repo secret already exists
if kubectl get secret "${REPO_SECRET}" -n "${ARGOCD_NS}" >/dev/null 2>&1; then
echo "Secret ${REPO_SECRET} already exists in ${ARGOCD_NS}, skipping"
exit 0
fi
# Read admin credentials # Read admin credentials
ADMIN_USER=$(kubectl get secret forgejo-admin-secret -n "${NAMESPACE}" -o jsonpath='{.data.username}' | base64 -d) ADMIN_USER=$(kubectl get secret forgejo-admin-secret -n "${NAMESPACE}" -o jsonpath='{.data.username}' | base64 -d)
ADMIN_PASS=$(kubectl get secret forgejo-admin-secret -n "${NAMESPACE}" -o jsonpath='{.data.password}' | base64 -d) ADMIN_PASS=$(kubectl get secret forgejo-admin-secret -n "${NAMESPACE}" -o jsonpath='{.data.password}' | base64 -d)
# Generate ed25519 SSH keypair PRIVKEY=$(echo "${PRIV_B64}" | base64 -d)
KEYDIR=$(mktemp -d) PUBKEY=$(echo "${PUB_B64}" | base64 -d)
ssh-keygen -t ed25519 -f "${KEYDIR}/id_ed25519" -N "" -q
PRIVKEY=$(cat "${KEYDIR}/id_ed25519")
PUBKEY=$(cat "${KEYDIR}/id_ed25519.pub")
rm -rf "${KEYDIR}"
# Register deploy key via Forgejo API # Register deploy key via Forgejo API
echo "Registering deploy key..." echo "Registering deploy key..."

View file

@ -0,0 +1,13 @@
#@ load("@ytt:data", "data")
#@ ns = data.values.application.namespace
---
apiVersion: v1
kind: Secret
metadata:
name: forgejo-repo-keypair
namespace: #@ ns
annotations:
secret-generator.v1.mittwald.de/autogenerate: ssh-keypair
type: Opaque

View file

@ -0,0 +1,5 @@
#@data/values-schema
---
#@overlay/match-child-defaults missing_ok=True
application:
namespace: kubernetes-secret-generator

View file

@ -0,0 +1,11 @@
---
watchNamespace: ""
regenerateInsecure: false
resources:
requests:
cpu: 10m
memory: 32Mi
limits:
memory: 128Mi

View file

@ -0,0 +1,16 @@
#@ load("@ytt:data", "data")
#@ app = data.values.application
---
apiVersion: vendir.k14s.io/v1alpha1
kind: Config
directories:
- path: #@ "charts/" + app.name
contents:
- path: .
helmChart:
name: #@ app.name
version: #@ app.version
repository:
url: #@ app.url
lazy: true

View file

@ -0,0 +1,8 @@
#@data/values-schema
---
#@overlay/match-child-defaults missing_ok=True
application:
#! renovate: datasource=helm
name: kubernetes-secret-generator
url: https://helm.mittwald.de
version: 3.4.1

View file

@ -0,0 +1,16 @@
#@ load("@ytt:data", "data")
#@ load("@ytt:overlay", "overlay")
#@ ns = data.values.application.namespace
---
apiVersion: v1
kind: Namespace
metadata:
name: #@ ns
#@overlay/match by=overlay.all, expects="1+"
---
metadata:
#@overlay/match missing_ok=True
namespace: #@ ns

View file

@ -0,0 +1,82 @@
#@ load("@ytt:data", "data")
#@ ns = data.values.application.namespace
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: ocis-external-secret-precheck
namespace: #@ ns
annotations:
argocd.argoproj.io/hook: PreSync
argocd.argoproj.io/sync-wave: "-2"
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: ocis-external-secret-precheck
namespace: #@ ns
annotations:
argocd.argoproj.io/hook: PreSync
argocd.argoproj.io/sync-wave: "-2"
rules:
- apiGroups: [""]
resources: ["secrets"]
verbs: ["get"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: ocis-external-secret-precheck
namespace: #@ ns
annotations:
argocd.argoproj.io/hook: PreSync
argocd.argoproj.io/sync-wave: "-2"
subjects:
- kind: ServiceAccount
name: ocis-external-secret-precheck
namespace: #@ ns
roleRef:
kind: Role
name: ocis-external-secret-precheck
apiGroup: rbac.authorization.k8s.io
---
apiVersion: batch/v1
kind: Job
metadata:
name: ocis-external-secret-precheck
namespace: #@ ns
annotations:
argocd.argoproj.io/hook: PreSync
argocd.argoproj.io/sync-wave: "-1"
argocd.argoproj.io/sync-options: Replace=true
spec:
ttlSecondsAfterFinished: 300
template:
spec:
serviceAccountName: ocis-external-secret-precheck
restartPolicy: OnFailure
containers:
- name: precheck
image: alpine/k8s:1.32.3
command:
- sh
- -c
- |
set -e
for s in ocis-s3-credentials ocis-storagebox-credentials; do
if ! kubectl get secret "$s" -n "${NAMESPACE}" >/dev/null 2>&1; then
echo "ERROR: External secret $s must be created manually before deploying ocis"
exit 1
fi
echo "OK: $s exists"
done
env:
- name: NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace

View file

@ -0,0 +1,70 @@
#@ load("@ytt:data", "data")
#@ ns = data.values.application.namespace
---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: ocis-idp-rsa
namespace: #@ ns
spec:
secretName: ocis-idp-rsa
commonName: ocis-idp
duration: 87600h
renewBefore: 720h
privateKey:
algorithm: RSA
size: 4096
issuerRef:
name: selfsigned
kind: ClusterIssuer
---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: ocis-ldap-ca
namespace: #@ ns
spec:
secretName: ocis-ldap-ca-tls
commonName: ocis-ldap-ca
isCA: true
duration: 87600h
renewBefore: 720h
privateKey:
algorithm: RSA
size: 2048
issuerRef:
name: selfsigned
kind: ClusterIssuer
---
apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
name: ocis-ldap-ca
namespace: #@ ns
spec:
ca:
secretName: ocis-ldap-ca-tls
---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: ocis-ldap-cert
namespace: #@ ns
spec:
secretName: ocis-ldap-cert-tls
commonName: idm
dnsNames:
- idm
duration: 87600h
renewBefore: 720h
privateKey:
algorithm: RSA
size: 2048
issuerRef:
name: ocis-ldap-ca
kind: Issuer

View file

@ -0,0 +1,30 @@
#@ load("@ytt:data", "data")
#@ ns = data.values.application.namespace
#@ secrets = [
#@ ("ocis-admin-user", "password,user-id"),
#@ ("ocis-jwt-secret", "jwt-secret"),
#@ ("ocis-machine-auth-api-key", "machine-auth-api-key"),
#@ ("ocis-storage-system-jwt-secret", "storage-system-jwt-secret"),
#@ ("ocis-storage-system", "api-key,user-id"),
#@ ("ocis-transfer-secret", "transfer-secret"),
#@ ("ocis-thumbnails-transfer-secret", "thumbnails-transfer-secret"),
#@ ("ocis-service-account-secret", "service-account-secret"),
#@ ("ocis-collaboration-wopi-secret", "wopi-secret"),
#@ ("ocis-ldap-bind-secrets", "reva-ldap-bind-password,idp-ldap-bind-password,graph-ldap-bind-password"),
#@ ("ocis-idp-encryption", "encryption.key"),
#@ ]
#@ for name, fields in secrets:
---
apiVersion: v1
kind: Secret
metadata:
name: #@ name
namespace: #@ ns
annotations:
secret-generator.v1.mittwald.de/autogenerate: #@ fields
secret-generator.v1.mittwald.de/length: "32"
type: Opaque
#@ end

View file

@ -1,182 +0,0 @@
#@ load("@ytt:data", "data")
#@ ns = data.values.application.namespace
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: ocis-secret-init
namespace: #@ ns
annotations:
argocd.argoproj.io/hook: PreSync
argocd.argoproj.io/sync-wave: "-2"
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: ocis-secret-init
namespace: #@ ns
annotations:
argocd.argoproj.io/hook: PreSync
argocd.argoproj.io/sync-wave: "-2"
rules:
- apiGroups: [""]
resources: ["secrets"]
verbs: ["get", "create"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: ocis-secret-init
namespace: #@ ns
annotations:
argocd.argoproj.io/hook: PreSync
argocd.argoproj.io/sync-wave: "-2"
subjects:
- kind: ServiceAccount
name: ocis-secret-init
namespace: #@ ns
roleRef:
kind: Role
name: ocis-secret-init
apiGroup: rbac.authorization.k8s.io
---
apiVersion: batch/v1
kind: Job
metadata:
name: ocis-secret-init
namespace: #@ ns
annotations:
argocd.argoproj.io/hook: PreSync
argocd.argoproj.io/sync-wave: "-1"
argocd.argoproj.io/sync-options: Replace=true
spec:
ttlSecondsAfterFinished: 300
template:
spec:
serviceAccountName: ocis-secret-init
restartPolicy: OnFailure
containers:
- name: init
image: alpine/k8s:1.32.3
command:
- sh
- -c
- |
set -e
apk add --no-cache openssl >/dev/null 2>&1
gen_random() {
head -c 32 /dev/urandom | base64 | tr -dc 'a-zA-Z0-9' | head -c "$1"
}
gen_uuid() {
cat /proc/sys/kernel/random/uuid
}
create_secret_if_missing() {
local name="$1"
shift
if kubectl get secret "$name" -n "${NAMESPACE}" >/dev/null 2>&1; then
echo "Secret $name already exists, skipping"
return
fi
kubectl create secret generic "$name" -n "${NAMESPACE}" "$@"
echo "Created secret $name"
}
# Validate external secrets exist
if ! kubectl get secret ocis-s3-credentials -n "${NAMESPACE}" >/dev/null 2>&1; then
echo "ERROR: External secret ocis-s3-credentials must be created manually"
exit 1
fi
if ! kubectl get secret ocis-storagebox-credentials -n "${NAMESPACE}" >/dev/null 2>&1; then
echo "ERROR: External secret ocis-storagebox-credentials must be created manually"
exit 1
fi
# Admin user
create_secret_if_missing ocis-admin-user \
--from-literal=password="$(gen_random 32)" \
--from-literal=user-id="$(gen_uuid)"
# JWT secret
create_secret_if_missing ocis-jwt-secret \
--from-literal=jwt-secret="$(gen_random 32)"
# Machine auth API key
create_secret_if_missing ocis-machine-auth-api-key \
--from-literal=machine-auth-api-key="$(gen_random 32)"
# Storage system JWT secret
create_secret_if_missing ocis-storage-system-jwt-secret \
--from-literal=storage-system-jwt-secret="$(gen_random 32)"
# Storage system secret
create_secret_if_missing ocis-storage-system \
--from-literal=api-key="$(gen_random 32)" \
--from-literal=user-id="$(gen_uuid)"
# Transfer secret
create_secret_if_missing ocis-transfer-secret \
--from-literal=transfer-secret="$(gen_random 32)"
# Thumbnails transfer secret
create_secret_if_missing ocis-thumbnails-transfer-secret \
--from-literal=thumbnails-transfer-secret="$(gen_random 32)"
# Service account secret
create_secret_if_missing ocis-service-account-secret \
--from-literal=service-account-secret="$(gen_random 32)"
# Collaboration WOPI secret
create_secret_if_missing ocis-collaboration-wopi-secret \
--from-literal=wopi-secret="$(gen_random 32)"
# LDAP bind secrets (three passwords for different bind users)
create_secret_if_missing ocis-ldap-bind-secrets \
--from-literal=reva-ldap-bind-password="$(gen_random 32)" \
--from-literal=idp-ldap-bind-password="$(gen_random 32)" \
--from-literal=graph-ldap-bind-password="$(gen_random 32)"
# IDP secret (encryption key + RSA private key)
create_secret_if_missing ocis-idp-secrets \
--from-literal=encryption.key="$(gen_random 32)" \
--from-literal=private-key.pem="$(openssl genrsa 4096 2>/dev/null)"
# LDAP CA cert + key (self-signed)
if ! kubectl get secret ocis-ldap-ca -n "${NAMESPACE}" >/dev/null 2>&1; then
openssl req -x509 -newkey rsa:2048 -keyout /tmp/ldap-ca.key -out /tmp/ldap-ca.crt \
-days 3650 -nodes -subj "/CN=ldap-ca" 2>/dev/null
kubectl create secret generic ocis-ldap-ca -n "${NAMESPACE}" \
--from-file=ldap-ca.crt=/tmp/ldap-ca.crt
echo "Created secret ocis-ldap-ca"
# LDAP server cert signed by the CA
printf "subjectAltName=DNS:idm" > /tmp/ldap-ext.cnf
openssl req -newkey rsa:2048 -keyout /tmp/ldap.key -out /tmp/ldap.csr \
-nodes -subj "/CN=idm" -addext "subjectAltName=DNS:idm" 2>/dev/null
openssl x509 -req -in /tmp/ldap.csr -CA /tmp/ldap-ca.crt -CAkey /tmp/ldap-ca.key \
-CAcreateserial -out /tmp/ldap.crt -days 3650 \
-extfile /tmp/ldap-ext.cnf 2>/dev/null
kubectl create secret generic ocis-ldap-cert -n "${NAMESPACE}" \
--from-file=ldap.crt=/tmp/ldap.crt \
--from-file=ldap.key=/tmp/ldap.key
echo "Created secret ocis-ldap-cert"
rm -f /tmp/ldap-ca.key /tmp/ldap-ca.crt /tmp/ldap.key /tmp/ldap.crt /tmp/ldap.csr /tmp/ldap-ca.srl /tmp/ldap-ext.cnf
else
echo "Secret ocis-ldap-ca already exists, skipping LDAP certs"
fi
echo "All secrets initialized successfully"
env:
- name: NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace

View file

@ -0,0 +1,59 @@
#@ load("@ytt:overlay", "overlay")
#@ ldap_ca_deployments = ["idp", "graph", "groups", "users"]
#@ for dep_name in ldap_ca_deployments:
#@overlay/match by=overlay.subset({"kind": "Deployment", "metadata": {"name": dep_name}})
---
spec:
template:
spec:
volumes:
#@overlay/match by=overlay.subset({"secret": {"secretName": "ocis-ldap-ca"}})
#@overlay/replace
- name: ldap-ca
secret:
secretName: ocis-ldap-ca-tls
items:
- key: tls.crt
path: ldap-ca.crt
#@ end
#@overlay/match by=overlay.subset({"kind": "Deployment", "metadata": {"name": "idp"}})
---
spec:
template:
spec:
volumes:
#@overlay/match by=overlay.subset({"secret": {"secretName": "ocis-idp-secrets"}})
#@overlay/replace
- name: idp-secrets
projected:
sources:
- secret:
name: ocis-idp-encryption
items:
- key: encryption.key
path: encryption.key
- secret:
name: ocis-idp-rsa
items:
- key: tls.key
path: private-key.pem
#@overlay/match by=overlay.subset({"kind": "Deployment", "metadata": {"name": "idm"}})
---
spec:
template:
spec:
volumes:
#@overlay/match by=overlay.subset({"secret": {"secretName": "ocis-ldap-cert"}})
#@overlay/replace
- name: ldap-cert
secret:
secretName: ocis-ldap-cert-tls
items:
- key: tls.crt
path: ldap.crt
- key: tls.key
path: ldap.key

View file

@ -2,6 +2,7 @@ apiVersion: argoproj.io/v1alpha1
kind: Application kind: Application
metadata: metadata:
annotations: annotations:
argocd.argoproj.io/sync-wave: "-10"
myks.dev/environment: production myks.dev/environment: production
finalizers: finalizers:
- resources-finalizer.argocd.argoproj.io - resources-finalizer.argocd.argoproj.io

View file

@ -0,0 +1,26 @@
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
annotations:
argocd.argoproj.io/sync-wave: "-10"
myks.dev/environment: production
finalizers:
- resources-finalizer.argocd.argoproj.io
name: app-production-kubernetes-secret-generator
namespace: argocd
spec:
destination:
namespace: kubernetes-secret-generator
server: https://kubernetes.default.svc
project: env-production
source:
path: rendered/envs/production/kubernetes-secret-generator
repoURL: ssh://git@git.tr1ceracop.de:222/gitea_admin/k8s-and-chill.git
targetRevision: main
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
- ServerSideApply=true

View file

@ -0,0 +1,9 @@
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
annotations:
a8r.io/repository: ssh://git@git.tr1ceracop.de:222/gitea_admin/k8s-and-chill.git
name: selfsigned
namespace: cert-manager
spec:
selfSigned: {}

View file

@ -13,3 +13,5 @@ rules:
verbs: verbs:
- get - get
- create - create
- patch
- update

View file

@ -4,6 +4,7 @@ metadata:
annotations: annotations:
a8r.io/repository: ssh://git@git.tr1ceracop.de:222/gitea_admin/k8s-and-chill.git a8r.io/repository: ssh://git@git.tr1ceracop.de:222/gitea_admin/k8s-and-chill.git
argocd.argoproj.io/sync-options: Replace=true argocd.argoproj.io/sync-options: Replace=true
argocd.argoproj.io/sync-wave: "1"
name: argocd-deploy-key-init name: argocd-deploy-key-init
namespace: forgejo namespace: forgejo
spec: spec:
@ -16,8 +17,6 @@ spec:
- | - |
set -e set -e
apk add --no-cache openssh-keygen > /dev/null 2>&1
ARGOCD_NS="argocd" ARGOCD_NS="argocd"
REPO_SECRET="forgejo-repo" REPO_SECRET="forgejo-repo"
REPO_URL="ssh://git@git.tr1ceracop.de:222/gitea_admin/k8s-and-chill.git" REPO_URL="ssh://git@git.tr1ceracop.de:222/gitea_admin/k8s-and-chill.git"
@ -25,6 +24,27 @@ spec:
REPO_OWNER="gitea_admin" REPO_OWNER="gitea_admin"
REPO_NAME="k8s-and-chill" REPO_NAME="k8s-and-chill"
# Check if ArgoCD repo secret already exists
if kubectl get secret "${REPO_SECRET}" -n "${ARGOCD_NS}" >/dev/null 2>&1; then
echo "Secret ${REPO_SECRET} already exists in ${ARGOCD_NS}, skipping"
exit 0
fi
# Wait for mittwald to populate the keypair and admin secrets
echo "Waiting for forgejo-repo-keypair to be populated..."
for i in $(seq 1 60); do
PRIV_B64=$(kubectl get secret forgejo-repo-keypair -n "${NAMESPACE}" -o jsonpath='{.data.ssh-privatekey}' 2>/dev/null || true)
PUB_B64=$(kubectl get secret forgejo-repo-keypair -n "${NAMESPACE}" -o jsonpath='{.data.ssh-publickey}' 2>/dev/null || true)
if [ -n "${PRIV_B64}" ] && [ -n "${PUB_B64}" ]; then
break
fi
if [ "$i" -eq 60 ]; then
echo "forgejo-repo-keypair was not populated in time"
exit 1
fi
sleep 5
done
# Wait for Forgejo to be ready # Wait for Forgejo to be ready
echo "Waiting for Forgejo to be ready..." echo "Waiting for Forgejo to be ready..."
for i in $(seq 1 60); do for i in $(seq 1 60); do
@ -39,22 +59,12 @@ spec:
sleep 5 sleep 5
done done
# Check if ArgoCD repo secret already exists
if kubectl get secret "${REPO_SECRET}" -n "${ARGOCD_NS}" >/dev/null 2>&1; then
echo "Secret ${REPO_SECRET} already exists in ${ARGOCD_NS}, skipping"
exit 0
fi
# Read admin credentials # Read admin credentials
ADMIN_USER=$(kubectl get secret forgejo-admin-secret -n "${NAMESPACE}" -o jsonpath='{.data.username}' | base64 -d) ADMIN_USER=$(kubectl get secret forgejo-admin-secret -n "${NAMESPACE}" -o jsonpath='{.data.username}' | base64 -d)
ADMIN_PASS=$(kubectl get secret forgejo-admin-secret -n "${NAMESPACE}" -o jsonpath='{.data.password}' | base64 -d) ADMIN_PASS=$(kubectl get secret forgejo-admin-secret -n "${NAMESPACE}" -o jsonpath='{.data.password}' | base64 -d)
# Generate ed25519 SSH keypair PRIVKEY=$(echo "${PRIV_B64}" | base64 -d)
KEYDIR=$(mktemp -d) PUBKEY=$(echo "${PUB_B64}" | base64 -d)
ssh-keygen -t ed25519 -f "${KEYDIR}/id_ed25519" -N "" -q
PRIVKEY=$(cat "${KEYDIR}/id_ed25519")
PUBKEY=$(cat "${KEYDIR}/id_ed25519.pub")
rm -rf "${KEYDIR}"
# Register deploy key via Forgejo API # Register deploy key via Forgejo API
echo "Registering deploy key..." echo "Registering deploy key..."

View file

@ -1,36 +0,0 @@
apiVersion: batch/v1
kind: Job
metadata:
annotations:
a8r.io/repository: ssh://git@git.tr1ceracop.de:222/gitea_admin/k8s-and-chill.git
argocd.argoproj.io/sync-options: Replace=true
name: forgejo-admin-secret-init
namespace: forgejo
spec:
template:
spec:
containers:
- command:
- sh
- -c
- |
if kubectl get secret forgejo-admin-secret -n ${NAMESPACE} >/dev/null 2>&1; then
echo "Secret already exists, skipping"
exit 0
fi
PASSWORD=$(head -c 32 /dev/urandom | base64 | tr -dc 'a-zA-Z0-9' | head -c 24)
kubectl create secret generic forgejo-admin-secret \
-n ${NAMESPACE} \
--from-literal=username=gitea_admin \
--from-literal=password="${PASSWORD}"
echo "Created forgejo-admin-secret with random password"
env:
- name: NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
image: alpine/k8s:1.32.3
name: init
restartPolicy: OnFailure
serviceAccountName: forgejo-admin-secret-init
ttlSecondsAfterFinished: 300

View file

@ -1,15 +0,0 @@
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
annotations:
a8r.io/repository: ssh://git@git.tr1ceracop.de:222/gitea_admin/k8s-and-chill.git
name: forgejo-admin-secret-init
namespace: forgejo
rules:
- apiGroups:
- ""
resources:
- secrets
verbs:
- get
- create

View file

@ -1,15 +0,0 @@
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
annotations:
a8r.io/repository: ssh://git@git.tr1ceracop.de:222/gitea_admin/k8s-and-chill.git
name: forgejo-admin-secret-init
namespace: forgejo
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: forgejo-admin-secret-init
subjects:
- kind: ServiceAccount
name: forgejo-admin-secret-init
namespace: forgejo

View file

@ -0,0 +1,13 @@
apiVersion: v1
kind: Secret
metadata:
annotations:
a8r.io/repository: ssh://git@git.tr1ceracop.de:222/gitea_admin/k8s-and-chill.git
secret-generator.v1.mittwald.de/autogenerate: password
secret-generator.v1.mittwald.de/encoding: hex
secret-generator.v1.mittwald.de/length: "24"
name: forgejo-admin-secret
namespace: forgejo
stringData:
username: gitea_admin
type: Opaque

View file

@ -0,0 +1,9 @@
apiVersion: v1
kind: Secret
metadata:
annotations:
a8r.io/repository: ssh://git@git.tr1ceracop.de:222/gitea_admin/k8s-and-chill.git
secret-generator.v1.mittwald.de/autogenerate: ssh-keypair
name: forgejo-repo-keypair
namespace: forgejo
type: Opaque

View file

@ -0,0 +1,39 @@
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
annotations:
a8r.io/repository: ssh://git@git.tr1ceracop.de:222/gitea_admin/k8s-and-chill.git
labels:
app.kubernetes.io/instance: kubernetes-secret-generator
app.kubernetes.io/managed-by: Helm
app.kubernetes.io/name: kubernetes-secret-generator
app.kubernetes.io/version: v3.4.1
helm.sh/chart: kubernetes-secret-generator-3.4.1
name: kubernetes-secret-generator
name: mittwald:kubernetes-secret-generator
namespace: kubernetes-secret-generator
rules:
- apiGroups:
- ""
resources:
- secrets
verbs:
- get
- create
- list
- watch
- update
- apiGroups:
- secretgenerator.mittwald.de
resources:
- basicauths
- basicauths/status
- sshkeypairs
- sshkeypairs/status
- stringsecrets
- stringsecrets/status
verbs:
- get
- list
- watch
- update

View file

@ -0,0 +1,22 @@
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
annotations:
a8r.io/repository: ssh://git@git.tr1ceracop.de:222/gitea_admin/k8s-and-chill.git
labels:
app.kubernetes.io/instance: kubernetes-secret-generator
app.kubernetes.io/managed-by: Helm
app.kubernetes.io/name: kubernetes-secret-generator
app.kubernetes.io/version: v3.4.1
helm.sh/chart: kubernetes-secret-generator-3.4.1
name: kubernetes-secret-generator
name: mittwald:kubernetes-secret-generator
namespace: kubernetes-secret-generator
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: mittwald:kubernetes-secret-generator
subjects:
- kind: ServiceAccount
name: kubernetes-secret-generator
namespace: kubernetes-secret-generator

View file

@ -0,0 +1,81 @@
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
a8r.io/repository: ssh://git@git.tr1ceracop.de:222/gitea_admin/k8s-and-chill.git
name: basicauths.secretgenerator.mittwald.de
namespace: kubernetes-secret-generator
spec:
group: secretgenerator.mittwald.de
names:
kind: BasicAuth
listKind: BasicAuthList
plural: basicauths
singular: basicauth
scope: Namespaced
versions:
- name: v1alpha1
schema:
openAPIV3Schema:
description: BasicAuth is the Schema for the basicauths API
properties:
apiVersion:
description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
type: string
kind:
description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
type: string
metadata:
type: object
spec:
description: BasicAuthSpec defines the desired state of BasicAuth
properties:
data:
additionalProperties:
type: string
type: object
encoding:
type: string
forceRegenerate:
type: boolean
length:
type: string
username:
type: string
required:
- username
type: object
status:
description: BasicAuthStatus defines the observed state of BasicAuth
properties:
secret:
description: ObjectReference contains enough information to let you inspect or modify the referred object.
properties:
apiVersion:
description: API version of the referent.
type: string
fieldPath:
description: 'If referring to a piece of an object instead of an entire object, this string should contain a valid JSON/Go field access statement, such as desiredState.manifest.containers[2]. For example, if the object reference is to a container within a pod, this would take on a value like: "spec.containers{name}" (where "name" refers to the name of the container that triggered the event) or if no container name is specified "spec.containers[2]" (container with index 2 in this pod). This syntax is chosen only to have some well-defined way of referencing a part of an object. TODO: this design is not final and this field is subject to change in the future.'
type: string
kind:
description: 'Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
type: string
name:
description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names'
type: string
namespace:
description: 'Namespace of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/'
type: string
resourceVersion:
description: 'Specific resourceVersion to which this reference is made, if any. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency'
type: string
uid:
description: 'UID of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids'
type: string
type: object
type: object
type: object
served: true
storage: true
subresources:
status: {}

View file

@ -0,0 +1,79 @@
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
a8r.io/repository: ssh://git@git.tr1ceracop.de:222/gitea_admin/k8s-and-chill.git
name: sshkeypairs.secretgenerator.mittwald.de
namespace: kubernetes-secret-generator
spec:
group: secretgenerator.mittwald.de
names:
kind: SSHKeyPair
listKind: SSHKeyPairList
plural: sshkeypairs
singular: sshkeypair
scope: Namespaced
versions:
- name: v1alpha1
schema:
openAPIV3Schema:
description: SSHKeyPair is the Schema for the sshkeypairs API
properties:
apiVersion:
description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
type: string
kind:
description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
type: string
metadata:
type: object
spec:
description: SSHKeyPairSpec defines the desired state of SSHKeyPair
properties:
data:
additionalProperties:
type: string
type: object
forceRegenerate:
type: boolean
length:
type: string
privateKey:
type: string
type:
type: string
type: object
status:
description: SSHKeyPairStatus defines the observed state of SSHKeyPair
properties:
secret:
description: 'INSERT ADDITIONAL STATUS FIELD - define observed state of cluster Important: Run "operator-sdk generate k8s" to regenerate code after modifying this file Add custom validation using kubebuilder tags: https://book-v1.book.kubebuilder.io/beyond_basics/generating_crd.html'
properties:
apiVersion:
description: API version of the referent.
type: string
fieldPath:
description: 'If referring to a piece of an object instead of an entire object, this string should contain a valid JSON/Go field access statement, such as desiredState.manifest.containers[2]. For example, if the object reference is to a container within a pod, this would take on a value like: "spec.containers{name}" (where "name" refers to the name of the container that triggered the event) or if no container name is specified "spec.containers[2]" (container with index 2 in this pod). This syntax is chosen only to have some well-defined way of referencing a part of an object. TODO: this design is not final and this field is subject to change in the future.'
type: string
kind:
description: 'Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
type: string
name:
description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names'
type: string
namespace:
description: 'Namespace of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/'
type: string
resourceVersion:
description: 'Specific resourceVersion to which this reference is made, if any. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency'
type: string
uid:
description: 'UID of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids'
type: string
type: object
type: object
type: object
served: true
storage: true
subresources:
status: {}

View file

@ -0,0 +1,88 @@
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
a8r.io/repository: ssh://git@git.tr1ceracop.de:222/gitea_admin/k8s-and-chill.git
name: stringsecrets.secretgenerator.mittwald.de
namespace: kubernetes-secret-generator
spec:
group: secretgenerator.mittwald.de
names:
kind: StringSecret
listKind: StringSecretList
plural: stringsecrets
singular: stringsecret
scope: Namespaced
versions:
- name: v1alpha1
schema:
openAPIV3Schema:
description: StringSecret is the Schema for the stringsecrets API
properties:
apiVersion:
description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
type: string
kind:
description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
type: string
metadata:
type: object
spec:
description: StringSecretSpec defines the desired state of StringSecret
properties:
data:
additionalProperties:
type: string
type: object
fields:
items:
properties:
encoding:
type: string
fieldName:
type: string
length:
type: string
type: object
type: array
forceRegenerate:
type: boolean
type:
type: string
required:
- fields
type: object
status:
description: StringSecretStatus defines the observed state of StringSecret
properties:
secret:
description: ObjectReference contains enough information to let you inspect or modify the referred object.
properties:
apiVersion:
description: API version of the referent.
type: string
fieldPath:
description: 'If referring to a piece of an object instead of an entire object, this string should contain a valid JSON/Go field access statement, such as desiredState.manifest.containers[2]. For example, if the object reference is to a container within a pod, this would take on a value like: "spec.containers{name}" (where "name" refers to the name of the container that triggered the event) or if no container name is specified "spec.containers[2]" (container with index 2 in this pod). This syntax is chosen only to have some well-defined way of referencing a part of an object. TODO: this design is not final and this field is subject to change in the future.'
type: string
kind:
description: 'Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
type: string
name:
description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names'
type: string
namespace:
description: 'Namespace of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/'
type: string
resourceVersion:
description: 'Specific resourceVersion to which this reference is made, if any. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency'
type: string
uid:
description: 'UID of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids'
type: string
type: object
type: object
type: object
served: true
storage: true
subresources:
status: {}

View file

@ -0,0 +1,83 @@
apiVersion: apps/v1
kind: Deployment
metadata:
annotations:
a8r.io/repository: ssh://git@git.tr1ceracop.de:222/gitea_admin/k8s-and-chill.git
labels:
app.kubernetes.io/instance: kubernetes-secret-generator
app.kubernetes.io/managed-by: Helm
app.kubernetes.io/name: kubernetes-secret-generator
app.kubernetes.io/version: v3.4.1
helm.sh/chart: kubernetes-secret-generator-3.4.1
name: kubernetes-secret-generator
name: kubernetes-secret-generator
namespace: kubernetes-secret-generator
spec:
replicas: 1
selector:
matchLabels:
app.kubernetes.io/instance: kubernetes-secret-generator
app.kubernetes.io/name: kubernetes-secret-generator
name: kubernetes-secret-generator
strategy:
type: Recreate
template:
metadata:
labels:
app.kubernetes.io/instance: kubernetes-secret-generator
app.kubernetes.io/name: kubernetes-secret-generator
name: kubernetes-secret-generator
spec:
automountServiceAccountToken: null
containers:
- args: []
env:
- name: WATCH_NAMESPACE
value: null
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: OPERATOR_NAME
value: kubernetes-secret-generator
- name: REGENERATE_INSECURE
value: "false"
- name: SECRET_LENGTH
value: "40"
- name: USE_METRICS_SERVICE
value: "false"
image: quay.io/mittwald/kubernetes-secret-generator:v3.4.1
imagePullPolicy: Always
livenessProbe:
failureThreshold: 3
httpGet:
path: /healthz
port: healthcheck
initialDelaySeconds: 6
periodSeconds: 3
successThreshold: 1
timeoutSeconds: 1
name: kubernetes-secret-generator
ports:
- containerPort: 8080
name: healthcheck
readinessProbe:
failureThreshold: 3
httpGet:
path: /readyz
port: healthcheck
initialDelaySeconds: 6
periodSeconds: 3
successThreshold: 1
timeoutSeconds: 1
resources:
limits:
memory: 128Mi
requests:
cpu: 10m
memory: 32Mi
securityContext: {}
volumeMounts: []
securityContext: {}
serviceAccountName: kubernetes-secret-generator
volumes: []

View file

@ -1,7 +1,7 @@
apiVersion: v1 apiVersion: v1
kind: ServiceAccount kind: Namespace
metadata: metadata:
annotations: annotations:
a8r.io/repository: ssh://git@git.tr1ceracop.de:222/gitea_admin/k8s-and-chill.git a8r.io/repository: ssh://git@git.tr1ceracop.de:222/gitea_admin/k8s-and-chill.git
name: forgejo-admin-secret-init name: kubernetes-secret-generator
namespace: forgejo namespace: kubernetes-secret-generator

View file

@ -0,0 +1,37 @@
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
annotations:
a8r.io/repository: ssh://git@git.tr1ceracop.de:222/gitea_admin/k8s-and-chill.git
labels:
app.kubernetes.io/instance: kubernetes-secret-generator
app.kubernetes.io/managed-by: Helm
app.kubernetes.io/name: kubernetes-secret-generator
app.kubernetes.io/version: v3.4.1
helm.sh/chart: kubernetes-secret-generator-3.4.1
name: kubernetes-secret-generator
name: mittwald:kubernetes-secret-generator
namespace: kubernetes-secret-generator
rules:
- apiGroups:
- ""
resources:
- configmaps
verbs:
- create
- delete
- get
- apiGroups:
- ""
resources:
- pods
verbs:
- delete
- get
- apiGroups:
- monitoring.coreos.com
resources:
- servicemonitors
verbs:
- get
- create

View file

@ -0,0 +1,22 @@
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
annotations:
a8r.io/repository: ssh://git@git.tr1ceracop.de:222/gitea_admin/k8s-and-chill.git
labels:
app.kubernetes.io/instance: kubernetes-secret-generator
app.kubernetes.io/managed-by: Helm
app.kubernetes.io/name: kubernetes-secret-generator
app.kubernetes.io/version: v3.4.1
helm.sh/chart: kubernetes-secret-generator-3.4.1
name: kubernetes-secret-generator
name: mittwald:kubernetes-secret-generator
namespace: kubernetes-secret-generator
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: mittwald:kubernetes-secret-generator
subjects:
- kind: ServiceAccount
name: kubernetes-secret-generator
namespace: kubernetes-secret-generator

View file

@ -0,0 +1,15 @@
apiVersion: v1
automountServiceAccountToken: null
kind: ServiceAccount
metadata:
annotations:
a8r.io/repository: ssh://git@git.tr1ceracop.de:222/gitea_admin/k8s-and-chill.git
labels:
app.kubernetes.io/instance: kubernetes-secret-generator
app.kubernetes.io/managed-by: Helm
app.kubernetes.io/name: kubernetes-secret-generator
app.kubernetes.io/version: v3.4.1
helm.sh/chart: kubernetes-secret-generator-3.4.1
name: kubernetes-secret-generator
name: kubernetes-secret-generator
namespace: kubernetes-secret-generator

View file

@ -0,0 +1,18 @@
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
annotations:
a8r.io/repository: ssh://git@git.tr1ceracop.de:222/gitea_admin/k8s-and-chill.git
name: ocis-idp-rsa
namespace: ocis
spec:
commonName: ocis-idp
duration: 87600h
issuerRef:
kind: ClusterIssuer
name: selfsigned
privateKey:
algorithm: RSA
size: 4096
renewBefore: 720h
secretName: ocis-idp-rsa

View file

@ -0,0 +1,19 @@
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
annotations:
a8r.io/repository: ssh://git@git.tr1ceracop.de:222/gitea_admin/k8s-and-chill.git
name: ocis-ldap-ca
namespace: ocis
spec:
commonName: ocis-ldap-ca
duration: 87600h
isCA: true
issuerRef:
kind: ClusterIssuer
name: selfsigned
privateKey:
algorithm: RSA
size: 2048
renewBefore: 720h
secretName: ocis-ldap-ca-tls

View file

@ -0,0 +1,20 @@
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
annotations:
a8r.io/repository: ssh://git@git.tr1ceracop.de:222/gitea_admin/k8s-and-chill.git
name: ocis-ldap-cert
namespace: ocis
spec:
commonName: idm
dnsNames:
- idm
duration: 87600h
issuerRef:
kind: Issuer
name: ocis-ldap-ca
privateKey:
algorithm: RSA
size: 2048
renewBefore: 720h
secretName: ocis-ldap-cert-tls

View file

@ -1,6 +1,6 @@
apiVersion: v1 apiVersion: v1
data: data:
service-account-id: dc1bcd59-afe8-432c-b2c6-bd56fff10cbb service-account-id: af9235cc-560a-4135-8d16-788aa729b35f
kind: ConfigMap kind: ConfigMap
metadata: metadata:
annotations: annotations:

View file

@ -1,6 +1,6 @@
apiVersion: v1 apiVersion: v1
data: data:
application-id: 91d502c3-d090-49a2-989e-7c8e690751d1 application-id: 105ef2f0-3450-491e-9df3-9d3567f2377c
kind: ConfigMap kind: ConfigMap
metadata: metadata:
annotations: annotations:

View file

@ -1,6 +1,6 @@
apiVersion: v1 apiVersion: v1
data: data:
storage-uuid: cf2b3372-ac52-4654-99ae-cf6518b7a75b storage-uuid: aa2eff14-4594-407b-b06b-aaf7e22a95d1
kind: ConfigMap kind: ConfigMap
metadata: metadata:
annotations: annotations:

View file

@ -152,4 +152,7 @@ spec:
name: messaging-system-ca name: messaging-system-ca
- name: ldap-ca - name: ldap-ca
secret: secret:
secretName: ocis-ldap-ca items:
- key: tls.crt
path: ldap-ca.crt
secretName: ocis-ldap-ca-tls

View file

@ -118,4 +118,7 @@ spec:
name: tmp-volume name: tmp-volume
- name: ldap-ca - name: ldap-ca
secret: secret:
secretName: ocis-ldap-ca items:
- key: tls.crt
path: ldap-ca.crt
secretName: ocis-ldap-ca-tls

View file

@ -150,7 +150,12 @@ spec:
volumes: volumes:
- name: ldap-cert - name: ldap-cert
secret: secret:
secretName: ocis-ldap-cert items:
- key: tls.crt
path: ldap.crt
- key: tls.key
path: ldap.key
secretName: ocis-ldap-cert-tls
- name: idm-data - name: idm-data
persistentVolumeClaim: persistentVolumeClaim:
claimName: idm-data claimName: idm-data

View file

@ -118,7 +118,20 @@ spec:
name: ocis-data-tmp name: ocis-data-tmp
- name: ldap-ca - name: ldap-ca
secret: secret:
secretName: ocis-ldap-ca items:
- key: tls.crt
path: ldap-ca.crt
secretName: ocis-ldap-ca-tls
- name: idp-secrets - name: idp-secrets
secret: projected:
secretName: ocis-idp-secrets sources:
- secret:
items:
- key: encryption.key
path: encryption.key
name: ocis-idp-encryption
- secret:
items:
- key: tls.key
path: private-key.pem
name: ocis-idp-rsa

View file

@ -118,4 +118,7 @@ spec:
name: tmp-volume name: tmp-volume
- name: ldap-ca - name: ldap-ca
secret: secret:
secretName: ocis-ldap-ca items:
- key: tls.crt
path: ldap-ca.crt
secretName: ocis-ldap-ca-tls

View file

@ -0,0 +1,10 @@
apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
annotations:
a8r.io/repository: ssh://git@git.tr1ceracop.de:222/gitea_admin/k8s-and-chill.git
name: ocis-ldap-ca
namespace: ocis
spec:
ca:
secretName: ocis-ldap-ca-tls

View file

@ -0,0 +1,36 @@
apiVersion: batch/v1
kind: Job
metadata:
annotations:
a8r.io/repository: ssh://git@git.tr1ceracop.de:222/gitea_admin/k8s-and-chill.git
argocd.argoproj.io/hook: PreSync
argocd.argoproj.io/sync-options: Replace=true
argocd.argoproj.io/sync-wave: "-1"
name: ocis-external-secret-precheck
namespace: ocis
spec:
template:
spec:
containers:
- command:
- sh
- -c
- |
set -e
for s in ocis-s3-credentials ocis-storagebox-credentials; do
if ! kubectl get secret "$s" -n "${NAMESPACE}" >/dev/null 2>&1; then
echo "ERROR: External secret $s must be created manually before deploying ocis"
exit 1
fi
echo "OK: $s exists"
done
env:
- name: NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
image: alpine/k8s:1.32.3
name: precheck
restartPolicy: OnFailure
serviceAccountName: ocis-external-secret-precheck
ttlSecondsAfterFinished: 300

View file

@ -1,136 +0,0 @@
apiVersion: batch/v1
kind: Job
metadata:
annotations:
a8r.io/repository: ssh://git@git.tr1ceracop.de:222/gitea_admin/k8s-and-chill.git
argocd.argoproj.io/hook: PreSync
argocd.argoproj.io/sync-options: Replace=true
argocd.argoproj.io/sync-wave: "-1"
name: ocis-secret-init
namespace: ocis
spec:
template:
spec:
containers:
- command:
- sh
- -c
- |
set -e
apk add --no-cache openssl >/dev/null 2>&1
gen_random() {
head -c 32 /dev/urandom | base64 | tr -dc 'a-zA-Z0-9' | head -c "$1"
}
gen_uuid() {
cat /proc/sys/kernel/random/uuid
}
create_secret_if_missing() {
local name="$1"
shift
if kubectl get secret "$name" -n "${NAMESPACE}" >/dev/null 2>&1; then
echo "Secret $name already exists, skipping"
return
fi
kubectl create secret generic "$name" -n "${NAMESPACE}" "$@"
echo "Created secret $name"
}
# Validate external secrets exist
if ! kubectl get secret ocis-s3-credentials -n "${NAMESPACE}" >/dev/null 2>&1; then
echo "ERROR: External secret ocis-s3-credentials must be created manually"
exit 1
fi
if ! kubectl get secret ocis-storagebox-credentials -n "${NAMESPACE}" >/dev/null 2>&1; then
echo "ERROR: External secret ocis-storagebox-credentials must be created manually"
exit 1
fi
# Admin user
create_secret_if_missing ocis-admin-user \
--from-literal=password="$(gen_random 32)" \
--from-literal=user-id="$(gen_uuid)"
# JWT secret
create_secret_if_missing ocis-jwt-secret \
--from-literal=jwt-secret="$(gen_random 32)"
# Machine auth API key
create_secret_if_missing ocis-machine-auth-api-key \
--from-literal=machine-auth-api-key="$(gen_random 32)"
# Storage system JWT secret
create_secret_if_missing ocis-storage-system-jwt-secret \
--from-literal=storage-system-jwt-secret="$(gen_random 32)"
# Storage system secret
create_secret_if_missing ocis-storage-system \
--from-literal=api-key="$(gen_random 32)" \
--from-literal=user-id="$(gen_uuid)"
# Transfer secret
create_secret_if_missing ocis-transfer-secret \
--from-literal=transfer-secret="$(gen_random 32)"
# Thumbnails transfer secret
create_secret_if_missing ocis-thumbnails-transfer-secret \
--from-literal=thumbnails-transfer-secret="$(gen_random 32)"
# Service account secret
create_secret_if_missing ocis-service-account-secret \
--from-literal=service-account-secret="$(gen_random 32)"
# Collaboration WOPI secret
create_secret_if_missing ocis-collaboration-wopi-secret \
--from-literal=wopi-secret="$(gen_random 32)"
# LDAP bind secrets (three passwords for different bind users)
create_secret_if_missing ocis-ldap-bind-secrets \
--from-literal=reva-ldap-bind-password="$(gen_random 32)" \
--from-literal=idp-ldap-bind-password="$(gen_random 32)" \
--from-literal=graph-ldap-bind-password="$(gen_random 32)"
# IDP secret (encryption key + RSA private key)
create_secret_if_missing ocis-idp-secrets \
--from-literal=encryption.key="$(gen_random 32)" \
--from-literal=private-key.pem="$(openssl genrsa 4096 2>/dev/null)"
# LDAP CA cert + key (self-signed)
if ! kubectl get secret ocis-ldap-ca -n "${NAMESPACE}" >/dev/null 2>&1; then
openssl req -x509 -newkey rsa:2048 -keyout /tmp/ldap-ca.key -out /tmp/ldap-ca.crt \
-days 3650 -nodes -subj "/CN=ldap-ca" 2>/dev/null
kubectl create secret generic ocis-ldap-ca -n "${NAMESPACE}" \
--from-file=ldap-ca.crt=/tmp/ldap-ca.crt
echo "Created secret ocis-ldap-ca"
# LDAP server cert signed by the CA
printf "subjectAltName=DNS:idm" > /tmp/ldap-ext.cnf
openssl req -newkey rsa:2048 -keyout /tmp/ldap.key -out /tmp/ldap.csr \
-nodes -subj "/CN=idm" -addext "subjectAltName=DNS:idm" 2>/dev/null
openssl x509 -req -in /tmp/ldap.csr -CA /tmp/ldap-ca.crt -CAkey /tmp/ldap-ca.key \
-CAcreateserial -out /tmp/ldap.crt -days 3650 \
-extfile /tmp/ldap-ext.cnf 2>/dev/null
kubectl create secret generic ocis-ldap-cert -n "${NAMESPACE}" \
--from-file=ldap.crt=/tmp/ldap.crt \
--from-file=ldap.key=/tmp/ldap.key
echo "Created secret ocis-ldap-cert"
rm -f /tmp/ldap-ca.key /tmp/ldap-ca.crt /tmp/ldap.key /tmp/ldap.crt /tmp/ldap.csr /tmp/ldap-ca.srl /tmp/ldap-ext.cnf
else
echo "Secret ocis-ldap-ca already exists, skipping LDAP certs"
fi
echo "All secrets initialized successfully"
env:
- name: NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
image: alpine/k8s:1.32.3
name: init
restartPolicy: OnFailure
serviceAccountName: ocis-secret-init
ttlSecondsAfterFinished: 300

View file

@ -5,7 +5,7 @@ metadata:
a8r.io/repository: ssh://git@git.tr1ceracop.de:222/gitea_admin/k8s-and-chill.git a8r.io/repository: ssh://git@git.tr1ceracop.de:222/gitea_admin/k8s-and-chill.git
argocd.argoproj.io/hook: PreSync argocd.argoproj.io/hook: PreSync
argocd.argoproj.io/sync-wave: "-2" argocd.argoproj.io/sync-wave: "-2"
name: ocis-secret-init name: ocis-external-secret-precheck
namespace: ocis namespace: ocis
rules: rules:
- apiGroups: - apiGroups:
@ -14,4 +14,3 @@ rules:
- secrets - secrets
verbs: verbs:
- get - get
- create

View file

@ -5,13 +5,13 @@ metadata:
a8r.io/repository: ssh://git@git.tr1ceracop.de:222/gitea_admin/k8s-and-chill.git a8r.io/repository: ssh://git@git.tr1ceracop.de:222/gitea_admin/k8s-and-chill.git
argocd.argoproj.io/hook: PreSync argocd.argoproj.io/hook: PreSync
argocd.argoproj.io/sync-wave: "-2" argocd.argoproj.io/sync-wave: "-2"
name: ocis-secret-init name: ocis-external-secret-precheck
namespace: ocis namespace: ocis
roleRef: roleRef:
apiGroup: rbac.authorization.k8s.io apiGroup: rbac.authorization.k8s.io
kind: Role kind: Role
name: ocis-secret-init name: ocis-external-secret-precheck
subjects: subjects:
- kind: ServiceAccount - kind: ServiceAccount
name: ocis-secret-init name: ocis-external-secret-precheck
namespace: ocis namespace: ocis

View file

@ -0,0 +1,10 @@
apiVersion: v1
kind: Secret
metadata:
annotations:
a8r.io/repository: ssh://git@git.tr1ceracop.de:222/gitea_admin/k8s-and-chill.git
secret-generator.v1.mittwald.de/autogenerate: password,user-id
secret-generator.v1.mittwald.de/length: "32"
name: ocis-admin-user
namespace: ocis
type: Opaque

View file

@ -0,0 +1,10 @@
apiVersion: v1
kind: Secret
metadata:
annotations:
a8r.io/repository: ssh://git@git.tr1ceracop.de:222/gitea_admin/k8s-and-chill.git
secret-generator.v1.mittwald.de/autogenerate: wopi-secret
secret-generator.v1.mittwald.de/length: "32"
name: ocis-collaboration-wopi-secret
namespace: ocis
type: Opaque

View file

@ -0,0 +1,10 @@
apiVersion: v1
kind: Secret
metadata:
annotations:
a8r.io/repository: ssh://git@git.tr1ceracop.de:222/gitea_admin/k8s-and-chill.git
secret-generator.v1.mittwald.de/autogenerate: encryption.key
secret-generator.v1.mittwald.de/length: "32"
name: ocis-idp-encryption
namespace: ocis
type: Opaque

View file

@ -0,0 +1,10 @@
apiVersion: v1
kind: Secret
metadata:
annotations:
a8r.io/repository: ssh://git@git.tr1ceracop.de:222/gitea_admin/k8s-and-chill.git
secret-generator.v1.mittwald.de/autogenerate: jwt-secret
secret-generator.v1.mittwald.de/length: "32"
name: ocis-jwt-secret
namespace: ocis
type: Opaque

View file

@ -0,0 +1,10 @@
apiVersion: v1
kind: Secret
metadata:
annotations:
a8r.io/repository: ssh://git@git.tr1ceracop.de:222/gitea_admin/k8s-and-chill.git
secret-generator.v1.mittwald.de/autogenerate: reva-ldap-bind-password,idp-ldap-bind-password,graph-ldap-bind-password
secret-generator.v1.mittwald.de/length: "32"
name: ocis-ldap-bind-secrets
namespace: ocis
type: Opaque

View file

@ -0,0 +1,10 @@
apiVersion: v1
kind: Secret
metadata:
annotations:
a8r.io/repository: ssh://git@git.tr1ceracop.de:222/gitea_admin/k8s-and-chill.git
secret-generator.v1.mittwald.de/autogenerate: machine-auth-api-key
secret-generator.v1.mittwald.de/length: "32"
name: ocis-machine-auth-api-key
namespace: ocis
type: Opaque

View file

@ -0,0 +1,10 @@
apiVersion: v1
kind: Secret
metadata:
annotations:
a8r.io/repository: ssh://git@git.tr1ceracop.de:222/gitea_admin/k8s-and-chill.git
secret-generator.v1.mittwald.de/autogenerate: service-account-secret
secret-generator.v1.mittwald.de/length: "32"
name: ocis-service-account-secret
namespace: ocis
type: Opaque

View file

@ -0,0 +1,10 @@
apiVersion: v1
kind: Secret
metadata:
annotations:
a8r.io/repository: ssh://git@git.tr1ceracop.de:222/gitea_admin/k8s-and-chill.git
secret-generator.v1.mittwald.de/autogenerate: storage-system-jwt-secret
secret-generator.v1.mittwald.de/length: "32"
name: ocis-storage-system-jwt-secret
namespace: ocis
type: Opaque

View file

@ -0,0 +1,10 @@
apiVersion: v1
kind: Secret
metadata:
annotations:
a8r.io/repository: ssh://git@git.tr1ceracop.de:222/gitea_admin/k8s-and-chill.git
secret-generator.v1.mittwald.de/autogenerate: api-key,user-id
secret-generator.v1.mittwald.de/length: "32"
name: ocis-storage-system
namespace: ocis
type: Opaque

View file

@ -0,0 +1,10 @@
apiVersion: v1
kind: Secret
metadata:
annotations:
a8r.io/repository: ssh://git@git.tr1ceracop.de:222/gitea_admin/k8s-and-chill.git
secret-generator.v1.mittwald.de/autogenerate: thumbnails-transfer-secret
secret-generator.v1.mittwald.de/length: "32"
name: ocis-thumbnails-transfer-secret
namespace: ocis
type: Opaque

View file

@ -0,0 +1,10 @@
apiVersion: v1
kind: Secret
metadata:
annotations:
a8r.io/repository: ssh://git@git.tr1ceracop.de:222/gitea_admin/k8s-and-chill.git
secret-generator.v1.mittwald.de/autogenerate: transfer-secret
secret-generator.v1.mittwald.de/length: "32"
name: ocis-transfer-secret
namespace: ocis
type: Opaque

View file

@ -5,5 +5,5 @@ metadata:
a8r.io/repository: ssh://git@git.tr1ceracop.de:222/gitea_admin/k8s-and-chill.git a8r.io/repository: ssh://git@git.tr1ceracop.de:222/gitea_admin/k8s-and-chill.git
argocd.argoproj.io/hook: PreSync argocd.argoproj.io/hook: PreSync
argocd.argoproj.io/sync-wave: "-2" argocd.argoproj.io/sync-wave: "-2"
name: ocis-secret-init name: ocis-external-secret-precheck
namespace: ocis namespace: ocis