Skip to main content

On This Page

Secure Linux Privilege Management with File Capabilities and systemd

3 min read
Share

These articles are AI-generated summaries. Please check the original sources for full details.

Stop Using setuid for Everything: Practical Linux File Capabilities with getcap, setcap, and systemd

Linux capabilities decompose the traditional root privilege model into smaller, manageable units to prevent handing over the entire system kingdom. For example, CAP_NET_BIND_SERVICE allows a process to bind to ports below 1024 without full root access.

Why This Matters

Traditional Unix privilege is a blunt instrument where processes either have full root access or none at all, creating a massive blast radius during security breaches. By shifting to a capability-based model, engineers can implement ‘least privilege’ effectively, though they must avoid common pitfalls like over-granting CAP_SYS_ADMIN which remains dangerously broad.

The technical reality involves auditing existing setuid binaries and replacing them with scoped capabilities. Using systemd for these declarations is often superior to file metadata because it centralizes security policy and enables modern protections like NoNewPrivileges=true to prevent further privilege escalation.

Key Insights

  • Linux capabilities split root’s all-or-nothing privilege into units like CAP_NET_BIND_SERVICE and CAP_NET_RAW (man7.org).
  • The libcap2-bin package provides getcap and setcap tools necessary for auditing and modifying file capabilities on Debian/Ubuntu.
  • CAP_SYS_ADMIN is often overloaded and should be treated as equivalent to full root access in most security models.
  • Applying capabilities to shared interpreters like /usr/bin/python3 or /usr/bin/node grants those privileges to every script they execute.
  • Modern systemd units can manage privileges via AmbientCapabilities and CapabilityBoundingSet without modifying executable metadata.

Working Examples

Granting and verifying the capability to bind to privileged ports on a specific binary.

sudo setcap 'cap_net_bind_service=+ep' /usr/local/bin/myapp
getcap /usr/local/bin/myapp

Configuring a systemd service to use specific capabilities while restricting all others.

[Service]
User=myapp
Group=myapp
ExecStart=/usr/local/bin/myapp
AmbientCapabilities=CAP_NET_BIND_SERVICE
CapabilityBoundingSet=CAP_NET_BIND_SERVICE
NoNewPrivileges=true

Auditing the entire file system for existing file capabilities.

sudo getcap -r / 2>/dev/null

Practical Applications

  • Web Server Deployment: Use CAP_NET_BIND_SERVICE to allow a non-root service user to listen on port 80 or 443. Pitfall: Using an interpreter like Node.js directly with setcap instead of a dedicated binary or systemd wrapper.
  • Network Diagnostics: Grant CAP_NET_RAW to specialized tools to allow packet capture without root. Pitfall: Relying on old documentation for ‘ping’ which may now use kernel-level net.ipv4.ping_group_range instead of capabilities.
  • Service Hardening: Implement CapabilityBoundingSet in systemd to ensure a compromised service cannot acquire new capabilities. Pitfall: Failing to set NoNewPrivileges=true, leaving the service vulnerable to gaining privileges via execve.

References:

Continue reading

Next article

Accelerating Apache Iceberg Migration with Federated Semantic Layers

Related Content