mirror of
https://github.com/kerberos-io/helm-charts.git
synced 2026-10-28 19:08:26 +00:00
Compare commits
15
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6f1dcf8f00 | ||
|
|
371408e5eb | ||
|
|
0eff4bf2ee | ||
|
|
18e456d490 | ||
|
|
374d40aa41 | ||
|
|
073c12f8eb | ||
|
|
001c835068 | ||
|
|
b520e2ecc1 | ||
|
|
c42388b96f | ||
|
|
04d6990460 | ||
|
|
5af6472826 | ||
|
|
e0a273422e | ||
|
|
71e2db095e | ||
|
|
356f510ba9 | ||
|
|
95ffe47edb |
@@ -16,7 +16,7 @@ type: application
|
||||
# This is the chart version. This version number should be incremented each time you make changes
|
||||
# to the chart and its templates, including the app version.
|
||||
# Versions are expected to follow Semantic Versioning (https://semver.org/)
|
||||
version: 0.164.0
|
||||
version: 0.167.3
|
||||
|
||||
# This is the version number of the application being deployed. This version number should be
|
||||
# incremented each time you make changes to the application. Versions are not expected to
|
||||
|
||||
+320
-5
@@ -31,6 +31,281 @@ Uninstall the Kerberos Hub chart
|
||||
|
||||
helm uninstall hub -n kerberos-hub
|
||||
|
||||
### Hardened frontend images
|
||||
|
||||
New shell-free frontend images run as `65532:65532` and listen on port `8080`.
|
||||
Before selecting such an image tag, set the following values together. Defaults
|
||||
remain compatible with legacy images on port `80`; this chart does not upgrade
|
||||
the frontend image automatically. The Service and ingress remain on port `80`.
|
||||
Use chart `0.167.2` or newer when the demo frontend is enabled; `0.167.1`
|
||||
supports these settings only on the primary frontend.
|
||||
|
||||
```yaml
|
||||
kerberoshub:
|
||||
frontend:
|
||||
containerPort: 8080
|
||||
securityContext:
|
||||
runAsNonRoot: true
|
||||
runAsUser: 65532
|
||||
runAsGroup: 65532
|
||||
readOnlyRootFilesystem: true
|
||||
allowPrivilegeEscalation: false
|
||||
capabilities:
|
||||
drop: ["ALL"]
|
||||
seccompProfile:
|
||||
type: RuntimeDefault
|
||||
volumes:
|
||||
- name: frontend-tmp
|
||||
emptyDir:
|
||||
medium: Memory
|
||||
sizeLimit: 16Mi
|
||||
volumeMounts:
|
||||
- name: frontend-tmp
|
||||
mountPath: /tmp
|
||||
```
|
||||
|
||||
Merge the temporary volume into any existing branding/translation mounts rather
|
||||
than replacing them. Custom files must be readable by UID `65532`; mount them
|
||||
read-only. Only `/tmp` needs writes for generated public runtime configuration
|
||||
and nginx's PID/temp files. The web root stays read-only. Health checks can use
|
||||
`GET /healthz` on the named `http` container port. These settings apply to both
|
||||
the primary and demo frontends, which share the frontend image tag. Update the
|
||||
live Argo CD Application's chart pin as well as its values: an image-only
|
||||
upgrade leaves older charts forwarding to port `80` and causes gateway errors.
|
||||
|
||||
### Hardened Admin image (opt-in, chart 0.167.3)
|
||||
|
||||
`charts/hub` deploys Admin in `all` and `ui` modes. Shell-free Admin `v1.4.3`
|
||||
runs as `65532:65532` and listens on `8080`, unlike the legacy image on `80`.
|
||||
Set these values together; the chart does not upgrade the image automatically:
|
||||
|
||||
```yaml
|
||||
admin:
|
||||
repository: ghcr.io/uug-ai/admin
|
||||
tag: v1.4.3
|
||||
containerPort: 8080
|
||||
securityContext:
|
||||
runAsNonRoot: true
|
||||
runAsUser: 65532
|
||||
runAsGroup: 65532
|
||||
readOnlyRootFilesystem: true
|
||||
allowPrivilegeEscalation: false
|
||||
capabilities:
|
||||
drop: ["ALL"]
|
||||
seccompProfile:
|
||||
type: RuntimeDefault
|
||||
volumes:
|
||||
- name: admin-tmp
|
||||
emptyDir:
|
||||
medium: Memory
|
||||
sizeLimit: 16Mi # Example; size for your runtime configuration.
|
||||
volumeMounts:
|
||||
- name: admin-tmp
|
||||
mountPath: /tmp
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /healthz
|
||||
port: http
|
||||
periodSeconds: 10
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /healthz
|
||||
port: http
|
||||
initialDelaySeconds: 10
|
||||
periodSeconds: 10
|
||||
```
|
||||
|
||||
Merge the scratch volume with any existing mounts. Only `/tmp` is writable;
|
||||
application files remain read-only. Memory-backed scratch counts against the
|
||||
pod memory limit. The `admin-svc` Service and Admin ingress still use port `80`;
|
||||
the Service targets the named `http` container port. OAuth2-proxy is unchanged.
|
||||
Defaults remain port `80` with no security context, added volumes, or probes. Coordinate
|
||||
the image, values and a chart revision containing these settings: published
|
||||
`0.167.2` does not contain Admin runtime support.
|
||||
|
||||
Rollbacks to legacy images must restore port `80`, remove the hardened
|
||||
security context, and disable `/healthz` probes together. Run
|
||||
`bash scripts/check-admin-runtime.sh` from the repository root to verify
|
||||
legacy defaults, hardened runtime/probes, and unchanged OAuth2 routing.
|
||||
|
||||
### Hardened Hub workers (opt-in, chart 0.167.3)
|
||||
|
||||
The worker templates accept **per-component** `securityContext` (container) and
|
||||
`podSecurityContext` (pod) maps. Both default to `{}` and are omitted from the
|
||||
rendered Deployment. Image tags, ports, existing mounts and legacy permissions
|
||||
are unchanged. Support covers:
|
||||
|
||||
| Values path | Deployment |
|
||||
| --- | --- |
|
||||
| `kerberospipeline.counting` | `pipe-counting` |
|
||||
| `kerberospipeline.throttler` | `pipe-throttler` |
|
||||
| `kerberospipeline.notify` | `pipe-notify` |
|
||||
| `kerberospipeline.thumbnail` | `pipe-thumbnail` |
|
||||
| `kerberospipeline.dominantColor` | `pipe-dominantcolor` |
|
||||
| `kerberoshub.cleanup` | `hub-cleanup` |
|
||||
| `kerberoshub.monitordevice` | `hub-monitor-device` |
|
||||
| `kerberoshub.services.workflows` | `hub-workflows` |
|
||||
| `kerberoshub.services.<stage>` | Enabled generic workflow workers, e.g. loitering, ANPR or forwarder |
|
||||
|
||||
These application hardening changes are proposed in draft PRs, not release
|
||||
clearance. First select and validate a compatible hardened image for each
|
||||
component, then deploy a chart revision containing this support and its matching
|
||||
values together. Published chart `0.167.2` does not contain these worker settings.
|
||||
Do not enable these settings for an unverified legacy, Python/GPU or custom image.
|
||||
There is no global switch that hardens every worker.
|
||||
|
||||
For a verified `65532:65532` Go worker, explicitly configure its own values path:
|
||||
|
||||
```yaml
|
||||
kerberospipeline:
|
||||
counting:
|
||||
securityContext:
|
||||
runAsNonRoot: true
|
||||
runAsUser: 65532
|
||||
runAsGroup: 65532
|
||||
readOnlyRootFilesystem: true
|
||||
allowPrivilegeEscalation: false
|
||||
capabilities:
|
||||
drop: ["ALL"]
|
||||
seccompProfile:
|
||||
type: RuntimeDefault
|
||||
```
|
||||
|
||||
Apply that same container map independently to other verified workers; it is not
|
||||
inherited by them. The confirmed loitering image processes trajectories entirely
|
||||
in memory and needs no writable mount. Worker metrics Services remain on `8080`;
|
||||
`GET /metrics` is process liveness, not RabbitMQ/dependency readiness.
|
||||
|
||||
The notification image proposed by `uug-ai/hub-pipeline-notification#149` retains
|
||||
its existing worker/provider/FFmpeg behavior. Configure its own
|
||||
`kerberospipeline.notify` security maps only after selecting that compatible
|
||||
image; retain provider settings and merge existing mounts/resources. Mount
|
||||
workload-sized writable scratch at `/tmp` (the default temporary directory);
|
||||
the worker creates `/tmp/hub-pipeline-thumbnail` for generated media. Keep
|
||||
`/mail/templates` override mounts read-only and custom CA mounts readable by UID
|
||||
`65532`. The application working directory need not be writable. MongoDB TLS
|
||||
secret mounts remain read-only. Final native image proof is still required.
|
||||
|
||||
Thumbnail and dominant-colour workers need writable **`/tmp`**, including legacy
|
||||
debug image output. Merge a disk-backed `emptyDir` and its mount into each
|
||||
component's existing `volumes`/`volumeMounts`. For example, after sizing for the
|
||||
workload:
|
||||
|
||||
```yaml
|
||||
kerberospipeline:
|
||||
thumbnail:
|
||||
# Also configure the container securityContext above for this worker.
|
||||
podSecurityContext:
|
||||
fsGroup: 65532
|
||||
volumes:
|
||||
- name: media-scratch
|
||||
emptyDir:
|
||||
sizeLimit: 4Gi # Illustrative only, NOT a safe universal limit.
|
||||
volumeMounts:
|
||||
- name: media-scratch
|
||||
mountPath: /tmp
|
||||
resources:
|
||||
requests:
|
||||
ephemeral-storage: 4Gi
|
||||
limits:
|
||||
ephemeral-storage: 6Gi
|
||||
```
|
||||
|
||||
Size scratch for the sum of concurrently downloaded full recordings, extracted
|
||||
JPEG/PNG frames, debug files and headroom. Align ephemeral-storage requests/limits
|
||||
with scratch and other pod usage; retain suitable CPU/memory resources.
|
||||
Memory-backed scratch counts against the pod memory limit and can double-count
|
||||
media already buffered in Go. Small CI fixture volumes are not production sizing.
|
||||
Only opt in to `fsGroup` after reviewing **all** mounts on that pod: persistent
|
||||
volumes can be affected by Kubernetes/CSI ownership handling. Do not add a
|
||||
recursive chmod/chown init container or mount over the root-owned application.
|
||||
|
||||
The proposed ANPR image uses a workload-sized writable directory such as `/work`
|
||||
with `kerberoshub.services.anpr.env.TMPDIR: /work`; configure matching
|
||||
`volumes`/`volumeMounts` and container security settings on that stage only.
|
||||
Optional dataset mode also requires its own writable `ANPR_DATASET_DIR` mount.
|
||||
Models and application directories stay read-only.
|
||||
|
||||
This repository has **no Vault server or design-system chart**. The files named
|
||||
`vault-proxy` and `vault-forwarder` deploy different legacy applications; their
|
||||
ports are not the Vault server's port. Do not change them for `uug-ai/vault#119`.
|
||||
That proposed Vault image requires port `8080` (legacy `80`), UID/GID `65532`,
|
||||
read-only `/home/vault`, workload-sized writable `/run/vault`, and compatible
|
||||
ownership on existing optional `LOCAL_CACHE_DIRECTORY` mounts. Its liveness
|
||||
endpoint is `GET /api/health`. Wire those settings in the actual Vault deployment
|
||||
only when a compatible image release is selected; no migration is required.
|
||||
|
||||
### Passkeys
|
||||
|
||||
Users and sub-users can create and manage their own passkeys in **Profile >
|
||||
Passkeys**, then use **Sign in with a passkey** for passwordless authentication
|
||||
with device PIN/biometric verification. Existing password/MFA sign-in remains
|
||||
available. Enrollment and removal require the current password and an
|
||||
authenticator-app code when MFA is enabled.
|
||||
|
||||
Deploy Hub API and frontend images that support passkeys before enabling this
|
||||
configuration; the chart version alone does not update the service image tags.
|
||||
Passkeys are disabled by default. Configure their trust explicitly:
|
||||
|
||||
```yaml
|
||||
kerberoshub:
|
||||
api:
|
||||
passkeys:
|
||||
enabled: true
|
||||
rpId: "hub.example.com"
|
||||
origins:
|
||||
- "https://hub.example.com"
|
||||
rpName: "Kerberos Hub"
|
||||
```
|
||||
|
||||
| Value | Default | Purpose |
|
||||
| --- | --- | --- |
|
||||
| `kerberoshub.api.passkeys.enabled` | `false` | Inject passkey configuration into Hub API pods only |
|
||||
| `kerberoshub.api.passkeys.rpId` | `""` | Required when enabled; sets `PASSKEY_RP_ID` |
|
||||
| `kerberoshub.api.passkeys.origins` | `[]` | Required when enabled; exact trusted frontend origins, joined with commas into `PASSKEY_ORIGINS` |
|
||||
| `kerberoshub.api.passkeys.rpName` | `"Kerberos Hub"` | Display name; sets `PASSKEY_RP_NAME` |
|
||||
|
||||
Use the **frontend's public hostname and origins**, not the API URL. Origins
|
||||
must have no path or trailing slash. Each origin hostname must equal the RP ID
|
||||
or be its subdomain. Include only trusted origins, especially when sharing an
|
||||
RP ID across tenant subdomains. Keep the RP ID stable: changing it makes
|
||||
existing passkeys unusable. The API validates the RP/origin relationship at
|
||||
runtime; missing/invalid configuration makes passkeys unavailable without
|
||||
affecting password/MFA sign-in.
|
||||
|
||||
HTTPS and a compatible browser are required. Local development may use
|
||||
`rpId: "localhost"` with `origins: ["http://localhost:4200"]`.
|
||||
The frontend displays translated cancellation, verification and unavailable
|
||||
messages when a ceremony cannot complete.
|
||||
|
||||
Apply values with a Helm upgrade or GitOps sync; changes trigger an API rollout.
|
||||
If these variables were previously configured through `kerberoshub.extraEnv`,
|
||||
remove those entries when enabling the dedicated settings to avoid duplicate
|
||||
environment variables. Setting `enabled: false` omits the chart-managed
|
||||
variables; any passkey variables supplied through `extraEnv` still take effect.
|
||||
|
||||
The API's MongoDB identity needs reads on `users`, CRUD on `passkeys`, and
|
||||
insert/find-and-delete on `passkey_challenges`. It attempts to create the
|
||||
`passkey_challenge_expiry_ttl` index on `expiresAt` with
|
||||
`expireAfterSeconds: 0`; expiration and single-use enforcement work without the
|
||||
index, but the index is needed for automatic expired-challenge cleanup.
|
||||
|
||||
### Sequence logging
|
||||
|
||||
Set `kerberospipeline.sequence.logLevel` to control the Sequence service's
|
||||
`LOG_LEVEL` environment variable. The default is `info`; supported values are
|
||||
`debug`, `info`, `warn`, and `error` (lowercase).
|
||||
|
||||
```yaml
|
||||
kerberospipeline:
|
||||
sequence:
|
||||
logLevel: "debug"
|
||||
```
|
||||
|
||||
Apply the values with a Helm upgrade or GitOps sync. Changing the value updates
|
||||
the pod template and triggers a rollout. Debug logging includes Sequence
|
||||
processing checkpoints when supported by the deployed service image.
|
||||
|
||||
### Sprite Resolution
|
||||
|
||||
`kerberospipeline.sprite.width` and `height` set the pixel dimensions of each
|
||||
@@ -268,10 +543,8 @@ kerberospipeline:
|
||||
| `opentelemetry.enabled` | Enable or disable OpenTelemetry instrumentation. | `false` |
|
||||
| `opentelemetry.routingEnabled` | Enable or disable OpenTelemetry routing/export behavior. | `false` |
|
||||
| `opentelemetry.collector.endpoint` | OpenTelemetry collector endpoint used for trace export. | `"http://otel-collector:4317"` |
|
||||
| `opentelemetry.sampler.type` | Trace sampler for the services downstream of the Vault (hub-workflows, `pipe-*`, workflow stages), exported as `OTEL_TRACES_SAMPLER`. They continue the Vault's trace, so use `parentbased_always_off` and sample at the Vault. Empty keeps the SDK default (sample everything). | `""` |
|
||||
| `opentelemetry.sampler.arg` | Sampler argument for those services, exported as `OTEL_TRACES_SAMPLER_ARG`. | `""` |
|
||||
| `opentelemetry.apiSampler.type` | Trace sampler for hub-api, which starts its own traces for user requests (e.g. `parentbased_traceidratio`). Empty keeps the SDK default. | `""` |
|
||||
| `opentelemetry.apiSampler.arg` | Sampler argument for hub-api (e.g. `"0.1"` for ~10% of requests). | `""` |
|
||||
| `opentelemetry.apiSamplingRatios.operator` | hub-api: share of user and API-client requests traced (0–1), rendered as `TRACE_SAMPLER_RATIO_OPERATOR` (hub-api on trace v1.2.6+). With no ratio set, hub-api records every trace. hub-workflows, the `pipe-*` services and the workflow stages always follow the trace they continue (`parentbased_always_off`); uploads are sampled at the Vault. | `""` |
|
||||
| `opentelemetry.apiSamplingRatios.other` | hub-api: share of everything else traced (device heartbeats, agent uploads and live segments, background work), rendered as `TRACE_SAMPLER_RATIO_OTHER`; `0` turns it off. | `""` |
|
||||
| `openai.enabled` | Enable or disable OpenAI-backed semantic features. | `false` |
|
||||
| `openai.apikey` | OpenAI API key used when OpenAI integration is enabled. | `"xxx"` |
|
||||
| `kerberosvault.uri` | The default Kerberos Vault uri (you can add multiple within the app), by default `'https://api.storage.yourdomain.com'` | `"https://api.vault.yourdomain.com"` |
|
||||
@@ -529,6 +802,7 @@ kerberospipeline:
|
||||
| `kerberoshub.monitordevice.tag` | The Docker image tag/version. | `"v1.4.0"` |
|
||||
| `kerberoshub.monitordevice.replicas` | Number of replicas for `kerberoshub.monitordevice`. | `1` |
|
||||
| `kerberoshub.monitordevice.logLevel` | Log verbosity level for `kerberoshub.monitordevice`. | `"info"` |
|
||||
| `kerberoshub.monitordevice.scanIntervalSeconds` | Seconds between device status scans. Sets how quickly a camera going offline or online is detected and, for accounts with "Group notifications" on, how long notifications are held to collect other cameras that change status at the same time. | `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"` |
|
||||
@@ -676,6 +950,8 @@ kerberospipeline:
|
||||
| `email.templates.device` | The template which is send when a camera goes online or offline, by default `'device'`. | `"device"` |
|
||||
| `email.templates.alertTitle` | Email subject title for the `alert` template. | `"[Alert] Kerberos Hub detected something an event"` |
|
||||
| `email.templates.deviceTitle` | Email subject title for the `device` template. | `"[Device] A Kerberos Agent's status has been changed"` |
|
||||
| `email.templates.deviceGroup` | The template sent instead of `device` when several cameras of an account change status in the same scan and the account groups device notifications ("Group notifications"). SMTP only; see *Grouped camera status* below. | `"device_group"` |
|
||||
| `email.templates.deviceGroupTitle` | Email subject title for the `device_group` template. | `"[Device] Several cameras changed status"` |
|
||||
| `email.mailgun.apiKey` | The Mailgun API key linked to your Mailgun domain. | `""` |
|
||||
| `imagePullSecrets.name` | Docker registry secret name, which is also granted with the license. This allows you to download the Docker images. | `""` |
|
||||
| `kerberoshub.forwarder.pullPolicy` | The Docker registry pull policy. | `""` |
|
||||
@@ -729,11 +1005,50 @@ Sent by `hub-monitor-device` when a camera goes online, offline or idle. The sub
|
||||
| `{{user}}` | Username of the account the camera belongs to. |
|
||||
| `{{title}}` | The email subject. |
|
||||
| `{{text}}` | The status change, for example "`<camera>` is online, but the camera stream is not connected." |
|
||||
| `{{status}}` | `online`, `offline` or `idle`. |
|
||||
| `{{status}}` | `active` (online), `inactive` (offline) or `idle` (online, camera stream not connected). |
|
||||
| `{{devicename}}`, `{{deviceid}}` | Camera name and camera key. |
|
||||
| `{{eventdate}}`, `{{eventtime}}`, `{{eventdatetime}}` | When the status changed, in the server's timezone (normally UTC), not the account's. |
|
||||
| `{{link}}` | Not set; replaced with an empty value. |
|
||||
|
||||
|
||||
#### Grouped camera status: `device_group`
|
||||
|
||||
Sent by `hub-monitor-device` instead of `device` when an account has **Group notifications** switched on (Alerts → Devices, stored as `notificationSettings.devices.groupNotifications`) and **several** of its cameras change status in the same scan. A change is held for one scan (`kerberoshub.monitordevice.scanIntervalSeconds`, default 60 seconds) so that cameras of the same outage arrive in one email. When only one camera changed, the usual `device` email is sent. The subject is `email.templates.deviceGroupTitle`; the template name is `email.templates.deviceGroup` (default `device_group`, so custom files are `device_group.html` and `device_group.txt`).
|
||||
|
||||
| Placeholder | Value |
|
||||
|---|---|
|
||||
| `{{user}}` | Name of the account the cameras belong to: first and last name, or the username when both are empty. |
|
||||
| `{{title}}` | The email subject. |
|
||||
| `{{text}}` | One-line summary, for example "6 cameras changed status: 4 offline, 1 lost their camera stream, 1 back online." |
|
||||
| `{{count}}` | Number of cameras in the email. |
|
||||
| `{{status}}` | `active`, `inactive` or `idle` when all cameras have the same new status, otherwise `mixed`. |
|
||||
| `{{sections}}` | The cameras as HTML, one section per status and site, group or "Other cameras" (see below). |
|
||||
| `{{sectionsText}}` | The same sections as plain text, for `device_group.txt`. |
|
||||
| `{{link}}` | The cameras page, `<tenant URL>/devices`, built from `kerberoshub.frontend.schema` and `kerberoshub.frontend.url` (see *Links* above). |
|
||||
| `{{eventdate}}`, `{{eventtime}}`, `{{eventdatetime}}` | When the email was sent, in the server's timezone (normally UTC). |
|
||||
|
||||
**Sections.** Sections are ordered by status (offline, camera stream lost, back online), then sites, groups and "Other cameras". Each camera is listed once: under the site or group that holds the most cameras of the email with the same status (a site wins a tie), or under "Other cameras" when it is in no site or group. A section title reads `Site Gate · Offline · all 3 cameras`, `Group Perimeter · Back online · 2 cameras` or `Other cameras · Camera stream lost · 1 camera`; "all" is added when every camera of the site or group is in the section. Each camera line has its name and a detail: `no response for 12 min`, `agent online, camera stream not connected` or `online`.
|
||||
|
||||
`{{sections}}` is generated HTML with inline styles (email clients ignore stylesheets): per section a `<p>` with the title, followed by a `<table role="presentation" width="100%">` with one row per camera (name left, detail right). Place it where the camera list should appear; its look can't be changed from the template. For full control over the layout, use `{{sectionsText}}`. Camera, site and group names are HTML-escaped.
|
||||
|
||||
`{{sectionsText}}` example:
|
||||
|
||||
```text
|
||||
Site Gate · Offline · all 3 cameras
|
||||
- Gate camera 1 (no response for 4 min)
|
||||
- Gate camera 2 (no response for 4 min)
|
||||
- Gate camera 3 (no response for 4 min)
|
||||
|
||||
Other cameras · Camera stream lost · 1 camera
|
||||
- Lobby camera (agent online, camera stream not connected)
|
||||
```
|
||||
|
||||
**Custom templates** for this email are read by `hub-monitor-device`, so mount the `custom-email-templates` volume on `kerberoshub.monitordevice` as well (see the commented example in `values.yaml`).
|
||||
|
||||
**Which channels are grouped.** Grouping applies to the channels people read: email, Slack, Telegram, Pushover, Pushbullet, SMS and Alexa. Webhooks, IFTTT and the real-time feed are never grouped: they keep receiving one notification per camera, with the same fields and at the same moment as without grouping, so existing integrations keep working.
|
||||
|
||||
**When email stays per camera.** The grouped email is only sent with the SMTP mail provider. With SendGrid or Mailgun the template name is a template id in that account, so those keep one `device` email per camera. With SMTP, a deployment that mounts its own `device` template but no `device_group` template also keeps one email per camera, so a white-labelled device email never falls back to the default layout. Add a `device_group` template next to your `device` template to get grouped emails.
|
||||
|
||||
#### Account and sharing emails
|
||||
|
||||
Sent by the API (`kerberoshub.api`). The subject of each email is the matching `email.templates.<name>Title` value.
|
||||
|
||||
@@ -0,0 +1,431 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<meta
|
||||
name="viewport"
|
||||
content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1"
|
||||
/>
|
||||
<meta name="description" content="Kerberos.io Mailing">
|
||||
<style type="text/css">
|
||||
|
||||
@font-face {
|
||||
font-family: 'Inter';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url("https://kerberos.io/dist/fonts/Inter-Regular.woff?v=/dist/fonts/Inter-Regular.woff2?v=3.183.18") format("woff2"),
|
||||
url("https://kerberos.io/dist/fonts/Inter-Regular.woff?v=/dist/fonts/Inter-Regular.woff2?v=3.183.18") format("woff");
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Inter';
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
font-display: swap;
|
||||
src: url("https://kerberos.io/dist/fonts/Inter-Medium.woff2?v=3.18") format("woff2"),
|
||||
url("https://kerberos.io/dist/fonts/Inter-Medium.woff?v=3.18") format("woff");
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Inter';
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
font-display: swap;
|
||||
src: url("https://kerberos.io/dist/fonts/Inter-SemiBold.woff2?v=3.18") format("woff2"),
|
||||
url("https://kerberos.io/dist/fonts/Inter-SemiBold.woff?v=3.18") format("woff");
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Inter var';
|
||||
font-weight: 100 900;
|
||||
font-display: swap;
|
||||
font-style: normal;
|
||||
font-named-instance: 'Regular';
|
||||
src: url("https://kerberos.io/dist/fonts/Inter-roman.var.woff2?v=3.18") format("woff2");
|
||||
}
|
||||
|
||||
body{
|
||||
background: #E5E5E5;
|
||||
margin-top:0;
|
||||
margin-bottom: 0;
|
||||
margin-right: 0;
|
||||
margin-left: 0;
|
||||
padding-top: 0;
|
||||
padding-left: 0;
|
||||
padding-right: 0;
|
||||
padding-bottom: 0;
|
||||
font-family: 'Inter';
|
||||
}
|
||||
a, a:hover, a:active {
|
||||
color: #262424;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.corner-td{
|
||||
width: 60px;
|
||||
}
|
||||
|
||||
table {border-collapse:separate;max-width: 850px; margin: 0 auto; width: 100%;}
|
||||
.ExternalClass p, .ExternalClass span, .ExternalClass font, .ExternalClass td {line-height: 100%;}
|
||||
.ExternalClass {width: 100%;}
|
||||
@media screen and (max-width:500px){
|
||||
.tab-td{
|
||||
padding-left: 10px!important;
|
||||
}
|
||||
.tab-td a h4{
|
||||
font-size: 14px!important;
|
||||
}
|
||||
.corner-td{
|
||||
width: 20px!important;
|
||||
}
|
||||
.company-name-td h3{
|
||||
font-size: 16px!important;
|
||||
}
|
||||
table.header-table{
|
||||
padding-top: 8px!important;
|
||||
padding-right: 0px!important;
|
||||
padding-bottom: 24px!important;
|
||||
padding-left: 0px!important;
|
||||
}
|
||||
.colored-card-td h4{
|
||||
font-size: 14px!important;
|
||||
}
|
||||
.colored-card-td h2{
|
||||
font-size: 20px!important;
|
||||
}
|
||||
.colored-card-td a p{
|
||||
font-size: 12px!important;
|
||||
width: 143px!important;
|
||||
}
|
||||
.colored-card-td{
|
||||
padding-top: 24px!important;
|
||||
padding-right: 24px!important;
|
||||
padding-bottom: 24px!important;
|
||||
padding-left: 24px!important;
|
||||
}
|
||||
.colorless-card-td{
|
||||
padding-top: 24px!important;
|
||||
padding-right: 24px!important;
|
||||
padding-bottom: 24px!important;
|
||||
padding-left: 24px!important;
|
||||
}
|
||||
.colorless-card-td h3{
|
||||
font-size: 18px!important;
|
||||
}
|
||||
.colorless-card-td p{
|
||||
font-size: 14px!important;
|
||||
}
|
||||
.colorless-card-table{
|
||||
margin-left: 0px!important;
|
||||
margin-right: 0px!important;
|
||||
margin-top: 24px!important;
|
||||
margin-bottom: 24px!important;
|
||||
}
|
||||
.footer-td{
|
||||
display: table-row!important;
|
||||
}
|
||||
}
|
||||
@media screen and (max-width:600px) {
|
||||
.footer-td{
|
||||
display: table-row!important;
|
||||
}
|
||||
}
|
||||
@media screen and (max-width:650px) {
|
||||
.footer-table{
|
||||
margin-left: 0px!important;
|
||||
margin-right: 0px!important;
|
||||
margin-top: 0px!important;
|
||||
margin-bottom: 36px!important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body height="100%" width="100%">
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" bgcolor="E5E5E5" style="border-collaps:collaps; mso-table-lspace:0pt; mso-table-rspace:0pt;">
|
||||
<tr>
|
||||
<td bgcolor="E5E5E5">
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" height="36" class="header-table" style="padding-top: 36px ;padding-right: 0;padding-bottom: 36px;padding-left: 0;border-collaps:collaps; mso-table-lspace:0pt; mso-table-rspace:0pt;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="corner-td" align="left"></td>
|
||||
<td width="48" height="36" align="left"><img alt="Kerberos.io" width="36" height="36" src="https://kerberos.io/images/email/kerberos.png"/></td>
|
||||
<td height="36" align="left" class="company-name-td">
|
||||
<h3 width="36" height="36" style=" font-family: Inter;
|
||||
font-size: 20px;
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
line-height: 24px;
|
||||
mso-line-height-rule:exactly;
|
||||
letter-spacing: 0em;
|
||||
text-align: left;
|
||||
color: #262424;">Kerberos.io</h3>
|
||||
</td>
|
||||
<td height="36" width="36" style="padding-left: 36px;" class="tab-td" align="right">
|
||||
<a style="text-decoration: none;color: none;" href={{tab1_href}}>
|
||||
<h4 style="font-family: Inter;
|
||||
font-size: 16px;
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
line-height: 36px;
|
||||
mso-line-height-rule:exactly;
|
||||
letter-spacing: 0em;
|
||||
text-align: right;
|
||||
color: #6D6666;">{{tab1_title}}</h4>
|
||||
</a>
|
||||
</td>
|
||||
<td height="36" width="36" style="padding-left: 36px;" class="tab-td" align="right" >
|
||||
<a style="text-decoration: none;color: none;" href={{tab2_href}}>
|
||||
<h4 style="font-family: Inter;
|
||||
font-size: 16px;
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
line-height: 36px;
|
||||
mso-line-height-rule:exactly;
|
||||
letter-spacing: 0em;
|
||||
text-align: right;
|
||||
color: #6D6666;">{{tab2_title}}</h4>
|
||||
</a>
|
||||
</td>
|
||||
<td class="corner-td" align="right"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" bgcolor="E5E5E5" style="border-collaps:collaps; mso-table-lspace:0pt; mso-table-rspace:0pt;">
|
||||
<tr>
|
||||
<td bgcolor="E5E5E5">
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" style="border-collaps:collaps; mso-table-lspace:0pt; mso-table-rspace:0pt;" >
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="corner-td" align="left"></td>
|
||||
<td class="colored-card-td" bgcolor="#57356B" style="padding-left: 48px;padding-right: 48px;padding-top: 48px;padding-bottom: 48px;border-radius: 4px;background-color:#57356B;">
|
||||
<h4 style="font-family: Inter;
|
||||
font-size: 16px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 24px;
|
||||
mso-line-height-rule:exactly;
|
||||
letter-spacing: 0em;
|
||||
text-align: left;
|
||||
color:#b09fb9;">Hey, {{user}}</h4>
|
||||
<h2 style=" font-family: Inter;
|
||||
font-size: 24px;
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
line-height: 36px;
|
||||
mso-line-height-rule:exactly;
|
||||
letter-spacing: 0em;
|
||||
text-align: left;
|
||||
color:#FFFFFF;
|
||||
padding-top: 12px;
|
||||
margin-bottom: 0;
|
||||
padding-bottom: 0;
|
||||
padding-left: 0;
|
||||
padding-right: 0;">Several of your cameras changed status</h2>
|
||||
<h2 style=" font-family: Inter;
|
||||
font-size: 20px;
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
line-height: 36px;
|
||||
mso-line-height-rule:exactly;
|
||||
letter-spacing: 0em;
|
||||
text-align: left;
|
||||
color:#FFFFFF;
|
||||
padding-top: 0;
|
||||
padding-bottom: 24px;
|
||||
padding-left: 0;
|
||||
padding-right: 0;
|
||||
color:#b09fb9;">{{text}}</h2>
|
||||
</td>
|
||||
<td class="corner-td" align="right"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" bgcolor="E5E5E5" style="border-collaps:collaps; mso-table-lspace:0pt; mso-table-rspace:0pt;">
|
||||
<tr>
|
||||
<td bgcolor="E5E5E5">
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" class="colorless-card-table" style="margin-top: 36px;margin-bottom: 36px;margin-left: 0;margin-right: 0;border-collaps:collaps; mso-table-lspace:0pt; mso-table-rspace:0pt;" >
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="corner-td" align="left"></td>
|
||||
<td class="colorless-card-td" bgcolor="#FFFFFF" style="background-color:#FFFFFF;padding-top: 36px;padding-right: 43px;padding-bottom: 25px;padding-left: 43px;border-radius: 4px;">
|
||||
<h3 style=" font-family: Inter;
|
||||
font-size: 20px;
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
line-height: 36px;
|
||||
mso-line-height-rule:exactly;
|
||||
letter-spacing: 0em;
|
||||
text-align: left;
|
||||
color: #262424;
|
||||
width: 480px">What changed</h3>
|
||||
{{sections}}
|
||||
<p style=" font-family: Inter;
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 24px;
|
||||
mso-line-height-rule:exactly;
|
||||
letter-spacing: 0em;
|
||||
text-align: left;
|
||||
color: #6D6666;
|
||||
margin-top: 12px;">Cameras that go offline together usually share a cause, such as a site losing its internet connection or power. Have a look into your Kerberos Hub account for the latest recordings and the status of your cameras.</p>
|
||||
|
||||
<a style="text-decoration: none;color: none;" href="{{link}}">
|
||||
<p style="font-family: Inter;
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
line-height: 24px;
|
||||
mso-line-height-rule:exactly;
|
||||
letter-spacing: 0em;
|
||||
text-align: left;
|
||||
color:#FFFFFF;
|
||||
background-color: #84559F;
|
||||
padding-top: 6px;
|
||||
padding-bottom: 6px;
|
||||
padding-right: 16px;
|
||||
padding-left: 16px;
|
||||
width: 160px;
|
||||
border-radius: 4px;
|
||||
text-align: center;
|
||||
cursor: pointer;">Go to Kerberos Hub -></p>
|
||||
</a>
|
||||
</td>
|
||||
<td class="corner-td" align="right"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" bgcolor="E5E5E5" style="border-collaps:collaps; mso-table-lspace:0pt; mso-table-rspace:0pt;">
|
||||
<tr>
|
||||
<td bgcolor="E5E5E5">
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" class="footer-table" style="margin-top: 0;border-collaps:collaps; mso-table-lspace:0pt; mso-table-rspace:0pt;" >
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="corner-td" align="left"></td>
|
||||
<!--[if mso | IE]>
|
||||
<table role="presentation" border="0" cellpadding="0" cellspacing="0"><tr><td style="vertical-align:top;display:table-row !important">
|
||||
<![endif]-->
|
||||
<td height="146" width="190" class="footer-td" style="margin-bottom: 12px;" valign="top" align="left">
|
||||
<table style="border-collaps:collaps; mso-table-lspace:0pt; mso-table-rspace:0pt;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<h4 style=" font-family: Inter;
|
||||
font-size: 16px;
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
line-height: 36px;
|
||||
mso-line-height-rule:exactly;
|
||||
letter-spacing: 0em;
|
||||
text-align: left;
|
||||
color:#6D6666;">Get in touch</h4>
|
||||
<a style="text-decoration: none;color: none;" href="mailto:support@verstraeten.io">
|
||||
<p style=" font-family: Inter;
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 16px;
|
||||
mso-line-height-rule:exactly;
|
||||
letter-spacing: 0em;
|
||||
text-align: left;
|
||||
color: #A69D9D;">support@kerberos.io</p>
|
||||
</a>
|
||||
<p style=" font-family: Inter;
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 16px;
|
||||
mso-line-height-rule:exactly;
|
||||
letter-spacing: 0em;
|
||||
text-align: left;
|
||||
color: #A69D9D;">9000 Ghent, BE</p>
|
||||
|
||||
<a style="text-decoration: none;color: none;" href="https://kerberos.io/">
|
||||
<p style=" font-family: Inter;
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 24px;
|
||||
mso-line-height-rule:exactly;
|
||||
letter-spacing: 0em;
|
||||
text-align: left;
|
||||
color: #A69D9D;">https://kerberos.io</p>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
<!--[if mso | IE]>
|
||||
<table role="presentation" border="0" cellpadding="0" cellspacing="0"><tr><td style="vertical-align:top;display:table-row !important">
|
||||
<![endif]-->
|
||||
<td class="footer-td" style="border-radius: 4px;padding-left: 0;padding-right: 0;padding-top: 0;padding-bottom: 0; margin-bottom: 12px;" valign="top" align="left">
|
||||
<table style="border-collaps:collaps; mso-table-lspace:0pt; mso-table-rspace:0pt;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<h4 style=" font-family: Inter;
|
||||
font-size: 16px;
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
line-height: 36px;
|
||||
mso-line-height-rule:exactly;
|
||||
letter-spacing: 0em;
|
||||
text-align: left;
|
||||
color:#6D6666;">About Kerberos</h4>
|
||||
<p style=" font-family: Inter;
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 24px;
|
||||
mso-line-height-rule:exactly;
|
||||
letter-spacing: 0em;
|
||||
text-align: left;
|
||||
color: #A69D9D;">Welcome to the revolutionary video analytics and video management platform. Open, modular, and extensible for everyone, anywhere.</p>
|
||||
|
||||
|
||||
<p style="margin-top: 12px;">
|
||||
<a href="https://twitter.com/kerberosio" style="text-decoration: none;color: none;">
|
||||
<img width="24" height="24" alt="Twitter" src="https://kerberos.io/images/email/twitter.png"/>
|
||||
</a>
|
||||
<a href="https://reddit.com/r/kerberos_io" style="text-decoration: none;color: none;">
|
||||
<img g width="24" height="24" alt="Reddit" src="https://kerberos.io/images/email/reddit.png"/>
|
||||
</a>
|
||||
<a href="https://www.youtube.com/channel/UCnd9q7iRNNw4W95eQwQuECA" style="text-decoration: none;color: none;">
|
||||
<img g width="24" height="24" alt="Youtube" src="https://kerberos.io/images/email/youtube.png"/>
|
||||
</a>
|
||||
<a href="https://github.com/kerberos-io" style="text-decoration: none;color: none;">
|
||||
<img g width="24" height="24" alt="Github" src="https://kerberos.io/images/email/github.png"/>
|
||||
</a>
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
<td class="corner-td" align="right"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<table style="border-collaps:collaps; mso-table-lspace:0pt; mso-table-rspace:0pt;">
|
||||
<tbody>
|
||||
<tr style="height: 50px">
|
||||
<td></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,22 @@
|
||||
Kerberos.io
|
||||
------------
|
||||
|
||||
Hey, {{user}}
|
||||
|
||||
Several of your cameras changed status
|
||||
{{text}}
|
||||
|
||||
{{sectionsText}}
|
||||
|
||||
Go to Kerberos Hub ->
|
||||
{{link}}
|
||||
|
||||
Get in touch
|
||||
------------
|
||||
support@kerberos.io
|
||||
9000 Ghent, BE
|
||||
https://kerberos.io
|
||||
|
||||
About Kerberos
|
||||
------------
|
||||
Welcome to the revolutionary video analytics and video management platform. Open, modular, and extensible for everyone, anywhere.
|
||||
@@ -84,30 +84,25 @@ Usage: {{- include "hub.mongodb.appUriEnv" (dict "root" $ "app" "hub-api") | nin
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
OpenTelemetry sampler env entries, rendered only when configured. The OTel SDKs
|
||||
read these natively; unset keeps the SDK default (parentbased_always_on).
|
||||
"hub.opentelemetry.samplerEnv" renders opentelemetry.sampler, the sampler of the
|
||||
services downstream of the Vault (pipeline, workflows engine and stages), which
|
||||
continue the Vault's trace; "hub.opentelemetry.apiSamplerEnv" renders
|
||||
opentelemetry.apiSampler for hub-api, which starts its own traces for user
|
||||
requests.
|
||||
OpenTelemetry sampling env.
|
||||
"hub.opentelemetry.samplerEnv" is for the services that only continue traces
|
||||
started elsewhere (hub-workflows, pipe-*, workflow stages): they follow the
|
||||
parent's sampling decision and never start traces of their own.
|
||||
"hub.opentelemetry.apiSamplerEnv" renders hub-api's per-class sampling ratios
|
||||
(opentelemetry.apiSamplingRatios) as TRACE_SAMPLER_RATIO_<CLASS>; empty values
|
||||
are not rendered.
|
||||
Usage: {{- include "hub.opentelemetry.samplerEnv" $ | trim | nindent 8 }}
|
||||
*/}}
|
||||
{{- define "hub.opentelemetry.samplerEnv" -}}
|
||||
{{- include "hub.opentelemetry.samplerEnvFrom" .Values.opentelemetry.sampler -}}
|
||||
- name: OTEL_TRACES_SAMPLER
|
||||
value: "parentbased_always_off"
|
||||
{{- end -}}
|
||||
|
||||
{{- define "hub.opentelemetry.apiSamplerEnv" -}}
|
||||
{{- include "hub.opentelemetry.samplerEnvFrom" (.Values.opentelemetry.apiSampler | default dict) -}}
|
||||
{{- end -}}
|
||||
|
||||
{{- define "hub.opentelemetry.samplerEnvFrom" -}}
|
||||
{{- with .type }}
|
||||
- name: OTEL_TRACES_SAMPLER
|
||||
value: {{ . | quote }}
|
||||
{{- range $class, $ratio := (.Values.opentelemetry.apiSamplingRatios | default dict) }}
|
||||
{{- if ne (toString $ratio) "" }}
|
||||
- name: TRACE_SAMPLER_RATIO_{{ upper $class }}
|
||||
value: {{ toString $ratio | quote }}
|
||||
{{- end }}
|
||||
{{- with .arg }}
|
||||
- name: OTEL_TRACES_SAMPLER_ARG
|
||||
value: {{ . | quote }}
|
||||
{{- end }}
|
||||
{{- end -}}
|
||||
|
||||
@@ -26,6 +26,10 @@ spec:
|
||||
- name: admin
|
||||
image: "{{ .Values.global.imageRegistry }}{{ .Values.admin.repository }}:{{ .Values.admin.tag }}"
|
||||
imagePullPolicy: {{ .Values.admin.pullPolicy }}
|
||||
{{- with .Values.admin.securityContext }}
|
||||
securityContext:
|
||||
{{- toYaml . | nindent 12 }}
|
||||
{{- end }}
|
||||
{{- with .Values.admin.resources }}
|
||||
resources:
|
||||
{{- toYaml . | nindent 12 }}
|
||||
@@ -36,8 +40,16 @@ spec:
|
||||
{{- end }}
|
||||
ports:
|
||||
- name: http
|
||||
containerPort: 80
|
||||
containerPort: {{ .Values.admin.containerPort | default 80 }}
|
||||
protocol: TCP
|
||||
{{- with .Values.admin.readinessProbe }}
|
||||
readinessProbe:
|
||||
{{- toYaml . | nindent 12 }}
|
||||
{{- end }}
|
||||
{{- with .Values.admin.livenessProbe }}
|
||||
livenessProbe:
|
||||
{{- toYaml . | nindent 12 }}
|
||||
{{- end }}
|
||||
env:
|
||||
- name: LOG_LEVEL
|
||||
value: "{{ .Values.admin.logLevel }}"
|
||||
|
||||
@@ -10,7 +10,7 @@ spec:
|
||||
ports:
|
||||
- protocol: TCP
|
||||
port: 80
|
||||
targetPort: 80
|
||||
targetPort: http
|
||||
name: http
|
||||
selector:
|
||||
app: admin
|
||||
|
||||
@@ -436,6 +436,15 @@ spec:
|
||||
- name: MFA_ISSUER
|
||||
value: "{{ .Values.kerberoshub.api.mfaIssuer }}"
|
||||
|
||||
{{- if .Values.kerberoshub.api.passkeys.enabled }}
|
||||
- name: PASSKEY_RP_ID
|
||||
value: {{ required "kerberoshub.api.passkeys.rpId is required when passkeys are enabled" .Values.kerberoshub.api.passkeys.rpId | quote }}
|
||||
- name: PASSKEY_ORIGINS
|
||||
value: {{ required "kerberoshub.api.passkeys.origins is required when passkeys are enabled" .Values.kerberoshub.api.passkeys.origins | join "," | quote }}
|
||||
- name: PASSKEY_RP_NAME
|
||||
value: {{ .Values.kerberoshub.api.passkeys.rpName | quote }}
|
||||
{{- end }}
|
||||
|
||||
# AWS (Legacy, use Kerberos Vault instead)
|
||||
- name: AWS_REGION
|
||||
value: "{{ .Values.kerberoshub.api.aws.region }}"
|
||||
|
||||
@@ -22,6 +22,10 @@ spec:
|
||||
labels:
|
||||
app: hub-cleanup
|
||||
spec:
|
||||
{{- with .Values.kerberoshub.cleanup.podSecurityContext }}
|
||||
securityContext:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- if .Values.kerberoshub.serviceAccount.create }}
|
||||
serviceAccountName: {{ default (printf "%s-%s-sa" .Release.Name .Chart.Name | trunc 63 | trimSuffix "-") .Values.kerberoshub.serviceAccount.name }}
|
||||
{{- end }}
|
||||
@@ -47,6 +51,10 @@ spec:
|
||||
- name: hub-cleanup
|
||||
image: "{{ .Values.global.imageRegistry }}{{ .Values.kerberoshub.cleanup.repository }}:{{ .Values.kerberoshub.cleanup.tag }}"
|
||||
imagePullPolicy: {{ .Values.kerberoshub.cleanup.pullPolicy }}
|
||||
{{- with .Values.kerberoshub.cleanup.securityContext }}
|
||||
securityContext:
|
||||
{{- toYaml . | nindent 12 }}
|
||||
{{- end }}
|
||||
{{- with .Values.kerberoshub.cleanup.resources }}
|
||||
resources:
|
||||
{{- toYaml . | nindent 12 }}
|
||||
|
||||
@@ -10,7 +10,7 @@ spec:
|
||||
ports:
|
||||
- protocol: TCP
|
||||
port: 80
|
||||
targetPort: 80
|
||||
targetPort: http
|
||||
name: http
|
||||
selector:
|
||||
app: hub-frontend-demo
|
||||
@@ -101,12 +101,16 @@ spec:
|
||||
- name: hub-frontend-demo
|
||||
image: "{{ .Values.global.imageRegistry }}{{ .Values.kerberoshub.frontend.repository }}:{{ .Values.kerberoshub.frontend.tag }}"
|
||||
imagePullPolicy: {{ .Values.kerberoshub.frontend.pullPolicy }}
|
||||
{{- with .Values.kerberoshub.frontend.securityContext }}
|
||||
securityContext:
|
||||
{{- toYaml . | nindent 10 }}
|
||||
{{- end }}
|
||||
{{- with .Values.kerberoshub.frontend.resources }}
|
||||
resources:
|
||||
{{- toYaml . | nindent 12 }}
|
||||
{{- end }}
|
||||
ports:
|
||||
- containerPort: 80
|
||||
- containerPort: {{ .Values.kerberoshub.frontend.containerPort | default 80 }}
|
||||
name: http
|
||||
{{- with .Values.kerberoshub.frontend.volumeMounts}}
|
||||
volumeMounts:
|
||||
|
||||
@@ -11,7 +11,7 @@ spec:
|
||||
ports:
|
||||
- protocol: TCP
|
||||
port: 80
|
||||
targetPort: 80
|
||||
targetPort: http
|
||||
name: http
|
||||
selector:
|
||||
app: hub-frontend
|
||||
@@ -182,12 +182,16 @@ spec:
|
||||
- name: hub-frontend
|
||||
image: "{{ .Values.global.imageRegistry }}{{ .Values.kerberoshub.frontend.repository }}:{{ .Values.kerberoshub.frontend.tag }}"
|
||||
imagePullPolicy: {{ .Values.kerberoshub.frontend.pullPolicy }}
|
||||
{{- with .Values.kerberoshub.frontend.securityContext }}
|
||||
securityContext:
|
||||
{{- toYaml . | nindent 10 }}
|
||||
{{- end }}
|
||||
{{- with .Values.kerberoshub.frontend.resources }}
|
||||
resources:
|
||||
{{- toYaml . | nindent 12 }}
|
||||
{{- end }}
|
||||
ports:
|
||||
- containerPort: 80
|
||||
- containerPort: {{ .Values.kerberoshub.frontend.containerPort | default 80 }}
|
||||
name: http
|
||||
{{- with .Values.kerberoshub.frontend.volumeMounts}}
|
||||
volumeMounts:
|
||||
|
||||
@@ -22,6 +22,10 @@ spec:
|
||||
labels:
|
||||
app: hub-monitor-device
|
||||
spec:
|
||||
{{- with .Values.kerberoshub.monitordevice.podSecurityContext }}
|
||||
securityContext:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- if .Values.kerberoshub.serviceAccount.create }}
|
||||
serviceAccountName: {{ default (printf "%s-%s-sa" .Release.Name .Chart.Name | trunc 63 | trimSuffix "-") .Values.kerberoshub.serviceAccount.name }}
|
||||
{{- end }}
|
||||
@@ -47,6 +51,10 @@ spec:
|
||||
- name: hub-monitor-device
|
||||
image: "{{ .Values.global.imageRegistry }}{{ .Values.kerberoshub.monitordevice.repository }}:{{ .Values.kerberoshub.monitordevice.tag }}"
|
||||
imagePullPolicy: {{ .Values.kerberoshub.monitordevice.pullPolicy }}
|
||||
{{- with .Values.kerberoshub.monitordevice.securityContext }}
|
||||
securityContext:
|
||||
{{- toYaml . | nindent 12 }}
|
||||
{{- end }}
|
||||
{{- with .Values.kerberoshub.monitordevice.resources }}
|
||||
resources:
|
||||
{{- toYaml . | nindent 12 }}
|
||||
@@ -67,6 +75,8 @@ spec:
|
||||
{{- include "hub.mongodb.appUriEnv" (dict "root" $ "app" "hub-monitor-device") | nindent 12 }}
|
||||
- name: LOG_LEVEL
|
||||
value: "{{ .Values.kerberoshub.monitordevice.logLevel }}"
|
||||
- name: DEVICE_SCAN_INTERVAL_SECONDS
|
||||
value: "{{ .Values.kerberoshub.monitordevice.scanIntervalSeconds | default 60 }}"
|
||||
- name: READ_ONLY
|
||||
value: "{{ .Values.readonly }}"
|
||||
# Mail settings
|
||||
@@ -82,6 +92,13 @@ spec:
|
||||
value: "{{ .Values.email.templates.device }}"
|
||||
- name: DEVICE_TITLE
|
||||
value: "{{ .Values.email.templates.deviceTitle }}"
|
||||
- name: DEVICE_GROUP_TEMPLATE
|
||||
value: "{{ .Values.email.templates.deviceGroup | default "device_group" }}"
|
||||
- name: DEVICE_GROUP_TITLE
|
||||
value: "{{ .Values.email.templates.deviceGroupTitle | default "[Device] Several cameras changed status" }}"
|
||||
# Base URL for the link in grouped device notifications.
|
||||
- name: PUBLIC_URL
|
||||
value: "{{ .Values.kerberoshub.frontend.schema }}://{{ .Values.kerberoshub.frontend.url }}"
|
||||
|
||||
# - Plain SMTP
|
||||
- name: SMTP_SERVER
|
||||
|
||||
@@ -50,6 +50,10 @@ spec:
|
||||
labels:
|
||||
app: hub-{{ $name }}
|
||||
spec:
|
||||
{{- with $svc.podSecurityContext }}
|
||||
securityContext:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- if $root.Values.kerberoshub.serviceAccount.create }}
|
||||
serviceAccountName: {{ default (printf "%s-%s-sa" $root.Release.Name $root.Chart.Name | trunc 63 | trimSuffix "-") $root.Values.kerberoshub.serviceAccount.name }}
|
||||
{{- end }}
|
||||
@@ -69,6 +73,10 @@ spec:
|
||||
- name: hub-{{ $name }}
|
||||
image: "{{ $root.Values.global.imageRegistry }}{{ $svc.repository }}:{{ $svc.tag }}"
|
||||
imagePullPolicy: {{ $svc.pullPolicy | default "IfNotPresent" }}
|
||||
{{- with $svc.securityContext }}
|
||||
securityContext:
|
||||
{{- toYaml . | nindent 10 }}
|
||||
{{- end }}
|
||||
{{- with $svc.resources }}
|
||||
resources:
|
||||
{{- toYaml . | nindent 12 }}
|
||||
|
||||
@@ -23,6 +23,10 @@ spec:
|
||||
labels:
|
||||
app: hub-workflows
|
||||
spec:
|
||||
{{- with .Values.kerberoshub.services.workflows.podSecurityContext }}
|
||||
securityContext:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- if .Values.kerberoshub.serviceAccount.create }}
|
||||
serviceAccountName: {{ default (printf "%s-%s-sa" .Release.Name .Chart.Name | trunc 63 | trimSuffix "-") .Values.kerberoshub.serviceAccount.name }}
|
||||
{{- end }}
|
||||
@@ -48,6 +52,10 @@ spec:
|
||||
- name: hub-workflows
|
||||
image: "{{ .Values.global.imageRegistry }}{{ .Values.kerberoshub.services.workflows.repository }}:{{ .Values.kerberoshub.services.workflows.tag }}"
|
||||
imagePullPolicy: {{ .Values.kerberoshub.services.workflows.pullPolicy }}
|
||||
{{- with .Values.kerberoshub.services.workflows.securityContext }}
|
||||
securityContext:
|
||||
{{- toYaml . | nindent 10 }}
|
||||
{{- end }}
|
||||
{{- with .Values.kerberoshub.services.workflows.resources }}
|
||||
resources:
|
||||
{{- toYaml . | nindent 12 }}
|
||||
|
||||
@@ -24,6 +24,10 @@ spec:
|
||||
labels:
|
||||
app: pipe-counting
|
||||
spec:
|
||||
{{- with .Values.kerberospipeline.counting.podSecurityContext }}
|
||||
securityContext:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- if .Values.kerberoshub.serviceAccount.create }}
|
||||
serviceAccountName: {{ default (printf "%s-%s-sa" .Release.Name .Chart.Name | trunc 63 | trimSuffix "-") .Values.kerberoshub.serviceAccount.name }}
|
||||
{{- end }}
|
||||
@@ -43,6 +47,10 @@ spec:
|
||||
- name: pipe-counting
|
||||
image: "{{ .Values.global.imageRegistry }}{{ .Values.kerberospipeline.counting.repository }}:{{ .Values.kerberospipeline.counting.tag }}"
|
||||
imagePullPolicy: {{ .Values.kerberospipeline.counting.pullPolicy }}
|
||||
{{- with .Values.kerberospipeline.counting.securityContext }}
|
||||
securityContext:
|
||||
{{- toYaml . | nindent 10 }}
|
||||
{{- end }}
|
||||
{{- with .Values.kerberospipeline.counting.resources }}
|
||||
resources:
|
||||
{{- toYaml . | nindent 12 }}
|
||||
|
||||
@@ -24,6 +24,10 @@ spec:
|
||||
labels:
|
||||
app: pipe-dominantcolor
|
||||
spec:
|
||||
{{- with .Values.kerberospipeline.dominantColor.podSecurityContext }}
|
||||
securityContext:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- if .Values.kerberoshub.serviceAccount.create }}
|
||||
serviceAccountName: {{ default (printf "%s-%s-sa" .Release.Name .Chart.Name | trunc 63 | trimSuffix "-") .Values.kerberoshub.serviceAccount.name }}
|
||||
{{- end }}
|
||||
@@ -43,6 +47,10 @@ spec:
|
||||
- name: pipe-dominantcolor
|
||||
image: "{{ .Values.global.imageRegistry }}{{ .Values.kerberospipeline.dominantColor.repository }}:{{ .Values.kerberospipeline.dominantColor.tag }}"
|
||||
imagePullPolicy: {{ .Values.kerberospipeline.dominantColor.pullPolicy }}
|
||||
{{- with .Values.kerberospipeline.dominantColor.securityContext }}
|
||||
securityContext:
|
||||
{{- toYaml . | nindent 10 }}
|
||||
{{- end }}
|
||||
{{- with .Values.kerberospipeline.dominantColor.resources }}
|
||||
resources:
|
||||
{{- toYaml . | nindent 12 }}
|
||||
|
||||
@@ -26,6 +26,10 @@ spec:
|
||||
labels:
|
||||
app: pipe-notify
|
||||
spec:
|
||||
{{- with .Values.kerberospipeline.notify.podSecurityContext }}
|
||||
securityContext:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- if .Values.kerberoshub.serviceAccount.create }}
|
||||
serviceAccountName: {{ default (printf "%s-%s-sa" .Release.Name .Chart.Name | trunc 63 | trimSuffix "-") .Values.kerberoshub.serviceAccount.name }}
|
||||
{{- end }}
|
||||
@@ -51,6 +55,10 @@ spec:
|
||||
- name: pipe-notify
|
||||
image: "{{ .Values.global.imageRegistry }}{{ .Values.kerberospipeline.notify.repository }}:{{ .Values.kerberospipeline.notify.tag }}"
|
||||
imagePullPolicy: {{ .Values.kerberospipeline.notify.pullPolicy }}
|
||||
{{- with .Values.kerberospipeline.notify.securityContext }}
|
||||
securityContext:
|
||||
{{- toYaml . | nindent 10 }}
|
||||
{{- end }}
|
||||
{{- with .Values.kerberospipeline.notify.resources }}
|
||||
resources:
|
||||
{{- toYaml . | nindent 12 }}
|
||||
|
||||
@@ -69,6 +69,8 @@ spec:
|
||||
name: mongodb-config
|
||||
env:
|
||||
{{- include "hub.mongodb.appUriEnv" (dict "root" $ "app" "hub-pipeline-sequence") | nindent 8 }}
|
||||
- name: LOG_LEVEL
|
||||
value: "{{ .Values.kerberospipeline.sequence.logLevel }}"
|
||||
- name: CLOUD_PROVIDER
|
||||
value: "{{ .Values.cloudProvider }}"
|
||||
- name: QUEUE_SYSTEM
|
||||
|
||||
@@ -26,6 +26,10 @@ spec:
|
||||
labels:
|
||||
app: pipe-throttler
|
||||
spec:
|
||||
{{- with .Values.kerberospipeline.throttler.podSecurityContext }}
|
||||
securityContext:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- if .Values.kerberoshub.serviceAccount.create }}
|
||||
serviceAccountName: {{ default (printf "%s-%s-sa" .Release.Name .Chart.Name | trunc 63 | trimSuffix "-") .Values.kerberoshub.serviceAccount.name }}
|
||||
{{- end }}
|
||||
@@ -51,6 +55,10 @@ spec:
|
||||
- name: pipe-throttler
|
||||
image: "{{ .Values.global.imageRegistry }}{{ .Values.kerberospipeline.throttler.repository }}:{{ .Values.kerberospipeline.throttler.tag }}"
|
||||
imagePullPolicy: {{ .Values.kerberospipeline.throttler.pullPolicy }}
|
||||
{{- with .Values.kerberospipeline.throttler.securityContext }}
|
||||
securityContext:
|
||||
{{- toYaml . | nindent 10 }}
|
||||
{{- end }}
|
||||
{{- with .Values.kerberospipeline.throttler.resources }}
|
||||
resources:
|
||||
{{- toYaml . | nindent 12 }}
|
||||
|
||||
@@ -24,6 +24,10 @@ spec:
|
||||
labels:
|
||||
app: pipe-thumbnail
|
||||
spec:
|
||||
{{- with .Values.kerberospipeline.thumbnail.podSecurityContext }}
|
||||
securityContext:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- if .Values.kerberoshub.serviceAccount.create }}
|
||||
serviceAccountName: {{ default (printf "%s-%s-sa" .Release.Name .Chart.Name | trunc 63 | trimSuffix "-") .Values.kerberoshub.serviceAccount.name }}
|
||||
{{- end }}
|
||||
@@ -43,6 +47,10 @@ spec:
|
||||
- name: pipe-thumbnail
|
||||
image: "{{ .Values.global.imageRegistry }}{{ .Values.kerberospipeline.thumbnail.repository }}:{{ .Values.kerberospipeline.thumbnail.tag }}"
|
||||
imagePullPolicy: {{ .Values.kerberospipeline.thumbnail.pullPolicy }}
|
||||
{{- with .Values.kerberospipeline.thumbnail.securityContext }}
|
||||
securityContext:
|
||||
{{- toYaml . | nindent 10 }}
|
||||
{{- end }}
|
||||
{{- with .Values.kerberospipeline.thumbnail.resources }}
|
||||
resources:
|
||||
{{- toYaml . | nindent 12 }}
|
||||
@@ -99,6 +107,8 @@ spec:
|
||||
value: "{{ .Values.kerberospipeline.thumbnail.width }}"
|
||||
- name: THUMBNAIL_HEIGHT
|
||||
value: "{{ .Values.kerberospipeline.thumbnail.height }}"
|
||||
- name: THUMBNAIL_SKIP_NON_KEYFRAMES
|
||||
value: "{{ .Values.kerberospipeline.thumbnail.skipNonKeyframes }}"
|
||||
|
||||
# There is an option to store the thumbnails in a storage provider in Kerberos Vault.
|
||||
# This will lower storage in mongodb.
|
||||
|
||||
@@ -62,6 +62,38 @@
|
||||
"api": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"passkeys": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"enabled": {"type": "boolean"},
|
||||
"rpId": {
|
||||
"type": "string",
|
||||
"description": "Stable WebAuthn relying-party hostname, without scheme, port or path. Must match each frontend origin's hostname or be its parent domain."
|
||||
},
|
||||
"origins": {
|
||||
"type": "array",
|
||||
"uniqueItems": true,
|
||||
"items": {
|
||||
"type": "string",
|
||||
"pattern": "^https://[^/?#]+$|^http://localhost(:[0-9]+)?$"
|
||||
},
|
||||
"description": "Exact trusted frontend origins, with no path or trailing slash. HTTPS is required except for HTTP localhost development."
|
||||
},
|
||||
"rpName": {"type": "string"}
|
||||
},
|
||||
"if": {
|
||||
"required": ["enabled"],
|
||||
"properties": {"enabled": {"const": true}}
|
||||
},
|
||||
"then": {
|
||||
"required": ["rpId", "origins"],
|
||||
"properties": {
|
||||
"rpId": {"minLength": 1},
|
||||
"origins": {"minItems": 1}
|
||||
}
|
||||
}
|
||||
},
|
||||
"corsAllowedOrigins": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
|
||||
+62
-21
@@ -209,28 +209,27 @@ opentelemetry:
|
||||
# the collector terminates TLS. A scheme-less value defaults to TLS and
|
||||
# fails against a plaintext collector ("server gave HTTP response to HTTPS client").
|
||||
endpoint: "http://otel-collector:4318"
|
||||
# Trace sampling, passed to the services as OTEL_TRACES_SAMPLER and
|
||||
# OTEL_TRACES_SAMPLER_ARG. Leave empty to keep the SDK default, which records
|
||||
# every trace.
|
||||
# Supported types: always_on, always_off, traceidratio, parentbased_always_on,
|
||||
# parentbased_always_off, parentbased_traceidratio.
|
||||
# Trace sampling is decided where a trace starts:
|
||||
# - Uploads start at the Vault, whose deployment sets their rate
|
||||
# (TRACE_SAMPLER_RATIO_UPLOAD / TRACE_SAMPLER_RATIO_OTHER).
|
||||
# - hub-api starts its own traces; apiSamplingRatios below sets its rates.
|
||||
# hub-workflows, the pipe-* services and the workflow stages only continue
|
||||
# traces started elsewhere: they follow that decision and never start traces
|
||||
# of their own (OTEL_TRACES_SAMPLER=parentbased_always_off, not configurable).
|
||||
#
|
||||
# sampler applies to the services downstream of the Vault: hub-workflows, the
|
||||
# pipe-* services and the workflow stages. They continue the trace the Vault
|
||||
# starts for an upload, so head sampling belongs at the Vault
|
||||
# (parentbased_traceidratio on the Vault deployment) and these follow its
|
||||
# decision without starting traces of their own:
|
||||
# type: "parentbased_always_off"
|
||||
sampler:
|
||||
type: ""
|
||||
arg: ""
|
||||
# apiSampler applies to hub-api, which starts its own traces for user
|
||||
# requests. Example: keep ~10% of them:
|
||||
# type: "parentbased_traceidratio"
|
||||
# arg: "0.1"
|
||||
apiSampler:
|
||||
type: ""
|
||||
arg: ""
|
||||
# apiSamplingRatios: hub-api's sampling ratio (0 to 1) per kind of work,
|
||||
# rendered as TRACE_SAMPLER_RATIO_<CLASS> (hub-api on trace library v1.2.6+).
|
||||
# Empty values are not rendered; with none set, hub-api records every trace.
|
||||
# operator: requests from users and API clients (UI, API tokens).
|
||||
# other: everything else hub-api starts: device heartbeats, agent uploads
|
||||
# and live segments, background work, queries outside a request.
|
||||
# Unset with operator set: records everything.
|
||||
# Example: ~10% of user requests, 1% of everything else:
|
||||
# operator: "0.1"
|
||||
# other: "0.01"
|
||||
apiSamplingRatios:
|
||||
operator: ""
|
||||
other: ""
|
||||
############################################
|
||||
# OpenAI configuration (semantic search)
|
||||
#
|
||||
@@ -269,6 +268,11 @@ kerberosvault:
|
||||
###########################################################################
|
||||
admin:
|
||||
repository: uugai/admin
|
||||
# Opt in to 8080 and a securityContext only with a compatible hardened image.
|
||||
containerPort: 80
|
||||
securityContext: {}
|
||||
readinessProbe: {}
|
||||
livenessProbe: {}
|
||||
pullPolicy: IfNotPresent
|
||||
tag: "v1.3.0"
|
||||
replicas: 2
|
||||
@@ -393,6 +397,13 @@ kerberoshub:
|
||||
|
||||
# MFA issuer name
|
||||
mfaIssuer: "yourdomain.com"
|
||||
# WebAuthn passkeys for users and sub-users; requires compatible API/frontend images.
|
||||
# Use the public frontend hostname/origins, not the API URL. Keep rpId stable.
|
||||
passkeys:
|
||||
enabled: false
|
||||
rpId: "" # e.g. "hub.example.com", without scheme, port or path.
|
||||
origins: [] # e.g. ["https://hub.example.com"]; exact origins, no trailing slash.
|
||||
rpName: "Kerberos Hub"
|
||||
# Admin API's are made available for automation of Kerberos Hub.
|
||||
# To access those API's (e.g. creation of owner users), an API key needs to be provided.
|
||||
apiKey: "a-random-admin-api-key"
|
||||
@@ -468,6 +479,9 @@ kerberoshub:
|
||||
pullPolicy: IfNotPresent
|
||||
tag: "v1.13.9"
|
||||
replicas: 2
|
||||
# Hardened frontend images listen on 8080; retain 80 for existing images.
|
||||
containerPort: 80
|
||||
securityContext: {}
|
||||
topologySpreadConstraints: [] # Optional pod topology spread constraints (empty = none).
|
||||
# Set to false to skip rendering the hub-frontend Service (e.g. when an
|
||||
# external/shared Service is managed elsewhere).
|
||||
@@ -745,6 +759,9 @@ kerberoshub:
|
||||
organization: "github-organization"
|
||||
team: "github-team"
|
||||
cleanup:
|
||||
# Explicit opt-in for a selected hardened image; legacy defaults are unchanged.
|
||||
securityContext: {}
|
||||
podSecurityContext: {}
|
||||
repository: ghcr.io/uug-ai/hub-cleanup
|
||||
pullPolicy: IfNotPresent
|
||||
tag: "v1.4.19"
|
||||
@@ -958,6 +975,8 @@ kerberoshub:
|
||||
# `enabled` here.
|
||||
workflows:
|
||||
repository: ghcr.io/uug-ai/hub-workflows
|
||||
securityContext: {}
|
||||
podSecurityContext: {}
|
||||
pullPolicy: IfNotPresent
|
||||
tag: "v1.0.0"
|
||||
replicas: 1 # Number of pods for the service.
|
||||
@@ -1066,12 +1085,18 @@ kerberoshub:
|
||||
# memory: 128Mi
|
||||
# cpu: 100m
|
||||
monitordevice:
|
||||
securityContext: {}
|
||||
podSecurityContext: {}
|
||||
repository: ghcr.io/uug-ai/hub-monitor-device
|
||||
pullPolicy: IfNotPresent
|
||||
tag: "v1.4.2"
|
||||
replicas: 1 # Number of pods for the service.
|
||||
topologySpreadConstraints: [] # Optional pod topology spread constraints (empty = none).
|
||||
logLevel: "info" # possible values: trace, debug, info, warn, error
|
||||
# Seconds between device status scans. Sets how quickly a camera going
|
||||
# offline/online is detected and, with "Group notifications" on, how long
|
||||
# notifications are held to collect other cameras that change at the same time.
|
||||
scanIntervalSeconds: 60
|
||||
resources:
|
||||
requests:
|
||||
memory: 10Mi
|
||||
@@ -1203,11 +1228,14 @@ kerberospipeline:
|
||||
#volumeMounts:
|
||||
# - name: extra
|
||||
# mountPath: /data
|
||||
logLevel: "info" # possible values: debug, info, warn, error
|
||||
resources:
|
||||
requests:
|
||||
memory: 10Mi
|
||||
cpu: 10m
|
||||
throttler:
|
||||
securityContext: {}
|
||||
podSecurityContext: {}
|
||||
repository: uugai/hub-pipeline-throttler
|
||||
deploymentAnnotations: {}
|
||||
# deploymentAnnotations:
|
||||
@@ -1229,6 +1257,8 @@ kerberospipeline:
|
||||
memory: 10Mi
|
||||
cpu: 10m
|
||||
notify:
|
||||
securityContext: {}
|
||||
podSecurityContext: {}
|
||||
repository: ghcr.io/uug-ai/hub-pipeline-notification
|
||||
deploymentAnnotations: {}
|
||||
# deploymentAnnotations:
|
||||
@@ -1304,6 +1334,8 @@ kerberospipeline:
|
||||
memory: 10Mi
|
||||
cpu: 10m
|
||||
dominantColor:
|
||||
securityContext: {}
|
||||
podSecurityContext: {}
|
||||
# Opt-in: deploys pipe-dominantcolor and tells the analysis stage to schedule
|
||||
# it (DOMINANTCOLOR_ENABLED). Requires an analysis image that reads the flag;
|
||||
# older images always publish to the dominantcolor queue.
|
||||
@@ -1332,6 +1364,8 @@ kerberospipeline:
|
||||
memory: 2Gi
|
||||
cpu: 1000m
|
||||
thumbnail:
|
||||
securityContext: {}
|
||||
podSecurityContext: {}
|
||||
repository: ghcr.io/uug-ai/hub-pipeline-thumbnail
|
||||
deploymentAnnotations: {}
|
||||
# deploymentAnnotations:
|
||||
@@ -1352,6 +1386,7 @@ kerberospipeline:
|
||||
quality: "1" # 1 (best) - 31 (worst)
|
||||
width: "600"
|
||||
height: "-1"
|
||||
skipNonKeyframes: true # Decode keyframes only for faster extraction; disable when exact-frame output is required.
|
||||
kerberosvault:
|
||||
enabled: true # If you want to use Kerberos Vault to store the thumbnails
|
||||
resources:
|
||||
@@ -1362,6 +1397,8 @@ kerberospipeline:
|
||||
memory: 2Gi
|
||||
cpu: 1000m
|
||||
counting:
|
||||
securityContext: {}
|
||||
podSecurityContext: {}
|
||||
repository: uugai/hub-pipeline-counting
|
||||
deploymentAnnotations: {}
|
||||
# deploymentAnnotations:
|
||||
@@ -1469,5 +1506,9 @@ email:
|
||||
disabled: "disable"
|
||||
highupload: "highupload"
|
||||
device: "device"
|
||||
# Sent instead of "device" when several cameras of an account change status
|
||||
# in the same scan and the account groups device notifications.
|
||||
deviceGroup: "device_group"
|
||||
alertTitle: "[Alert] Kerberos Hub detected something an event"
|
||||
deviceTitle: "[Device] A Kerberos Agent's status has been changed"
|
||||
deviceGroupTitle: "[Device] Several cameras changed status"
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
CHART_DIR="${1:-charts/hub}"
|
||||
|
||||
resource() {
|
||||
local source="$1"
|
||||
awk -v source="# Source: hub/templates/admin/$source.yaml" '
|
||||
/^---/ { selected=0 }
|
||||
/^# Source:/ { selected=($0 == source); next }
|
||||
selected { print }
|
||||
'
|
||||
}
|
||||
|
||||
assert_contains() {
|
||||
local rendered="$1" pattern="$2"
|
||||
if ! grep -Eq -- "$pattern" <<< "$rendered"; then
|
||||
printf 'Missing expected Admin setting: %s\n' "$pattern" >&2
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
assert_absent() {
|
||||
local rendered="$1" pattern="$2"
|
||||
if grep -Eq -- "$pattern" <<< "$rendered"; then
|
||||
printf 'Unexpected Admin setting: %s\n' "$pattern" >&2
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
HARDENED_FLAGS=(
|
||||
--set admin.containerPort=8080
|
||||
--set-json 'admin.securityContext={"runAsNonRoot":true,"runAsUser":65532,"runAsGroup":65532,"readOnlyRootFilesystem":true,"allowPrivilegeEscalation":false,"capabilities":{"drop":["ALL"]},"seccompProfile":{"type":"RuntimeDefault"}}'
|
||||
--set-json 'admin.volumes=[{"name":"admin-tmp","emptyDir":{"medium":"Memory","sizeLimit":"16Mi"}}]'
|
||||
--set-json 'admin.volumeMounts=[{"name":"admin-tmp","mountPath":"/tmp"}]'
|
||||
--set-json 'admin.readinessProbe={"httpGet":{"path":"/healthz","port":"http"},"periodSeconds":10}'
|
||||
--set-json 'admin.livenessProbe={"httpGet":{"path":"/healthz","port":"http"},"initialDelaySeconds":10,"periodSeconds":10}'
|
||||
)
|
||||
|
||||
for mode in all ui; do
|
||||
default_render="$(helm template hub "$CHART_DIR" --set "mode=$mode" --set admin.oauth2Proxy.enabled=true)"
|
||||
default_deployment="$(resource deployment <<< "$default_render")"
|
||||
default_service="$(resource service <<< "$default_render")"
|
||||
default_ingress="$(resource ingress <<< "$default_render")"
|
||||
default_oauth="$(resource oauth2-proxy <<< "$default_render")"
|
||||
assert_contains "$default_deployment" 'containerPort: 80$'
|
||||
assert_absent "$default_deployment" 'securityContext:|readinessProbe:|livenessProbe:'
|
||||
assert_contains "$default_service" 'port: 80$'
|
||||
assert_contains "$default_service" 'targetPort: http$'
|
||||
|
||||
hardened_render="$(helm template hub "$CHART_DIR" --set "mode=$mode" --set admin.oauth2Proxy.enabled=true "${HARDENED_FLAGS[@]}")"
|
||||
hardened_deployment="$(resource deployment <<< "$hardened_render")"
|
||||
assert_contains "$hardened_deployment" 'containerPort: 8080$'
|
||||
assert_absent "$hardened_deployment" 'containerPort: 80$'
|
||||
for setting in 'runAsNonRoot: true' 'runAsUser: 65532' 'runAsGroup: 65532' \
|
||||
'readOnlyRootFilesystem: true' 'allowPrivilegeEscalation: false' \
|
||||
'type: RuntimeDefault' 'drop:' '- ALL' 'medium: Memory' \
|
||||
'sizeLimit: 16Mi' 'mountPath: /tmp' 'name: admin-tmp'; do
|
||||
assert_contains "$hardened_deployment" "^[[:space:]]*(- )?$setting$"
|
||||
done
|
||||
for probe in readinessProbe livenessProbe; do
|
||||
probe_render="$(awk -v probe="$probe:" '
|
||||
/^ [a-zA-Z]/ { selected=($1 == probe) }
|
||||
selected { print }
|
||||
' <<< "$hardened_deployment")"
|
||||
assert_contains "$probe_render" 'path: /healthz$'
|
||||
assert_contains "$probe_render" 'port: http$'
|
||||
done
|
||||
|
||||
[[ "$(resource service <<< "$hardened_render")" == "$default_service" ]]
|
||||
[[ "$(resource ingress <<< "$hardened_render")" == "$default_ingress" ]]
|
||||
[[ "$(resource oauth2-proxy <<< "$hardened_render")" == "$default_oauth" ]]
|
||||
assert_contains "$default_ingress" 'nginx.ingress.kubernetes.io/auth-url:'
|
||||
assert_contains "$default_ingress" 'number: 80$'
|
||||
|
||||
echo "OK ($mode): legacy defaults; hardened runtime/probes; stable Service, ingress and OAuth2"
|
||||
done
|
||||
|
||||
pipeline_render="$(helm template hub "$CHART_DIR" --set mode=pipeline "${HARDENED_FLAGS[@]}")"
|
||||
assert_absent "$pipeline_render" '^# Source: hub/templates/admin/'
|
||||
|
||||
echo "All Admin runtime checks passed."
|
||||
@@ -0,0 +1,185 @@
|
||||
#!/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
|
||||
}
|
||||
|
||||
for mode in all pipeline ui; do
|
||||
defaults="$(helm template hub "$CHART_DIR" --set "mode=$mode")"
|
||||
if grep -q 'securityContext:' <<< "$defaults"; then
|
||||
echo "Default $mode mode unexpectedly enables 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[@]}"
|
||||
[[ "$(grep -c 'runAsUser: 65532' <<< "$hardened")" == "$expected" ]]
|
||||
[[ "$(grep -c 'fsGroup: 65532' <<< "$hardened")" == "$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."
|
||||
|
||||
admin_flags=(
|
||||
--set admin.containerPort=8080
|
||||
--set admin.securityContext.runAsNonRoot=true
|
||||
--set admin.securityContext.runAsUser=65532
|
||||
--set admin.securityContext.runAsGroup=65532
|
||||
--set admin.securityContext.readOnlyRootFilesystem=true
|
||||
--set admin.securityContext.allowPrivilegeEscalation=false
|
||||
--set admin.securityContext.capabilities.drop[0]=ALL
|
||||
--set admin.securityContext.seccompProfile.type=RuntimeDefault
|
||||
--set admin.volumes[0].name=admin-tmp
|
||||
--set admin.volumes[0].emptyDir.medium=Memory
|
||||
--set admin.volumes[0].emptyDir.sizeLimit=16Mi
|
||||
--set admin.volumeMounts[0].name=admin-tmp
|
||||
--set admin.volumeMounts[0].mountPath=/tmp
|
||||
--set admin.oauth2Proxy.enabled=true
|
||||
)
|
||||
for mode in all ui pipeline; do
|
||||
legacy="$(helm template hub "$CHART_DIR" --set "mode=$mode" --set admin.oauth2Proxy.enabled=true)"
|
||||
configured="$(helm template hub "$CHART_DIR" --set "mode=$mode" "${admin_flags[@]}")"
|
||||
helm lint "$CHART_DIR" --quiet --set "mode=$mode" "${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")" ]]
|
||||
[[ "$(grep 'image:' <<< "$legacy")" == "$(grep 'image:' <<< "$configured")" ]]
|
||||
# No other workload inherits Admin's container port or security settings.
|
||||
[[ "$(grep -c 'runAsUser: 65532' <<< "$configured")" == 1 ]]
|
||||
echo "OK ($mode): Admin opt-in port/security/tmpfs; legacy image, Service port, ingress and OAuth2-proxy preserved"
|
||||
done
|
||||
echo "All hardened Admin checks passed."
|
||||
Executable
+98
@@ -0,0 +1,98 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
CHART_DIR="${1:-charts/hub}"
|
||||
|
||||
extract_passkeys() {
|
||||
awk '
|
||||
/^---/ { deployment=0; name=""; variable="" }
|
||||
/^kind: Deployment$/ { deployment=1 }
|
||||
deployment && /^ name:/ { name=$2 }
|
||||
deployment && /- name: PASSKEY_/ { variable=$3; next }
|
||||
variable != "" && /value:/ {
|
||||
value=$0
|
||||
sub(/^[[:space:]]*value: /, "", value)
|
||||
print name, variable, value
|
||||
variable=""
|
||||
}
|
||||
' | sort
|
||||
}
|
||||
|
||||
assert_passkeys() {
|
||||
local rendered="$1" expected="$2" actual count expected_count
|
||||
actual="$(printf '%s\n' "$rendered" | extract_passkeys)"
|
||||
if [[ "$actual" != "$expected" ]]; then
|
||||
printf 'Unexpected passkey environment:\n%s\nExpected:\n%s\n' "$actual" "$expected" >&2
|
||||
return 1
|
||||
fi
|
||||
count="$(printf '%s\n' "$rendered" | awk '/- name: PASSKEY_/ { count++ } END { print count+0 }')"
|
||||
expected_count="$(printf '%s\n' "$expected" | awk 'NF { count++ } END { print count+0 }')"
|
||||
if [[ "$count" != "$expected_count" ]]; then
|
||||
echo "Passkey variables leaked outside the expected Deployment or were duplicated" >&2
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
CONFIGURED_FLAGS=(
|
||||
--set-json kerberoshub.api.passkeys.enabled=true
|
||||
--set-json 'kerberoshub.api.passkeys.rpId="example.com"'
|
||||
--set-json 'kerberoshub.api.passkeys.origins=["https://hub.example.com","https://legacy.example.com:8443"]'
|
||||
--set-json 'kerberoshub.api.passkeys.rpName="Example Hub"'
|
||||
)
|
||||
expected="$(printf '%s\n' \
|
||||
'hub-api PASSKEY_ORIGINS "https://hub.example.com,https://legacy.example.com:8443"' \
|
||||
'hub-api PASSKEY_RP_ID "example.com"' \
|
||||
'hub-api PASSKEY_RP_NAME "Example Hub"' | sort)"
|
||||
|
||||
for mode in all ui pipeline; do
|
||||
default_render="$(helm template hub "$CHART_DIR" --set "mode=$mode")"
|
||||
assert_passkeys "$default_render" ""
|
||||
configured_render="$(helm template hub "$CHART_DIR" --set "mode=$mode" "${CONFIGURED_FLAGS[@]}")"
|
||||
if [[ "$mode" == pipeline ]]; then
|
||||
assert_passkeys "$configured_render" ""
|
||||
else
|
||||
assert_passkeys "$configured_render" "$expected"
|
||||
fi
|
||||
echo "OK ($mode): defaults opt out; enabled settings affect Hub API only"
|
||||
done
|
||||
|
||||
disabled_render="$(helm template hub "$CHART_DIR" "${CONFIGURED_FLAGS[@]}" --set kerberoshub.api.passkeys.enabled=false)"
|
||||
assert_passkeys "$disabled_render" ""
|
||||
|
||||
localhost_render="$(helm template hub "$CHART_DIR" --set mode=ui \
|
||||
--set kerberoshub.api.passkeys.enabled=true \
|
||||
--set-string kerberoshub.api.passkeys.rpId=localhost \
|
||||
--set-json 'kerberoshub.api.passkeys.origins=["http://localhost:4200"]')"
|
||||
assert_passkeys "$localhost_render" "$(printf '%s\n' \
|
||||
'hub-api PASSKEY_ORIGINS "http://localhost:4200"' \
|
||||
'hub-api PASSKEY_RP_ID "localhost"' \
|
||||
'hub-api PASSKEY_RP_NAME "Kerberos Hub"' | sort)"
|
||||
|
||||
legacy_render="$(helm template hub "$CHART_DIR" --set mode=ui \
|
||||
--set-json 'kerberoshub.extraEnv=[{"name":"PASSKEY_RP_ID","value":"example.com"}]')"
|
||||
printf '%s\n' "$legacy_render" | grep -q -- '- name: PASSKEY_RP_ID'
|
||||
|
||||
expect_invalid() {
|
||||
local output
|
||||
if output="$(helm template hub "$CHART_DIR" "${CONFIGURED_FLAGS[@]}" "$@" 2>&1)"; then
|
||||
echo "Invalid passkey settings unexpectedly rendered: $*" >&2
|
||||
return 1
|
||||
fi
|
||||
if [[ "$output" != *"passkeys"* ]]; then
|
||||
printf 'Rendering failed for an unexpected reason:\n%s\n' "$output" >&2
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
expect_invalid --set-string kerberoshub.api.passkeys.rpId=
|
||||
expect_invalid --set-json 'kerberoshub.api.passkeys.origins=[]'
|
||||
expect_invalid --set-json 'kerberoshub.api.passkeys.origins=["https://hub.example.com/"]'
|
||||
expect_invalid --set-json 'kerberoshub.api.passkeys.origins=["http://hub.example.com"]'
|
||||
expect_invalid --set-json 'kerberoshub.api.passkeys.origins=["https://hub.example.com","https://hub.example.com"]'
|
||||
expect_invalid --set-json 'kerberoshub.api.passkeys.origins="https://hub.example.com"'
|
||||
expect_invalid --set-json 'kerberoshub.api.passkeys.enabled="true"'
|
||||
expect_invalid --set-json 'kerberoshub.api.passkeys.rpId=123'
|
||||
expect_invalid --set-json 'kerberoshub.api.passkeys.rpName=123'
|
||||
expect_invalid --set-string kerberoshub.api.passkeys.origin=https://hub.example.com
|
||||
|
||||
echo "All Hub passkey configuration checks passed."
|
||||
Reference in New Issue
Block a user