Linux Identity Model
This page captures the user, group, file ownership, and runtime directory model shared by systemd and static-pod deployments. The API server must be able to connect to the provider socket. It must not gain access to provider auth material or writable provider state through that socket access path.
Goals
- Run the provider as a non-root user.
- Let the provider read its configuration and selected auth material.
- Let
kube-apiserverconnect to the Unix socket. - Avoid giving
kube-apiserveraccess to provider auth material. - Avoid making the provider primary group equal to the
kube-apiservergroup. - Keep the model workable for kubeadm static-pod API servers and host-service API servers.
Selected Model
user: openbao-kms
group: openbao-kms
socket group: openbao-kms-socket
Package installs create these identities through sysusers.d where available. Host images without sysusers.d support should create equivalent system users and groups during image build or configuration management.
Permissions:
/etc/openbao-kms/config.yaml root:openbao-kms 0640
/etc/openbao-kms/tls/ca.crt root:root 0644
/var/lib/openbao-kms openbao-kms:openbao-kms 0750
/var/lib/openbao-kms/identity.jwt root:openbao-kms 0640
/etc/openbao-kms/client/client-chain.pem root:openbao-kms 0640
/etc/openbao-kms/pkcs11/pin root:openbao-kms 0640
/var/lib/openbao-kms/state openbao-kms:openbao-kms 0750
/run/openbao-kms openbao-kms:openbao-kms-socket 2750
/run/openbao-kms/kms.sock openbao-kms:openbao-kms-socket 0660
Access matrix:
| Actor | Required access | Must not have |
|---|---|---|
bao-kms-provider process | read config, CA, selected auth material; write local registry state; create and own kms.sock | broad host write access or Linux capabilities |
kube-apiserver process | connect to /run/openbao-kms/kms.sock | read access to provider auth material |
| OpenBao administrator | manage Transit key, policy, and provider auth | access to Kubernetes etcd plaintext through this model |
| package manager or host automation | create users, groups, directories, unit files, and examples | runtime access to provider token material after rollout |
systemd service:
User=openbao-kms
Group=openbao-kms
SupplementaryGroups=openbao-kms-socket
The local kube-apiserver identity should be allowed to connect through the socket group. On hosts where kube-apiserver runs as root, root can connect regardless. The group model still provides a non-root packaging path.
Static pod mode uses the numeric host GID for openbao-kms-socket in both:
spec.securityContext.supplementalGroups,server.socketGroupin provider configuration.
This avoids depending on host group names being present inside the distroless non-root image.
For static pods, use the numeric GID from the host:
getent group openbao-kms-socket
The third field in the output is the value used in both the pod manifest and static-pod provider configuration.
Runtime Directory Creation
RuntimeDirectory= alone may create /run/openbao-kms with the service primary group rather than the socket access group. Packaging should prefer one of:
- a
tmpfiles.dentry that creates/run/openbao-kmswithopenbao-kms:openbao-kms-socketand mode2750, - a privileged package install step that creates the directory before service start,
- a root pre-start helper that only creates and
chowns the runtime directory.
The provider validates the directory at startup and fails closed if it is unsafe.
The mode 2750 is intentional:
- owner
openbao-kmscan create and remove the socket, - group
openbao-kms-socketcan traverse the directory, - the setgid bit keeps the socket group stable,
- the group cannot replace arbitrary files in the directory because group write is absent,
- world access is absent.
The socket itself is 0660, so members of openbao-kms-socket can connect to the provider without receiving access to auth material or registry state.
Tradeoffs
Separate Socket Group
Pros:
kube-apiservergets socket access without auth-material access,kube-apiservercan connect without being able to replace the socket path,- the provider keeps a private primary group,
- the model works with non-root
kube-apiserverservices, - the privilege boundary is explicit.
Cons:
- packaging must create an additional group,
kube-apiservergroup membership varies by distribution,- static pod deployments need host group mapping or root access.
Primary Group Equals kube-apiserver Group
Pros:
- simpler socket access,
- fewer groups to create.
Cons:
- easier to accidentally expose provider-readable files to the API server group,
- weaker privilege separation,
- distribution-specific
kube-apiservergroup naming leaks into provider packaging.
Root-Owned Socket Directory
Pros:
- straightforward for kubeadm static-pod API servers running as root,
- avoids
kube-apiservergroup detection.
Cons:
- weaker non-root story,
- less portable to hardened API server services,
- can hide permission problems until deployment hardening.
Decision
The separate socket group is the default packaging model. Distribution packaging may choose different names, but it must preserve the same privilege split: provider auth-material access is separate from kube-apiserver socket access.