Skip to content

update ping privileged/unprivileged logic#1586

Merged
seriousm4x merged 9 commits into
seriousm4x:masterfrom
invario:new-logic-entrypoint
Mar 19, 2026
Merged

update ping privileged/unprivileged logic#1586
seriousm4x merged 9 commits into
seriousm4x:masterfrom
invario:new-logic-entrypoint

Conversation

@invario

@invario invario commented Jan 15, 2026

Copy link
Copy Markdown
Contributor

Resolves #1574

The following changes were made:

1. Dockerfile creates two copies of the UpSnap executable: a so-called privileged one with NET_RAW capability (which is already the current case) and an un-privileged one.
2. Dockerfile will generate an entrypoint.sh which will detect the UPSNAP_PING_PRIVILEGED user defined setting, NET_RAW capability, UID/GID, no-new-privs flag, and net.ipv4.ping_group_range.
3. If UPSNAP_PING_PRIVILEGED is not set to false or (0), it will default to TRUE and will attempt to run the privileged executable if all conditions are met. Otherwise, it will halt and display an error. I can reconfigure this behavior if you wish and have it "fall back" to attempt the unprivileged executable in (4) below
4. If it is set to FALSE, it will run the unprivileged executable if the GID is within the configured net.ipv4.ping_group_range. If not, fail with error.
5. Removed root.go and root_windows.go and default to true for privileged ping since the isRoot() function isn't needed. Windows requires setPrivileged(true) regardless of user/admin, and not being root on Linux is not an automatic disqualification for 'privileged' ping.
6. Update docker-compose.yml with additional notes and also explicitly cap_add NET_RAW and cap_drop everything else.

See this post below for the latest

To Do:

  • Update Wiki
  • Update Readme

Submitted for yours and anyone else's reviews. Open to comments, suggestions ,etc. Especially regarding item 3.

@seriousm4x

Copy link
Copy Markdown
Owner

I really don't know about this approach. I think it makes the code really tricky and convoluted. It's hard to understand what happens. I guess it works, but I'm not happy with having 2 binaries.

Also the shell script is cluttered with new line breaks. Please use a standalone shell script (entrypoint.sh) instead of printf to file.

If this get cleaned up I hope I will merge it.

@invario

invario commented Feb 12, 2026

Copy link
Copy Markdown
Contributor Author

I really don't know about this approach. I think it makes the code really tricky and convoluted. It's hard to understand what happens. I guess it works, but I'm not happy with having 2 binaries.

Also the shell script is cluttered with new line breaks. Please use a standalone shell script (entrypoint.sh) instead of printf to file.

If this get cleaned up I hope I will merge it.

Yeah, I agree with all your points. I've been racking my brains trying to figure out a more elegant solution to this that wouldn't require two binaries.

Also, I was originally going to do an entrypoint.sh as a file but was really trying to keep everything self-contained in the Dockerfile since that seemed to be how things are at the moment.

@ZeroKnight

ZeroKnight commented Feb 16, 2026

Copy link
Copy Markdown

While I would like to be able to drop all capabilities and run the container unprivileged with unprivileged pings, I'm also not keen on the complexity added here. If running the upsnap with CAP_NET_RAW should be conditional, then libcap should be added to the final layer and setcap should be run as part of the entrypoint (instead of at build time) rather than having two binaries with different capabilities.

Please use a standalone shell script (entrypoint.sh) instead of printf to file.

Very much agreed.


As an alternative, perhaps the libcap Go package could be a consideration? If I understand the capabilities interface correctly, it should be possible to allow upsnap to gain capabilities at its discretion. Instead of granting it CAP_NET_RAW unconditionally via +ep, I think you can instead allow it to gain the capability via +p or +ip and then claiming that capability at runtime (perhaps switching on UPSNAP_PING_PRIVILEGED) via the cap package. Take a peek at the capabilities(7) man page, specifically "Thread capability sets" and "Programmatically adjusting capability sets".

That said, I'm wondering if there's a simpler way still to support privileged and unprivileged use cases. Can we get away with not setting capabilities on upsnap at all? Assuming you don't setcap upsnap ..., would including --cap-add net_raw when running the container allow it to use privileged ping still? If so, then the extra logic wouldn't be necessary; just mandate --cap-add net_raw if privileged ping is desired.

FWIW, I think distros might be moving toward the net.ipv4.ping_group_range approach specifically for its use in containers/VMs and not having to rely on setuid and capabilities. Debian and Red Hat family distros like Fedora and openSUSE have gone this route by default.

@invario

invario commented Feb 21, 2026

Copy link
Copy Markdown
Contributor Author

While I would like to be able to drop all capabilities and run the container unprivileged with unprivileged pings, I'm also not keen on the complexity added here. If running the upsnap with CAP_NET_RAW should be conditional, then libcap should be added to the final layer and setcap should be run as part of the entrypoint (instead of at build time) rather than having two binaries with different capabilities.

Please use a standalone shell script (entrypoint.sh) instead of printf to file.

Very much agreed.

