The pod
The chart runs Lumovi locked down already. Keep these as they are:Chart defaults
- Not root: the image’s user,
65532, with no shell in the image. - A read-only root filesystem: Lumovi writes only to
/tmp, anemptyDir, and to the audit log’s volume. - No capabilities, and no way to gain privileges: every Linux capability dropped, and
allowPrivilegeEscalation: false. - The runtime’s default seccomp profile, which blocks system calls a web server has no use for.
restricted Pod Security Standard, so Lumovi’s namespace can enforce it:
kube-system unless nodeShell.namespace says otherwise), as the person who opens one, so this label doesn’t affect them. On OpenShift, see below.
Sign-in
Every way to sign in to Lumovi checks who people are: there’s no way to run it open to anyone. What matters is that nothing gets around it.- Prefer what needs no permissions. With tokens, or single sign-on that passes people’s tokens on, the API server checks each request itself, and Lumovi’s service account may do nothing.
- With single sign-on (
auth.mode: oidc), or behind an authenticating proxy (auth.mode: proxy), Lumovi acts as people by impersonating them. Setauth.usernamePrefixandauth.groupsPrefix, so no name or group from your provider is one of the cluster’s own by accident. - Behind a proxy, only the proxy may reach Lumovi. Lumovi trusts its headers completely: whoever reaches Lumovi without it can name themselves anyone. Leave the chart’s
ingressoff, keep the Service aClusterIP, and turn on the network policy with only the proxy infrom. - Keep sessions short enough: 12 hours unless
auth.sessionHourssays otherwise.
Sessions across restarts
A restart signs nobody out (auth.keepSessions, on by default). Lumovi keeps sessions, and the AI assistants people allowed, in a Secret, <release>-state, each entry sealed with a key kept apart, in <release>-state-key. Reading the state shows nothing, and writing it alone can’t make anyone’s session. Whoever can both write it and read its key can, though: keep Lumovi’s namespace admin-only, and watch for controllers there that sync Secrets, like External Secrets or a GitOps tool. See Across restarts.
-
Rendered with
helm template, as Argo CD and some other tools do, the chart can’t see the key it made before (lookupfinds nothing there), so each render would make a new one, and a new key signs everyone out. Make the key yourself, and name it inauth.stateKeySecret:It needs at least 32 random characters, undervalues.yamlkey. -
With
rbac.create: false, give Lumovi’s service accountgetandpatchon<release>-stateyourself, or it doesn’t start. It needs nothing on the key’s Secret. -
With
auth.keepSessions: false, sessions live in memory, and every restart signs everyone out.
The network
values.yaml
- TLS at the ingress. Lumovi serves plain HTTP on port 8080, inside the cluster. Terminate TLS at the ingress or load balancer, and set
urlto thehttps:address, so Lumovi’s cookies areSecure. See Give it an address. - Only what you name reaches it.
networkPolicylets in only whatfromallows: your ingress controller’s pods, or the authenticating proxy’s. With an emptyfrom, it lets anything in on Lumovi’s port, so always name them. Your cluster’s network plugin must enforce network policies. A fleet’s agents come in through the ingress too. - Behind your company’s proxy, give Lumovi the proxy, and the certificate authority it signs with: see Proxies and certificate authorities. A proxy that wants credentials takes them in its URL: put that in a Secret, and name it in
proxy.secret, so they stay out of the Deployment. Lumovi never says them, in its log or its errors. They reach the kubeconfig each run ofhelmgets, askubectlwould have them, in a private temporary folder deleted after the run.
The service account
What Lumovi’s service account may do depends on how people sign in:
With AI assistants, or Access, it also gets
get and patch on the one ConfigMap that holds each, and no other. With auth.keepSessions, get and patch on the Secret that keeps sessions across restarts, and no other Secret: not even the key it’s sealed with, which reaches the pod as an environment variable. In a fleet, with fleet.secrets, list on Secrets in the namespaces you name.
Never give it more, like cluster-admin: it doesn’t need it, and whoever controls the pod would have it. Whoever can exec into Lumovi’s pod, read its token, or change its Deployment can act as anyone it may impersonate. So run it in a namespace of its own, where only cluster administrators can exec into pods, read or change Secrets, or change workloads and ConfigMaps. To manage its permissions yourself, set rbac.create: false: see Helm values.
Who may do what
Kubernetes RBAC decides what each person may do in the cluster. Lumovi can keep them to less:- Access: name your admins in
access.admins, and set inaccess.policywhat shouldn’t depend on anyone’s clicks: a read-onlyeveryone, profiles for your teams, and limits for production, like no shells and no Secrets’ values there. The server holds to it, and every refusal is in the audit log. - Read-only:
readOnly: true(LUMOVI_READ_ONLY) keeps everyone from changing anything through Lumovi, for a dashboard people should only look at. Without it, a cluster made read-only on the page is read-only for everyone on the server, and so are where its metrics come from and where its node shells run: name admins inaccess.admins, so only they change these. With none, anyone signed in can. - AI assistants: turn them off with
assistants.enabled: falseif your team doesn’t use them. Otherwise, set limits for everyone inassistants.rules(LUMOVI_ASSISTANT_RULES): hide system namespaces, and keep changes ataskornever, and Secrets atkeysorhidden, where they matter. - Shells on nodes:
nodeShell.enabled: falseunless people should open them through Lumovi. - Charts from private addresses: leave
helm.allowPrivateChartsoff unless your charts are in your own network.
The audit log
The audit log records what everyone does through Lumovi. For production:- Name your auditors in
audit.auditors: they see everyone’s events. Everyone else sees their own. - Keep the history on its volume (
audit.persistence, on by default), foraudit.retentionDays. - Send it to your SIEM as it happens, with
audit.webhook, its headers (a token, say) in a Secret:audit.webhook.headersSecret. Or collect it from Lumovi’s output, where every event is a line of JSON. - Keep a copy Lumovi can’t reach. The chain shows a changed, removed or inserted event, but whoever can write the volume could rewrite it all: compare the history’s newest hash with your SIEM’s. See What it proves.
The image
Each release’s image and chart are built and signed by Lumovi’s release workflow on GitHub, from the release’s commit onmain:
- Signed with cosign, keyless, twice: as a Sigstore bundle, and with a
.sigtag. By the workflow’s identity,https://github.com/Lumovi/Lumovi/.github/workflows/release.yml@refs/heads/main, from the issuerhttps://token.actions.githubusercontent.com, and recorded in Sigstore’s public log. - Attested: a build provenance attestation for each, which
gh attestation verifychecks. - With bills of materials: its JavaScript packages in CycloneDX, attested with it, and its system packages in SPDX.
Pin it by digest
A tag can be moved; a digest can’t. Find the image’s:values.yaml
cosign verify, as for the image.
Require the signature
An admission controller can refuse any Lumovi pod whose image isn’t signed by the release workflow. The examples below, for Kyverno and Sigstore’s policy controller, check the identity and issuer above.- Kyverno
- Sigstore policy controller
lumovi-signed.yaml
mutateDigest pins each pod’s image to the digest Kyverno checked.cosign copy): point imageReferences or glob at your copy instead.
OpenShift
The chart works under OpenShift’srestricted-v2 security context constraint, which gives each pod a user, a group and an fsGroup of its own, from its namespace’s ranges.
openshift: auto, the default, finds out by asking whether the cluster hassecurity.openshift.io/v1. There, it leavesrunAsUser,runAsGroupandfsGroupout ofpodSecurityContext, for OpenShift to choose, and keeps the rest.trueorfalsesays so instead.- Rendering the chart away from the cluster, with
helm templatesay,autocan’t ask: setopenshift: true, or pass--api-versions security.openshift.io/v1. - The image’s own folders are its user’s and group
0’s, as OpenShift runs a pod’s user in group0. The audit log’s volume is the pod’sfsGroup’s, as OpenShift gives it. - The chart’s Ingress is served by OpenShift’s router, as a Route.
- The cluster’s proxy isn’t passed to Lumovi by itself: see OpenShift.
All together
A values file for a team’s Lumovi behind single sign-on, with the settings above:values.yaml
Checklist
Keep the chart’s pod and container security contexts, and enforce the
restricted Pod Security Standard on Lumovi’s namespace.Pick token sign-in, or single sign-on with tokens passed on, when you can: Lumovi then needs no permissions.
Run Lumovi in its own namespace, which only cluster administrators can exec into, read or change Secrets in, or change workloads and ConfigMaps in.
Set
auth.usernamePrefix and auth.groupsPrefix when Lumovi impersonates people.Turn on
networkPolicy, and name only your ingress controller, or the authenticating proxy, in from.Serve it over HTTPS, and set
url to the https: address.Name admins in
access.admins, and give people only what they need through Lumovi: a read-only everyone, grants for teams where they work, and limits on production in access.policy. Only admins then change clusters’ read-only, metrics source and node-shell settings, which are everyone’s.Turn AI assistants off (
assistants.enabled: false) unless your team uses them, or set limits for everyone in assistants.rules.Set
nodeShell.enabled: false unless people should open shells on nodes through Lumovi, and leave helm.allowPrivateCharts off unless your charts are in your own network.Consider
readOnly: true for a dashboard people should only look at.Name your auditors in
audit.auditors, and send the audit log to your SIEM with audit.webhook, or from Lumovi’s output.Pin the image by digest, check its signature with
cosign verify, and require it with an admission controller.In a fleet, give each cluster a member’s credentials, or an agent, rather than administrators’, and give Lumovi agents’
tokenSha256, not their tokens.Security
What Lumovi holds, what it may do, and how it’s contained.
Proxies and certificate authorities
Lumovi behind your company’s proxy.