From fc9ae03eb7a9065c2a102de963226f4f0bed1cbd Mon Sep 17 00:00:00 2001 From: RichardDorian Date: Tue, 7 Jul 2026 19:25:22 +0200 Subject: [PATCH 1/5] feat(storage): allow custom chown value --- plugins/storage/subcommands.go | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/plugins/storage/subcommands.go b/plugins/storage/subcommands.go index 3b264834c..2dec6470a 100644 --- a/plugins/storage/subcommands.go +++ b/plugins/storage/subcommands.go @@ -6,6 +6,7 @@ import ( "fmt" "os" "path/filepath" + "strconv" "strings" "github.com/dokku/dokku/plugins/common" @@ -99,7 +100,13 @@ func ResolveChownID(chownFlag string) (string, error) { case "false": return "false", nil default: - return "", errors.New("Unsupported chown permissions") + id, err := strconv.ParseUint(chownFlag, 10, 16) + + if err != nil { + return "", errors.New("Unsupported chown permissions") + } else { + chownID = fmt.Sprint(id) + } } userns, err := isUserNamespacesEnabled() From 962c54bb2a15a7ee64bffcb16f838d4fc123c496 Mon Sep 17 00:00:00 2001 From: RichardDorian Date: Tue, 7 Jul 2026 19:28:27 +0200 Subject: [PATCH 2/5] docs(storage): add documentation regarding custom chown values --- docs/advanced-usage/persistent-storage.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/advanced-usage/persistent-storage.md b/docs/advanced-usage/persistent-storage.md index 5f10a64b2..2dc09dc25 100644 --- a/docs/advanced-usage/persistent-storage.md +++ b/docs/advanced-usage/persistent-storage.md @@ -106,6 +106,8 @@ By default, permissions are set for usage with Herokuish buildpacks. These permi - `--chown root`: Use `0:0` as the folder permissions. - This is used for containers that run their processes as root, as is typical for most Dockerfile or Docker image deploys. - `--chown false`: Skips the `chown` call. +- `--chown `: Use `:` as the folder permissions, where `` is a custom numeric user/group id. + - This is used for containers that run their processes as a uid/gid that doesn't correspond to any of the above named options. Users deploying via Dockerfile will want to specify `--chown false` and manually `chown` the created directory if the user and/or group id of the runnning process in the deployed container do not correspond to any of the above options. From a0361d59c10e2d620fe8a774be0e89ea586f55ef Mon Sep 17 00:00:00 2001 From: RichardDorian Date: Tue, 7 Jul 2026 20:48:34 +0200 Subject: [PATCH 3/5] test(storage): add unit test for custom chown values --- tests/unit/storage.bats | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/tests/unit/storage.bats b/tests/unit/storage.bats index 662f3694f..fd2fab276 100644 --- a/tests/unit/storage.bats +++ b/tests/unit/storage.bats @@ -394,6 +394,22 @@ teardown() { assert_success } +@test "(storage:create) --chown accepts a custom numeric uid" { + run /bin/bash -c "dokku storage:create --chown 1500 rdmtest-chown-numeric" + echo "output: $output" + echo "status: $status" + assert_success + + run /bin/bash -c "stat -c '%u:%g' $DOKKU_LIB_ROOT/data/storage/rdmtest-chown-numeric" + echo "output: $output" + echo "status: $status" + assert_success + assert_output "1500:1500" + + run /bin/bash -c "dokku storage:destroy rdmtest-chown-numeric --force" + assert_success +} + @test "(storage:create) --chown rejects a non-default host path" { custom_path="/tmp/rdmtest-chown-custom" rm -rf "$custom_path" From 06bedebce352cf7f9036a41a9f68b2d1235d9d2d Mon Sep 17 00:00:00 2001 From: RichardDorian Date: Wed, 8 Jul 2026 08:29:13 +0200 Subject: [PATCH 4/5] fix(storage): allow custom chown values in chown-storage-dir script --- plugins/storage/bin/chown-storage-dir | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/plugins/storage/bin/chown-storage-dir b/plugins/storage/bin/chown-storage-dir index 4be58586c..3ce7c5c7b 100755 --- a/plugins/storage/bin/chown-storage-dir +++ b/plugins/storage/bin/chown-storage-dir @@ -16,13 +16,15 @@ main() { exit 1 fi - case "$CHOWN_ID" in - 0 | 1000 | 2000 | 32767 | 165536 | 166536 | 167536 | 198303) ;; - *) - echo " ! Unsupported chown permissions. Supported values: 0, 1000, 2000, 32767 (and user namespace offset variants)" 1>&2 - exit 1 - ;; - esac + if [[ ! "$CHOWN_ID" =~ ^[0-9]+$ ]]; then + echo " ! Unsupported chown permissions. Value must be a non-negative integer." 1>&2 + exit 1 + fi + + if ((CHOWN_ID > 65535 && (CHOWN_ID < 165536 || CHOWN_ID > 231071))); then + echo " ! Unsupported chown permissions. Supported values: 0-65535 (and user namespace offset variants, 165536-231071)" 1>&2 + exit 1 + fi chown -R "$CHOWN_ID:$CHOWN_ID" "${DOKKU_LIB_ROOT}/data/storage/$DIRECTORY" } From 513b200f5c8c88e01b89c0c930ca238a7e196838 Mon Sep 17 00:00:00 2001 From: RichardDorian Date: Wed, 8 Jul 2026 19:36:29 +0200 Subject: [PATCH 5/5] test(storage): add out-of-bounds chown coverage for go code and script --- plugins/storage/subcommands_test.go | 17 ++++++++++ tests/unit/storage.bats | 49 +++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+) create mode 100644 plugins/storage/subcommands_test.go diff --git a/plugins/storage/subcommands_test.go b/plugins/storage/subcommands_test.go new file mode 100644 index 000000000..d0d5bfe4e --- /dev/null +++ b/plugins/storage/subcommands_test.go @@ -0,0 +1,17 @@ +package storage + +import ( + "testing" + + . "github.com/onsi/gomega" +) + +func TestResolveChownIDRejectsOutOfBoundsValues(t *testing.T) { + RegisterTestingT(t) + + for _, chownFlag := range []string{"-1", "65536", "100000", "231072", "abc", "1.5", ""} { + _, err := ResolveChownID(chownFlag) + Expect(err).To(HaveOccurred(), "expected %q to be rejected", chownFlag) + Expect(err.Error()).To(ContainSubstring("Unsupported chown permissions")) + } +} diff --git a/tests/unit/storage.bats b/tests/unit/storage.bats index fd2fab276..3acc757bc 100644 --- a/tests/unit/storage.bats +++ b/tests/unit/storage.bats @@ -107,6 +107,38 @@ teardown() { assert_output_contains "Setting directory ownership to 32767:32767" 1 } +@test "(storage) chown-storage-dir rejects out-of-bounds chown values" { + run /bin/bash -c "DOKKU_LIB_ROOT=$DOKKU_LIB_ROOT $PLUGIN_AVAILABLE_PATH/storage/bin/chown-storage-dir $TEST_APP 65536" + echo "output: $output" + echo "status: $status" + assert_failure + assert_output_contains "Unsupported chown permissions" + + run /bin/bash -c "DOKKU_LIB_ROOT=$DOKKU_LIB_ROOT $PLUGIN_AVAILABLE_PATH/storage/bin/chown-storage-dir $TEST_APP 165535" + echo "output: $output" + echo "status: $status" + assert_failure + assert_output_contains "Unsupported chown permissions" + + run /bin/bash -c "DOKKU_LIB_ROOT=$DOKKU_LIB_ROOT $PLUGIN_AVAILABLE_PATH/storage/bin/chown-storage-dir $TEST_APP 231072" + echo "output: $output" + echo "status: $status" + assert_failure + assert_output_contains "Unsupported chown permissions" + + run /bin/bash -c "DOKKU_LIB_ROOT=$DOKKU_LIB_ROOT $PLUGIN_AVAILABLE_PATH/storage/bin/chown-storage-dir $TEST_APP -1" + echo "output: $output" + echo "status: $status" + assert_failure + assert_output_contains "Value must be a non-negative integer" + + run /bin/bash -c "DOKKU_LIB_ROOT=$DOKKU_LIB_ROOT $PLUGIN_AVAILABLE_PATH/storage/bin/chown-storage-dir $TEST_APP abc" + echo "output: $output" + echo "status: $status" + assert_failure + assert_output_contains "Value must be a non-negative integer" +} + @test "(storage) storage:mount, storage:list, storage:umount" { run /bin/bash -c "dokku storage:mount $TEST_APP /tmp/mount:/mount" echo "output: $output" @@ -410,6 +442,23 @@ teardown() { assert_success } +@test "(storage:create) --chown rejects an out-of-bounds numeric uid" { + run /bin/bash -c "dokku storage:create --chown 65536 rdmtest-chown-oob" + echo "output: $output" + echo "status: $status" + assert_failure + assert_output_contains "Unsupported chown permissions" + + run /bin/bash -c "dokku storage:create --chown -1 rdmtest-chown-oob" + echo "output: $output" + echo "status: $status" + assert_failure + assert_output_contains "Unsupported chown permissions" + + run /bin/bash -c "dokku storage:list-entries --format json | jq -r '.[].name' | grep '^rdmtest-chown-oob$' || true" + assert_output "" +} + @test "(storage:create) --chown rejects a non-default host path" { custom_path="/tmp/rdmtest-chown-custom" rm -rf "$custom_path"