Skip to content

Fix setuid iptables DOCKER-USER rules on legacy backends - #3353

Open
jbemmel wants to merge 2 commits into
srl-labs:mainfrom
jbemmel:fix/setuid-iptables-legacy-exit-111
Open

Fix setuid iptables DOCKER-USER rules on legacy backends#3353
jbemmel wants to merge 2 commits into
srl-labs:mainfrom
jbemmel:fix/setuid-iptables-legacy-exit-111

Conversation

@jbemmel

@jbemmel jbemmel commented Aug 23, 2026

Copy link
Copy Markdown
Contributor

What is broken

Containerlab can be installed setuid-root for clab_admins. In that mode the process keeps the invoking user’s real UID (ruid) and elevates only the effective UID (euid=0). Privilege work runs as root; later file ownership under $HOME still follows the real user.

For mgmt external-access, if the nftables path isn’t usable (e.g. Docker on iptables-legacy, DOCKER-USER only in legacy iptables), containerlab shells out to iptables / ip6tables to add/remove DOCKER-USER ACCEPT rules for the mgmt bridge.

From iptables 1.8.8, libxtables refuses to run when getuid() != geteuid(). It loads match/target plugins via dlopen and trusts env like LD_*, so running under a setuid parent is treated as unsafe. It exits 111 with little/no message. Containerlab logged iptables check error: exit status 111 and skipped the rules — so external access failed for setuid users. sudo containerlab worked because both UIDs are 0.

What this PR changes

Only the iptables CLI spawn path:

Matching root UIDs in the child — via SysProcAttr.Credential so the child gets Uid: 0 (Linux setuid(0) as euid-root sets real/effective/saved). Parent keeps ruid=user.
Strip LD_* from the child’s env so loader overrides don’t ride along once UIDs match.
Nftables is untouched

iptables ≥ 1.8.8 exits 111 when ruid != euid; run those CLIs with
matching root UIDs in the child and strip LD_* so setuid clab_admins
hosts can install/remove mgmt external-access rules.

Co-authored-by: Cursor <cursoragent@cursor.com>
@jbemmel

jbemmel commented Aug 23, 2026

Copy link
Copy Markdown
Contributor Author

This can probably be simplified to not pass any env variables at all

@jbemmel
jbemmel marked this pull request as draft August 24, 2026 00:57
Use an empty child environment instead of filtering LD_*, and drop the
separate exec.go so the iptables spawn path stays in one file.

Co-authored-by: Cursor <cursoragent@cursor.com>
@jbemmel
jbemmel marked this pull request as ready for review August 24, 2026 01:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant