treewide: use runuser for dropping privileges

When running as root, use runuser instead of sudo.
As opposed to sudo or doas, runuser is a standalone
binary that needs no external configuration.
Also, it's a bit faster.
This commit is contained in:
Erik Arvstedt 2021-01-30 23:08:42 +01:00 committed by nixbitcoin
parent f9683889d9
commit ce2b445777
No known key found for this signature in database
GPG Key ID: DD11F9AD5308B3BA
2 changed files with 18 additions and 17 deletions

View File

@ -166,7 +166,6 @@ in {
wantedBy = [ "multi-user.target" ]; wantedBy = [ "multi-user.target" ];
requires = [ "bitcoind.service" ]; requires = [ "bitcoind.service" ];
after = [ "bitcoind.service" ]; after = [ "bitcoind.service" ];
path = [ pkgs.sudo ];
serviceConfig = nbLib.defaultHardening // { serviceConfig = nbLib.defaultHardening // {
ExecStartPre = nbLib.privileged "joinmarket-create-config" '' ExecStartPre = nbLib.privileged "joinmarket-create-config" ''
install -o '${cfg.user}' -g '${cfg.group}' -m 640 ${configFile} ${cfg.dataDir}/joinmarket.cfg install -o '${cfg.user}' -g '${cfg.group}' -m 640 ${configFile} ${cfg.dataDir}/joinmarket.cfg
@ -183,7 +182,8 @@ in {
echo "Create wallet" echo "Create wallet"
pw=$(cat "${secretsDir}"/jm-wallet-password) pw=$(cat "${secretsDir}"/jm-wallet-password)
cd ${cfg.dataDir} cd ${cfg.dataDir}
if ! sudo -u ${cfg.user} ${nbPkgs.joinmarket}/bin/jm-genwallet --datadir=${cfg.dataDir} $walletname $pw \ if ! ${pkgs.utillinux}/bin/runuser -u ${cfg.user} -- \
${nbPkgs.joinmarket}/bin/jm-genwallet --datadir=${cfg.dataDir} $walletname $pw \
| grep 'recovery_seed' \ | grep 'recovery_seed' \
| cut -d ':' -f2 \ | cut -d ':' -f2 \
| (umask u=r,go=; cat > "${secretsDir}/jm-wallet-seed"); then | (umask u=r,go=; cat > "${secretsDir}/jm-wallet-seed"); then

View File

@ -91,18 +91,18 @@ def _():
machine.wait_for_unit("bitcoind") machine.wait_for_unit("bitcoind")
# `systemctl status` run by unprivileged users shouldn't leak cgroup info # `systemctl status` run by unprivileged users shouldn't leak cgroup info
assert_matches( assert_matches(
"sudo -u electrs systemctl status bitcoind 2>&1 >/dev/null", "runuser -u electrs -- systemctl status bitcoind 2>&1 >/dev/null",
"Failed to dump process list for 'bitcoind.service', ignoring: Access denied", "Failed to dump process list for 'bitcoind.service', ignoring: Access denied",
) )
# The 'operator' with group 'proc' has full access # The 'operator' with group 'proc' has full access
assert_full_match("sudo -u operator systemctl status bitcoind 2>&1 >/dev/null", "") assert_full_match("runuser -u operator -- systemctl status bitcoind 2>&1 >/dev/null", "")
@test("bitcoind") @test("bitcoind")
def _(): def _():
assert_running("bitcoind") assert_running("bitcoind")
machine.wait_until_succeeds("bitcoin-cli getnetworkinfo") machine.wait_until_succeeds("bitcoin-cli getnetworkinfo")
assert_matches("su operator -c 'bitcoin-cli getnetworkinfo' | jq", '"version"') assert_matches("runuser -u operator -- bitcoin-cli getnetworkinfo | jq", '"version"')
# RPC access for user 'public' should be restricted # RPC access for user 'public' should be restricted
machine.fail( machine.fail(
"bitcoin-cli -rpcuser=public -rpcpassword=$(cat /secrets/bitcoin-rpcpassword-public) stop" "bitcoin-cli -rpcuser=public -rpcpassword=$(cat /secrets/bitcoin-rpcpassword-public) stop"
@ -131,14 +131,14 @@ def _():
def _(): def _():
assert_running("liquidd") assert_running("liquidd")
machine.wait_until_succeeds("elements-cli getnetworkinfo") machine.wait_until_succeeds("elements-cli getnetworkinfo")
assert_matches("su operator -c 'elements-cli getnetworkinfo' | jq", '"version"') assert_matches("runuser -u operator -- elements-cli getnetworkinfo | jq", '"version"')
succeed("su operator -c 'liquidswap-cli --help'") succeed("runuser -u operator -- liquidswap-cli --help")
@test("clightning") @test("clightning")
def _(): def _():
assert_running("clightning") assert_running("clightning")
assert_matches("su operator -c 'lightning-cli getinfo' | jq", '"id"') assert_matches("runuser -u operator -- lightning-cli getinfo | jq", '"id"')
if test_data["clightning-plugins"]: if test_data["clightning-plugins"]:
plugin_list = succeed("lightning-cli plugin list") plugin_list = succeed("lightning-cli plugin list")
plugins = json.loads(plugin_list)["plugins"] plugins = json.loads(plugin_list)["plugins"]
@ -158,7 +158,7 @@ def _():
@test("lnd") @test("lnd")
def _(): def _():
assert_running("lnd") assert_running("lnd")
assert_matches("su operator -c 'lncli getinfo' | jq", '"version"') assert_matches("runuser -u operator -- lncli getinfo | jq", '"version"')
assert_no_failure("lnd") assert_no_failure("lnd")
@ -170,7 +170,7 @@ def _():
@test("lightning-loop") @test("lightning-loop")
def _(): def _():
assert_running("lightning-loop") assert_running("lightning-loop")
assert_matches("su operator -c 'loop --version'", "version") assert_matches("runuser -u operator -- loop --version", "version")
# Check that lightning-loop fails with the right error, making sure # Check that lightning-loop fails with the right error, making sure
# lightning-loop can connect to lnd # lightning-loop can connect to lnd
machine.wait_until_succeeds( machine.wait_until_succeeds(
@ -191,7 +191,7 @@ def _():
wait_for_open_port(ip("btcpayserver"), 23000) wait_for_open_port(ip("btcpayserver"), 23000)
# test lnd custom macaroon # test lnd custom macaroon
assert_matches( assert_matches(
"sudo -u btcpayserver curl -s --cacert /secrets/lnd-cert " "runuser -u btcpayserver -- curl -s --cacert /secrets/lnd-cert "
'--header "Grpc-Metadata-macaroon: $(xxd -ps -u -c 1000 /run/lnd/btcpayserver.macaroon)" ' '--header "Grpc-Metadata-macaroon: $(xxd -ps -u -c 1000 /run/lnd/btcpayserver.macaroon)" '
f"-X GET https://{ip('lnd')}:8080/v1/getinfo | jq", f"-X GET https://{ip('lnd')}:8080/v1/getinfo | jq",
'"version"', '"version"',
@ -232,7 +232,7 @@ def _():
status, _ = machine.execute("systemctl is-enabled --quiet onion-addresses 2> /dev/null") status, _ = machine.execute("systemctl is-enabled --quiet onion-addresses 2> /dev/null")
if status == 0: if status == 0:
machine.wait_for_unit("onion-addresses") machine.wait_for_unit("onion-addresses")
json_info = succeed("sudo -u operator nodeinfo") json_info = succeed("runuser -u operator -- nodeinfo")
info = json.loads(json_info) info = json.loads(json_info)
assert info["bitcoind"]["local_address"] assert info["bitcoind"]["local_address"]
@ -277,7 +277,8 @@ def _():
if "joinmarket" in enabled_tests: if "joinmarket" in enabled_tests:
# netns-exec should drop capabilities # netns-exec should drop capabilities
assert_full_match( assert_full_match(
"su operator -c 'netns-exec nb-joinmarket capsh --print | grep Current'", "Current: =\n" "runuser -u operator -- netns-exec nb-joinmarket capsh --print | grep Current",
"Current: =\n",
) )
if "clightning" in enabled_tests: if "clightning" in enabled_tests:
@ -285,7 +286,7 @@ def _():
machine.fail("netns-exec nb-clightning ip a") machine.fail("netns-exec nb-clightning ip a")
# netns-exec should only be executable by the operator user # netns-exec should only be executable by the operator user
machine.fail("sudo -u clightning netns-exec nb-bitcoind ip a") machine.fail("runuser -u clightning -- netns-exec nb-bitcoind ip a")
# Impure: stops bitcoind (and dependent services) # Impure: stops bitcoind (and dependent services)
@ -349,17 +350,17 @@ def _():
assert_full_match(get_block_height_cmd, "10\n") assert_full_match(get_block_height_cmd, "10\n")
if "clightning" in enabled_tests: if "clightning" in enabled_tests:
machine.wait_until_succeeds( machine.wait_until_succeeds(
"[[ $(sudo -u operator lightning-cli getinfo | jq -M .blockheight) == 10 ]]" "[[ $(runuser -u operator -- lightning-cli getinfo | jq -M .blockheight) == 10 ]]"
) )
if "lnd" in enabled_tests: if "lnd" in enabled_tests:
machine.wait_until_succeeds( machine.wait_until_succeeds(
"[[ $(sudo -u operator lncli getinfo | jq -M .block_height) == 10 ]]" "[[ $(runuser -u operator -- lncli getinfo | jq -M .block_height) == 10 ]]"
) )
if "lightning-loop" in enabled_tests: if "lightning-loop" in enabled_tests:
machine.wait_until_succeeds( machine.wait_until_succeeds(
log_has_string("lightning-loop", "Starting event loop at height 10") log_has_string("lightning-loop", "Starting event loop at height 10")
) )
succeed("sudo -u operator loop getparams") succeed("runuser -u operator -- loop getparams")
if "netns-isolation" in enabled_tests: if "netns-isolation" in enabled_tests: