How it works
- The agent connects to Lumovi’s
api/agentwith a WebSocket, naming its cluster and sending its token. Lumovi checks them against the agents it knows. - It tells Lumovi how to reach its cluster: the API server’s certificate authority, and its service account’s token. That service account may only impersonate users and groups.
- Lumovi’s connections to the API server run through it. The agent connects each one to the API server, at the address Kubernetes gives its pod. TLS runs inside, from Lumovi to the API server: Lumovi checks the API server’s certificate against the cluster’s certificate authority, as
kubernetes.default.svc, and the agent only passes on bytes it can’t read. - Lumovi acts as whoever signed in, impersonating them with the service account’s token, so the cluster applies their RBAC, as with any member.
- Kubernetes rotates the token, and the agent reads it again every minute. When it changed, the agent sends Lumovi the new one.
Set it up
1
Make a token for the agent
The agent proves which cluster it is with a token. Lumovi only needs its SHA-256:Hash the token itself, without a line break after it, as
printf %s does.2
Tell Lumovi about it
List the agents that may connect in With the Helm chart, put the list in a Secret, under Lumovi reads the list when it starts: restart it after changing the list (
LUMOVI_FLEET_AGENTS, as YAML, or that encoded in base64 on one line:agents.yaml
agents.yaml, in Lumovi’s namespace, and name it in fleet.agentsSecret:values.yaml
kubectl rollout restart deployment/lumovi --namespace lumovi). Its pod doesn’t start until the Secret is there. Until the agent connects, the cluster’s card says Unreachable: Its agent isn't connected.3
Install the agent in the cluster
With your kubeconfig pointing at the private cluster, store the token, and install the chart as an agent, with the name Lumovi knows it by:
agent.hubUrl is the address people open Lumovi at, with its base path if it has one. clusterName must be the agent’s name in Lumovi’s list: unless set, it’s in-cluster.The chart’s notes, after installing, print a command that gives the SHA-256 of the token in agent.tokenSecret, and the entry for Lumovi’s list, if you’d rather make the token here first.4
See it connect
The agent’s log says when it’s connected:Lumovi’s log says
The agent of private-eu connected (Lumovi 1.1.0), and the cluster is on the fleet page.The agents Lumovi knows
LUMOVI_FLEET_AGENTS (or the chart’s fleet.agentsSecret) is a list, each with:
string
required
The cluster’s name in the fleet, and the agent’s
LUMOVI_AGENT_NAME (the chart’s clusterName). Two agents can’t have the same name.string
The SHA-256 of the agent’s token, in hex: 64 characters. Lumovi’s settings then don’t hold the token itself.
string
The agent’s token itself, at least 32 characters, instead of
tokenSha256.object
The cluster’s labels, like
{ env: production }.string[]
Only people in one of these groups see the cluster: groups as your provider or proxy names them, without prefixes. Everyone signed in does, without it, and an empty list hides it from everyone.
boolean
default:"false"
true: requests carry each person’s own token, for an API server that trusts your provider, instead of impersonating them. Lumovi must have their tokens: set LUMOVI_OIDC_FORWARD_TOKEN.LUMOVI_FLEET_AGENTS[0] (private-eu) needs a token of at least 32 characters, or its tokenSha256.
What the chart makes
Withmode: agent, the chart makes no Lumovi in the cluster, only its agent:
- A Deployment,
lumovi-agentfor a release calledlumovi, that runs the agent from Lumovi’s image. It has one replica, whateverreplicaCountsays, and is replaced with theRecreatestrategy: a second agent of the same cluster would take over from the first. - A service account,
lumovi, with a ClusterRole and a ClusterRoleBinding,lumovi-impersonate, that let it impersonate users and groups, and nothing else (unlessrbac.createisfalse). Its token is mounted in the agent’s pod. - Health checks on port 8081.
/healthzanswers200 connectedwhile the agent is connected to Lumovi, and503 not connectedotherwise, so the pod is ready only while connected. It’s alive as long as the port answers.
10m of CPU and 32Mi of memory, and is limited to 128Mi (agent.resources). The chart’s image, extraEnv, podSecurityContext, securityContext, nodeSelector, tolerations, affinity and priorityClassName apply to it too. See Helm values.
Run it another way
The agent is in Lumovi’s image, as/app/out/agent/agent.js. It runs in the cluster it relays to: it connects to the API server at KUBERNETES_SERVICE_HOST and KUBERNETES_SERVICE_PORT (443 unless set), which Kubernetes sets in every pod, and needs its pod’s service account token and CA certificate. Give its service account permission to impersonate users and groups, and nothing more, as a member’s has.
string
required
Lumovi’s address, with its base path if it has one: an
http or https URL. The agent connects to api/agent below it, with a wss or ws WebSocket. Use https: the token travels in the connection’s headers.string
required
The cluster’s name, as Lumovi’s list of agents has it.
string
required
The agent’s token. Spaces and line breaks around it are left out, so hash it without them.
number
default:"8081"
Where
GET /healthz says whether the agent is connected. 0: nowhere.number
default:"60"
How often the agent reads its service account’s token again, in seconds.
string
default:"/var/run/secrets/kubernetes.io/serviceaccount"
Where the pod’s service account token and CA certificate are.
KUBERNETES_SERVICE_HOST among them), or without the service account’s token, the agent stops at once and says why:
The agent of private-eu disconnected.
Connecting, and staying connected
- When it can’t reach Lumovi, or its connection closes, the agent tries again after 1 second, then 2, 4, 8 and 15, and every 30 seconds after that. Once connected, it starts again from 1.
- When Lumovi restarts, for an upgrade say, it closes its agents’ connections, and they connect to the new one.
- Lumovi pings its agents as often as it checks pages’ connections (
LUMOVI_HEARTBEAT_SECONDS, every 30 seconds), and closes a connection that doesn’t answer. - The agent listens for those pings, as Lumovi tells it how often they come. A connection dropped somewhere on the way, without a word, goes quiet: after two heartbeats and a second without one (61 seconds), the agent says
Nothing from the hub in 61 s, closes it, and connects again. - When Lumovi refuses it, the agent stops, as trying again wouldn’t help, and Kubernetes restarts it until it’s fixed:
While it’s away, Lumovi’s log says
The agent of private-eu disconnected, and the cluster’s card says Its agent isn't connected. An agent that’s connected but can’t reach its own API server stays connected, and the cluster’s card says Unreachable, as for any cluster that doesn’t answer.
Other problems reaching Lumovi are in the agent’s log, like Can't reach the hub at https://lumovi.example.com: …, then Connecting again in 4 s: it couldn't connect.
In front of Lumovi
Agents connect toapi/agent below Lumovi’s base path, with a WebSocket, and keep it open. Whatever is in front of Lumovi (an ingress, a load balancer, a CDN) must pass WebSockets there, as it does for pages at api/socket, and keep idle connections open longer than the heartbeat. Run one replica of Lumovi: an agent connects to one of them.
- Behind an authenticating proxy, let
api/agentthrough to Lumovi without signing in (with oauth2-proxy,--skip-auth-route). Agents can’t sign in to a proxy, and Lumovi checks their names and tokens itself. A proxy that answers401makes an agent stop, saying Lumovi doesn’t know its name and token. - With the chart’s
networkPolicy, itsfrommust let in whatever agents come through, like your ingress controller or proxy. - A server of one cluster refuses agents, with
403: only a fleet takes them.
Security
- Nothing in the cluster listens. The agent only dials out, to Lumovi, and has no Service.
- The agent can’t read what it relays. Lumovi checks the API server’s certificate itself, end to end, against the certificate authority the agent sent.
- Its service account may only impersonate, like a member’s, and Lumovi uses its token only while the agent is connected.
- Whoever has an agent’s token can connect as its cluster. As the agent says which certificate authority to trust, they could stand in for the cluster with a server of their own, and see what people send it: their own tokens too, with
forwardToken. Keep the token in a Secret in that cluster that only its administrators can read, and give Lumovi only its SHA-256 (tokenSha256), so Lumovi’s settings don’t hold it. - A takeover shows. When another agent connects with the same name and token, the first one stops, and its log says another agent connected as it. Unless two clusters were given the same name and token by mistake, that’s a sign the token is somewhere it shouldn’t be: make a new one.
A fleet of clusters
The fleet page, and the clusters Lumovi reaches itself.
Server configuration
Every fleet setting.