feat(hub): keep the reactivate-subscriptions service

hub-reactivate-subscription is still needed: restore its template,
kerberoshub.reactivate values, README rows and its Kyverno signature and
hardening policies (Audit until the chart uses the signed
ghcr.io/uug-ai/hub-reactivatesubscriptions image). Only hub-proxy and
vault-forwarder are removed. The reactivate output is unchanged from main.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Cédric Verstraeten
2026-11-11 20:40:03 +00:00
co-authored by Claude Opus 5.5
parent 5a445e0179
commit 5eb0d41aa7
5 changed files with 236 additions and 3 deletions
+12 -3
View File
@@ -376,6 +376,7 @@ signed for its own `uug-ai/<image>` repository. "Mode" is the
| `hub-pipeline-sprite-signed-images.yaml` | `ghcr.io/uug-ai/hub-pipeline-sprite` | ✅ signed (from v1.1.28) | value |
| `hub-pipeline-thumbnail-signed-images.yaml` | `ghcr.io/uug-ai/hub-pipeline-thumbnail` | ✅ signed (from v1.3.24) | value |
| `admin-signed-images.yaml` | `uugai/admin` (Docker Hub) / `ghcr.io/uug-ai/admin` | ❌ Docker Hub image not signed; switch to GHCR | always `Audit` |
| `hub-reactivatesubscriptions-signed-images.yaml` | `uugai/hub-reactivatesubscriptions` (Docker Hub) / `ghcr.io/uug-ai/hub-reactivatesubscriptions` | ❌ Docker Hub image not signed; switch to GHCR | always `Audit` |
| `oauth2-proxy-signed-images.yaml` | `quay.io/oauth2-proxy/oauth2-proxy` (third-party) | ❌ visibility only; cannot be signed by UUG.AI | always `Audit` |
Since chart **`0.182.0`** every `ghcr.io/uug-ai` image's releases are signed, so
@@ -391,9 +392,9 @@ Since chart **`0.184.0`** every container image the chart can deploy has a
signature policy, and every UUG.AI image a hardening policy. Images that are
not signed or hardened yet stay in `Audit` (the tables mark them), so they show
up as failures in Policy Reporter without ever being blocked: the Docker Hub
image `uugai/admin` (its repository already releases signed
`ghcr.io/uug-ai/admin` images; switching the chart to that fixes it) and the
third-party `oauth2-proxy` (visibility only; it needs its upstream signing
images `uugai/admin` and `uugai/hub-reactivatesubscriptions` (their repositories
already release signed `ghcr.io/uug-ai` images; switching the chart to those
fixes them) and the third-party `oauth2-proxy` (visibility only; it needs its upstream signing
identity, and should be pinned instead of `:latest`). `oauth2-proxy` has no
hardening policy, since only UUG.AI releases carry the hardening attestation.
@@ -484,6 +485,7 @@ image whose repository makes hardened releases, in `templates/kyverno/`:
| `hub-pipeline-sprite-hardened-images.yaml` | `ghcr.io/uug-ai/hub-pipeline-sprite` (not hardened yet; always `Audit`) |
| `hub-pipeline-thumbnail-hardened-images.yaml` | `ghcr.io/uug-ai/hub-pipeline-thumbnail` (not hardened yet; always `Audit`) |
| `admin-hardened-images.yaml` | `uugai/admin / ghcr.io/uug-ai/admin` (not hardened yet; always `Audit`) |
| `hub-reactivatesubscriptions-hardened-images.yaml` | `uugai/hub-reactivatesubscriptions / ghcr.io/uug-ai/hub-reactivatesubscriptions` (not hardened yet; always `Audit`) |
Each requires the attestation to be signed by the UUG.AI release workflow for
the image's own repository, and its `result` and `repository` to be `passed`
@@ -1131,6 +1133,13 @@ kerberospipeline:
| `kerberoshub.monitordevice.scanIntervalSeconds` | Seconds between device status scans. Sets how quickly a camera going offline or online is detected. For accounts with "Group notifications" on, `hub-monitor-device` v1.4.10 and later then hold a change 20 seconds (`DEVICE_GROUPING_HOLD_SECONDS`) to collect other cameras that change status at the same time; older releases hold it one scan. | `60` |
| `kerberoshub.monitordevice.resources.requests.memory` | Memory request for `kerberoshub.monitordevice`. | `"10Mi"` |
| `kerberoshub.monitordevice.resources.requests.cpu` | CPU request for `kerberoshub.monitordevice`. | `"10m"` |
| `kerberoshub.reactivate.repository` | Container image repository for `kerberoshub.reactivate`. | `"uugai/hub-reactivatesubscriptions"` |
| `kerberoshub.reactivate.pullPolicy` | Image pull policy for `kerberoshub.reactivate`. | `"IfNotPresent"` |
| `kerberoshub.reactivate.tag` | Container image tag/version for `kerberoshub.reactivate`. | `"v1.0.2"` |
| `kerberoshub.reactivate.replicas` | Number of replicas for `kerberoshub.reactivate`. Set to `0` to disable. | `0` |
| `kerberoshub.reactivate.logLevel` | Log verbosity level for `kerberoshub.reactivate`. | `"info"` |
| `kerberoshub.reactivate.resources.requests.memory` | Memory request for `kerberoshub.reactivate`. | `"10Mi"` |
| `kerberoshub.reactivate.resources.requests.cpu` | CPU request for `kerberoshub.reactivate`. | `"10m"` |
| `kerberospipeline.event.repository` | The [event orchestration](https://doc.kerberos.io/hub/pipeline/#orchestrator) microservice. | `"ghcr.io/uug-ai/hub-pipeline-event"` |
| `kerberospipeline.event.pullPolicy` | The Docker registry pull policy. | `"IfNotPresent"` |
| `kerberospipeline.event.tag` | The Docker image tag/version. | `"v1.3.10"` |
@@ -0,0 +1,72 @@
{{- if or (eq .Values.mode "all") (eq .Values.mode "ui") -}}
apiVersion: apps/v1
kind: Deployment
metadata:
name: hub-reactivate-subscription
namespace: {{ .Release.Namespace }}
spec:
replicas: {{ .Values.kerberoshub.reactivate.replicas }}
selector:
matchLabels:
app: hub-reactivate-subscription
minReadySeconds: 10
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 1
maxSurge: 1
template:
metadata:
annotations:
checksum/config: {{ include (print $.Template.BasePath "/configMaps/configmap-mongodb.yaml") . | sha256sum }}
labels:
app: hub-reactivate-subscription
spec:
{{- with .Values.imagePullSecrets }}
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- $mongodbTLS := .Values.mongodb.tls }}
{{- if or .Values.kerberoshub.reactivate.volumes (and $mongodbTLS.enabled $mongodbTLS.existingSecret) }}
volumes:
{{- with .Values.kerberoshub.reactivate.volumes }}
{{- toYaml . | nindent 8 }}
{{- end }}
{{- if and $mongodbTLS.enabled $mongodbTLS.existingSecret }}
{{- include "hub.mongodb.tlsVolume" . | nindent 8 }}
{{- end }}
{{- end }}
{{- with .Values.kerberoshub.reactivate.topologySpreadConstraints }}
topologySpreadConstraints:
{{- toYaml . | nindent 8 }}
{{- end }}
containers:
- name: hub-reactivate-subscription
image: "{{ .Values.global.imageRegistry }}{{ .Values.kerberoshub.reactivate.repository }}:{{ .Values.kerberoshub.reactivate.tag }}"
imagePullPolicy: {{ .Values.kerberoshub.reactivate.pullPolicy }}
{{- if or .Values.kerberoshub.reactivate.volumeMounts (and $mongodbTLS.enabled $mongodbTLS.existingSecret) }}
volumeMounts:
{{- with .Values.kerberoshub.reactivate.volumeMounts }}
{{- toYaml . | nindent 12 }}
{{- end }}
{{- if and $mongodbTLS.enabled $mongodbTLS.existingSecret }}
{{- include "hub.mongodb.tlsVolumeMount" . | nindent 12 }}
{{- end }}
{{- end }}
envFrom:
- configMapRef:
name: mongodb-config
env:
{{- include "hub.mongodb.appUriEnv" (dict "root" $ "app" "hub-reactivatesubscriptions") | nindent 12 }}
- name: LOG_LEVEL
value: "{{ .Values.kerberoshub.reactivate.logLevel }}"
- name: READ_ONLY
value: "{{ .Values.readonly }}"
- name: AWS_ACCESS_KEY_ID
value: "{{ .Values.kerberoshub.api.aws.accessKey }}"
- name: AWS_SECRET_ACCESS_KEY
value: "{{ .Values.kerberoshub.api.aws.secretKey }}"
{{- if .Values.kerberoshub.extraEnv }}
{{- toYaml .Values.kerberoshub.extraEnv | nindent 12 }}
{{- end }}
{{- end }}
@@ -0,0 +1,73 @@
{{- /*
hub-reactivatesubscriptions images must come from a hardened release: they carry the signed
image-hardening attestation (result "passed") that the UUG.AI release workflow
adds after all hardening gates pass, for uug-ai/hub-reactivatesubscriptions.
The signature itself is checked by hub-reactivatesubscriptions-signed-images.yaml.
A namespaced Policy, so it applies only to the release namespace.
Rendered only when imageHardening.enabled is true and Kyverno is installed.
*/ -}}
{{- if and .Values.imageHardening.enabled (.Capabilities.APIVersions.Has "kyverno.io/v1") }}
apiVersion: kyverno.io/v1
kind: Policy
metadata:
name: hub-reactivatesubscriptions-hardened-images
annotations:
policies.kyverno.io/title: hub-reactivatesubscriptions images must come from a hardened UUG.AI release
policies.kyverno.io/category: Image hardening
policies.kyverno.io/severity: medium
policies.kyverno.io/subject: Pod
# Kyverno defaults apply: background scans of running pods, attestation required.
# Only settings whose Kyverno default would change behaviour are set below.
spec:
# Not hardened yet: uug-ai/hub-reactivatesubscriptions has no hardened release with the
# image-hardening attestation (the image does not meet the hardened contract
# yet). This policy reports that gap and stays in Audit, even when
# imageHardening.validationFailureAction is Enforce. Once its releases are
# hardened, use the same line as hub-api-hardened-images.yaml.
validationFailureAction: Audit
# Kyverno's default (Fail) would stop hub-reactivatesubscriptions pods starting while Kyverno is down.
failurePolicy: Ignore
# Verifying against GHCR and Rekor can exceed the 10s default.
webhookTimeoutSeconds: 30
rules:
- name: verify-hub-reactivatesubscriptions-hardened
match:
any:
- resources:
kinds:
- Pod
verifyImages:
- imageReferences:
- "uugai/hub-reactivatesubscriptions:*"
- "uugai/hub-reactivatesubscriptions@*"
- "docker.io/uugai/hub-reactivatesubscriptions:*"
- "docker.io/uugai/hub-reactivatesubscriptions@*"
- "ghcr.io/uug-ai/hub-reactivatesubscriptions:*"
- "ghcr.io/uug-ai/hub-reactivatesubscriptions@*"
# Kyverno's defaults (true) would rewrite pod images to digests and
# report every tag-based image; enable together with Enforce.
mutateDigest: false
verifyDigest: false
attestations:
- type: https://github.com/uug-ai/workflows/attestations/image-hardening/v1
attestors:
- entries:
- keyless:
issuer: https://token.actions.githubusercontent.com
# The UUG.AI release workflow, at a branch/tag ref or a commit.
subjectRegExp: '^https://github\.com/uug-ai/workflows/\.github/workflows/release-create\.yml@(refs/.+|[0-9a-f]{40})$'
additionalExtensions:
githubWorkflowRepository: uug-ai/hub-reactivatesubscriptions
rekor:
url: https://rekor.sigstore.dev
# Fields of the attestation's predicate. {{`{{ ... }}`}} is Kyverno
# syntax, escaped so Helm leaves it alone.
conditions:
- all:
- key: "{{`{{ result }}`}}"
operator: Equals
value: passed
- key: "{{`{{ repository }}`}}"
operator: Equals
value: uug-ai/hub-reactivatesubscriptions
{{- end }}
@@ -0,0 +1,61 @@
{{- /*
hub-reactivatesubscriptions images must be signed by UUG.AI: a keyless cosign signature issued by
GitHub Actions, made by the UUG.AI release workflow, for uug-ai/hub-reactivatesubscriptions.
A namespaced Policy, so it applies only to the release namespace.
Rendered only when imageVerification.enabled is true and Kyverno is installed.
*/ -}}
{{- if and .Values.imageVerification.enabled (.Capabilities.APIVersions.Has "kyverno.io/v1") }}
apiVersion: kyverno.io/v1
kind: Policy
metadata:
name: hub-reactivatesubscriptions-signed-images
annotations:
policies.kyverno.io/title: hub-reactivatesubscriptions images must be signed by UUG.AI
policies.kyverno.io/category: Image signing
policies.kyverno.io/severity: high
policies.kyverno.io/subject: Pod
# Kyverno defaults apply: background scans of running pods, signature required.
# Only settings whose Kyverno default would change behaviour are set below.
spec:
# Not signed by UUG.AI yet. The chart pulls Docker Hub uugai/hub-reactivatesubscriptions, which the UUG.AI
# release workflow does not sign. Switch the chart to the ghcr.io/uug-ai image
# released by the shared workflow and sign its releases (sign: true).
# This policy reports that gap and stays in Audit, even when
# imageVerification.validationFailureAction is Enforce.
validationFailureAction: Audit
# Kyverno's default (Fail) would stop hub-reactivatesubscriptions pods starting while Kyverno is down.
failurePolicy: Ignore
# Verifying against GHCR and Rekor can exceed the 10s default.
webhookTimeoutSeconds: 30
rules:
- name: verify-hub-reactivatesubscriptions
match:
any:
- resources:
kinds:
- Pod
verifyImages:
- imageReferences:
- "uugai/hub-reactivatesubscriptions:*"
- "uugai/hub-reactivatesubscriptions@*"
- "docker.io/uugai/hub-reactivatesubscriptions:*"
- "docker.io/uugai/hub-reactivatesubscriptions@*"
- "ghcr.io/uug-ai/hub-reactivatesubscriptions:*"
- "ghcr.io/uug-ai/hub-reactivatesubscriptions@*"
# Kyverno's defaults (true) would rewrite pod images to digests and
# report every tag-based image; enable together with Enforce.
mutateDigest: false
verifyDigest: false
attestors:
- entries:
- keyless:
issuer: https://token.actions.githubusercontent.com
# The UUG.AI release workflow, at a branch/tag ref or a commit.
subjectRegExp: '^https://github\.com/uug-ai/workflows/\.github/workflows/release-create\.yml@(refs/.+|[0-9a-f]{40})$'
additionalExtensions:
# That workflow signs for every UUG.AI repository; this
# binds the signature to uug-ai/hub-reactivatesubscriptions.
githubWorkflowRepository: uug-ai/hub-reactivatesubscriptions
rekor:
url: https://rekor.sigstore.dev
{{- end }}
+18
View File
@@ -1224,6 +1224,24 @@ kerberoshub:
# - name: custom-email-templates
# persistentVolumeClaim:
# claimName: custom-layout-claim
reactivate:
repository: uugai/hub-reactivatesubscriptions
pullPolicy: IfNotPresent
tag: "v1.0.2"
replicas: 0 # Number of pods for the service. Set to 0 to disable.
topologySpreadConstraints: [] # Optional pod topology spread constraints (empty = none).
# Optional extra volumes / volumeMounts for this deployment (empty = none).
#volumes:
# - name: extra
# emptyDir: {}
#volumeMounts:
# - name: extra
# mountPath: /data
logLevel: "info" # possible values: trace, debug, info, warn, error
resources:
requests:
memory: 10Mi
cpu: 10m
# The Kerberos Hub pipeline is a series of microservices that process the recordings.
# Each microservice is responsible for a specific task, like counting objects, generating thumbnails, etc.
# The pipeline is orchestrated through Kafka topics or RabbitMQ queues.