Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for "rootless" ping #642

Merged
merged 2 commits into from
Jun 16, 2020
Merged

Add support for "rootless" ping #642

merged 2 commits into from
Jun 16, 2020

Conversation

dgl
Copy link
Member

@dgl dgl commented Jun 16, 2020

This works for Linux and Darwin.

On Linux the user running the exporter needs to be a member of a group
with an ID in the range specified in the sysctl
net.ipv4.ping_group_range.

Fixes #147.

I've tested this on:

Darwin, macOS 10.15.4

Works, able to set ID and confirmed with tcpdump the ID is the same on outgoing
packets.

Linux 5.4.43 (NixOS)

Kernel always overwrites ID, e.g.:

ts=2020-06-16T11:53:06.722Z caller=main.go:169 module=icmp target=127.0.0.1 level=debug msg="Creating ICMP packet" seq=46268 id=17449

tcpdump:

12:53:06.722861 IP 127.0.0.1 > 127.0.0.1: ICMP echo request, id 62, seq 46268, length 36

The code takes this into account.

Works when:

sysctl -w net.ipv4.ping_group_range='0	2147483647'

Example log:

ts=2020-06-16T11:53:06.722Z caller=main.go:169 module=icmp target=127.0.0.1 level=debug msg="Creating socket"
ts=2020-06-16T11:53:06.722Z caller=main.go:169 module=icmp target=127.0.0.1 level=debug msg="Creating ICMP packet" seq=46268 id=17449
ts=2020-06-16T11:53:06.722Z caller=main.go:169 module=icmp target=127.0.0.1 level=debug msg="Writing out packet"
ts=2020-06-16T11:53:06.722Z caller=main.go:169 module=icmp target=127.0.0.1 level=debug msg="Waiting for reply packets"
ts=2020-06-16T11:53:06.722Z caller=main.go:169 module=icmp target=127.0.0.1 level=debug msg="Found matching reply packet"

Fails when running as user with:

sysctl -w net.ipv4.ping_group_range='0 0'

Example log:

ts=2020-06-16T11:42:16.671Z caller=main.go:169 module=icmp target=127.0.0.1 level=debug msg="Unable to do unprivileged listen on socket, will attempt privileged" err="socket: permission denied"
ts=2020-06-16T11:42:16.671Z caller=main.go:169 module=icmp target=127.0.0.1 level=debug msg="Error listening to socket" err="listen ip4:icmp 0.0.0.0: socket: operation not permitted"

Succeeds using raw sockets when run as user with CAP_NET_RAW but not in ping_group_range:

sudo setcap cap_net_raw=eip ./blackbox_exporter

Example log:

ts=2020-06-16T11:43:49.143Z caller=main.go:169 module=icmp target=127.0.0.1 level=debug msg="Unable to do unprivileged listen on socket, will attempt privileged" err="socket: permission denied"
ts=2020-06-16T11:43:49.143Z caller=main.go:169 module=icmp target=127.0.0.1 level=debug msg="Creating ICMP packet" seq=41274 id=16135

Succeeds when run as root with (non default):

sysctl -w net.ipv4.ping_group_range='0 0'

Example log:

ts=2020-06-16T11:45:19.848Z caller=main.go:169 module=icmp target=127.0.0.1 level=debug msg="Creating socket"
ts=2020-06-16T11:45:19.848Z caller=main.go:169 module=icmp target=127.0.0.1 level=debug msg="Creating ICMP packet" seq=6944 id=16229
ts=2020-06-16T11:45:19.848Z caller=main.go:169 module=icmp target=127.0.0.1 level=debug msg="Writing out packet"
ts=2020-06-16T11:45:19.848Z caller=main.go:169 module=icmp target=127.0.0.1 level=debug msg="Waiting for reply packets"
ts=2020-06-16T11:45:19.848Z caller=main.go:169 module=icmp target=127.0.0.1 level=debug msg="Found matching reply packet"

