From 4a6fb64be8e39ec788168fff441162d4b078623b Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Fri, 7 Aug 2026 16:35:01 +0000 Subject: [PATCH 1/2] Add Alpine Docker Engine install via apk get.docker.com rejects Alpine, so menu option 1 now detects Alpine and installs docker + docker-cli-compose from the community repo, enabling the service through OpenRC. Also use addgroup for Docker group membership on Alpine and guard the path with security checks. Co-authored-by: Michael --- README.md | 2 +- install.sh | 160 +++++++++++++++++++++++++++++++-------- tests/security-checks.sh | 10 +++ 3 files changed, 140 insertions(+), 32 deletions(-) diff --git a/README.md b/README.md index 3c75f82..51a8eb3 100644 --- a/README.md +++ b/README.md @@ -68,7 +68,7 @@ The script guides you through a simplified menu to perform the following: | Action | Description | | :--- | :--- | -| **Install Docker Engine** | Downloads the official installer to a temp file, then runs it (never `curl \| sh`) | +| **Install Docker Engine** | On Alpine, installs via `apk` from the community repo; elsewhere downloads the official installer to a temp file and runs it (never `curl \| sh`) | | **User Configuration** | Creates a new user specifically for Docker or adds your current user | | **Security Groups** | Adds the selected user to the `docker` group for non-root command execution | | **Portainer Deployment** | Pulls and runs Portainer CE pinned to a verified image digest from the LTS release channel on port 9443 | diff --git a/install.sh b/install.sh index 6d4f6a5..fa9ac11 100644 --- a/install.sh +++ b/install.sh @@ -438,6 +438,108 @@ execute_script() { fi } +# detect_distro_id returns the OS ID from /etc/os-release (e.g. alpine, ubuntu). +detect_distro_id() { + if [[ -f /etc/os-release ]]; then + awk -F= '/^ID=/{gsub(/"/, "", $2); print $2; exit}' /etc/os-release + else + printf '%s\n' "unknown" + fi +} + +# enable_docker_service enables and starts the Docker daemon via systemd or OpenRC. +enable_docker_service() { + local SUDO="${1:-}" + + if command -v systemctl >/dev/null 2>&1; then + $SUDO systemctl enable docker 2>/dev/null || true + $SUDO systemctl start docker 2>/dev/null || true + print_success "Docker service enabled and started." + return 0 + fi + + if command -v rc-update >/dev/null 2>&1; then + $SUDO rc-update add docker default 2>/dev/null || true + if command -v rc-service >/dev/null 2>&1; then + $SUDO rc-service docker start 2>/dev/null || true + elif command -v service >/dev/null 2>&1; then + $SUDO service docker start 2>/dev/null || true + fi + print_success "Docker service enabled and started." + return 0 + fi + + print_warn "Could not detect systemd or OpenRC; start Docker manually." +} + +# install_docker_alpine installs Docker Engine and Compose from Alpine community packages. +install_docker_alpine() { + local SUDO="${1:-}" + local alpine_version repo_url exit_code + + print_status "Alpine detected — using apk (get.docker.com does not support Alpine)." + print_line "-" "$BLUE" + + if [[ ! -f /etc/alpine-release ]]; then + print_error "Alpine release file not found." + return 1 + fi + + alpine_version=$(cut -d'.' -f1,2 /etc/alpine-release) + repo_url="https://dl-cdn.alpinelinux.org/alpine/v${alpine_version}/community" + + if ! grep -qE '/alpine/(v[^/]+|edge)/community' /etc/apk/repositories 2>/dev/null; then + print_status "Enabling Alpine community repository..." + echo "$repo_url" | $SUDO tee -a /etc/apk/repositories >/dev/null + fi + + print_status "Installing Docker & Compose via apk..." + exit_code=0 + $SUDO apk update || exit_code=$? + if [[ $exit_code -eq 0 ]]; then + $SUDO apk add docker docker-cli-compose || exit_code=$? + fi + + if [[ $exit_code -ne 0 ]]; then + print_error "Docker installation failed." + return "$exit_code" + fi + + print_success "Docker installed successfully!" + enable_docker_service "$SUDO" + return 0 +} + +# install_docker_official downloads get.docker.com to a temp file, then executes that file. +install_docker_official() { + local SUDO="${1:-}" + local installer_path exit_code download_status + + print_status "Using official Docker installation script..." + print_line "-" "$BLUE" + + installer_path=$(mktemp "${TMPDIR:-/tmp}/get-docker.XXXXXXXX.sh") + if curl -fsSL https://get.docker.com -o "$installer_path"; then + exit_code=0 + $SUDO sh "$installer_path" || exit_code=$? + rm -f "$installer_path" + + if [[ $exit_code -eq 0 ]]; then + print_success "Docker installed successfully!" + enable_docker_service "$SUDO" + return 0 + fi + + print_error "Docker installation failed." + return "$exit_code" + fi + + download_status=$? + rm -f "$installer_path" + print_error "Failed to download Docker installation script." + return "$download_status" +} + install_docker() { echo "" print_status "Installing Docker Engine..." @@ -479,37 +581,19 @@ install_docker() { SUDO="sudo" fi - print_status "Using official Docker installation script..." - print_line "-" "$BLUE" + local distro_id exit_code + distro_id=$(detect_distro_id) + exit_code=0 - # Download the installer to a temp file, then execute that file. - local installer_path exit_code download_status - installer_path=$(mktemp "${TMPDIR:-/tmp}/get-docker.XXXXXXXX.sh") - if curl -fsSL https://get.docker.com -o "$installer_path"; then - exit_code=0 - $SUDO sh "$installer_path" || exit_code=$? - rm -f "$installer_path" - - if [[ $exit_code -eq 0 ]]; then - print_success "Docker installed successfully!" - - # Enable and start Docker - if command -v systemctl >/dev/null 2>&1; then - $SUDO systemctl enable docker 2>/dev/null || true - $SUDO systemctl start docker 2>/dev/null || true - print_success "Docker service enabled and started." - fi - else - print_error "Docker installation failed." - pause - return "$exit_code" - fi + if [[ "$distro_id" == "alpine" ]]; then + install_docker_alpine "$SUDO" || exit_code=$? else - download_status=$? - rm -f "$installer_path" - print_error "Failed to download Docker installation script." + install_docker_official "$SUDO" || exit_code=$? + fi + + if [[ $exit_code -ne 0 ]]; then pause - return "$download_status" + return "$exit_code" fi pause @@ -555,14 +639,28 @@ add_user_to_docker_group() { local SUDO="" fi + local distro_id + distro_id=$(detect_distro_id) + # Ensure docker group exists if ! getent group docker >/dev/null 2>&1; then print_status "Creating docker group..." - $SUDO groupadd docker + if [[ "$distro_id" == "alpine" ]]; then + $SUDO addgroup docker + else + $SUDO groupadd docker + fi + fi + + # Add user to group (Alpine uses addgroup; others use usermod) + local group_ok=0 + if [[ "$distro_id" == "alpine" ]]; then + $SUDO addgroup "$current_user" docker && group_ok=1 + else + $SUDO usermod -aG docker "$current_user" && group_ok=1 fi - # Add user to group - if $SUDO usermod -aG docker "$current_user"; then + if [[ $group_ok -eq 1 ]]; then print_success "User '$current_user' added to docker group." echo "" print_warn "You must log out and back in for this to take effect." diff --git a/tests/security-checks.sh b/tests/security-checks.sh index 97d38a3..3afcb88 100755 --- a/tests/security-checks.sh +++ b/tests/security-checks.sh @@ -57,6 +57,16 @@ if ! grep -Eq '^VERSION="2\.[0-9]+\.[0-9]+"' install.sh; then exit 1 fi +if ! grep -q 'install_docker_alpine' install.sh; then + echo "install.sh must provide an Alpine apk Docker install path." >&2 + exit 1 +fi + +if ! grep -q 'apk add docker docker-cli-compose' install.sh; then + echo "install.sh Alpine path must install docker and docker-cli-compose via apk." >&2 + exit 1 +fi + if rg -n 'git clean[[:space:]]+-[^[:space:]]*f' install.sh; then echo "Broad destructive git clean is forbidden." >&2 exit 1 From 585f97bc934b7cad0e58fbb19301f30c31bec664 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Fri, 7 Aug 2026 16:46:24 +0000 Subject: [PATCH 2/2] Harden Docker service start failures and Alpine dispatch checks Propagate enable/start errors from systemd and OpenRC instead of swallowing them with || true, and require security-checks to assert the alpine/non-alpine installer branches call the matching helpers. Co-authored-by: Michael --- install.sh | 40 ++++++++++++++++++++++++++++++++-------- tests/security-checks.sh | 14 ++++++++++---- 2 files changed, 42 insertions(+), 12 deletions(-) diff --git a/install.sh b/install.sh index fa9ac11..3dd0f78 100644 --- a/install.sh +++ b/install.sh @@ -448,28 +448,48 @@ detect_distro_id() { } # enable_docker_service enables and starts the Docker daemon via systemd or OpenRC. +# Returns nonzero if enable/start fails or neither init system is available. enable_docker_service() { local SUDO="${1:-}" if command -v systemctl >/dev/null 2>&1; then - $SUDO systemctl enable docker 2>/dev/null || true - $SUDO systemctl start docker 2>/dev/null || true + if ! $SUDO systemctl enable docker; then + print_error "Failed to enable Docker service." + return 1 + fi + if ! $SUDO systemctl start docker; then + print_error "Failed to start Docker service." + return 1 + fi print_success "Docker service enabled and started." return 0 fi if command -v rc-update >/dev/null 2>&1; then - $SUDO rc-update add docker default 2>/dev/null || true + if ! $SUDO rc-update add docker default; then + print_error "Failed to enable Docker service." + return 1 + fi if command -v rc-service >/dev/null 2>&1; then - $SUDO rc-service docker start 2>/dev/null || true + if ! $SUDO rc-service docker start; then + print_error "Failed to start Docker service." + return 1 + fi elif command -v service >/dev/null 2>&1; then - $SUDO service docker start 2>/dev/null || true + if ! $SUDO service docker start; then + print_error "Failed to start Docker service." + return 1 + fi + else + print_error "No OpenRC service command found to start Docker." + return 1 fi print_success "Docker service enabled and started." return 0 fi - print_warn "Could not detect systemd or OpenRC; start Docker manually." + print_error "Could not detect systemd or OpenRC; start Docker manually." + return 1 } # install_docker_alpine installs Docker Engine and Compose from Alpine community packages. @@ -506,7 +526,9 @@ install_docker_alpine() { fi print_success "Docker installed successfully!" - enable_docker_service "$SUDO" + if ! enable_docker_service "$SUDO"; then + return 1 + fi return 0 } @@ -526,7 +548,9 @@ install_docker_official() { if [[ $exit_code -eq 0 ]]; then print_success "Docker installed successfully!" - enable_docker_service "$SUDO" + if ! enable_docker_service "$SUDO"; then + return 1 + fi return 0 fi diff --git a/tests/security-checks.sh b/tests/security-checks.sh index 3afcb88..fb9ff95 100755 --- a/tests/security-checks.sh +++ b/tests/security-checks.sh @@ -57,13 +57,19 @@ if ! grep -Eq '^VERSION="2\.[0-9]+\.[0-9]+"' install.sh; then exit 1 fi -if ! grep -q 'install_docker_alpine' install.sh; then - echo "install.sh must provide an Alpine apk Docker install path." >&2 +if ! grep -q 'apk add docker docker-cli-compose' install.sh; then + echo "install.sh Alpine path must install docker and docker-cli-compose via apk." >&2 exit 1 fi -if ! grep -q 'apk add docker docker-cli-compose' install.sh; then - echo "install.sh Alpine path must install docker and docker-cli-compose via apk." >&2 +# Alpine / non-Alpine branches must call the matching installer (not merely define it). +if ! rg -U -q '\[\[ "\$distro_id" == "alpine" \]\]; then[[:space:]]+install_docker_alpine "\$SUDO"' install.sh; then + echo 'Alpine distro_id branch must call install_docker_alpine "$SUDO".' >&2 + exit 1 +fi + +if ! rg -U --multiline-dotall -q '\[\[ "\$distro_id" == "alpine" \]\]; then[[:space:]]+install_docker_alpine "\$SUDO".*else[[:space:]]+install_docker_official "\$SUDO"' install.sh; then + echo 'Non-Alpine distro_id branch must call install_docker_official "$SUDO".' >&2 exit 1 fi