mirror of
https://github.com/kerberos-io/helm-charts.git
synced 2026-11-12 21:30:24 +00:00
Default to the signed, hardened releases of admin, hub-audit and the dominantcolors, event, export, notification, notification-test, sprite and thumbnail pipelines; run hub-reactivatesubscriptions from ghcr.io/uug-ai (v1.0.3). Their policies now follow the configured validationFailureAction; only hub-objecttracking (unsigned -gpu image) and trusted-registry stay in Audit. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
203 lines
8.9 KiB
Bash
203 lines
8.9 KiB
Bash
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
CHART_DIR="${1:-charts/hub}"
|
|
components=(
|
|
kerberospipeline.counting kerberospipeline.throttler
|
|
kerberospipeline.notify
|
|
kerberospipeline.thumbnail kerberospipeline.dominantColor
|
|
kerberoshub.cleanup kerberoshub.monitordevice
|
|
kerberoshub.services.workflows kerberoshub.services.loitering
|
|
)
|
|
fixtures=(
|
|
--set kerberospipeline.dominantColor.enabled=true
|
|
--set kerberoshub.workflows.enabled=true
|
|
--set kerberoshub.services.loitering.enabled=true
|
|
--set kerberoshub.services.loitering.repository=example/loitering
|
|
--set kerberoshub.services.loitering.tag=test
|
|
--set kerberoshub.services.loitering.queue=loitering
|
|
--set mongodb.tls.enabled=true
|
|
--set mongodb.tls.existingSecret=mongodb-ca
|
|
--set mongodb.tls.caFileName=ca.crt
|
|
)
|
|
security=()
|
|
for component in "${components[@]}"; do
|
|
security+=(
|
|
--set "$component.securityContext.runAsNonRoot=true"
|
|
--set "$component.securityContext.runAsUser=65532"
|
|
--set "$component.securityContext.runAsGroup=65532"
|
|
--set "$component.securityContext.readOnlyRootFilesystem=true"
|
|
--set "$component.securityContext.allowPrivilegeEscalation=false"
|
|
--set "$component.securityContext.capabilities.drop[0]=ALL"
|
|
--set "$component.securityContext.seccompProfile.type=RuntimeDefault"
|
|
--set "$component.podSecurityContext.fsGroup=65532"
|
|
--set "$component.volumes[0].name=scratch"
|
|
--set "$component.volumes[0].emptyDir.sizeLimit=4Gi"
|
|
--set "$component.volumeMounts[0].name=scratch"
|
|
--set "$component.volumeMounts[0].mountPath=/tmp"
|
|
--set "$component.resources.requests.ephemeral-storage=4Gi"
|
|
--set "$component.resources.limits.ephemeral-storage=6Gi"
|
|
)
|
|
done
|
|
|
|
resource() {
|
|
local kind="$1" name="$2"
|
|
awk -v kind="$kind" -v name="$name" '
|
|
function emit() { if (matchedKind && matchedName) printf "%s", block }
|
|
/^---$/ { emit(); block=""; matchedKind=0; matchedName=0 }
|
|
{ block=block $0 "\n" }
|
|
$0 == "kind: " kind { matchedKind=1 }
|
|
$0 == " name: " name { matchedName=1 }
|
|
END { emit() }
|
|
'
|
|
}
|
|
|
|
assert_line() {
|
|
local block="$1" pattern="$2"
|
|
if ! grep -Eq "$pattern" <<< "$block"; then
|
|
printf 'Missing expected pattern: %s\n%s\n' "$pattern" "$block" >&2
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
without_admin() {
|
|
awk '
|
|
/^---$/ { skip=0 }
|
|
/^# Source: hub\/templates\/admin\/deployment\.yaml$/ { skip=1 }
|
|
!skip { print }
|
|
'
|
|
}
|
|
|
|
without_frontends() {
|
|
awk '
|
|
/^---$/ { skip=0 }
|
|
/^# Source: hub\/templates\/kerberos-hub\/(hub-frontend(-demo)?|hub-api)\.yaml$/ { skip=1 }
|
|
!skip { print }
|
|
'
|
|
}
|
|
|
|
for mode in all pipeline ui; do
|
|
defaults="$(helm template hub "$CHART_DIR" --set "mode=$mode")"
|
|
# Frontends, hub-api and Admin (since 0.184.0) are hardened by default.
|
|
worker_defaults="$(without_frontends <<< "$defaults" | without_admin)"
|
|
if grep -q 'securityContext:' <<< "$worker_defaults"; then
|
|
echo "Default $mode mode unexpectedly enables worker security settings" >&2
|
|
exit 1
|
|
fi
|
|
|
|
legacy="$(helm template hub "$CHART_DIR" --set "mode=$mode" "${fixtures[@]}")"
|
|
hardened="$(helm template hub "$CHART_DIR" --set "mode=$mode" "${fixtures[@]}" "${security[@]}")"
|
|
helm lint "$CHART_DIR" --quiet --set "mode=$mode" "${fixtures[@]}" "${security[@]}"
|
|
names=()
|
|
if [[ "$mode" != ui ]]; then
|
|
names+=(pipe-counting pipe-throttler pipe-notify pipe-thumbnail pipe-dominantcolor hub-workflows hub-loitering)
|
|
fi
|
|
if [[ "$mode" != pipeline ]]; then
|
|
names+=(hub-cleanup hub-monitor-device)
|
|
fi
|
|
for name in "${names[@]}"; do
|
|
block="$(resource Deployment "$name" <<< "$hardened")"
|
|
for pattern in \
|
|
'^[ ]{6}securityContext:$' '^[ ]{8}fsGroup: 65532$' \
|
|
'^[ ]+(runAsUser|runAsGroup): 65532$' \
|
|
'^[ ]+runAsNonRoot: true$' '^[ ]+readOnlyRootFilesystem: true$' \
|
|
'^[ ]+allowPrivilegeEscalation: false$' '^[ ]+capabilities:$' \
|
|
'^[ ]+drop:$' '^[ ]+- ALL$' '^[ ]+seccompProfile:$' \
|
|
'^[ ]+type: RuntimeDefault$' '^[ ]+(- )?mountPath: /tmp$' \
|
|
'^[ ]+sizeLimit: 4Gi$' '^[ ]+ephemeral-storage: 6Gi$' \
|
|
'^[ ]+ephemeral-storage: 4Gi$'; do
|
|
assert_line "$block" "$pattern"
|
|
done
|
|
# Verify both identity axes independently.
|
|
assert_line "$block" '^[ ]+runAsUser: 65532$'
|
|
assert_line "$block" '^[ ]+runAsGroup: 65532$'
|
|
assert_line "$block" '^[ ]{8,10}securityContext:$'
|
|
if [[ "$name" == hub-cleanup || "$name" == hub-monitor-device || "$name" == hub-workflows || "$name" == pipe-notify ]]; then
|
|
assert_line "$block" '^[ ]+secretName: mongodb-ca$'
|
|
assert_line "$block" '^[ ]+mountPath: /etc/mongodb/tls$'
|
|
assert_line "$block" '^[ ]+readOnly: true$'
|
|
fi
|
|
if [[ "$name" == pipe-notify ]]; then
|
|
# Provider, queue, FFmpeg and all other worker settings are unchanged.
|
|
old_block="$(resource Deployment pipe-notify <<< "$legacy")"
|
|
[[ "$(sed -n '/^ env:/,$p' <<< "$old_block")" == "$(sed -n '/^ env:/,$p' <<< "$block")" ]]
|
|
fi
|
|
if [[ "$name" != hub-monitor-device ]]; then
|
|
service="$(resource Service "$name" <<< "$hardened")"
|
|
assert_line "$service" '^[ ]+port: 8080$'
|
|
assert_line "$service" '^[ ]+targetPort: 8080$'
|
|
fi
|
|
done
|
|
expected="${#names[@]}"
|
|
[[ "$(without_frontends <<< "$hardened" | without_admin | grep -c 'runAsUser: 65532')" == "$expected" ]]
|
|
[[ "$(without_frontends <<< "$hardened" | without_admin | grep -c 'fsGroup: 65532')" == "$expected" ]]
|
|
|
|
# Image selection, Services, env and TLS mounts must not change.
|
|
for pattern in '^[ ]+image:' '^[ ]+(port|targetPort|containerPort):' 'mongodb-tls'; do
|
|
[[ "$(grep -E "$pattern" <<< "$legacy")" == "$(grep -E "$pattern" <<< "$hardened")" ]]
|
|
done
|
|
echo "OK ($mode): defaults unchanged; $expected independent hardened workers preserve images, ports and MongoDB TLS"
|
|
done
|
|
|
|
# Hardening one worker must not opt in another, even on the same pod mode.
|
|
isolated="$(helm template hub "$CHART_DIR" --set mode=pipeline \
|
|
--set kerberospipeline.counting.securityContext.runAsUser=65532)"
|
|
[[ "$(grep -c 'runAsUser: 65532' <<< "$isolated")" == 1 ]]
|
|
assert_line "$(resource Deployment pipe-counting <<< "$isolated")" '^[ ]+runAsUser: 65532$'
|
|
echo "All hardened worker checks passed."
|
|
|
|
# Since chart 0.184.0 the hardened Admin runtime is the default; these flags
|
|
# opt back into a legacy image on port 80.
|
|
legacy_admin_flags=(
|
|
--set admin.repository=uugai/admin
|
|
--set admin.tag=v1.3.0
|
|
--set admin.containerPort=80
|
|
--set admin.securityContext=null
|
|
--set admin.readinessProbe=null
|
|
--set admin.livenessProbe=null
|
|
--set admin.volumes=null
|
|
--set admin.volumeMounts=null
|
|
--set admin.oauth2Proxy.enabled=true
|
|
)
|
|
for mode in all ui pipeline; do
|
|
legacy="$(helm template hub "$CHART_DIR" --set "mode=$mode" "${legacy_admin_flags[@]}")"
|
|
configured="$(helm template hub "$CHART_DIR" --set "mode=$mode" --set admin.oauth2Proxy.enabled=true)"
|
|
helm lint "$CHART_DIR" --quiet --set "mode=$mode" "${legacy_admin_flags[@]}"
|
|
if [[ "$mode" == pipeline ]]; then
|
|
[[ -z "$(resource Deployment admin <<< "$configured")" ]]
|
|
[[ -z "$(resource Service admin-svc <<< "$configured")" ]]
|
|
[[ "$legacy" == "$configured" ]]
|
|
echo "OK (pipeline): Admin settings render no Admin resources or unrelated changes"
|
|
continue
|
|
fi
|
|
old="$(resource Deployment admin <<< "$legacy")"
|
|
block="$(resource Deployment admin <<< "$configured")"
|
|
assert_line "$old" '^[ ]+containerPort: 80$'
|
|
[[ "$old" != *securityContext:* && "$old" != *admin-tmp* ]]
|
|
for pattern in \
|
|
'^[ ]+containerPort: 8080$' '^[ ]{10}securityContext:$' \
|
|
'^[ ]+runAsUser: 65532$' '^[ ]+runAsGroup: 65532$' \
|
|
'^[ ]+runAsNonRoot: true$' '^[ ]+readOnlyRootFilesystem: true$' \
|
|
'^[ ]+allowPrivilegeEscalation: false$' '^[ ]+capabilities:$' \
|
|
'^[ ]+drop:$' '^[ ]+- ALL$' '^[ ]+seccompProfile:$' \
|
|
'^[ ]+type: RuntimeDefault$' '^[ ]+medium: Memory$' \
|
|
'^[ ]+sizeLimit: 16Mi$' '^[ ]+(- )?mountPath: /tmp$'; do
|
|
assert_line "$block" "$pattern"
|
|
done
|
|
old_service="$(resource Service admin-svc <<< "$legacy")"
|
|
service="$(resource Service admin-svc <<< "$configured")"
|
|
assert_line "$old_service" '^[ ]+targetPort: http$'
|
|
assert_line "$service" '^[ ]+port: 80$'
|
|
assert_line "$service" '^[ ]+targetPort: http$'
|
|
[[ "$(resource Ingress admin-ingress <<< "$legacy")" == "$(resource Ingress admin-ingress <<< "$configured")" ]]
|
|
[[ "$(resource Deployment oauth2-proxy-admin <<< "$legacy")" == "$(resource Deployment oauth2-proxy-admin <<< "$configured")" ]]
|
|
[[ "$(resource Service oauth2-proxy-admin <<< "$legacy")" == "$(resource Service oauth2-proxy-admin <<< "$configured")" ]]
|
|
[[ "$(without_admin <<< "$legacy" | grep 'image:')" == "$(without_admin <<< "$configured" | grep 'image:')" ]]
|
|
assert_line "$old" 'image: "uugai/admin:v1.3.0"$'
|
|
assert_line "$block" 'image: "ghcr.io/uug-ai/admin:v1.4.6"$'
|
|
# No other workload inherits Admin's container port or security settings.
|
|
[[ "$(without_frontends <<< "$configured" | grep -c 'runAsUser: 65532')" == 1 ]]
|
|
echo "OK ($mode): Admin hardened defaults; legacy opt-out; Service port, ingress and OAuth2-proxy preserved"
|
|
done
|
|
echo "All hardened Admin checks passed."
|