Note this uses "udp" sockets when run as root, if they work (no fallback
output). The thinking being root in a Docker container may not have CAP_NET_RAW
but may be able to use ping sockets.

Succeeds using raw sockets when run as root with ping_group_range disabled
(this is the default Linux kernel setting, but overridden via sysctl.conf on
many recent distributions default configuration):

sudo sysctl -w net.ipv4.ping_group_range='1 0'

Example log:

ts=2020-06-16T11:49:28.473Z caller=main.go:169 module=icmp target=127.0.0.1 level=debug msg="Creating socket"
ts=2020-06-16T11:49:28.473Z caller=main.go:169 module=icmp target=127.0.0.1 level=debug msg="Unable to do unprivileged listen on socket, will attempt privileged" err="socket: permission denied"
ts=2020-06-16T11:49:28.473Z caller=main.go:169 module=icmp target=127.0.0.1 level=debug msg="Creating ICMP packet" seq=39844 id=16445
ts=2020-06-16T11:49:28.473Z caller=main.go:169 module=icmp target=127.0.0.1 level=debug msg="Writing out packet"
ts=2020-06-16T11:49:28.473Z caller=main.go:169 module=icmp target=127.0.0.1 level=debug msg="Waiting for reply packets"
ts=2020-06-16T11:49:28.473Z caller=main.go:169 module=icmp target=127.0.0.1 level=debug msg="Found matching reply packet"

Prober configured with dont_fragment: true, fails running as normal user as
it must use raw sockets:

ts=2020-06-16T12:00:21.924Z caller=main.go:169 module=icmp target=127.0.0.1 level=debug msg="Creating socket"
ts=2020-06-16T12:00:21.924Z caller=main.go:169 module=icmp target=127.0.0.1 level=debug msg="Error listening to socket" err="listen ip4:icmp 0.0.0.0: socket: operation not permitted"

This works for Linux and Darwin.

On Linux the user running the exporter needs to be a member of a group
with an ID in the range specified in the sysctl
net.ipv4.ping_group_range.

Signed-off-by: David Leadbeater <[email protected]>
prober/icmp.go Show resolved Hide resolved
prober/icmp.go Outdated Show resolved Hide resolved
README.md Outdated Show resolved Hide resolved
Update some comments and mention that dont_fragment needs raw sockets.

Signed-off-by: David Leadbeater <[email protected]>
@dgl
Copy link
Member Author

dgl commented Jun 16, 2020

Thanks for the review, I've updated some comments, also added a note about dont_fragment in the configuration doc.

Copy link
Member

@SuperQ SuperQ left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, this is great!

@brian-brazil brian-brazil merged commit 3007522 into prometheus:master Jun 16, 2020
@brian-brazil
Copy link
Contributor

Thanks!

@dgl
Copy link
Member Author

dgl commented Jun 16, 2020

Did a little more investigation into where ping_group_range is set on Linux, as on several machines I was seeing net.ipv4.ping_group_range set to different values but it was unclear what differed.

It turns out as of systemd/systemd#13141 (via https://fedoraproject.org/wiki/Changes/EnableSysctlPingGroupRange) if you're using systemd then this is set by it; the relevant file ends up not in /etc, but somewhere like /usr/lib/sysctl.d/50-default.conf

Not sure it's worth documenting the intricacies of systemd in the blackbox_exporter README, but maybe this helps someone understand what's going on.

@dgl dgl deleted the i147 branch June 16, 2020 19:55
@brian-brazil
Copy link
Contributor

I think mentioning the standard sysctl.conf location is fine, rather than getting into per-distro stuff.

`net.ipv4.ping_group_range = 0 2147483647` to allow any user the ability
to use ping.
* Alternatively the capability can be set by executing `setcap cap_net_raw+ep
blackbox_exporter`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another way is to do it in the systemd unit which starts blackbox_exporter:

[Service]
...
AmbientCapabilities=CAP_NET_RAW

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.

rootless ICMP ping
4 participants