Merge pull request #9060 from dokku/9044-global-flags-are-dropped-on-sudo-rs-sudo-e-preserves-nothing
Preserve global flags when sudo is sudo-rs
This commit is contained in:
13
.github/workflows/ci.yml
vendored
13
.github/workflows/ci.yml
vendored
@@ -219,6 +219,19 @@ jobs:
|
||||
name: coverage.${{ matrix.index }}
|
||||
path: test-results/coverage
|
||||
|
||||
sudo-preserve-env:
|
||||
name: sudo-preserve-env
|
||||
runs-on: ubuntu-26.04
|
||||
needs: check-commit
|
||||
if: ${{ needs.check-commit.outputs.skip != 'true' }}
|
||||
timeout-minutes: 10
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
|
||||
- name: check that the preserved environment survives sudo-rs
|
||||
run: ./tests/ci/sudo_preserve_env.sh
|
||||
|
||||
publish-test-results:
|
||||
name: publish-test-results
|
||||
needs: unit-tests
|
||||
|
||||
4
Makefile
4
Makefile
@@ -126,10 +126,10 @@ else
|
||||
endif
|
||||
|
||||
plugin-dependencies: plugn procfile-util
|
||||
sudo -E dokku plugin:install-dependencies --core
|
||||
sudo dokku plugin:install-dependencies --core
|
||||
|
||||
plugins: plugn procfile-util docker
|
||||
sudo -E dokku plugin:install --core
|
||||
sudo dokku plugin:install --core
|
||||
|
||||
dependencies: apt-update jq docker-image-labeler docker-container-healthchecker lambda-builder netrc sshcommand plugn procfile-util docker help2man man-db sigil dos2unix parallel
|
||||
$(MAKE) -e stack
|
||||
|
||||
@@ -110,14 +110,14 @@ install-dokku() {
|
||||
elif [[ "$use_plugin" == "true" ]]; then
|
||||
install-dokku-from-package "$DOKKU_SEMVER"
|
||||
echo "--> Running post-install dependency installation"
|
||||
sudo -E dokku plugin:install-dependencies --core
|
||||
sudo dokku plugin:install-dependencies --core
|
||||
else
|
||||
install-dokku-from-source "$DOKKU_TAG"
|
||||
fi
|
||||
else
|
||||
install-dokku-from-package
|
||||
echo "--> Running post-install dependency installation"
|
||||
sudo -E dokku plugin:install-dependencies --core
|
||||
sudo dokku plugin:install-dependencies --core
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
@@ -138,6 +138,9 @@ The following config variables have special meanings and can be set in a variety
|
||||
| `DOKKU_EVENTS_LOGFILE` | `$DOKKU_LOGS_DIR/events.log` | `/etc/environment` <br /> `~dokku/.dokkurc` <br /> `~dokku/.dokkurc/*` | Where the events log file is written to. |
|
||||
| `DOKKU_APP_NAME` | none | `--app APP` flag | Name of application to work on. Respected by core plugins. |
|
||||
| `DOKKU_APPS_FORCE_DELETE` | none | `--force` flag | Whether to force delete an application. Also used by other plugins for destructive actions. |
|
||||
| `DOKKU_DISABLE_TTY` | none | `dokku run --no-tty` <br /> environment | Forces `dokku run` to treat the session as non-interactive. |
|
||||
| `DOKKU_FORCE_TTY` | none | `dokku run --force-tty` <br /> environment | Forces `dokku run` to treat the session as interactive. |
|
||||
| `DOKKU_PRESERVE_ENV` | none | `/etc/default/dokku` <br /> `~dokku/.dokkurc` <br /> `~dokku/.dokkurc/*` <br /> environment | Comma-separated list of extra environment variable names to carry across the privilege drop to the `dokku` user. See [Forwarding environment variables](#forwarding-environment-variables). |
|
||||
| `DOKKU_CHECKS_URL` | `https://dokku.com/docs/deployment/zero-downtime-deploys/` | `/etc/environment` <br /> `~dokku/.dokkurc` <br /> `~dokku/.dokkurc/*` | Url displayed during deployment when no CHECKS file exists. |
|
||||
| `DOKKU_QUIET_OUTPUT` | none | `--quiet` flag | Silences certain header output for `dokku` commands. |
|
||||
| `DOKKU_RM_CONTAINER` | none | `dokku config:set` <br /> | Deprecated: Whether to keep `dokku run` containers around or not. |
|
||||
@@ -145,6 +148,45 @@ The following config variables have special meanings and can be set in a variety
|
||||
| `DOKKU_SYSTEM_GROUP` | `dokku` | `/etc/environment` <br /> `~dokku/.dokkurc` <br /> `~dokku/.dokkurc/*` | System group to chown files as. |
|
||||
| `DOKKU_SYSTEM_USER` | `dokku` | `/etc/environment` <br /> `~dokku/.dokkurc` <br /> `~dokku/.dokkurc/*` | System user to chown files as. |
|
||||
|
||||
## Forwarding environment variables
|
||||
|
||||
When `dokku` is invoked by any user other than `dokku` - `root`, or a member of the `sudo` group - it
|
||||
re-executes itself as the `dokku` user. Most configuration does not need to survive that step, since
|
||||
`/etc/default/dokku`, `~dokku/dokkurc` and `~dokku/.dokkurc/*` are read again by the re-executed
|
||||
process, and `/etc/environment` is applied by PAM.
|
||||
|
||||
What cannot be recomputed is state that belongs to the invocation itself, so those variables are
|
||||
named explicitly and forwarded:
|
||||
|
||||
| Variable | Source |
|
||||
| --- | --- |
|
||||
| `DOKKU_APP_NAME`, `DOKKU_APPS_FORCE_DELETE`, `DOKKU_QUIET_OUTPUT`, `DOKKU_TRACE` | the `--app`, `--force`, `--quiet` and `--trace` global flags, or the environment |
|
||||
| `DOKKU_GLOBAL_FLAGS` | every global flag as given, replayed by plugins that need them |
|
||||
| `DOKKU_DISABLE_TTY`, `DOKKU_FORCE_TTY` | the `dokku run` tty flags, or the environment |
|
||||
| `DOKKU_PRESERVE_ENV` | the list below |
|
||||
| `SSH_USER`, `SSH_NAME`, `NAME`, `FINGERPRINT`, `SSH_ORIGINAL_COMMAND` | the ssh session the command arrived on |
|
||||
| `DOCKER_BIN`, `DOKKU_CNB_BUILDER`, `DOKKU_EVENTS_LOGFILE`, `DOKKU_HOST_ROOT`, `DOKKU_IMAGE`, `DOKKU_LIB_PATH`, `DOKKU_LOGS_DIR`, `DOKKU_LOGS_HOST_DIR`, `DOKKU_ROOT`, `DOKKU_SYSTEM_GROUP`, `DOKKU_SYSTEM_USER`, `PLUGIN_PATH`, `PLUGIN_AVAILABLE_PATH`, `PLUGIN_ENABLED_PATH`, `PLUGIN_CORE_PATH`, `PLUGIN_CORE_AVAILABLE_PATH`, `PLUGIN_CORE_ENABLED_PATH` | the environment, so the re-executed process derives the same paths as the caller |
|
||||
| `DOKKU_INIT_SYSTEM`, `DOKKU_LIB_HOST_ROOT` | the environment, which is where they live while a Docker image is being built |
|
||||
|
||||
`DOKKU_PID` is deliberately not forwarded: it is unset before the privilege drop so the re-executed
|
||||
process gets its own.
|
||||
|
||||
To forward anything else - a variable read by a third-party plugin, or a one-off override of a
|
||||
setting that normally lives in `/etc/default/dokku` - name it in `DOKKU_PRESERVE_ENV`:
|
||||
|
||||
```shell
|
||||
DOKKU_PRESERVE_ENV=DOCKER_BUILDKIT DOCKER_BUILDKIT=0 dokku ps:rebuild node-js-app
|
||||
```
|
||||
|
||||
Set it persistently to forward a variable for every invocation:
|
||||
|
||||
```shell
|
||||
echo "export DOKKU_PRESERVE_ENV=MY_PLUGIN_DEBUG" | sudo tee -a /etc/default/dokku
|
||||
```
|
||||
|
||||
Variables sudo manages for the target user - `HOME`, `PATH`, `SHELL`, `USER`, `LOGNAME`, `MAIL` and
|
||||
`SUDO_*` - are never forwarded, and naming them in `DOKKU_PRESERVE_ENV` is not supported.
|
||||
|
||||
## Properties
|
||||
|
||||
### Internal properties
|
||||
|
||||
@@ -135,3 +135,9 @@ Use `visudo /etc/sudoers.d/dokku-users`, or `visudo /etc/sudoers` to add the fol
|
||||
```
|
||||
%dokku ALL=(ALL:ALL) NOPASSWD:SETENV: /usr/bin/dokku
|
||||
```
|
||||
|
||||
The `SETENV:` tag is required. When a user other than `dokku` runs the `dokku` binary, it re-executes
|
||||
itself as the `dokku` user and names the environment variables that have to survive that step via
|
||||
`sudo --preserve-env=`. Without `SETENV:` those variables are filtered out, and global flags such as
|
||||
`--quiet`, `--trace` and `--force` stop taking effect. See
|
||||
[Forwarding environment variables](/docs/configuration/environment-variables.md#forwarding-environment-variables).
|
||||
|
||||
7
dokku
7
dokku
@@ -67,6 +67,9 @@ export DOKKU_GLOBAL_RUN_ARGS="--label=org.label-schema.schema-version=1.0 --labe
|
||||
|
||||
source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions"
|
||||
|
||||
# the original argv is kept so the re-exec as the dokku user can replay it verbatim
|
||||
# and let the child re-parse the global flags for itself. parse_args and the
|
||||
# flag-stripping loop below must therefore stay pure functions of argv.
|
||||
parse_args "$@"
|
||||
args=("$@")
|
||||
skip_arg=false
|
||||
@@ -91,7 +94,9 @@ if [[ $(id -un) != "dokku" ]]; then
|
||||
if [[ ! $1 =~ plugin:* ]] && [[ $1 != "ssh-keys:add" ]] && [[ $1 != "ssh-keys:remove" ]] && [[ $1 != "scheduler-k3s:initialize" ]] && [[ $1 != "scheduler-k3s:uninstall" ]]; then
|
||||
unset DOKKU_PID TMP TMPDIR TEMP TEMPDIR
|
||||
export SSH_USER=$(id -un)
|
||||
sudo -u dokku -E -H "$0" "$@"
|
||||
read -ra preserved_env_names <<<"$(fn-cli-preserved-env-names)"
|
||||
preserve_env_flag="$(fn-sudo-preserve-env-flag "${preserved_env_names[@]}")"
|
||||
sudo -u dokku "$preserve_env_flag" -H "$0" "${args[@]}"
|
||||
exit $?
|
||||
fi
|
||||
fi
|
||||
|
||||
@@ -476,6 +476,56 @@ is_abs_path() {
|
||||
fi
|
||||
}
|
||||
|
||||
fn-cli-preserved-env-names() {
|
||||
declare desc="names the environment variables the re-exec as the dokku user must carry"
|
||||
|
||||
# every variable dokku honours from the environment has to be listed, or the child
|
||||
# derives different values than the parent - during the docker image build these only
|
||||
# ever exist in the environment. DOKKU_PID is the one exclusion: it is unset above so
|
||||
# the child gets its own. DOKKU_PRESERVE_ENV carries anything else
|
||||
echo "DOKKU_APP_NAME DOKKU_APPS_FORCE_DELETE DOKKU_DISABLE_TTY DOKKU_FORCE_TTY" \
|
||||
"DOKKU_GLOBAL_FLAGS DOKKU_PRESERVE_ENV DOKKU_QUIET_OUTPUT DOKKU_TRACE" \
|
||||
"FINGERPRINT NAME SSH_NAME SSH_ORIGINAL_COMMAND SSH_USER" \
|
||||
"DOCKER_BIN DOKKU_CNB_BUILDER DOKKU_EVENTS_LOGFILE DOKKU_HOST_ROOT DOKKU_IMAGE" \
|
||||
"DOKKU_INIT_SYSTEM DOKKU_LIB_HOST_ROOT DOKKU_LIB_PATH DOKKU_LOGS_DIR" \
|
||||
"DOKKU_LOGS_HOST_DIR DOKKU_ROOT DOKKU_SYSTEM_GROUP DOKKU_SYSTEM_USER" \
|
||||
"PLUGIN_AVAILABLE_PATH PLUGIN_CORE_AVAILABLE_PATH PLUGIN_CORE_ENABLED_PATH" \
|
||||
"PLUGIN_CORE_PATH PLUGIN_ENABLED_PATH PLUGIN_PATH"
|
||||
}
|
||||
|
||||
fn-sudo-preserve-env-flag() {
|
||||
declare desc="prints the sudo flag that carries environment variables across a privilege drop"
|
||||
local names=("$@")
|
||||
local extra_names=()
|
||||
local joined name
|
||||
|
||||
# sudo-rs, the default sudo on ubuntu 25.10 and later, ignores -E and bare
|
||||
# --preserve-env, so every variable that has to survive must be named
|
||||
if [[ "${#names[@]}" -eq 0 ]]; then
|
||||
for name in $(compgen -e); do
|
||||
# preserving these would defeat -H and override the sudoers secure_path
|
||||
case "$name" in
|
||||
HOME | LOGNAME | MAIL | PATH | SHELL | USER | _ | SUDO_*)
|
||||
continue
|
||||
;;
|
||||
esac
|
||||
names+=("$name")
|
||||
done
|
||||
fi
|
||||
|
||||
if [[ -n "$DOKKU_PRESERVE_ENV" ]]; then
|
||||
read -ra extra_names <<<"${DOKKU_PRESERVE_ENV//,/ }"
|
||||
names+=("${extra_names[@]}")
|
||||
fi
|
||||
|
||||
if [[ "${#names[@]}" -eq 0 ]]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
printf -v joined "%s," "${names[@]}"
|
||||
echo "--preserve-env=${joined%,}"
|
||||
}
|
||||
|
||||
parse_args() {
|
||||
declare desc="top-level cli arg parser"
|
||||
local next_index=1
|
||||
|
||||
@@ -1,13 +1,22 @@
|
||||
#!/usr/bin/env bash
|
||||
set -eo pipefail
|
||||
[[ $DOKKU_TRACE ]] && set -x
|
||||
source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions"
|
||||
|
||||
cmd-plugin-trigger() {
|
||||
declare desc="trigger an arbitrary plugin hook"
|
||||
declare cmd="plugin:trigger"
|
||||
[[ "$1" == "$cmd" ]] && shift 1
|
||||
|
||||
sudo -u dokku -E -H plugn trigger "$@"
|
||||
local sudo_args=(-u dokku)
|
||||
local preserve_env_flag
|
||||
preserve_env_flag="$(fn-sudo-preserve-env-flag)"
|
||||
if [[ -n "$preserve_env_flag" ]]; then
|
||||
sudo_args+=("$preserve_env_flag")
|
||||
fi
|
||||
sudo_args+=(-H)
|
||||
|
||||
sudo "${sudo_args[@]}" plugn trigger "$@"
|
||||
}
|
||||
|
||||
cmd-plugin-trigger "$@"
|
||||
|
||||
112
tests/ci/sudo_preserve_env.sh
Executable file
112
tests/ci/sudo_preserve_env.sh
Executable file
@@ -0,0 +1,112 @@
|
||||
#!/usr/bin/env bash
|
||||
# Checks that the environment dokku forwards across its privilege drop survives the
|
||||
# local sudo implementation. sudo-rs, the default sudo on ubuntu 25.10 and later,
|
||||
# ignores -E and bare --preserve-env, so dokku names every variable it needs.
|
||||
set -eo pipefail
|
||||
|
||||
PLUGIN_CORE_AVAILABLE_PATH="${PLUGIN_CORE_AVAILABLE_PATH:-$(dirname "$0")/../../plugins}"
|
||||
export PLUGIN_CORE_AVAILABLE_PATH
|
||||
source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions"
|
||||
|
||||
FAILURES=0
|
||||
|
||||
fail() {
|
||||
declare desc="report a failed check"
|
||||
echo "not ok - $1" 1>&2
|
||||
FAILURES=$((FAILURES + 1))
|
||||
}
|
||||
|
||||
pass() {
|
||||
declare desc="report a passing check"
|
||||
echo "ok - $1"
|
||||
}
|
||||
|
||||
report-whole-environment-support() {
|
||||
declare desc="record whether this sudo honours -E, for the log"
|
||||
local warning
|
||||
|
||||
warning="$(DOKKU_TEST_CROSSING=1 sudo -E -u nobody true 2>&1 >/dev/null || true)"
|
||||
if [[ -n "$warning" ]]; then
|
||||
echo "# sudo -E is not supported here: $warning"
|
||||
else
|
||||
echo "# sudo -E is supported here"
|
||||
fi
|
||||
}
|
||||
|
||||
check-named-list-crosses() {
|
||||
declare desc="the named variables reach the target user"
|
||||
local flag output
|
||||
|
||||
flag="$(DOKKU_QUIET_OUTPUT=1 DOKKU_TRACE=1 SSH_USER=ci fn-sudo-preserve-env-flag DOKKU_QUIET_OUTPUT DOKKU_TRACE SSH_USER)"
|
||||
output="$(DOKKU_QUIET_OUTPUT=1 DOKKU_TRACE=1 SSH_USER=ci sudo "$flag" -u nobody /usr/bin/env)"
|
||||
|
||||
local name
|
||||
for name in DOKKU_QUIET_OUTPUT=1 DOKKU_TRACE=1 SSH_USER=ci; do
|
||||
if grep -qxF "$name" <<<"$output"; then
|
||||
pass "$name crossed the privilege drop"
|
||||
else
|
||||
fail "$name did not cross the privilege drop"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
check-named-list-is-silent() {
|
||||
declare desc="no warning is written to stderr"
|
||||
local flag stderr
|
||||
|
||||
flag="$(SSH_USER=ci fn-sudo-preserve-env-flag SSH_USER)"
|
||||
stderr="$(SSH_USER=ci sudo "$flag" -u nobody true 2>&1 >/dev/null || true)"
|
||||
if [[ -z "$stderr" ]]; then
|
||||
pass "no warning on stderr"
|
||||
else
|
||||
fail "unexpected stderr: $stderr"
|
||||
fi
|
||||
}
|
||||
|
||||
check-absent-names-are-tolerated() {
|
||||
declare desc="naming a variable that is not set is not an error"
|
||||
local flag
|
||||
|
||||
flag="$(fn-sudo-preserve-env-flag DOKKU_DEFINITELY_NOT_SET_HERE SSH_USER)"
|
||||
if sudo "$flag" -u nobody true; then
|
||||
pass "unset names are ignored"
|
||||
else
|
||||
fail "sudo rejected a list naming an unset variable"
|
||||
fi
|
||||
}
|
||||
|
||||
check-home-still-wins() {
|
||||
declare desc="-H is not defeated by the preserved list"
|
||||
local flag output
|
||||
local names=()
|
||||
|
||||
read -ra names <<<"$(fn-cli-preserved-env-names)"
|
||||
flag="$(fn-sudo-preserve-env-flag "${names[@]}")"
|
||||
output="$(sudo "$flag" -H -u nobody /usr/bin/env)"
|
||||
if grep -qxF "HOME=$(getent passwd nobody | cut -d: -f6)" <<<"$output"; then
|
||||
pass "-H still sets HOME for the target user"
|
||||
else
|
||||
fail "HOME was not reset for the target user"
|
||||
fi
|
||||
}
|
||||
|
||||
main() {
|
||||
declare desc="run every check"
|
||||
|
||||
echo "# sudo version:"
|
||||
sudo --version | head -1 | sed -e 's/^/# /'
|
||||
|
||||
report-whole-environment-support
|
||||
check-named-list-crosses
|
||||
check-named-list-is-silent
|
||||
check-absent-names-are-tolerated
|
||||
check-home-still-wins
|
||||
|
||||
if [[ "$FAILURES" -gt 0 ]]; then
|
||||
echo "$FAILURES check(s) failed" 1>&2
|
||||
exit 1
|
||||
fi
|
||||
echo "all checks passed"
|
||||
}
|
||||
|
||||
main "$@"
|
||||
@@ -120,3 +120,77 @@ teardown() {
|
||||
assert_line 0 "--label"
|
||||
assert_line 1 "a b"
|
||||
}
|
||||
|
||||
@test "(common) [preserve-env] fn-sudo-preserve-env-flag emits only the requested names" {
|
||||
run /bin/bash -c "source '$PLUGIN_CORE_AVAILABLE_PATH/common/functions'; fn-sudo-preserve-env-flag DOKKU_TRACE DOKKU_APP_NAME"
|
||||
echo "output: $output"
|
||||
echo "status: $status"
|
||||
assert_success
|
||||
assert_output "--preserve-env=DOKKU_TRACE,DOKKU_APP_NAME"
|
||||
}
|
||||
|
||||
@test "(common) [preserve-env] fn-sudo-preserve-env-flag omits sudo managed names" {
|
||||
run /bin/bash -c "source '$PLUGIN_CORE_AVAILABLE_PATH/common/functions'; export DOKKU_TEST_CROSSING=1; fn-sudo-preserve-env-flag | sed 's/^--preserve-env=//' | tr ',' '\\n' | grep -cx DOKKU_TEST_CROSSING"
|
||||
echo "output: $output"
|
||||
echo "status: $status"
|
||||
assert_success
|
||||
assert_output "1"
|
||||
|
||||
run /bin/bash -c "source '$PLUGIN_CORE_AVAILABLE_PATH/common/functions'; fn-sudo-preserve-env-flag | sed 's/^--preserve-env=//' | tr ',' '\\n' | grep -cxE 'HOME|LOGNAME|MAIL|PATH|SHELL|USER|_|SUDO_.*'"
|
||||
echo "output: $output"
|
||||
echo "status: $status"
|
||||
assert_output "0"
|
||||
}
|
||||
|
||||
@test "(common) [preserve-env] fn-sudo-preserve-env-flag honors DOKKU_PRESERVE_ENV" {
|
||||
run /bin/bash -c "source '$PLUGIN_CORE_AVAILABLE_PATH/common/functions'; DOKKU_PRESERVE_ENV=ALPHA,BETA fn-sudo-preserve-env-flag DOKKU_TRACE"
|
||||
echo "output: $output"
|
||||
echo "status: $status"
|
||||
assert_success
|
||||
assert_output "--preserve-env=DOKKU_TRACE,ALPHA,BETA"
|
||||
|
||||
run /bin/bash -c "source '$PLUGIN_CORE_AVAILABLE_PATH/common/functions'; DOKKU_PRESERVE_ENV=ALPHA fn-sudo-preserve-env-flag | sed 's/^--preserve-env=//' | tr ',' '\\n' | grep -cx ALPHA"
|
||||
echo "output: $output"
|
||||
echo "status: $status"
|
||||
assert_success
|
||||
assert_output "1"
|
||||
}
|
||||
|
||||
@test "(common) [preserve-env] fn-cli-preserved-env-names covers every parse_args export" {
|
||||
run /bin/bash -c "source '$PLUGIN_CORE_AVAILABLE_PATH/common/functions'; fn-cli-preserved-env-names | tr ' ' '\\n' | grep -cxE 'DOKKU_APP_NAME|DOKKU_APPS_FORCE_DELETE|DOKKU_GLOBAL_FLAGS|DOKKU_PRESERVE_ENV|DOKKU_QUIET_OUTPUT|DOKKU_TRACE|SSH_USER'"
|
||||
echo "output: $output"
|
||||
echo "status: $status"
|
||||
assert_success
|
||||
assert_output "7"
|
||||
}
|
||||
|
||||
@test "(common) [preserve-env] fn-cli-preserved-env-names covers the docker image build variables" {
|
||||
# neither is read by the dokku script itself, so the derivation test below does not
|
||||
# catch them. both exist only in the environment while the docker image is built
|
||||
run /bin/bash -c "source '$PLUGIN_CORE_AVAILABLE_PATH/common/functions'; fn-cli-preserved-env-names | tr ' ' '\\n' | grep -cxE 'DOKKU_INIT_SYSTEM|DOKKU_LIB_HOST_ROOT'"
|
||||
echo "output: $output"
|
||||
echo "status: $status"
|
||||
assert_success
|
||||
assert_output "2"
|
||||
}
|
||||
|
||||
@test "(common) [preserve-env] every environment-overridable dokku variable is preserved" {
|
||||
# test_helper leaves these unexported, so the single-quoted script below cannot see
|
||||
# them without this
|
||||
export PLUGIN_CORE_AVAILABLE_PATH
|
||||
export DOKKU_SOURCE="${BATS_TEST_DIRNAME}/../../dokku"
|
||||
run /bin/bash -c 'source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions"; comm -23 <(grep -oE "[{][A-Z_]+:=" "$DOKKU_SOURCE" | tr -cd "A-Z_\n" | sort -u | grep -vx DOKKU_PID) <(fn-cli-preserved-env-names | tr " " "\n" | sort -u)'
|
||||
echo "output: $output"
|
||||
echo "status: $status"
|
||||
assert_success
|
||||
assert_output ""
|
||||
unset DOKKU_SOURCE
|
||||
}
|
||||
|
||||
@test "(common) [preserve-env] parse_args records global flags for replay" {
|
||||
run /bin/bash -c "source '$PLUGIN_CORE_AVAILABLE_PATH/common/functions'; parse_args --label=a=b run foo env; echo \"\$DOKKU_GLOBAL_FLAGS\""
|
||||
echo "output: $output"
|
||||
echo "status: $status"
|
||||
assert_success
|
||||
assert_output "--label=a=b"
|
||||
}
|
||||
|
||||
105
tests/unit/core_3.bats
Normal file
105
tests/unit/core_3.bats
Normal file
@@ -0,0 +1,105 @@
|
||||
#!/usr/bin/env bats
|
||||
|
||||
load test_helper
|
||||
|
||||
setup() {
|
||||
global_setup
|
||||
create_app
|
||||
}
|
||||
|
||||
teardown() {
|
||||
destroy_app
|
||||
global_teardown
|
||||
}
|
||||
|
||||
@test "(core) [global-flags] --quiet crosses the privilege drop" {
|
||||
run /bin/bash -c "dokku apps:list"
|
||||
echo "output: $output"
|
||||
echo "status: $status"
|
||||
assert_success
|
||||
assert_output_contains "My Apps"
|
||||
|
||||
run /bin/bash -c "dokku --quiet apps:list"
|
||||
echo "output: $output"
|
||||
echo "status: $status"
|
||||
assert_success
|
||||
assert_output_not_contains "My Apps"
|
||||
assert_output_contains "$TEST_APP"
|
||||
}
|
||||
|
||||
@test "(core) [global-flags] --trace crosses the privilege drop" {
|
||||
run /bin/bash -c "dokku --trace apps:list 2>&1 >/dev/null"
|
||||
echo "output: $output"
|
||||
echo "status: $status"
|
||||
assert_success
|
||||
assert_output_contains "execute_dokku_cmd"
|
||||
}
|
||||
|
||||
@test "(core) [global-flags] --force crosses the privilege drop" {
|
||||
local force_app="${TEST_APP}-force"
|
||||
run /bin/bash -c "dokku apps:create $force_app"
|
||||
echo "output: $output"
|
||||
echo "status: $status"
|
||||
assert_success
|
||||
|
||||
run /bin/bash -c "dokku --force apps:destroy $force_app </dev/null"
|
||||
echo "output: $output"
|
||||
echo "status: $status"
|
||||
assert_success
|
||||
assert_output_not_contains "To proceed, type"
|
||||
}
|
||||
|
||||
@test "(core) [global-flags] --app crosses the privilege drop" {
|
||||
run /bin/bash -c "dokku --app $TEST_APP config:set --no-restart TEST_PRESERVED=crossed"
|
||||
echo "output: $output"
|
||||
echo "status: $status"
|
||||
assert_success
|
||||
|
||||
run /bin/bash -c "dokku config:get $TEST_APP TEST_PRESERVED"
|
||||
echo "output: $output"
|
||||
echo "status: $status"
|
||||
assert_success
|
||||
assert_output "crossed"
|
||||
}
|
||||
|
||||
@test "(core) [global-flags] a clean invocation writes nothing to stderr" {
|
||||
run /bin/bash -c "dokku apps:list 2>&1 >/dev/null"
|
||||
echo "output: $output"
|
||||
echo "status: $status"
|
||||
assert_success
|
||||
assert_output ""
|
||||
}
|
||||
|
||||
@test "(core) [global-flags] the caller environment crosses the privilege drop" {
|
||||
run /bin/bash -c "DOKKU_QUIET_OUTPUT=1 dokku apps:list"
|
||||
echo "output: $output"
|
||||
echo "status: $status"
|
||||
assert_success
|
||||
assert_output_not_contains "My Apps"
|
||||
assert_output_contains "$TEST_APP"
|
||||
}
|
||||
|
||||
@test "(core) [global-flags] an unlisted variable only crosses when DOKKU_PRESERVE_ENV names it" {
|
||||
# DOKKU_TRACE is set in the environment rather than passed as --trace so that the
|
||||
# parent traces too: the sudo line naming the forwarded variables is only emitted there
|
||||
run /bin/bash -c "DOKKU_TRACE=1 DOKKU_TEST_CROSSING=1 dokku apps:list 2>&1 >/dev/null | grep -o -- '--preserve-env=[^ ]*' | head -1"
|
||||
echo "output: $output"
|
||||
echo "status: $status"
|
||||
assert_success
|
||||
assert_output_contains "--preserve-env="
|
||||
assert_output_not_contains "DOKKU_TEST_CROSSING"
|
||||
|
||||
run /bin/bash -c "DOKKU_TRACE=1 DOKKU_PRESERVE_ENV=DOKKU_TEST_CROSSING DOKKU_TEST_CROSSING=1 dokku apps:list 2>&1 >/dev/null | grep -o -- '--preserve-env=[^ ]*' | head -1"
|
||||
echo "output: $output"
|
||||
echo "status: $status"
|
||||
assert_success
|
||||
assert_output_contains "DOKKU_TEST_CROSSING"
|
||||
}
|
||||
|
||||
@test "(core) [global-flags] the system sudo supports a named preserve-env list" {
|
||||
run /bin/bash -c "DOKKU_TEST_CROSSING=1 sudo -u dokku --preserve-env=DOKKU_TEST_CROSSING -H /usr/bin/env | grep -cxE 'DOKKU_TEST_CROSSING=1|HOME=/home/dokku'"
|
||||
echo "output: $output"
|
||||
echo "status: $status"
|
||||
assert_success
|
||||
assert_output "2"
|
||||
}
|
||||
@@ -116,7 +116,7 @@ teardown() {
|
||||
echo "status: $status"
|
||||
assert_success
|
||||
|
||||
run /bin/bash -c "sudo -E -u nobody dokku plugin:uninstall $TEST_PLUGIN_NAME"
|
||||
run /bin/bash -c "sudo -u nobody dokku plugin:uninstall $TEST_PLUGIN_NAME"
|
||||
echo "output: $output"
|
||||
echo "status: $status"
|
||||
assert_failure
|
||||
@@ -158,7 +158,7 @@ teardown() {
|
||||
echo "status: $status"
|
||||
assert_success
|
||||
|
||||
run /bin/bash -c "sudo -E -u nobody dokku plugin:uninstall $TEST_PLUGIN_NAME"
|
||||
run /bin/bash -c "sudo -u nobody dokku plugin:uninstall $TEST_PLUGIN_NAME"
|
||||
echo "output: $output"
|
||||
echo "status: $status"
|
||||
assert_failure
|
||||
@@ -212,7 +212,7 @@ teardown() {
|
||||
}
|
||||
|
||||
@test "(plugin) plugin:install, plugin:disable, plugin:uninstall as non-root user failure" {
|
||||
run /bin/bash -c "sudo -E -u nobody dokku plugin:install $TEST_PLUGIN_GIT_REPO"
|
||||
run /bin/bash -c "sudo -u nobody dokku plugin:install $TEST_PLUGIN_GIT_REPO"
|
||||
echo "output: $output"
|
||||
echo "status: $status"
|
||||
assert_failure
|
||||
@@ -227,7 +227,7 @@ teardown() {
|
||||
echo "status: $status"
|
||||
assert_success
|
||||
|
||||
run /bin/bash -c "sudo -E -u nobody dokku plugin:disable $TEST_PLUGIN_NAME"
|
||||
run /bin/bash -c "sudo -u nobody dokku plugin:disable $TEST_PLUGIN_NAME"
|
||||
echo "output: $output"
|
||||
echo "status: $status"
|
||||
assert_failure
|
||||
@@ -372,3 +372,10 @@ teardown() {
|
||||
assert_output_contains "root" "0"
|
||||
assert_output_contains "dokku" "3"
|
||||
}
|
||||
|
||||
@test "(plugin:trigger) forwards the caller environment across the privilege drop" {
|
||||
run /bin/bash -c "DOKKU_TEST_CROSSING=1 DOKKU_TRACE=1 dokku plugin:trigger app-list 2>&1 >/dev/null | grep -q -- '--preserve-env=[^ ]*DOKKU_TEST_CROSSING'"
|
||||
echo "output: $output"
|
||||
echo "status: $status"
|
||||
assert_success
|
||||
}
|
||||
|
||||
@@ -101,6 +101,8 @@ teardown() {
|
||||
echo "status: $status"
|
||||
assert_success
|
||||
|
||||
# DOKKU_OMIT_RESOURCE_ARGS has to survive the privilege drop in
|
||||
# plugins/plugin/subcommands/trigger, so this also covers that forwarding
|
||||
run_plugn_trigger DOKKU_OMIT_RESOURCE_ARGS=1 docker-args-process-build "$TEST_APP" herokuish
|
||||
echo "output: $output"
|
||||
echo "status: $status"
|
||||
|
||||
@@ -35,6 +35,9 @@ teardown() {
|
||||
echo "status: $status"
|
||||
assert_success
|
||||
|
||||
# the leading --label is replayed out of DOKKU_GLOBAL_FLAGS by
|
||||
# plugins/scheduler-docker-local/scheduler-run, so this also covers that variable
|
||||
# surviving the re-exec as the dokku user
|
||||
RANDOM_RUN_CID="$(dokku --label=com.dokku.test-label=value run:detached $TEST_APP sleep 300)"
|
||||
run /bin/bash -c "docker inspect -f '{{ .State.Status }}' $RANDOM_RUN_CID"
|
||||
echo "output: $output"
|
||||
|
||||
Reference in New Issue
Block a user