Skip to content

Commit

Permalink
Add user to docker group if not superuser
Browse files Browse the repository at this point in the history
This allows docker commands to function with a non-root
ssh user

Fixes: #980
  • Loading branch information
NeilW committed Oct 25, 2024
1 parent b917d7c commit 77311e6
Show file tree
Hide file tree
Showing 4 changed files with 39 additions and 0 deletions.
8 changes: 8 additions & 0 deletions lib/kamal/cli/server.rb
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,14 @@ def bootstrap
if execute(*KAMAL.docker.superuser?, raise_on_non_zero_exit: false)
info "Missing Docker on #{host}. Installing…"
execute *KAMAL.docker.install
begin
execute *KAMAL.docker.add_group

# If the groups change, the session is terminated to force a re-login.
# Catch the resulting IOError and inform the user
rescue IOError
info "Session refreshed due to group change."
end
else
missing << host
end
Expand Down
6 changes: 6 additions & 0 deletions lib/kamal/commands/docker.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,12 @@ def superuser?
[ '[ "${EUID:-$(id -u)}" -eq 0 ] || command -v sudo >/dev/null || command -v su >/dev/null' ]
end

# If we're not root and not already in the docker group
# add us to the docker group and terminate all our current sessions
def add_group
[ '[ "${EUID:-$(id -u)}" -eq 0 ] || id -nG "${USER:-$(id -un)}" | grep -qw docker || { sudo usermod -aG docker "${USER:-$(id -un)}" && kill -HUP ${PPID:-ps -o ppid= -p $$}; }' ]
end

def create_network
docker :network, :create, :kamal
end
Expand Down
21 changes: 21 additions & 0 deletions test/cli/server_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ class CliServerTest < CliTestCase
stub_setup
SSHKit::Backend::Abstract.any_instance.expects(:execute).with(:docker, "-v", raise_on_non_zero_exit: false).returns(false).at_least_once
SSHKit::Backend::Abstract.any_instance.expects(:execute).with('[ "${EUID:-$(id -u)}" -eq 0 ] || command -v sudo >/dev/null || command -v su >/dev/null', raise_on_non_zero_exit: false).returns(true).at_least_once
SSHKit::Backend::Abstract.any_instance.expects(:execute).with('[ "${EUID:-$(id -u)}" -eq 0 ] || id -nG "${USER:-$(id -un)}" | grep -qw docker || { sudo usermod -aG docker "${USER:-$(id -un)}" && kill -HUP ${PPID:-ps -o ppid= -p $$}; }').at_least_once
SSHKit::Backend::Abstract.any_instance.expects(:execute).with(:sh, "-c", "'curl -fsSL https://get.docker.com || wget -O - https://get.docker.com || echo \"exit 1\"'", "|", :sh).at_least_once
SSHKit::Backend::Abstract.any_instance.expects(:execute).with(:mkdir, "-p", ".kamal").returns("").at_least_once
Kamal::Commands::Hook.any_instance.stubs(:hook_exists?).returns(true)
Expand All @@ -66,6 +67,26 @@ class CliServerTest < CliTestCase
end
end

test "bootstrap install as sudo non-root user" do
stub_setup
SSHKit::Backend::Abstract.any_instance.expects(:execute).with(:docker, "-v", raise_on_non_zero_exit: false).returns(false).at_least_once
SSHKit::Backend::Abstract.any_instance.expects(:execute).with('[ "${EUID:-$(id -u)}" -eq 0 ] || command -v sudo >/dev/null || command -v su >/dev/null', raise_on_non_zero_exit: false).returns(true).at_least_once
SSHKit::Backend::Abstract.any_instance.expects(:execute).with('[ "${EUID:-$(id -u)}" -eq 0 ] || id -nG "${USER:-$(id -un)}" | grep -qw docker || { sudo usermod -aG docker "${USER:-$(id -un)}" && kill -HUP ${PPID:-ps -o ppid= -p $$}; }').at_least_once.raises(IOError, "closed stream")
SSHKit::Backend::Abstract.any_instance.expects(:execute).with(:sh, "-c", "'curl -fsSL https://get.docker.com || wget -O - https://get.docker.com || echo \"exit 1\"'", "|", :sh).at_least_once
SSHKit::Backend::Abstract.any_instance.expects(:execute).with(:mkdir, "-p", ".kamal").returns("").at_least_once
Kamal::Commands::Hook.any_instance.stubs(:hook_exists?).returns(true)
SSHKit::Backend::Abstract.any_instance.expects(:execute).with(".kamal/hooks/pre-connect", anything).at_least_once
SSHKit::Backend::Abstract.any_instance.expects(:execute).with(".kamal/hooks/docker-setup", anything).at_least_once

run_command("bootstrap").tap do |output|
("1.1.1.1".."1.1.1.4").map do |host|
assert_match "Missing Docker on #{host}. Installing…", output
assert_match "Session refreshed due to group change.", output
assert_match "Running the docker-setup hook", output
end
end
end

private
def run_command(*command)
stdouted { Kamal::Cli::Server.start([ *command, "-c", "test/fixtures/deploy_with_accessories.yml" ]) }
Expand Down
4 changes: 4 additions & 0 deletions test/commands/docker_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,8 @@ class CommandsDockerTest < ActiveSupport::TestCase
test "superuser?" do
assert_equal '[ "${EUID:-$(id -u)}" -eq 0 ] || command -v sudo >/dev/null || command -v su >/dev/null', @docker.superuser?.join(" ")
end

test "add_group" do
assert_equal '[ "${EUID:-$(id -u)}" -eq 0 ] || id -nG "${USER:-$(id -un)}" | grep -qw docker || { sudo usermod -aG docker "${USER:-$(id -un)}" && kill -HUP ${PPID:-ps -o ppid= -p $$}; }', @docker.add_group.join(" ")
end
end

0 comments on commit 77311e6

Please sign in to comment.