When developing a program that uses/requires specific capabilities (e.g. cap_net_raw
), what is the recommended method for integrating the process of granting capabilities into the build process?
If I've understood the capabilities inheritance correctly, in order for the build system (e.g. CMake
) to be able to invoke setcap
to modify the capabilities of the build outputs, it would have to have the cap_setfcap
capability itself, or be ran with sudo
. And same for the parent context, be it a shell (e.g. bash
) or an IDE (e.g. VS Code).
The scheme above works nicely for debugging, e.g. to debug a program that requires cap_X
, I needed to give the same capability to gdb
and configure it to NOT start the program in a shell (to avoid having to give the same capability to bash
; I.e. the method outlined in this excellent thread: gdb appears to ignore executable capabilities)
Now, my obvious first choice would be to give the build system ( CMake
) the cap_setfcap
capability so that it can give the required capabilities to the build targets. This doesn't feel like proper solution, but more like an attempt to circumvent the whole capabilities system/framework, instead of operating within it.
Invoking setcap
using sudo
from the build also sounds like a bad idea, because it doesn't work nicely when invoking builds from within an IDE.
I figured I could add the following into sudo configuration:
<my-username-here> ALL=(ALL) NOPASSWD: /sbin/setcap
But this (use of sudo) also feels like a workaround and not a proper solution.
CodePudding user response:
Make a copy of the setcap
program. Make it executable only by you (or your build user) and give it the CAP_SETFCAP
capability.
$ cp `which setcap` ./build-setcap
$ chmod go-wrx ./build-setcap
$ sudo setcap cap_setfcap=p ./build-setcap
This binary is dangerous to have around, but you seem to be contemplating sudo with no password so this is equivalent.
Another method is to use an inheritable file capability:
$ cp `which setcap` ./build-setcap
$ chmod go-wrx ./build-setcap
$ sudo setcap cap_setfcap=i ./build-setcap
$ sudo capsh --inh=cap_setfcap --user=`whoami` --
which will place you in a shell where ./build-setcap
appears to be privileged. The last line causes a shell to start that has been granted an inheritable process capability. This is what unlocks the file-inheritable capability.
CodePudding user response:
what is the recommended method for integrating the process of granting capabilities into the build process?
My general philosophy for writing CMake is that only firm requirements (i.e. those that are necessary for producing a correct binary) belong in the CMakeLists.txt. If your program is useless without some capability, I think it's reasonable to consider setting it a "firm" requirement, even though it will technically build.
Thus, as far as CMake is concerned, a post-build custom command is most appropriate:
cmake_minimum_required(VERSION 3.22)
project(test)
##
# Find setcap and create an IMPORTED target for it
find_program(SETCAP_PROGRAM setcap REQUIRED)
#!! see below
add_executable(setcap::setcap IMPORTED)
set_target_properties(
setcap::setcap
PROPERTIES
IMPORTED_LOCATION "${SETCAP_PROGRAM}"
)
##
# Create privileged program with capability post-build step.
add_executable(privileged main.c)
add_custom_command(
TARGET privileged
POST_BUILD
COMMAND setcap::setcap cap_net_raw=p "$<TARGET_FILE:privileged>"
VERBATIM
)
Then you just need to build in an environment where you have the permissions to run ${SETCAP_PROGRAM}
, which is the system setcap
by default.
For instance, you could do as Tinkerer suggests and create your special build-setcap
binary somewhere and then set -DSETCAP_PROGRAM=/path/to/build-setcap
.
Alternatively, you could create (provide) a simple sudo-setcap.sh
script that prepends sudo
to the command line, like so:
#!/bin/sh
sudo -n setcap "$@"
Again, you'd set SETCAP_PROGRAM
to it. This would play nicely with your sudoers file approach. If you always build from the same directory, you could even put the exact command CMake runs in the sudoers file to limit its scope, as in /sbin/setcap cap_net_raw=p /home/user/project/build/privileged
As an elaboration on the sudo-setcap.sh
script idea, you could provide an option to users to automate this. Add the following code above in place of the #!! see below
comment:
option(SETCAP_USE_SUDO "When enabled, call setcap with sudo")
if (SETCAP_USE_SUDO)
find_program(SUDO_PROGRAM sudo REQUIRED)
file(CONFIGURE OUTPUT sudo-setcap.sh
CONTENT [[
#!/bin/sh
@SUDO_PROGRAM@ -n @SETCAP_PROGRAM@ "$@"
]]
@ONLY)
set(SETCAP_PROGRAM "${CMAKE_CURRENT_BINARY_DIR}/sudo-setcap.sh")
file(CHMOD "${SETCAP_PROGRAM}"
PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE)
endif ()
Then users can simply set -DSETCAP_USE_SUDO=YES
at the command line to wrap their system SETCAP_PROGRAM
with the same sudo
call I detailed above. This code overwrites SETCAP_PROGRAM
for the setcap::setcap
target to point at the script later, but does not touch the cache, which will still hold the path to the actual setcap
program.