Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .deploy/helm/eupf/Chart.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
apiVersion: v2
name: eupf
version: 0.7.1
version: 0.7.2
appVersion: 0.7.1
description: Helm chart for deploy eUPF to Kubernetes
icon: https://avatars.githubusercontent.com/u/119618995?s=200&v=4
15 changes: 14 additions & 1 deletion .deploy/helm/eupf/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,20 @@ The default values produce a working pod: eUPF is configured via `UPF_*` environ
privileged: true
```

- The `/sys` hostPath is mounted read-only into the pod (required by the entrypoint to mount bpffs/debugfs).
- **Non-root** (optional): the image carries `NET_ADMIN`, `SYS_RESOURCE`, `BPF` and `PERFMON` as file capabilities on the `eupf` binary, plus `DAC_OVERRIDE` so it can create its map pin directory under `/sys/fs/bpf`, which root owns. With them the container runs as a non-root user, with less privilege than root plus capabilities: `SYS_ADMIN` is not needed, since a non-root entrypoint mounts nothing and the kernels the chart supports have `CAP_BPF`:

```yaml
securityContext:
runAsUser: 1000
runAsGroup: 1000
runAsNonRoot: true
capabilities:
add: [NET_ADMIN, SYS_RESOURCE, BPF, PERFMON, DAC_OVERRIDE]
```

Grant exactly that set, as root or not: a binary with file capabilities fails to exec (`Operation not permitted`) when one of them is missing from the container's bounding set. The chart's defaults do; `docker run` needs `--privileged` or `--cap-add` for each. Leave `allowPrivilegeEscalation` unset: file capabilities need it. A non-root process cannot mount bpffs itself, so the **node must have bpffs mounted at `/sys/fs/bpf`**, as systemd hosts do (`sys-fs-bpf.mount`). kind nodes don't; bind the host's in with `extraMounts: [{hostPath: /sys/fs/bpf, containerPath: /sys/fs/bpf}]` on every node.

- The `/sys` hostPath is mounted read-only into the pod: as root the entrypoint mounts bpffs/debugfs under it, as non-root eUPF reaches the node's bpffs through it.

## Configuration

Expand Down
17 changes: 15 additions & 2 deletions .deploy/helm/eupf/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -95,8 +95,9 @@ configMaps: {}
# foo: bar

# volumes.
# The /sys hostPath volume is required: the container entrypoint mounts
# bpffs and debugfs under /sys to load the eBPF data plane.
# The /sys hostPath volume is required: as root, the container entrypoint
# mounts bpffs and debugfs under /sys to load the eBPF data plane; as a
# non-root user (see securityContext) eUPF uses the node's bpffs through it.
volumes:
- name: sys
hostPath:
Expand Down Expand Up @@ -232,6 +233,18 @@ podSecurityContext:
# blocks mount or bpf, a runtime that does not know CAP_BPF on a kernel
# before 5.8), fall back to a privileged container:
# privileged: true
# The image carries NET_ADMIN, SYS_RESOURCE, BPF and PERFMON as file
# capabilities on the eupf binary, plus DAC_OVERRIDE to create its map pin
# directory under /sys/fs/bpf, so the container can run as a non-root user
# wherever the node has bpffs mounted at /sys/fs/bpf (systemd hosts do; the
# README covers kind). Grant exactly that set: with one missing, exec fails
# with EPERM. SYS_ADMIN is not needed without root (no mounts, CAP_BPF on the
# kernels the chart supports):
# runAsUser: 1000
# runAsGroup: 1000
# runAsNonRoot: true
# capabilities:
# add: [NET_ADMIN, SYS_RESOURCE, BPF, PERFMON, DAC_OVERRIDE]
securityContext:
capabilities:
add: [NET_ADMIN, SYS_ADMIN, SYS_RESOURCE, BPF, PERFMON]
Expand Down
12 changes: 12 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -28,5 +28,17 @@ COPY --from=builder /app/cmd/docs/swagger.* /app/
COPY --from=builder /app/cmd/ebpf/zeroentrypoint_bpf.o /app/
COPY ./entrypoint.sh /app/bin/entrypoint.sh

# File capabilities, so eupf gets what it needs without running as root:
# BPF and PERFMON load the eBPF programs (kernel 5.8+, the chart requires
# 5.15), NET_ADMIN attaches XDP and sets routes, SYS_RESOURCE raises
# RLIMIT_MEMLOCK, and DAC_OVERRIDE creates the pin directory under
# /sys/fs/bpf, which is owned by root. Whoever runs the image, as root or
# not, must grant all of them (the chart does; docker needs --privileged or
# --cap-add): exec of a binary with the effective bit set fails with EPERM
# when one is missing from the bounding set.
RUN apk add --no-cache libcap \
&& setcap cap_net_admin,cap_sys_resource,cap_bpf,cap_perfmon,cap_dac_override=ep /app/bin/eupf \
&& apk del libcap
Comment thread
pirog-spb marked this conversation as resolved.
Resolved

# CMD is overridden if arguments are passed.
ENTRYPOINT [ "sh", "/app/bin/entrypoint.sh" ]
16 changes: 10 additions & 6 deletions entrypoint.sh
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
#!/bin/env sh

# Mount bpffs and debugfs if not present already
if [[ $(/bin/mount | /bin/grep /sys/fs/bpf -c) -eq 0 ]]; then
/bin/mount bpffs /sys/fs/bpf -t bpf;
fi
if [[ $(/bin/mount | /bin/grep /sys/kernel/debug -c) -eq 0 ]]; then
/bin/mount debugfs /sys/kernel/debug -t debugfs;
# Mount bpffs and debugfs if not present already. Only root can mount: as a
# non-root user the node has to provide bpffs at /sys/fs/bpf (the chart's
# README says how).
if [ "$(id -u)" -eq 0 ]; then
if [[ $(/bin/mount | /bin/grep /sys/fs/bpf -c) -eq 0 ]]; then
/bin/mount bpffs /sys/fs/bpf -t bpf;
fi
if [[ $(/bin/mount | /bin/grep /sys/kernel/debug -c) -eq 0 ]]; then
/bin/mount debugfs /sys/kernel/debug -t debugfs;
fi
fi

# Run app replacing current shell (to preserve signal handling) and forward cmd arguments
Expand Down
Loading