Skip to content

Commit

Permalink
src: allow CAP_NET_BIND_SERVICE in SafeGetenv
Browse files Browse the repository at this point in the history
This commit updates SafeGetenv to check if the current process has the
effective capability cap_net_bind_service set, and if so allows
environment variables to be read.

The motivation for this change is a use-case where Node is run in a
container, and the is a requirement to be able to listen to ports
below 1024. This is done by setting the capability of
cap_net_bind_service. In addition there is a need to set the
environment variable `NODE_EXTRA_CA_CERTS`. But currently this
environment variable will not be read when the capability has been set
on the executable.
  • Loading branch information
danbev committed Mar 29, 2021
1 parent 45cdc13 commit 0a07565
Show file tree
Hide file tree
Showing 6 changed files with 54 additions and 1 deletion.
4 changes: 4 additions & 0 deletions .github/workflows/build-tarball.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ jobs:
python-version: ${{ env.PYTHON_VERSION }}
- name: Environment Information
run: npx envinfo
- name: Install libcap-dev
run: sudo apt-get install -y libcap-dev
- name: Make tarball
run: |
export DISTTYPE=nightly
Expand All @@ -51,6 +53,8 @@ jobs:
python-version: ${{ env.PYTHON_VERSION }}
- name: Environment Information
run: npx envinfo
- name: Install libcap-dev
run: sudo apt-get install -y libcap-dev
- name: Download tarball
uses: actions/download-artifact@v1
with:
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/coverage-linux.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ jobs:
run: npx envinfo
- name: Install gcovr
run: pip install gcovr==4.2
- name: Install libcap-dev
run: sudo apt-get install -y libcap-dev
- name: Build
run: make build-ci -j2 V=1 CONFIG_FLAGS="--error-on-warn --coverage"
# TODO(bcoe): fix the couple tests that fail with the inspector enabled.
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/test-asan.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ jobs:
python-version: ${{ env.PYTHON_VERSION }}
- name: Environment Information
run: npx envinfo
- name: Install libcap-dev
run: sudo apt-get install -y libcap-dev
- name: Build
run: make build-ci -j2 V=1
- name: Test
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/test-linux.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ jobs:
python-version: ${{ env.PYTHON_VERSION }}
- name: Environment Information
run: npx envinfo
- name: Install libcap-dev
run: sudo apt-get install -y libcap-dev
- name: Build
run: make build-ci -j2 V=1 CONFIG_FLAGS="--error-on-warn"
- name: Test
Expand Down
3 changes: 3 additions & 0 deletions common.gypi
Original file line number Diff line number Diff line change
Expand Up @@ -373,6 +373,9 @@
'cflags': [ '-pthread' ],
'ldflags': [ '-pthread' ],
}],
[ 'OS in "linux"', {
'ldflags': [ '-Wl,-Bstatic -Wl,--whole-archive -lcap -Wl,--no-whole-archive -Wl,-Bdynamic' ],
}],
[ 'OS in "linux freebsd openbsd solaris android aix cloudabi"', {
'cflags': [ '-Wall', '-Wextra', '-Wno-unused-parameter', ],
'cflags_cc': [ '-fno-rtti', '-fno-exceptions', '-std=gnu++1y' ],
Expand Down
42 changes: 41 additions & 1 deletion src/node_credentials.cc
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@
#if !defined(_MSC_VER)
#include <unistd.h> // setuid, getuid
#endif
#ifdef __linux__
#include <sys/capability.h>
#endif // __linux__

namespace node {

Expand All @@ -33,11 +36,48 @@ bool linux_at_secure = false;

namespace credentials {

// Look up environment variable unless running as setuid root.
#if defined(__linux__)
// Returns true if the current process only has the passed-in capability.
bool HasOnly(cap_value_t capability) {
DCHECK(cap_valid(capability));

cap_t cap = cap_get_proc();
if (cap == nullptr) {
return false;
}

// Create a cap_t and set the capability passed in.
cap_t cap_cmp = cap_init();
if (cap_cmp == nullptr) {
cap_free(cap);
return false;
}
cap_value_t cap_list[] = { capability };
cap_set_flag(cap_cmp, CAP_EFFECTIVE, 1, cap_list, CAP_SET);
cap_set_flag(cap_cmp, CAP_PERMITTED, 1, cap_list, CAP_SET);
// Compare this to the process's effective capabilities
bool ret = cap_compare(cap, cap_cmp) == 0;

cap_free(cap);
cap_free(cap_cmp);

return ret;
}
#endif

// Look up the environment variable and allow the lookup if the current
// process only has the capability CAP_NET_BIND_SERVICE set. If the current
// process does not have any capabilities set and the process is running as
// setuid root then lookup will not be allowed.
bool SafeGetenv(const char* key, std::string* text, Environment* env) {
#if !defined(__CloudABI__) && !defined(_WIN32)
#if defined(__linux__)
if ((!HasOnly(CAP_NET_BIND_SERVICE) && per_process::linux_at_secure) ||
getuid() != geteuid() || getgid() != getegid())
#else
if (per_process::linux_at_secure || getuid() != geteuid() ||
getgid() != getegid())
#endif
goto fail;
#endif

Expand Down

0 comments on commit 0a07565

Please sign in to comment.