As an alternative, perhaps the libcap Go package could be a consideration? If I understand the capabilities interface correctly, it should be possible to allow upsnap to gain capabilities at its discretion. Instead of granting it CAP_NET_RAW unconditionally via +ep, I think you can instead allow it to gain the capability via +p or +ip and then claiming that capability at runtime (perhaps switching on UPSNAP_PING_PRIVILEGED) via the cap package. Take a peek at the capabilities(7) man page, specifically "Thread capability sets" and "Programmatically adjusting capability sets".

I'm going to look into this but what you wrote above sounds great if it works.

That said, I'm wondering if there's a simpler way still to support privileged and unprivileged use cases. Can we get away with not setting capabilities on upsnap at all? Assuming you don't setcap upsnap ..., would including --cap-add net_raw when running the container allow it to use privileged ping still? If so, then the extra logic wouldn't be necessary; just mandate --cap-add net_raw if privileged ping is desired.

FWIW, I think distros might be moving toward the net.ipv4.ping_group_range approach specifically for its use in containers/VMs and not having to rely on setuid and capabilities. Debian and Red Hat family distros like Fedora and openSUSE have gone this route by default.

If you don't setcap the binary via the Dockerfile, adding --cap-add net_raw at container runtime with non-root results in [ERROR] 2026/02/20 21:05:15 cronjobs.go:64: listen ip4:icmp : socket: operation not permitted due to root being required for net_raw due to using the host network/network mode (from my understanding.)

@invario

invario commented Mar 3, 2026

Copy link
Copy Markdown
Contributor Author

Still working on this. BTW, we can't run setcap during runtime (entrypoint) because setcap requires the CAP_SETFCAP capability on the container itself, and possibly also root.

Also, another way is to have a separate unprivileged build with a different tag. The regular nginx Docker image, for example, can run as non-root, but another (and IMHO, better) way is to run nginx-unprivileged at https://hub.docker.com/r/nginxinc/nginx-unprivileged

@invario invario force-pushed the new-logic-entrypoint branch from f39f57e to 41c05f0 Compare March 5, 2026 00:29
invario added 2 commits March 4, 2026 19:30
Signed-off-by: invario <67800603+invario@users.noreply.github.com>
Signed-off-by: invario <67800603+invario@users.noreply.github.com>
@invario invario force-pushed the new-logic-entrypoint branch from 41c05f0 to beb52a6 Compare March 5, 2026 00:35
@invario

invario commented Mar 5, 2026

Copy link
Copy Markdown
Contributor Author

@ZeroKnight Okay, based on your VERY useful suggestions/comments, here's what I did and it seems to work:

Changes:

  • Removed entrypoint.sh. I did away with this because it's not necessary now, and also even if I wanted it to work, it seemed to be preventing the upsnap binary from being given the ability to raise NET_RAW despitebcap_add=NET_RAW. It only works if the entrypoint is the actual binary. I can't even use sh -c for the entrypoint, thus why I removed it. This may possibly be due to no-new-privileges being set.
  • Dockerfile modified to only use +p which (as you pointed out) appears to allow the binary to still run!
  • Added the cap package (only for Linux builds)
  • Forked off ping.go to ping_linux.go and added code there to raised the NET_RAW capability when needed (this only pertains to Linux)
  • No need to check for root so I removed the corresponding files. Privileged ping is required for Windows anyway, and with the above changes, root is not required in Linux anymore, depending on settings
  • no-new-privileges CAN be set and privileged ping WILL still work if NET_RAW is enabled
  • And as before, UN-privileged ping still requires sysctl net.ipv4.ping_group_range to be set to a range that includes the UID the container runs with.

I would GREATLY appreciate comments, input, TESTING (especially this).

Thanks!

@invario invario changed the title update ping privileged/unprivileged logic, add entrypoint update ping privileged/unprivileged logic Mar 7, 2026
Signed-off-by: invario <67800603+invario@users.noreply.github.com>
@invario

invario commented Mar 15, 2026

Copy link
Copy Markdown
Contributor Author

Latest commit, didn't change code significantly. Refactored the code to only separate only the pingdevice function into separate files for Linux and other.

@seriousm4x

Copy link
Copy Markdown
Owner

Thanks for all your work you've put into this! I've just cleaned up some minor things.

I've run some tests:

  • Linux: Works as expected when setting sudo setcap cap_net_raw=+p ./upsnap.
  • Windows works, but doesn't need any preperation, just run it.
  • Mac: Has no setcap equivalent. Pings would fail when running as normal user, so I've set privileged = false on macOS which seems to work just fine.

Will merge and release a beta once i looked at the other pr's.

@seriousm4x seriousm4x merged commit 1d361e3 into seriousm4x:master Mar 19, 2026
1 check passed
@invario

invario commented Mar 19, 2026

Copy link
Copy Markdown
Contributor Author

Thanks for merge and all the fixes and the testing!

@invario invario deleted the new-logic-entrypoint branch March 19, 2026 12:57
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.

[BUG] better documentation needed for rootless (possibly needs behavior change also)

3 participants