mirror of
https://github.com/kerberos-io/helm-charts.git
synced 2026-10-26 18:48:26 +00:00
Compare commits
4
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b520e2ecc1 | ||
|
|
c42388b96f | ||
|
|
04d6990460 | ||
|
|
5af6472826 |
@@ -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.165.0
|
||||
version: 0.167.1
|
||||
|
||||
# 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
|
||||
|
||||
+136
-1
@@ -31,6 +31,99 @@ 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`.
|
||||
|
||||
```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. This setting applies to the
|
||||
primary frontend, not the separately configured legacy demo deployment.
|
||||
|
||||
### 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
|
||||
@@ -543,6 +636,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"` |
|
||||
@@ -690,6 +784,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. | `""` |
|
||||
@@ -743,11 +839,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.
|
||||
@@ -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 }}"
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -67,6 +67,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 +84,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
|
||||
|
||||
@@ -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": {
|
||||
|
||||
@@ -392,6 +392,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"
|
||||
@@ -467,6 +474,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).
|
||||
@@ -1071,6 +1081,10 @@ kerberoshub:
|
||||
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
|
||||
@@ -1470,5 +1484,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"
|
||||
|
||||
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