From 92a5d74ba9db531557a35e66d19e28b2e99322e2 Mon Sep 17 00:00:00 2001 From: rcourtman Date: Tue, 11 Nov 2025 23:07:29 +0000 Subject: [PATCH] Add Snap Docker support to install-docker-agent.sh Snap-installed Docker does not automatically create a docker group, causing permission denied errors when the pulse-docker service user tries to access /var/run/docker.sock. Changes: - Auto-detect Snap Docker installations - Create docker group if missing when Snap Docker is detected - Restart Snap Docker after group creation to refresh socket ACLs - Add socket access validation before starting the service - Handle symlinked Docker sockets in systemd unit ReadWritePaths - Document troubleshooting steps in DOCKER_MONITORING.md --- docs/DOCKER_MONITORING.md | 15 +++ scripts/install-docker-agent.sh | 162 +++++++++++++++++++++++++++++++- 2 files changed, 176 insertions(+), 1 deletion(-) diff --git a/docs/DOCKER_MONITORING.md b/docs/DOCKER_MONITORING.md index 2726f73..c1654df 100644 --- a/docs/DOCKER_MONITORING.md +++ b/docs/DOCKER_MONITORING.md @@ -271,6 +271,21 @@ Need the alerts but at a different tone? The same Containers tab exposes global **Fix:** Open **Settings → Docker → Removed Hosts**, locate the entry, and click **Allow re-enroll**. Then restart the agent or rerun the install script. If the install script runs with a token that includes the `docker:manage` scope it will automatically clear the removal block. +### Snap-installed Docker permission issues + +**Symptom:** Install script completes successfully but the agent service fails with `permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock`. The install script may have shown `[WARN] docker group not found`. + +**Cause:** Docker installed via Snap does not automatically create a system `docker` group, preventing the `pulse-docker` service user from accessing `/var/run/docker.sock`. + +**Fix:** The install script now automatically detects Snap Docker and creates the docker group when needed. If you previously installed with an older version of the script: + +1. Create the docker group: `sudo addgroup --system docker` +2. Add the service user to the group: `sudo adduser pulse-docker docker` +3. Restart Snap Docker to refresh socket ACLs: `sudo snap restart docker` +4. Restart the agent service: `sudo systemctl restart pulse-docker-agent` + +Verify socket access with: `sudo -u pulse-docker docker version` + ## Removing the agent Stop the systemd service or container and remove the binary. Pulse retains the last reported state until it ages out after a few minutes of inactivity. diff --git a/scripts/install-docker-agent.sh b/scripts/install-docker-agent.sh index 9ca0647..f2bb607 100755 --- a/scripts/install-docker-agent.sh +++ b/scripts/install-docker-agent.sh @@ -415,12 +415,54 @@ ensure_service_home() { chmod 750 "$SERVICE_HOME" >/dev/null 2>&1 || true } +detect_snap_docker() { + SNAP_DOCKER_DETECTED="false" + + # Check if the active docker binary resolves to a snap path + if command -v docker >/dev/null 2>&1; then + local docker_path + docker_path=$(readlink -f "$(command -v docker)" 2>/dev/null || echo "") + if [[ "$docker_path" == /snap/* ]] || [[ "$docker_path" == /var/lib/snapd/snap/* ]]; then + SNAP_DOCKER_DETECTED="true" + fi + fi +} + ensure_docker_group_membership() { SYSTEMD_SUPPLEMENTARY_GROUPS_LINE="" if [[ "$SERVICE_USER_ACTUAL" == "root" ]]; then return fi + # Detect Snap Docker installation + detect_snap_docker + + # If docker group doesn't exist and Snap Docker is detected, create it + if ! getent group docker >/dev/null 2>&1; then + if [[ "$SNAP_DOCKER_DETECTED" == "true" ]]; then + log_info "Snap-installed Docker detected without docker group; creating system docker group" + if command -v addgroup >/dev/null 2>&1; then + if addgroup --system docker >/dev/null 2>&1; then + log_success "Created docker system group for Snap Docker" + SNAP_DOCKER_GROUP_CREATED="true" + else + log_warn "Failed to create docker group; socket access may fail" + fi + elif command -v groupadd >/dev/null 2>&1; then + if groupadd -r docker >/dev/null 2>&1; then + log_success "Created docker system group for Snap Docker" + SNAP_DOCKER_GROUP_CREATED="true" + else + log_warn "Failed to create docker group; socket access may fail" + fi + else + log_warn "Unable to create docker group; ensure $SERVICE_USER_ACTUAL can access /var/run/docker.sock" + fi + else + log_warn "docker group not found; ensure the agent user can access /var/run/docker.sock" + fi + fi + if getent group docker >/dev/null 2>&1; then DOCKER_GROUP_PRESENT="true" if ! id -nG "$SERVICE_USER_ACTUAL" 2>/dev/null | tr ' ' '\n' | grep -Fxq "docker"; then @@ -431,11 +473,29 @@ ensure_docker_group_membership() { else log_warn "Unable to manage docker group membership; ensure $SERVICE_USER_ACTUAL can access /var/run/docker.sock" fi + + # Verify group membership was actually applied + if ! id -nG "$SERVICE_USER_ACTUAL" 2>/dev/null | tr ' ' '\n' | grep -Fxq "docker"; then + log_warn "Failed to verify docker group membership for $SERVICE_USER_ACTUAL" + log_warn "Group changes may not have taken effect; socket access validation will catch this" + fi fi if id -nG "$SERVICE_USER_ACTUAL" 2>/dev/null | tr ' ' '\n' | grep -Fxq "docker"; then SYSTEMD_SUPPLEMENTARY_GROUPS_LINE="SupplementaryGroups=docker" log_success "Ensured docker group access for $SERVICE_USER_ACTUAL" + + # If we created the group for Snap Docker, restart the snap to refresh socket ACLs + if [[ "$SNAP_DOCKER_DETECTED" == "true" && "$SNAP_DOCKER_GROUP_CREATED" == "true" ]]; then + if command -v snap >/dev/null 2>&1; then + log_info "Restarting Snap Docker to apply group permissions" + if snap restart docker >/dev/null 2>&1; then + log_success "Snap Docker restarted successfully" + else + log_warn "Failed to restart Snap Docker; socket permissions may not apply until manual restart" + fi + fi + fi else log_warn "Service user $SERVICE_USER_ACTUAL is not in docker group; ensure the Docker socket ACL grants access." fi @@ -481,6 +541,19 @@ write_env_file() { log_success "Wrote environment file: $target" } +detect_docker_socket_paths() { + DOCKER_SOCKET_PATHS="/var/run/docker.sock" + + # If /var/run/docker.sock is a symlink, include the real path too + if [[ -L /var/run/docker.sock ]]; then + local target + target=$(readlink -f /var/run/docker.sock 2>/dev/null || echo "") + if [[ -n "$target" && "$target" != "/var/run/docker.sock" ]]; then + DOCKER_SOCKET_PATHS="/var/run/docker.sock $target" + fi + fi +} + configure_polkit_rule() { if [[ "$SERVICE_USER_ACTUAL" == "root" ]]; then return @@ -521,6 +594,76 @@ EOF log_success "Configured polkit rule allowing $SERVICE_USER_ACTUAL to manage pulse-docker-agent service" } +validate_docker_socket_access() { + if [[ "$SERVICE_USER_ACTUAL" == "root" ]]; then + return 0 + fi + + log_header 'Validating Docker socket access' + + # Source the env file to test with the exact configuration the service will use + if [[ -f "$ENV_FILE" ]]; then + set -a + source "$ENV_FILE" 2>/dev/null || true + set +a + fi + + # If we just restarted Snap Docker, give it a moment to come back up + if [[ "$SNAP_DOCKER_DETECTED" == "true" && "$SNAP_DOCKER_GROUP_CREATED" == "true" ]]; then + log_info "Waiting for Snap Docker to restart" + sleep 3 + fi + + # Build env prefix for sudo (portable across sudo variants) + local env_prefix="" + [[ -n "${DOCKER_HOST:-}" ]] && env_prefix+="DOCKER_HOST='$DOCKER_HOST' " + [[ -n "${PULSE_URL:-}" ]] && env_prefix+="PULSE_URL='$PULSE_URL' " + [[ -n "${PULSE_TOKEN:-}" ]] && env_prefix+="PULSE_TOKEN='$PULSE_TOKEN' " + [[ -n "${PULSE_TARGETS:-}" ]] && env_prefix+="PULSE_TARGETS='$PULSE_TARGETS' " + + # Test socket access + local test_output + local test_exitcode + test_output=$(eval "env $env_prefix sudo -u $SERVICE_USER_ACTUAL docker version --format '{{.Server.Version}}'" 2>&1) + test_exitcode=$? + + if [[ $test_exitcode -eq 0 ]]; then + log_success "Docker socket access confirmed for $SERVICE_USER_ACTUAL" + return 0 + fi + + # Validation failed + log_warn "Docker socket access test failed for $SERVICE_USER_ACTUAL" + echo "" >&2 + echo "Validation output:" >&2 + echo "$test_output" >&2 + echo "" >&2 + + if [[ "$SNAP_DOCKER_DETECTED" == "true" ]]; then + echo "Snap-installed Docker detected. Common solutions:" >&2 + echo " 1. Ensure the docker group exists: getent group docker" >&2 + echo " 2. Ensure $SERVICE_USER_ACTUAL is in the docker group: id -nG $SERVICE_USER_ACTUAL" >&2 + echo " 3. Check if Snap Docker is running: snap services docker.dockerd" >&2 + echo " 4. Restart Snap Docker to refresh permissions: snap restart docker" >&2 + else + echo "Common solutions:" >&2 + echo " 1. Ensure $SERVICE_USER_ACTUAL is in the docker group: id -nG $SERVICE_USER_ACTUAL" >&2 + echo " 2. Check Docker socket permissions: ls -la /var/run/docker.sock" >&2 + echo " 3. Verify Docker is running: docker ps" >&2 + fi + echo "" >&2 + + read -p "Continue with installation anyway? (y/N) " -n 1 -r + echo + if [[ ! $REPLY =~ ^[Yy]$ ]]; then + echo "Installation aborted. Fix socket access and re-run the installer." >&2 + exit 1 + fi + + log_warn "Proceeding despite failed validation; service may not start correctly" + return 0 +} + remove_polkit_rule() { local polkit_dir="/etc/polkit-1/rules.d" local rule_path="${polkit_dir}/90-pulse-docker-agent.rules" @@ -734,6 +877,9 @@ SERVICE_USER_CREATED="false" SERVICE_USER_AVAILABLE="true" DOCKER_GROUP_PRESENT="false" SYSTEMD_SUPPLEMENTARY_GROUPS_LINE="" +SNAP_DOCKER_DETECTED="false" +SNAP_DOCKER_GROUP_CREATED="false" +DOCKER_SOCKET_PATHS="/var/run/docker.sock" # Parse arguments while [[ $# -gt 0 ]]; do @@ -1643,6 +1789,17 @@ ensure_service_home ensure_docker_group_membership write_env_file configure_polkit_rule +detect_docker_socket_paths + +# Build ReadWritePaths line for systemd unit +SYSTEMD_READ_WRITE_PATHS="" +for socket_path in $DOCKER_SOCKET_PATHS; do + if [[ -n "$SYSTEMD_READ_WRITE_PATHS" ]]; then + SYSTEMD_READ_WRITE_PATHS="$SYSTEMD_READ_WRITE_PATHS $socket_path" + else + SYSTEMD_READ_WRITE_PATHS="$socket_path" + fi +done # Create systemd service log_header 'Configuring systemd service' @@ -1677,7 +1834,7 @@ ProtectKernelLogs=yes LockPersonality=yes MemoryDenyWriteExecute=yes RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6 -ReadWritePaths=/var/run/docker.sock +ReadWritePaths=$SYSTEMD_READ_WRITE_PATHS ProtectHostname=yes ProtectClock=yes @@ -1687,6 +1844,9 @@ EOF log_success "Wrote unit file: $SERVICE_PATH" +# Validate socket access before starting the service +validate_docker_socket_access + # Reload systemd and start service log_info 'Starting service' systemctl daemon-reload