mirror of
https://github.com/kerberos-io/helm-charts.git
synced 2026-11-08 20:50:22 +00:00
Compare commits
10
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ca6986f512 | ||
|
|
96d9f51238 | ||
|
|
8b96a8c7b8 | ||
|
|
2e018357d8 | ||
|
|
0b9d07339f | ||
|
|
78cf3c7e2b | ||
|
|
f0b79bea0c | ||
|
|
535ddc847d | ||
|
|
1c1593a8a0 | ||
|
|
5dfaaf0363 |
@@ -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.170.1
|
||||
version: 0.177.0
|
||||
|
||||
# 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
|
||||
|
||||
+85
-1
@@ -335,6 +335,34 @@ 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.
|
||||
|
||||
### MCP
|
||||
|
||||
Deploy a Hub API image supporting MCP, then enable the read-only Streamable HTTP
|
||||
endpoint at `/mcp`:
|
||||
|
||||
```yaml
|
||||
kerberoshub:
|
||||
api:
|
||||
mcp:
|
||||
enabled: true
|
||||
allowedOrigins: []
|
||||
```
|
||||
|
||||
After upgrading the Helm release, an unauthenticated request to `/mcp` should
|
||||
return `401` instead of `404`. Connect using HTTPS and an existing Hub access
|
||||
token in `Authorization: Bearer <token>`; browser-session JWTs and cookies cannot
|
||||
authenticate MCP. Recording tools require `media.read`, device tools require
|
||||
`devices.read`, and both require effective resource grants.
|
||||
|
||||
VS Code and other clients without an `Origin` header need no `allowedOrigins`.
|
||||
For browser MCP clients, list exact origins such as `https://agent.example.com`.
|
||||
Wildcards and trailing slashes are not supported. These origins are independent
|
||||
of REST CORS; frontend and REST origins are not automatically copied into MCP.
|
||||
MCP stays disabled by default, and these settings apply only to Hub API pods
|
||||
in `all` and `ui` modes. The chart does not change the API image tag.
|
||||
|
||||
Validate rendering with `bash scripts/check-hub-mcp.sh` from the repository root.
|
||||
|
||||
### Passkeys
|
||||
|
||||
Users and sub-users can create and manage their own passkeys in **Profile >
|
||||
@@ -697,6 +725,8 @@ kerberospipeline:
|
||||
| `kerberoshub.api.schema` | The protocol to serve the Kerberos Hub API, `'http'` or `'https'`. | `"https"` |
|
||||
| `kerberoshub.api.url` | The Kerberos Hub API ingress to access the API. | `"api.yourdomain.com"` |
|
||||
| `kerberoshub.api.corsAllowedOrigins` | Additional complete HTTP(S) origins allowed to call the Hub API. Configured frontend origins remain allowed automatically. | `[]` |
|
||||
| `kerberoshub.api.mcp.enabled` | Expose the read-only `/mcp` endpoint; sets `MCP_ENABLED`. Requires a compatible Hub API image. | `false` |
|
||||
| `kerberoshub.api.mcp.allowedOrigins` | Exact HTTP(S) origins for browser MCP clients, joined into `MCP_ALLOWED_ORIGINS`. Independent of REST CORS; no wildcards or trailing slash. | `[]` |
|
||||
| `kerberoshub.api.disableAgentHeartbeat` | Temporarily stop serving the polled `GET /devices` list (hub-api returns `204` without database reads). Devices still load once through `/session/bootstrap`; online/offline status stops refreshing. | `false` |
|
||||
| `kerberoshub.api.mediaProxy.enabled` | Serve media through hub-api (`/media/proxy`) instead of handing out Vault/storage URLs, hiding the storage endpoint from clients. | `false` |
|
||||
| `kerberoshub.api.mediaProxy.publicUrl` | Public hub-api base URL used in proxied media URLs. Defaults to `{schema}://{url}`. | `""` |
|
||||
@@ -792,6 +822,7 @@ kerberospipeline:
|
||||
| `kerberoshub.frontend.features.organisations.switcherEnabled` | Enable or disable the organisation dropdown and switching. The current organisation remains visible when disabled. | `"false"` |
|
||||
| `kerberoshub.frontend.features.organisations.creationEnabled` | Enable or disable organisation creation. Requires organisation switching to be enabled. | `"false"` |
|
||||
| `kerberoshub.frontend.features.organisations.settingsEnabled` | Enable or disable the organisation identity link to organisation settings. | `"false"` |
|
||||
| `kerberoshub.frontend.features.organisations.membersAccessPreviewEnabled` | Show the "Users (preview)" tab in organisation settings to organisation owners: a mockup of project, role and resource assignments with mocked users and data, nothing saved. Sets `FEATURE_MEMBERS_ACCESS_PREVIEW_ENABLED`. | `"false"` |
|
||||
| `kerberoshub.frontend.features.projects.enabled` | Fallback project group switch used when the organisations umbrella is unset. | `""` |
|
||||
| `kerberoshub.frontend.features.projects.switcherEnabled` | Enable or disable the read-only project dropdown. | `"false"` |
|
||||
| `kerberoshub.frontend.features.projects.creationEnabled` | Reserved for the project creation UI. | `"false"` |
|
||||
@@ -901,7 +932,26 @@ kerberospipeline:
|
||||
| `kerberoshub.audit.dispatchInterval` | How often the mounted destination configuration is reloaded and eligible destinations are polled. | `"5s"` |
|
||||
| `kerberoshub.audit.leaseDuration` | Per-destination lease duration. It must exceed every destination timeout by at least 30 seconds. | `"2m"` |
|
||||
| `kerberoshub.audit.terminationGracePeriodSeconds` | Pod termination grace period. Keep this longer than `leaseDuration` so an in-flight cycle can finish. | `150` |
|
||||
| `kerberoshub.audit.destinations` | Audit destinations. Every entry supports `id`, `type` (`webhook` by default, or `s3`), `enabled`, delivery limits and filters. Webhooks add `url`, public `headers`, `bearerTokenSecret` and arbitrary `secretHeaders`. S3 destinations add `s3.bucket`, `s3.region`, optional `s3.prefix`, `s3.endpoint` and `s3.forcePathStyle` for S3-compatible stores, and an optional `credentialsSecret` (`name`, `accessKeyIdKey`, `secretAccessKeyKey`); without it the pod's AWS identity is used. The bucket must have Object Lock with a `COMPLIANCE` default retention unless `s3.allowUnlockedBucket` is `true` (stores without Object Lock such as Garage; the archive is then deletable). Every field, the delivery semantics and per-provider S3 settings are documented at https://learn.uug.ai/docs/hub/audit-events/#export-audit-events. Destination IDs retain independent checkpoints. | `[]` |
|
||||
| `kerberoshub.audit.destinations` | Audit destinations: webhooks that receive batches as JSON, or S3 buckets that archive them. Every entry is validated when the chart renders. Credentials (`bearerToken`, `secretHeaders.<name>.value`, `s3.accessKey` / `s3.secretKey`) are set inline; the chart stores them in Secret `hub-audit-<id>-credentials`, and changes take effect within a few minutes without a restart. Delivery semantics and per-provider S3 settings: https://learn.uug.ai/docs/hub/audit-events/#export-audit-events. | `[]` |
|
||||
| `kerberoshub.audit.destinations[].id` | Required. Unique name, `^[a-z][a-z0-9-]{0,62}$`. The delivery checkpoint is stored under it, so renaming starts a new destination. | |
|
||||
| `kerberoshub.audit.destinations[].type` | Optional. `webhook` or `s3`. | `webhook` |
|
||||
| `kerberoshub.audit.destinations[].enabled` | Optional. `false` pauses delivery and keeps the checkpoint. | `true` |
|
||||
| `kerberoshub.audit.destinations[].timeoutSeconds` | Optional. Timeout for one delivery, at most 60. | `10` |
|
||||
| `kerberoshub.audit.destinations[].batchSize` | Optional. Events per batch, at most 1000. | `100` |
|
||||
| `kerberoshub.audit.destinations[].maxAttempts` | Optional. Attempts before a batch is dead-lettered. Keep it high for S3 archives. | `10` |
|
||||
| `kerberoshub.audit.destinations[].startAt` | Optional. `latest` or `beginning`; only applies to a new `id`. | `latest` |
|
||||
| `kerberoshub.audit.destinations[].filters` | Optional. Lists `organisationIds`, `projectIds` (ObjectIds), `actions`, `categories` and `outcomes` (`success`, `failure`). An event must match every list that is set. | all events |
|
||||
| `kerberoshub.audit.destinations[].url` | Required for webhooks. `http` or `https` endpoint that receives each batch. | |
|
||||
| `kerberoshub.audit.destinations[].headers` | Optional, webhooks. Public request headers. `Authorization` and `Idempotency-Key` are reserved. | `{}` |
|
||||
| `kerberoshub.audit.destinations[].bearerToken` | Optional, webhooks. Sent as `Authorization: Bearer <token>`. | |
|
||||
| `kerberoshub.audit.destinations[].secretHeaders.<name>.value` | Optional, webhooks. Header with a secret value, such as an API key. | |
|
||||
| `kerberoshub.audit.destinations[].s3.bucket` | Required for S3. Bucket with versioning, Object Lock and a `COMPLIANCE` default retention. | |
|
||||
| `kerberoshub.audit.destinations[].s3.region` | Required for S3, also for S3-compatible stores. | |
|
||||
| `kerberoshub.audit.destinations[].s3.prefix` | Optional. Key prefix; objects are written as `<prefix>/<id>/yyyy/mm/dd/<firstId>-<lastId>.ndjson.gz`. | |
|
||||
| `kerberoshub.audit.destinations[].s3.endpoint` | Optional. Base URL of an S3-compatible store, without the bucket. Omit for AWS S3. | |
|
||||
| `kerberoshub.audit.destinations[].s3.forcePathStyle` | Optional. Path-style URLs, usually needed for Ceph RGW, MinIO and Garage. | `false` |
|
||||
| `kerberoshub.audit.destinations[].s3.allowUnlockedBucket` | Optional. Skips the Object Lock check for stores without Object Lock, such as Garage; archived batches are then deletable. | `false` |
|
||||
| `kerberoshub.audit.destinations[].s3.accessKey` / `kerberoshub.audit.destinations[].s3.secretKey` | Optional, both or neither, not empty. Without them the pod's AWS identity is used (AWS S3 only). | |
|
||||
| `kerberoshub.audit.serviceAccount.create` | Create a ServiceAccount used only by hub-audit, for example for S3 access through IRSA. | `false` |
|
||||
| `kerberoshub.audit.serviceAccount.name` | Name of the hub-audit ServiceAccount; with `create: false`, an existing ServiceAccount to use. Defaults to `hub-audit` when created; when empty and not created, `kerberoshub.serviceAccount` applies. | `""` |
|
||||
| `kerberoshub.audit.serviceAccount.annotations` | Annotations for the hub-audit ServiceAccount, such as `eks.amazonaws.com/role-arn`. | `{}` |
|
||||
@@ -911,6 +961,8 @@ kerberospipeline:
|
||||
| `kerberoshub.audit.volumes` | Additional pod volumes, for example a private webhook CA bundle. | `[]` |
|
||||
| `kerberoshub.audit.volumeMounts` | Additional audit service container volume mounts. | `[]` |
|
||||
| `kerberoshub.audit.extraEnv` | Additional audit service environment variables. | `[]` |
|
||||
| `kerberoshub.audit.resources` | Resource requests and limits for the audit service container. | `{}` |
|
||||
| `kerberoshub.audit.topologySpreadConstraints` | Topology spread constraints for audit service pods. | `[]` |
|
||||
| `kerberoshub.monitordevice.repository` | The monitoring microservice, following up the status of your cameras and Kerberos Agents. | `"ghcr.io/uug-ai/hub-monitor-device"` |
|
||||
| `kerberoshub.monitordevice.pullPolicy` | The Docker registry pull policy. | `"IfNotPresent"` |
|
||||
| `kerberoshub.monitordevice.tag` | The Docker image tag/version. | `"v1.4.0"` |
|
||||
@@ -1066,6 +1118,8 @@ kerberospipeline:
|
||||
| `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.templates.recording` | The template for recording check alerts (a continuously recording camera that is online but uploads no recordings, and again when it records). Empty uses `recording` with SMTP; for SendGrid or Mailgun set the template id, otherwise they use `device`. See *Camera recordings* below. | `""` |
|
||||
| `email.templates.recordingTitle` | Email subject for recording check alerts. Empty uses "[Device] A camera stopped recording", "[Device] A camera is recording again" and their forms for several cameras. | `""` |
|
||||
| `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. | `""` |
|
||||
@@ -1173,6 +1227,36 @@ Other cameras · Camera stream lost · 1 camera
|
||||
|
||||
**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.
|
||||
|
||||
#### Camera recordings: `recording`
|
||||
|
||||
Sent by `hub-monitor-device` for the **recording check** (Alerts → Devices → Recording check, stored as `notificationSettings.devices.recordingCheck` and `recordingCheckHours`). It alerts when a selected camera that records continuously is online but has not uploaded a recording for the account's period (1 to 72 hours, default 2), and again once it records. Cameras that record on motion only, cameras whose Agent does not report its recording mode, and offline cameras are not checked. The check uses the device alert channels, cameras and **Group notifications**: with grouping on, cameras that stop recording at the same time arrive in one email. The same template is used for one camera and for several.
|
||||
|
||||
The template name is `email.templates.recording` (default `recording`, so custom files are `recording.html` and `recording.txt`); the subject is `email.templates.recordingTitle`.
|
||||
|
||||
| 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}}` | For one camera, "`<camera>` is online, but hasn't uploaded a recording for 2 hours." (with "The account reached its upload limit, so new recordings are not stored." when it has) or "`<camera>` is recording again."; for several, "3 cameras stopped recording.", "2 cameras are recording again." or "3 cameras: 2 stopped recording, 1 recording again." |
|
||||
| `{{count}}` | Number of cameras in the email. |
|
||||
| `{{status}}` | `no_recordings` or `recording` when all cameras have the same change, otherwise `mixed`. |
|
||||
| `{{sections}}` | The cameras as HTML, as for `device_group`. The change labels are `No recordings` and `Recording again`, and the details `online, no recording for 3 hours` (plus `, upload limit reached`) and `recording again`. |
|
||||
| `{{sectionsText}}` | The same sections as plain text, for `recording.txt`. |
|
||||
| `{{link}}` | The cameras page, `<tenant URL>/devices` (see *Links* above). |
|
||||
| `{{eventdate}}`, `{{eventtime}}`, `{{eventdatetime}}` | When the email was sent, in the server's timezone (normally UTC). |
|
||||
|
||||
`{{sectionsText}}` example:
|
||||
|
||||
```text
|
||||
Site Gate · No recordings · all 2 cameras
|
||||
- Gate camera 1 (online, no recording for 2 hours)
|
||||
- Gate camera 2 (online, no recording for 2 hours)
|
||||
```
|
||||
|
||||
**Custom templates** for this email are read by `hub-monitor-device`; mount the `custom-email-templates` volume on `kerberoshub.monitordevice`. With SMTP, a deployment that mounts its own `device` template but no `recording` template keeps getting its `device` template (one email per camera), so a white-labelled email never falls back to the default layout. Add a `recording` template next to your `device` template to get the recording email. [`custom-layout/templates`](custom-layout/templates) has one to start from.
|
||||
|
||||
**Integrations.** Webhooks, IFTTT and the real-time feed get one message per camera with `data.status` set to `no_recordings` or `recording`, also with grouping on.
|
||||
|
||||
#### Account and sharing emails
|
||||
|
||||
Sent by the API (`kerberoshub.api`). The subject of each email is the matching `email.templates.<name>Title` value.
|
||||
|
||||
@@ -2,15 +2,15 @@ Kerberos.io
|
||||
------------
|
||||
|
||||
Hey, {{user}}
|
||||
Welcome to Kerberos Hub
|
||||
Activate your account ->
|
||||
|
||||
One of your Kerberos Agents changed
|
||||
{{text}}
|
||||
|
||||
Agents go offline due to a variety of reasons. The machine, node, micro controller on which your Agent runs, gets corrupted or disconnected from the internet. The camera itself is broken, damaged or in the worst case tampered. Have a look into your Kerberos Hub account for the latest recordings and/or verify the connection and status of your Kerberos Agent.
|
||||
|
||||
Go to Kerberos Hub ->
|
||||
{{link}}
|
||||
|
||||
Kerberos Hub in a nutshell
|
||||
With Kerberos Hub you can access your surveillance media remotely. By subscribing to a plan, you get access to a set of features, from advanced filtering, notifications to machine learning.
|
||||
|
||||
However before you get started this amazing applications, please activate your account.
|
||||
|
||||
Get in touch
|
||||
------------
|
||||
support@kerberos.io
|
||||
|
||||
@@ -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;">Camera recordings</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">Cameras</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;">These cameras record continuously, so a new recording normally arrives every few minutes. When a camera is online but its recordings stop arriving, check that the account has not reached its upload limit, that the storage the recordings are sent to is reachable and not full, and that the Agent's cloud settings and credentials are still valid. Cameras that are recording again need no action.</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,24 @@
|
||||
Kerberos.io
|
||||
------------
|
||||
|
||||
Hey, {{user}}
|
||||
|
||||
Camera recordings
|
||||
{{text}}
|
||||
|
||||
{{sectionsText}}
|
||||
|
||||
These cameras record continuously, so a new recording normally arrives every few minutes. When a camera is online but its recordings stop arriving, check that the account has not reached its upload limit, that the storage the recordings are sent to is reachable and not full, and that the Agent's cloud settings and credentials are still valid. Cameras that are recording again need no action.
|
||||
|
||||
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.
|
||||
@@ -106,3 +106,66 @@ Usage: {{- include "hub.opentelemetry.samplerEnv" $ | trim | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
hub.audit.destinations resolves the audit destinations into JSON. Inline
|
||||
credentials (s3.accessKey / s3.secretKey on S3 destinations, bearerToken and
|
||||
secretHeaders.<name>.value on webhooks) are the only way to pass credentials.
|
||||
They are stored in a chart-created Secret named hub-audit-<id>-credentials;
|
||||
the internal credentialsSecret / bearerTokenSecret / secretHeaders entries
|
||||
below describe that Secret for the other templates.
|
||||
Use: fromJsonArray (include "hub.audit.destinations" .)
|
||||
*/}}
|
||||
{{- define "hub.audit.destinations" -}}
|
||||
{{- $resolved := list -}}
|
||||
{{- range $index, $original := .Values.kerberoshub.audit.destinations -}}
|
||||
{{- $destination := deepCopy $original -}}
|
||||
{{- $label := default (printf "%d" $index) $destination.id -}}
|
||||
{{- if hasKey $destination "credentialsSecret" -}}
|
||||
{{- fail (printf "audit destination %s: credentialsSecret is not supported; set s3.accessKey and s3.secretKey" $label) -}}
|
||||
{{- end -}}
|
||||
{{- if hasKey $destination "bearerTokenSecret" -}}
|
||||
{{- fail (printf "audit destination %s: bearerTokenSecret is not supported; set bearerToken" $label) -}}
|
||||
{{- end -}}
|
||||
{{- $s3 := default dict $destination.s3 -}}
|
||||
{{- if or (hasKey $s3 "accessKey") (hasKey $s3 "secretKey") -}}
|
||||
{{- $id := required (printf "id is required for audit destination %d" $index) $destination.id -}}
|
||||
{{- if not (and $s3.accessKey $s3.secretKey) -}}
|
||||
{{- fail (printf "audit destination %s must set both s3.accessKey and s3.secretKey, and neither may be empty" $id) -}}
|
||||
{{- end -}}
|
||||
{{- $_ := set $destination "credentialsSecret" (dict
|
||||
"name" (printf "hub-audit-%s-credentials" $id)
|
||||
"create" true
|
||||
"accessKeyId" $s3.accessKey
|
||||
"secretAccessKey" $s3.secretKey) -}}
|
||||
{{- $_ := unset $s3 "accessKey" -}}
|
||||
{{- $_ := unset $s3 "secretKey" -}}
|
||||
{{- end -}}
|
||||
{{- if hasKey $destination "bearerToken" -}}
|
||||
{{- $id := required (printf "id is required for audit destination %d" $index) $destination.id -}}
|
||||
{{- if not $destination.bearerToken -}}
|
||||
{{- fail (printf "audit destination %s sets an empty bearerToken" $id) -}}
|
||||
{{- end -}}
|
||||
{{- $_ := set $destination "bearerTokenSecret" (dict
|
||||
"name" (printf "hub-audit-%s-credentials" $id)
|
||||
"key" "bearer-token"
|
||||
"create" true
|
||||
"value" $destination.bearerToken) -}}
|
||||
{{- $_ := unset $destination "bearerToken" -}}
|
||||
{{- end -}}
|
||||
{{- range $header, $secret := (default dict $destination.secretHeaders) -}}
|
||||
{{- if or (not (kindIs "map" $secret)) (not (hasKey $secret "value")) (hasKey $secret "secretName") (hasKey $secret "secretKey") -}}
|
||||
{{- fail (printf "audit destination %s header %s: set secretHeaders.%s.value" $label $header $header) -}}
|
||||
{{- end -}}
|
||||
{{- $id := required (printf "id is required for audit destination %d" $index) $destination.id -}}
|
||||
{{- if not $secret.value -}}
|
||||
{{- fail (printf "audit destination %s header %s sets an empty value" $id $header) -}}
|
||||
{{- end -}}
|
||||
{{- $_ := set $secret "secretName" (printf "hub-audit-%s-credentials" $id) -}}
|
||||
{{- $_ := set $secret "secretKey" (printf "header-%s" (sha256sum $header | trunc 12)) -}}
|
||||
{{- $_ := set $secret "create" true -}}
|
||||
{{- end -}}
|
||||
{{- $resolved = append $resolved $destination -}}
|
||||
{{- end -}}
|
||||
{{- toJson $resolved -}}
|
||||
{{- end -}}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{{- if .Values.audit.enabled -}}
|
||||
{{- $destinations := list -}}
|
||||
{{- $destinationIDs := dict -}}
|
||||
{{- range $index, $destination := .Values.kerberoshub.audit.destinations -}}
|
||||
{{- range $index, $destination := fromJsonArray (include "hub.audit.destinations" .) -}}
|
||||
{{- $id := required (printf "id is required for audit destination %d" $index) $destination.id -}}
|
||||
{{- if not (regexMatch "^[a-z][a-z0-9-]{0,62}$" $id) -}}
|
||||
{{- fail (printf "audit destination id %q must match ^[a-z][a-z0-9-]{0,62}$" $id) -}}
|
||||
@@ -14,7 +14,7 @@
|
||||
{{- $entry := dict
|
||||
"id" $id
|
||||
"type" $type
|
||||
"enabled" $destination.enabled
|
||||
"enabled" (ternary $destination.enabled true (hasKey $destination "enabled"))
|
||||
"timeoutSeconds" (default 10 $destination.timeoutSeconds)
|
||||
"batchSize" (default 100 $destination.batchSize)
|
||||
"maxAttempts" (default 10 $destination.maxAttempts)
|
||||
@@ -44,7 +44,7 @@
|
||||
{{- end -}}
|
||||
{{- else if eq $type "s3" -}}
|
||||
{{- if or $destination.url $destination.headers $destination.bearerTokenSecret $destination.secretHeaders -}}
|
||||
{{- fail (printf "audit destination %s is s3 and cannot set url, headers, bearerTokenSecret or secretHeaders" $id) -}}
|
||||
{{- fail (printf "audit destination %s is s3 and cannot set url, headers, bearerToken, bearerTokenSecret or secretHeaders" $id) -}}
|
||||
{{- end -}}
|
||||
{{- $s3 := required (printf "s3 is required for audit destination %s" $id) $destination.s3 -}}
|
||||
{{- $settings := dict
|
||||
|
||||
@@ -323,6 +323,11 @@ spec:
|
||||
{{- end }}
|
||||
- name: CORS_ALLOWED_ORIGINS
|
||||
value: {{ join "," $corsOrigins | quote }}
|
||||
{{- $mcp := .Values.kerberoshub.api.mcp | default dict }}
|
||||
- name: MCP_ENABLED
|
||||
value: {{ $mcp.enabled | default false | quote }}
|
||||
- name: MCP_ALLOWED_ORIGINS
|
||||
value: {{ join "," ($mcp.allowedOrigins | default list) | quote }}
|
||||
{{ if .Values.isPrivate }}
|
||||
- name: KERBEROS_PRIVATE_CLOUD
|
||||
value: "true"
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
{{- if .Values.audit.enabled }}
|
||||
{{- /*
|
||||
Secret the chart creates per audit destination from its inline credentials
|
||||
(s3.accessKey / s3.secretKey, bearerToken, secretHeaders.<name>.value).
|
||||
*/}}
|
||||
{{- $created := dict }}
|
||||
{{- range $index, $destination := fromJsonArray (include "hub.audit.destinations" .) }}
|
||||
{{- $data := dict }}
|
||||
{{- $name := "" }}
|
||||
{{- $credentials := default dict $destination.credentialsSecret }}
|
||||
{{- if $credentials.create }}
|
||||
{{- $name = required (printf "credentialsSecret.name is required for destination %s" $destination.id) $credentials.name }}
|
||||
{{- $accessKeyIdKey := default "access-key-id" $credentials.accessKeyIdKey }}
|
||||
{{- $secretAccessKeyKey := default "secret-access-key" $credentials.secretAccessKeyKey }}
|
||||
{{- if eq $accessKeyIdKey $secretAccessKeyKey }}
|
||||
{{- fail (printf "credentialsSecret.accessKeyIdKey and secretAccessKeyKey must differ for destination %s" $destination.id) }}
|
||||
{{- end }}
|
||||
{{- $_ := set $data $accessKeyIdKey (required (printf "credentialsSecret.accessKeyId is required for destination %s when credentialsSecret.create is true" $destination.id) $credentials.accessKeyId) }}
|
||||
{{- $_ := set $data $secretAccessKeyKey (required (printf "credentialsSecret.secretAccessKey is required for destination %s when credentialsSecret.create is true" $destination.id) $credentials.secretAccessKey) }}
|
||||
{{- end }}
|
||||
{{- $bearer := default dict $destination.bearerTokenSecret }}
|
||||
{{- if $bearer.create }}
|
||||
{{- $name = $bearer.name }}
|
||||
{{- $_ := set $data $bearer.key $bearer.value }}
|
||||
{{- end }}
|
||||
{{- range $header, $secret := (default dict $destination.secretHeaders) }}
|
||||
{{- if $secret.create }}
|
||||
{{- $name = $secret.secretName }}
|
||||
{{- $_ := set $data $secret.secretKey $secret.value }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- if $name }}
|
||||
{{- if hasKey $created $name }}
|
||||
{{- fail (printf "audit destinations %s and %s both create Secret %q" (get $created $name) $destination.id $name) }}
|
||||
{{- end }}
|
||||
{{- $_ := set $created $name $destination.id }}
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: {{ $name }}
|
||||
namespace: {{ $.Release.Namespace }}
|
||||
labels:
|
||||
app: hub-audit
|
||||
audit-destination: {{ $destination.id }}
|
||||
type: Opaque
|
||||
stringData:
|
||||
{{- range $key, $value := $data }}
|
||||
{{ $key | quote }}: {{ toString $value | quote }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
@@ -43,7 +43,7 @@ spec:
|
||||
- name: destinations
|
||||
configMap:
|
||||
name: hub-audit
|
||||
{{- range $index, $destination := .Values.kerberoshub.audit.destinations }}
|
||||
{{- range $index, $destination := fromJsonArray (include "hub.audit.destinations" $) }}
|
||||
{{- $bearer := default dict $destination.bearerTokenSecret }}
|
||||
{{- $secretHeaders := default dict $destination.secretHeaders }}
|
||||
{{- $credentials := default dict $destination.credentialsSecret }}
|
||||
@@ -112,7 +112,7 @@ spec:
|
||||
- name: destinations
|
||||
mountPath: /etc/hub-audit
|
||||
readOnly: true
|
||||
{{- range $index, $destination := .Values.kerberoshub.audit.destinations }}
|
||||
{{- range $index, $destination := fromJsonArray (include "hub.audit.destinations" $) }}
|
||||
{{- $bearer := default dict $destination.bearerTokenSecret }}
|
||||
{{- $secretHeaders := default dict $destination.secretHeaders }}
|
||||
{{- $credentials := default dict $destination.credentialsSecret }}
|
||||
|
||||
@@ -259,6 +259,8 @@ spec:
|
||||
value: "{{ .Values.kerberoshub.frontend.features.organisations.creationEnabled }}"
|
||||
- name: FEATURE_ORGANISATION_SETTINGS_ENABLED
|
||||
value: "{{ .Values.kerberoshub.frontend.features.organisations.settingsEnabled }}"
|
||||
- name: FEATURE_MEMBERS_ACCESS_PREVIEW_ENABLED
|
||||
value: "{{ .Values.kerberoshub.frontend.features.organisations.membersAccessPreviewEnabled }}"
|
||||
- name: FEATURE_PROJECTS_ENABLED
|
||||
value: "{{ .Values.kerberoshub.frontend.features.projects.enabled }}"
|
||||
- name: FEATURE_PROJECT_SWITCHER_ENABLED
|
||||
|
||||
@@ -338,6 +338,8 @@ spec:
|
||||
value: "{{ .Values.kerberoshub.frontend.features.organisations.creationEnabled }}"
|
||||
- name: FEATURE_ORGANISATION_SETTINGS_ENABLED
|
||||
value: "{{ .Values.kerberoshub.frontend.features.organisations.settingsEnabled }}"
|
||||
- name: FEATURE_MEMBERS_ACCESS_PREVIEW_ENABLED
|
||||
value: "{{ .Values.kerberoshub.frontend.features.organisations.membersAccessPreviewEnabled }}"
|
||||
- name: FEATURE_PROJECTS_ENABLED
|
||||
value: "{{ .Values.kerberoshub.frontend.features.projects.enabled }}"
|
||||
- name: FEATURE_PROJECT_SWITCHER_ENABLED
|
||||
|
||||
@@ -96,6 +96,14 @@ spec:
|
||||
value: "{{ .Values.email.templates.deviceGroup | default "device_group" }}"
|
||||
- name: DEVICE_GROUP_TITLE
|
||||
value: "{{ .Values.email.templates.deviceGroupTitle | default "[Device] Several cameras changed status" }}"
|
||||
{{- with .Values.email.templates.recording }}
|
||||
- name: RECORDING_TEMPLATE
|
||||
value: {{ . | quote }}
|
||||
{{- end }}
|
||||
{{- with .Values.email.templates.recordingTitle }}
|
||||
- name: RECORDING_TITLE
|
||||
value: {{ . | quote }}
|
||||
{{- end }}
|
||||
# Base URL for the link in grouped device notifications.
|
||||
- name: PUBLIC_URL
|
||||
value: "{{ .Values.kerberoshub.frontend.schema }}://{{ .Values.kerberoshub.frontend.url }}"
|
||||
|
||||
@@ -62,6 +62,22 @@
|
||||
"api": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"mcp": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"enabled": {"type": "boolean"},
|
||||
"allowedOrigins": {
|
||||
"type": "array",
|
||||
"uniqueItems": true,
|
||||
"items": {
|
||||
"type": "string",
|
||||
"pattern": "^https?://[^/?#*@,\\s]+$"
|
||||
},
|
||||
"description": "Exact HTTP(S) origins for browser MCP clients, without wildcards, credentials, paths or trailing slashes. Independent of REST CORS."
|
||||
}
|
||||
}
|
||||
},
|
||||
"passkeys": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
|
||||
+55
-42
@@ -370,6 +370,12 @@ kerberoshub:
|
||||
# Additional complete HTTP(S) origins allowed to call the Hub API.
|
||||
# The configured frontend origins remain allowed automatically.
|
||||
corsAllowedOrigins: []
|
||||
# Read-only Streamable HTTP MCP endpoint at /mcp; requires a compatible API image.
|
||||
mcp:
|
||||
enabled: false
|
||||
# Exact HTTP(S) origins for browser MCP clients; no wildcards or trailing slash.
|
||||
# Clients without an Origin header (such as VS Code) need no entries.
|
||||
allowedOrigins: []
|
||||
# Temporarily stop serving the polled GET /devices list: hub-api answers 204
|
||||
# without querying MongoDB. The frontend still loads devices once through
|
||||
# /session/bootstrap, but device online/offline status stops refreshing.
|
||||
@@ -655,6 +661,7 @@ kerberoshub:
|
||||
switcherEnabled: "false" # Enable or disable organisation switching 'true' or 'false'
|
||||
creationEnabled: "false" # Enable or disable organisation creation; requires switcherEnabled 'true' or 'false'
|
||||
settingsEnabled: "false" # Enable or disable the organisation settings link 'true' or 'false'
|
||||
membersAccessPreviewEnabled: "false" # Show the mocked "Users (preview)" tab in organisation settings to owners 'true' or 'false'
|
||||
projects:
|
||||
enabled: "" # Fallback project group switch used only when organisations.enabled is empty
|
||||
switcherEnabled: "false" # Enable or disable the read-only project dropdown 'true' or 'false'
|
||||
@@ -860,12 +867,17 @@ kerberoshub:
|
||||
cpu: 10m
|
||||
# Dispatches canonical audit_events to one or more destinations: webhook
|
||||
# sinks, or S3 Object Lock buckets for immutable storage.
|
||||
# Credentials are always read from existing Secrets and never stored here.
|
||||
# Destination credentials are set inline on each destination.
|
||||
audit:
|
||||
repository: ghcr.io/uug-ai/hub-audit
|
||||
pullPolicy: IfNotPresent
|
||||
tag: "v1.0.10"
|
||||
replicas: 1
|
||||
resources: {}
|
||||
# resources:
|
||||
# requests:
|
||||
# memory: 32Mi
|
||||
# cpu: 10m
|
||||
dispatchInterval: "5s"
|
||||
leaseDuration: "2m"
|
||||
terminationGracePeriodSeconds: 150 # Keep above leaseDuration so an in-flight cycle can finish.
|
||||
@@ -886,63 +898,57 @@ kerberoshub:
|
||||
serviceMonitor:
|
||||
enabled: true
|
||||
interval: 15s
|
||||
# Each destination is optional; see the README for every field. Webhook:
|
||||
destinations: []
|
||||
# - id: primary-siem
|
||||
# type: webhook
|
||||
# enabled: true
|
||||
# url: https://siem.example.com/api/audit
|
||||
# timeoutSeconds: 10
|
||||
# batchSize: 100
|
||||
# maxAttempts: 10
|
||||
# startAt: latest # latest | beginning, applied only on first creation
|
||||
# headers:
|
||||
# - id: primary-siem # required; ^[a-z][a-z0-9-]{0,62}$, keeps the checkpoint
|
||||
# type: webhook # optional: webhook (default) | s3
|
||||
# enabled: true # optional, default true; false pauses delivery
|
||||
# url: https://siem.example.com/api/audit # required, http or https
|
||||
# timeoutSeconds: 10 # optional, default 10, at most 60
|
||||
# batchSize: 100 # optional, default 100, at most 1000
|
||||
# maxAttempts: 10 # optional, default 10; then the batch is dead-lettered
|
||||
# startAt: latest # optional: latest (default) | beginning, for a new id only
|
||||
# headers: # optional; not Authorization or Idempotency-Key
|
||||
# X-Source: kerberos-hub
|
||||
# bearerTokenSecret:
|
||||
# name: audit-primary-siem
|
||||
# key: token
|
||||
# # Optional inline credentials. The chart stores them in Secret
|
||||
# # hub-audit-<id>-credentials and mounts them.
|
||||
# bearerToken: "xxx" # sent as Authorization: Bearer <token>
|
||||
# secretHeaders:
|
||||
# X-Api-Key:
|
||||
# secretName: audit-primary-siem
|
||||
# secretKey: api-key
|
||||
# filters:
|
||||
# value: "xxx"
|
||||
# filters: # optional, default all events; an event must match every list set
|
||||
# organisationIds: []
|
||||
# projectIds: []
|
||||
# actions: []
|
||||
# categories: []
|
||||
# outcomes: [] # success | failure
|
||||
# S3 Object Lock destination (hub-audit v1.0.9 or later). The bucket needs
|
||||
# versioning, Object Lock and a COMPLIANCE default retention; hub-audit
|
||||
# refuses to deliver otherwise. Each batch is written as
|
||||
# <prefix>/<id>/yyyy/mm/dd/<firstId>-<lastId>.ndjson.gz. Per-provider
|
||||
# region/endpoint values (AWS, Wasabi, B2, Ceph RGW, MinIO, Garage):
|
||||
# S3 Object Lock destination. The bucket needs versioning, Object Lock and a
|
||||
# COMPLIANCE default retention; hub-audit refuses to deliver otherwise. Each
|
||||
# batch is written as <prefix>/<id>/yyyy/mm/dd/<firstId>-<lastId>.ndjson.gz.
|
||||
# Per-provider region/endpoint values (AWS, Wasabi, B2, Ceph RGW, MinIO, Garage):
|
||||
# https://learn.uug.ai/docs/hub/audit-events/#s3-providers
|
||||
# - id: immutable-archive
|
||||
# type: s3
|
||||
# enabled: true
|
||||
# - id: immutable-archive # required
|
||||
# type: s3 # required for S3
|
||||
# enabled: true # optional, default true
|
||||
# timeoutSeconds: 30
|
||||
# batchSize: 500
|
||||
# maxAttempts: 100 # keep high: exhausted batches are dead-lettered, not archived
|
||||
# startAt: beginning
|
||||
# s3:
|
||||
# bucket: hub-audit-archive
|
||||
# region: eu-west-1
|
||||
# prefix: hub
|
||||
# endpoint: https://s3.example.com # S3-compatible stores only; omit for AWS S3
|
||||
# forcePathStyle: true # usually needed for Ceph RGW, MinIO and Garage
|
||||
# # Only for stores without Object Lock (e.g. Garage): skips the lock
|
||||
# # check, so archived batches are deletable. Needs hub-audit v1.0.10.
|
||||
# bucket: hub-audit-archive # required
|
||||
# region: eu-west-1 # required, also for S3-compatible stores
|
||||
# prefix: hub # optional, default none
|
||||
# endpoint: https://s3.example.com # optional; S3-compatible stores only, omit for AWS S3
|
||||
# forcePathStyle: true # optional, default false; usually true for Ceph RGW, MinIO and Garage
|
||||
# # Optional, default false. Only for stores without Object Lock (e.g. Garage):
|
||||
# # skips the lock check, so archived batches are deletable.
|
||||
# allowUnlockedBucket: false
|
||||
# # Omit credentialsSecret to use the pod's AWS identity (IRSA via
|
||||
# # kerberoshub.audit.serviceAccount, or an instance profile). That only
|
||||
# # works for AWS S3; every other store needs credentialsSecret.
|
||||
# credentialsSecret:
|
||||
# name: audit-immutable-archive
|
||||
# accessKeyIdKey: access-key-id
|
||||
# secretAccessKeyKey: secret-access-key
|
||||
# resources:
|
||||
# requests:
|
||||
# memory: 32Mi
|
||||
# cpu: 10m
|
||||
# # Optional inline credentials, both or neither. The chart stores them
|
||||
# # in Secret hub-audit-<id>-credentials and mounts them.
|
||||
# accessKey: "xxx"
|
||||
# secretKey: "xxx"
|
||||
# # Without them, hub-audit uses the pod's AWS identity (IRSA via
|
||||
# # kerberoshub.audit.serviceAccount, or an instance profile): AWS S3 only.
|
||||
# hub-workflows is the standalone, queue-driven workflow engine. It consumes
|
||||
# pipeline events and dispatches the stages declared in its workflow
|
||||
# definitions, tracking each run in its own `workflow_runs` collection. It shares events
|
||||
@@ -1599,3 +1605,10 @@ email:
|
||||
alertTitle: "[Alert] Kerberos Hub detected something an event"
|
||||
deviceTitle: "[Device] A Kerberos Agent's status has been changed"
|
||||
deviceGroupTitle: "[Device] Several cameras changed status"
|
||||
# Recording check alerts (Alerts → Devices → Recording check). Empty uses
|
||||
# the "recording" template with SMTP. For SendGrid or Mailgun, set the
|
||||
# template id; empty uses the device template there.
|
||||
recording: ""
|
||||
# Empty uses "[Device] A camera stopped recording", "[Device] A camera is
|
||||
# recording again" and their forms for several cameras.
|
||||
recordingTitle: ""
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
CHART_DIR="${1:-charts/hub}"
|
||||
|
||||
extract_mcp() {
|
||||
awk '
|
||||
/^---/ { deployment=0; name=""; variable="" }
|
||||
/^kind: Deployment$/ { deployment=1 }
|
||||
deployment && /^ name:/ { name=$2 }
|
||||
/- name: MCP_/ { variable=$3; next }
|
||||
variable != "" && /value:/ {
|
||||
value=$0
|
||||
sub(/^[[:space:]]*value: /, "", value)
|
||||
print (deployment ? name : "non-deployment"), variable, value
|
||||
variable=""
|
||||
}
|
||||
' | sort
|
||||
}
|
||||
|
||||
assert_mcp() {
|
||||
local rendered="$1" expected="$2" actual
|
||||
actual="$(printf '%s\n' "$rendered" | extract_mcp)"
|
||||
if [[ "$actual" != "$expected" ]]; then
|
||||
printf 'Unexpected MCP environment:\n%s\nExpected:\n%s\n' "$actual" "$expected" >&2
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
defaults="$(printf '%s\n' 'hub-api MCP_ALLOWED_ORIGINS ""' 'hub-api MCP_ENABLED "false"')"
|
||||
enabled="$(printf '%s\n' \
|
||||
'hub-api MCP_ALLOWED_ORIGINS "https://agent.example.com,http://localhost:4200"' \
|
||||
'hub-api MCP_ENABLED "true"')"
|
||||
flags=(
|
||||
--set kerberoshub.api.mcp.enabled=true
|
||||
--set-json 'kerberoshub.api.mcp.allowedOrigins=["https://agent.example.com","http://localhost:4200"]'
|
||||
)
|
||||
|
||||
for mode in all ui pipeline; do
|
||||
default_render="$(helm template hub "$CHART_DIR" --set "mode=$mode")"
|
||||
configured_render="$(helm template hub "$CHART_DIR" --set "mode=$mode" "${flags[@]}")"
|
||||
helm lint "$CHART_DIR" --quiet --set "mode=$mode" "${flags[@]}"
|
||||
if [[ "$mode" == pipeline ]]; then
|
||||
assert_mcp "$default_render" ""
|
||||
assert_mcp "$configured_render" ""
|
||||
[[ "$default_render" == "$configured_render" ]]
|
||||
else
|
||||
assert_mcp "$default_render" "$defaults"
|
||||
assert_mcp "$configured_render" "$enabled"
|
||||
rest_default="$(grep -A1 -- '- name: CORS_ALLOWED_ORIGINS' <<< "$default_render")"
|
||||
rest_configured="$(grep -A1 -- '- name: CORS_ALLOWED_ORIGINS' <<< "$configured_render")"
|
||||
[[ "$rest_default" == "$rest_configured" ]]
|
||||
fi
|
||||
echo "OK ($mode): MCP defaults, overrides, API-only environment and REST CORS isolation"
|
||||
done
|
||||
|
||||
assert_mcp "$(helm template hub "$CHART_DIR" --set kerberoshub.api.mcp.enabled=true)" \
|
||||
"$(printf '%s\n' 'hub-api MCP_ALLOWED_ORIGINS ""' 'hub-api MCP_ENABLED "true"')"
|
||||
assert_mcp "$(helm template hub "$CHART_DIR" --set kerberoshub.api.mcp=null)" "$defaults"
|
||||
assert_mcp "$(helm template hub "$CHART_DIR" "${flags[@]}" --set kerberoshub.api.mcp.enabled=false)" \
|
||||
"$(printf '%s\n' 'hub-api MCP_ALLOWED_ORIGINS "https://agent.example.com,http://localhost:4200"' 'hub-api MCP_ENABLED "false"')"
|
||||
|
||||
expect_invalid() {
|
||||
local output
|
||||
if output="$(helm template hub "$CHART_DIR" "$@" 2>&1)"; then
|
||||
echo "Invalid MCP settings unexpectedly rendered: $*" >&2
|
||||
return 1
|
||||
fi
|
||||
if [[ "$output" != *"mcp"* ]]; then
|
||||
printf 'Rendering failed for an unexpected reason:\n%s\n' "$output" >&2
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
expect_invalid --set-string kerberoshub.api.mcp.enabled=true
|
||||
expect_invalid --set-string kerberoshub.api.mcp.allowedOrigins=https://agent.example.com
|
||||
expect_invalid --set-string kerberoshub.api.mcp.origin=https://agent.example.com
|
||||
for origin in 'https://*.example.com' 'https://agent.example.com/' \
|
||||
'https://agent.example.com/path' 'https://agent.example.com?query' \
|
||||
'https://agent.example.com#fragment' 'https://user@agent.example.com' \
|
||||
'https://agent.example.com,https://other.example.com' 'https://bad host' 'ftp://agent.example.com'; do
|
||||
expect_invalid --set-json "kerberoshub.api.mcp.allowedOrigins=[\"$origin\"]"
|
||||
done
|
||||
expect_invalid --set-json 'kerberoshub.api.mcp.allowedOrigins=["https://agent.example.com","https://agent.example.com"]'
|
||||
|
||||
echo "All Hub MCP configuration checks passed."
|
||||
Reference in New Issue
Block a user