惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

N
Netflix TechBlog - Medium
月光博客
月光博客
Y
Y Combinator Blog
WordPress大学
WordPress大学
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
雷峰网
雷峰网
美团技术团队
T
Tailwind CSS Blog
小众软件
小众软件
量子位
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
有赞技术团队
有赞技术团队
P
Proofpoint News Feed
G
Google Developers Blog
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Recent Announcements
Recent Announcements
The GitHub Blog
The GitHub Blog
博客园 - 三生石上(FineUI控件)
云风的 BLOG
云风的 BLOG
Vercel News
Vercel News
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
爱范儿
爱范儿
V
Visual Studio Blog
H
Hackread – Cybersecurity News, Data Breaches, AI and More

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant
Stop Using setuid for Everything: Practical Linux File Ca...
Lyra · 2026-04-26 · via DEV Community

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

A lot of Linux software does not actually need full root power. It needs one specific privilege.

Maybe it only needs to bind to port 80. Maybe it needs raw sockets. Maybe it needs one network admin action during startup. Reaching for sudo, setuid, or a root-owned service for all of that is the old habit, not the best habit.

Linux capabilities split root's all-or-nothing privilege model into smaller units. Used carefully, they let you give a process one narrow power instead of handing it the whole kingdom.

This guide is a practical walkthrough for auditing, granting, and verifying capabilities on Linux, with examples you can adapt on Debian, Ubuntu, and similar distributions.

Why capabilities are worth using

Traditional Unix privilege is blunt:

  • root bypasses normal permission checks
  • non-root users do not

Linux capabilities break that into separate privileges like:

  • CAP_NET_BIND_SERVICE to bind to ports below 1024
  • CAP_NET_RAW to use raw and packet sockets
  • CAP_SYS_ADMIN for a huge pile of admin operations

That last one is important: some capabilities are narrow, but some are still extremely broad. CAP_SYS_ADMIN is famously overloaded, so treating it as "basically root" is often the safer mental model.

The operational goal is simple:

  • avoid setuid root when one small privilege will do
  • avoid running long-lived services as root when a bounded capability is enough
  • verify what you changed, instead of assuming it is safe

First, audit what is already privileged

On Debian and Ubuntu, the getcap and setcap tools come from libcap2-bin.

sudo apt update
sudo apt install -y libcap2-bin

Enter fullscreen mode Exit fullscreen mode

List files that already carry file capabilities:

sudo getcap -r / 2>/dev/null

Enter fullscreen mode Exit fullscreen mode

Typical setuid files are worth reviewing too:

sudo find / -xdev -perm -4000 -type f -printf '%M %u %g %p\n'

Enter fullscreen mode Exit fullscreen mode

That gives you two different privilege surfaces:

  • executables with file capabilities
  • executables with the setuid bit

If you are replacing an old setuid helper, this comparison is the right place to start.

The safest beginner use case: binding to port 80 without running as root

A classic example is a service that only needs to listen on port 80 or 443.

The relevant capability is:

  • CAP_NET_BIND_SERVICE

Suppose your service binary lives at /usr/local/bin/myapp.

Grant only that capability:

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

Enter fullscreen mode Exit fullscreen mode

Verify it:

getcap /usr/local/bin/myapp

Enter fullscreen mode Exit fullscreen mode

Expected output:

/usr/local/bin/myapp cap_net_bind_service=ep

Enter fullscreen mode Exit fullscreen mode

Now you can run the service as a non-root user and still bind to port 80.

Important warning: do not put capabilities on a shared interpreter

This is a common mistake.

Do not do this on a general-purpose interpreter such as:

  • /usr/bin/python3
  • /usr/bin/node
  • /usr/bin/bash

If you attach a file capability to a widely used interpreter, every script launched through that interpreter can inherit that privilege path. That is usually much broader than you intended.

Better options:

  • put the capability on a dedicated compiled binary
  • use a service manager such as systemd to grant the capability to one service
  • front the app with a reverse proxy that already handles privileged ports

Prefer systemd for services you own

For managed services, systemd is often cleaner than editing file metadata on the executable.

Here is a minimal example for a service that should run as myapp, bind to port 80, and get no extra network privileges beyond that.

# /etc/systemd/system/myapp.service
[Unit]
Description=My app
After=network.target

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

[Install]
WantedBy=multi-user.target

