Answer requests¶
With the CLI or hallpass web¶
Each mediated container is a session, until its process exits. A
kubectl exec into it joins the same session. Build the client
(make hallpass-node) and forward the agent's port of the node running
the pod:
kubectl -n hallpass-system get pods -o wide # find the agent on the pod's node
kubectl -n hallpass-system port-forward pod/hallpass-node-xxxxx 7323:7323 &
hallpass-node sessions # sessions on that node
hallpass-node pending agents/agent/agent # namespace/pod/container, or a session id
hallpass-node answer agents/agent/agent 1 once
hallpass-node version # of the client binary
The answers are the same as on a workstation: once, temp (5
minutes), always (for the session's lifetime: nothing is written back
to the policy), deny and never; see
Answers and rules. --addr selects another address
than 127.0.0.1:7323. A request made
in advance by a program in the
container takes grant or deny; hallpass-node pending shows its
duration and its reason, the program's own words. The program gets the
answer as an errno, as on a workstation (see the
list, with the shells' exit
codes): EALREADY granted, EPERM denied, ETIMEDOUT not
answered within the policy's promptTimeout, and ENOSYS when the policy's
mediate leaves out the request's category (write, exec, connect),
without a prompt; a shell exits 126 for
each of them.
hallpass web does the same in a browser, through the same
port-forward: the pending requests, the activity and the rules of every
session on that node, named namespace/pod/container.
hallpass web --node 127.0.0.1:7323 # prints the page's address, asks to confirm its pairing code
The pod's shell waits while the question is in the browser on another machine; denying it fails the write in the pod.
Asking in advance from a pod¶
The hallpass-request helper
asks in advance with a plain command line and exit codes. It is static
and runs in any container. Build its image (FROM scratch, the binary
only) as in Agents, and mount
it read-only with an image volume (on by default from Kubernetes 1.35;
containerd 2.1 or CRI-O 1.33):
spec:
containers:
- name: agent
volumeMounts:
- name: hallpass-request
mountPath: /opt/hallpass
readOnly: true
volumes:
- name: hallpass-request
image:
reference: <registry>/hallpass-request:<tag>
Without image volumes, copy it into the container's image instead
(COPY --from=<registry>/hallpass-request:<tag> /hallpass-request /usr/local/bin/).
Prefer either to an init container copying it to an emptyDir: the
emptyDir would have to be writable, and the agent could replace the
helper.
hallpass asks about it like any other program: allow it in the policy's
rules, allow exec /opt/hallpass/hallpass-request.
Logs and Events¶
The agent logs every request, answer and session
(kubectl -n hallpass-system logs).
The decisions are Events on the pod, shown by kubectl describe pod and
kubectl get events: HallpassStarted when a container's sandbox
starts (mode, policy, and how to answer), HallpassAllowed and
HallpassDenied for each
answer and who gave it, HallpassNotAnswered when a request timed out,
HallpassReleased when a program gave back what it was granted in
advance,
and HallpassPolicyDenied for the denials of the policy and of the
built-in rules, without a human (the first one at once, then at most a
summary per minute per container, with the most frequent paths):
Normal HallpassStarted container agent: sandboxed by hallpass (strict mode, policy "agent"); answer its requests with Headlamp or `hallpass web --node`
Warning HallpassPolicyDenied container agent: denied by the policy: write /etc/a1
Warning HallpassPolicyDenied container agent: denied by the policy 5 times in the last 1m0s: write /etc/a2 (2), write /etc/a0 (1), write /etc/a1 (1) and 1 more
Allowed accesses without a human are not reported. Events expire (after an hour by default); the agent's log keeps everything.
Headlamp plugin¶
The Headlamp plugin in
headlamp/ answers
requests from the browser. It connects to every hallpass-node pod
through the API server's port-forward, as kubectl port-forward does,
and needs nothing more in the cluster.
Sandboxing a Deployment is a pod action; its requests then show in the app bar badge, on every page.
Build it (Go, Node.js and npm):
make headlamp # headlamp/dist/{main.js,main.wasm.gz}
make headlamp-package # headlamp/hallpass-0.1.0.tar.gz
Install it in Headlamp's plugin directory, in a hallpass directory with
main.js, main.wasm.gz and package.json:
- Headlamp desktop:
~/.config/Headlamp/plugins/hallpass/on Linux (Settings > Plugins shows where on other systems). - Headlamp in a cluster or in a container: the directory given by
-plugins-dir(/headlamp/pluginsin the image), for example from an init container or a volume holding the tarball's content.
mkdir -p ~/.config/Headlamp/plugins/hallpass
cp headlamp/dist/main.js headlamp/dist/main.wasm.gz headlamp/package.json \
~/.config/Headlamp/plugins/hallpass/
It adds:
- hallpass in the sidebar: the pending requests of the cluster, the
sessions (pod, container, node, policy) and the agents' connection
state and version. Each request has the answer buttons
once,temp,always,denyandnever; a request made in advance shows its folder, its duration and the program's reason, marked as unverified, withgrantanddeny. - A badge in the app bar with the number of pending requests, on every page. A blocked container waits for an answer.
- A hallpass section in the details of a mediated pod: its policy, pending requests and rules. Each rule can be edited (in the rule syntax) or deleted, and Add rule adds one, for this session only; an allow rule gives at most what answering "always" would.
- A hallpass column in the pod list:
mediated, or the number of pending requests. - Sandbox with hallpass, a pod action: it sets or removes
hallpass.io/policyon the pod template of the pod's Deployment, StatefulSet, DaemonSet or CronJob. Only containers created with the annotation are mediated: a Deployment, or a StatefulSet or DaemonSet with a rolling update, replaces its pods; withOnDelete, a StatefulSetpartitionor a ReplicaSet without a Deployment, delete the pods yourself; a CronJob changes from its next Job. The dialog says which. The namespace still needs thehallpass.io/enabled=truelabel. Bare pods and Jobs cannot change: edit their manifest instead.
Who can answer¶
There is no authentication on the session API beyond Kubernetes' own
on port-forward. The user needs to list pods in hallpass-system and
to create pods/portforward there. Whoever has these permissions can
answer for every mediated container in the cluster, from the CLI or
from Headlamp: grant them like exec into the mediated pods. The
Headlamp sandbox action also needs to list hallpasspolicies and to
patch the workload.
If the API server is unreachable, answering through port-forward
fails: requests wait until their promptTimeout.