From 5eb0d41aa75fec91fff04bb01ff69faeb52adfeb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Verstraeten?= <1546779+cedricve@users.noreply.github.com> Date: Wed, 11 Nov 2026 20:40:03 +0000 Subject: [PATCH] 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 --- charts/hub/README.md | 15 +++- .../hub-reactivate-subscription.yaml | 72 ++++++++++++++++++ ...activatesubscriptions-hardened-images.yaml | 73 +++++++++++++++++++ ...reactivatesubscriptions-signed-images.yaml | 61 ++++++++++++++++ charts/hub/values.yaml | 18 +++++ 5 files changed, 236 insertions(+), 3 deletions(-) create mode 100644 charts/hub/templates/kerberos-hub/hub-reactivate-subscription.yaml create mode 100644 charts/hub/templates/kyverno/hub-reactivatesubscriptions-hardened-images.yaml create mode 100644 charts/hub/templates/kyverno/hub-reactivatesubscriptions-signed-images.yaml diff --git a/charts/hub/README.md b/charts/hub/README.md index 7975f7e..8636194 100644 --- a/charts/hub/README.md +++ b/charts/hub/README.md @@ -376,6 +376,7 @@ signed for its own `uug-ai/` 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"` | diff --git a/charts/hub/templates/kerberos-hub/hub-reactivate-subscription.yaml b/charts/hub/templates/kerberos-hub/hub-reactivate-subscription.yaml new file mode 100644 index 0000000..084ec31 --- /dev/null +++ b/charts/hub/templates/kerberos-hub/hub-reactivate-subscription.yaml @@ -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 }} diff --git a/charts/hub/templates/kyverno/hub-reactivatesubscriptions-hardened-images.yaml b/charts/hub/templates/kyverno/hub-reactivatesubscriptions-hardened-images.yaml new file mode 100644 index 0000000..f1b0319 --- /dev/null +++ b/charts/hub/templates/kyverno/hub-reactivatesubscriptions-hardened-images.yaml @@ -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 }} diff --git a/charts/hub/templates/kyverno/hub-reactivatesubscriptions-signed-images.yaml b/charts/hub/templates/kyverno/hub-reactivatesubscriptions-signed-images.yaml new file mode 100644 index 0000000..623c29b --- /dev/null +++ b/charts/hub/templates/kyverno/hub-reactivatesubscriptions-signed-images.yaml @@ -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 }} diff --git a/charts/hub/values.yaml b/charts/hub/values.yaml index f32d1ba..a8b223f 100644 --- a/charts/hub/values.yaml +++ b/charts/hub/values.yaml @@ -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.