Enter fullscreen mode Exit fullscreen mode

Apply it:

sudo systemctl daemon-reload
sudo systemctl enable --now myapp.service

Enter fullscreen mode Exit fullscreen mode

Why this is nicer for long-lived services:

  • privilege is declared in the unit, not hidden on the file
  • CapabilityBoundingSet= limits what the service can ever retain
  • AmbientCapabilities= passes the needed capability to a non-root process
  • NoNewPrivileges=true helps prevent gaining more privilege later

Check the resolved unit if you are debugging:

systemctl cat myapp.service
systemctl show myapp.service -p User -p Group -p AmbientCapabilities -p CapabilityBoundingSet -p NoNewPrivileges

Enter fullscreen mode Exit fullscreen mode

File capabilities vs systemd capabilities

Use file capabilities when:

  • you have one dedicated executable
  • the privilege should travel with that executable
  • the program may run outside systemd

Use systemd capability controls when:

  • the program is a service you manage
  • you want the privilege policy next to the rest of the service definition
  • you want a clean rollback by editing the unit rather than modifying executable metadata

My bias is simple:

  • for services, prefer systemd
  • for one-off dedicated binaries, file capabilities can be fine
  • for shared interpreters, avoid file capabilities

Removing or changing a capability

To remove file capabilities from an executable:

sudo setcap -r /usr/local/bin/myapp

Enter fullscreen mode Exit fullscreen mode

Verify removal:

getcap /usr/local/bin/myapp

Enter fullscreen mode Exit fullscreen mode

If nothing is printed, the file no longer has file capabilities.

A second example: inspecting ping is not enough anymore

Older writeups often use ping as the example for CAP_NET_RAW or setuid. That is not reliable as a universal teaching shortcut now.

Modern distributions vary:

  • some ship ping with file capabilities
  • some historically used setuid
  • some rely on kernel support for unprivileged ICMP echo sockets with net.ipv4.ping_group_range

So if you are auditing a real host, inspect the local system rather than assuming what /usr/bin/ping looks like.

Useful checks:

getcap "$(command -v ping)" 2>/dev/null || true
stat -c '%A %U:%G %n' "$(command -v ping)"
sysctl net.ipv4.ping_group_range 2>/dev/null || true

Enter fullscreen mode Exit fullscreen mode

That small habit avoids a lot of copy-paste folklore.

Capability names matter, and some are far riskier than they sound

A few practical rules:

  • prefer the narrowest capability that solves the problem
  • be suspicious of CAP_SYS_ADMIN
  • treat capability changes like a security change, not a convenience tweak
  • document why the capability exists
  • test as the unprivileged service user, not only as root

This is a bad pattern:

sudo setcap 'cap_sys_admin=+ep' /usr/local/bin/myapp

Enter fullscreen mode Exit fullscreen mode

This is the kind of pattern you should look for first:

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

Enter fullscreen mode Exit fullscreen mode

A practical audit workflow

When you want to replace broad privilege with something tighter, this sequence works well:

  1. identify what the program actually needs to do
  2. map that to the smallest capability that matches
  3. prefer service-level controls if the app is systemd-managed
  4. verify the file or service configuration after the change
  5. run a real functional test as the target non-root user
  6. document the reason so the next admin does not "fix" it back to root

Example verification checklist:

# file metadata
getcap /usr/local/bin/myapp

# service policy
systemctl show myapp.service -p AmbientCapabilities -p CapabilityBoundingSet -p NoNewPrivileges

# listener really came up on a privileged port
ss -ltnp '( sport = :80 )'

# service identity
ps -o user,group,comm,args -C myapp

Enter fullscreen mode Exit fullscreen mode

When capabilities are the wrong tool

Capabilities are not a magic replacement for every privileged workflow.

They are often the wrong choice when:

  • the application still needs broad filesystem access that effectively requires root
  • you are tempted to use CAP_SYS_ADMIN
  • the program is launched through a shared interpreter
  • a reverse proxy, socket activation, or a small privileged helper would be cleaner

Least privilege is not just "fewer root shells". It is choosing the least dangerous mechanism that still keeps operations simple.

Final thought

If a service only needs one narrow privilege, give it one narrow privilege.

That is the real value of Linux capabilities. Not novelty, not cleverness, just a smaller blast radius and a setup you can actually explain during an audit.

References