diff --git a/init.sh b/init.sh index 35a4137..c11fc5f 100755 --- a/init.sh +++ b/init.sh @@ -1191,6 +1191,12 @@ completeInitMessage() fi } +# Only run the installer entrypoint (root check + init flow) when init.sh is +# EXECUTED directly. When it's SOURCED — start.sh loads init.sh for its function +# defs at runtime, and under Model A start.sh runs as the manager, not root — the +# defs above are all that's wanted and this root check must NOT fire. +[[ "${BASH_SOURCE[0]}" != "${0}" ]] && return 0 2>/dev/null + if [[ $EUID -ne 0 ]]; then echo "This script must be run as root." exit 1 diff --git a/scripts/app/app_status.sh b/scripts/app/app_status.sh index 56e75d5..8abf15c 100755 --- a/scripts/app/app_status.sh +++ b/scripts/app/app_status.sh @@ -24,17 +24,17 @@ appStatus() isHeader "App Status: $app_name" # Check if container is running - if docker ps --format '{{.Names}}' | grep -q "^${app_name}$"; then + if runFileOp docker ps --format '{{.Names}}' | grep -q "^${app_name}$"; then isSuccessful "Container: RUNNING" else isNotice "Container: STOPPED" fi # Show basic container info if running - if docker ps --format '{{.Names}}' | grep -q "^${app_name}$"; then + if runFileOp docker ps --format '{{.Names}}' | grep -q "^${app_name}$"; then echo "" echo "Container Information:" - docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}" | grep "$app_name" || echo "No port information available" + runFileOp docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}" | grep "$app_name" || echo "No port information available" fi echo "" diff --git a/scripts/backup/db/backup_db.sh b/scripts/backup/db/backup_db.sh index 089dfa3..a982d26 100644 --- a/scripts/backup/db/backup_db.sh +++ b/scripts/backup/db/backup_db.sh @@ -148,9 +148,9 @@ _backupDbWaitReady() good=0 case "$kind" in postgres) - docker exec "$container" sh -c 'export PGPASSWORD="${POSTGRES_PASSWORD:-}"; psql -U "${POSTGRES_USER:-postgres}" -d "${POSTGRES_DB:-${POSTGRES_USER:-postgres}}" -tAc "SELECT 1"' >/dev/null 2>&1 && good=1 ;; + runFileOp docker exec "$container" sh -c 'export PGPASSWORD="${POSTGRES_PASSWORD:-}"; psql -U "${POSTGRES_USER:-postgres}" -d "${POSTGRES_DB:-${POSTGRES_USER:-postgres}}" -tAc "SELECT 1"' >/dev/null 2>&1 && good=1 ;; *) - docker exec "$container" sh -c 'RP="${MARIADB_ROOT_PASSWORD:-$MYSQL_ROOT_PASSWORD}"; mariadb -uroot -p"$RP" -N -e "SELECT 1" 2>/dev/null || mysql -uroot -p"$RP" -N -e "SELECT 1"' >/dev/null 2>&1 && good=1 ;; + runFileOp docker exec "$container" sh -c 'RP="${MARIADB_ROOT_PASSWORD:-$MYSQL_ROOT_PASSWORD}"; mariadb -uroot -p"$RP" -N -e "SELECT 1" 2>/dev/null || mysql -uroot -p"$RP" -N -e "SELECT 1"' >/dev/null 2>&1 && good=1 ;; esac if [[ $good -eq 1 ]]; then ok=$((ok + 1)) @@ -199,7 +199,7 @@ backupDbDump() case "$kind" in postgres) isNotice "Dumping postgres ($container) — live, consistent" - if docker exec "$container" sh -c \ + if runFileOp docker exec "$container" sh -c \ 'export PGPASSWORD="${POSTGRES_PASSWORD:-}"; pg_dump --clean --if-exists -U "${POSTGRES_USER:-postgres}" -d "${POSTGRES_DB:-${POSTGRES_USER:-postgres}}"' \ 2>/dev/null | gzip | runFileWrite "$dump"; then isSuccessful "postgres dump written ($container)" @@ -209,7 +209,7 @@ backupDbDump() ;; mysql|mariadb) isNotice "Dumping $kind ($container) — live, consistent" - if docker exec "$container" sh -c \ + if runFileOp docker exec "$container" sh -c \ 'RP="${MARIADB_ROOT_PASSWORD:-$MYSQL_ROOT_PASSWORD}"; DB="${MARIADB_DATABASE:-$MYSQL_DATABASE}"; (mariadb-dump -uroot -p"$RP" --single-transaction --routines --triggers --databases "$DB" 2>/dev/null || mysqldump -uroot -p"$RP" --single-transaction --routines --triggers --databases "$DB")' \ 2>/dev/null | gzip | runFileWrite "$dump"; then isSuccessful "$kind dump written ($container)" diff --git a/scripts/checks/requirements/check_docker_network.sh b/scripts/checks/requirements/check_docker_network.sh index 8f01033..c4c2d13 100755 --- a/scripts/checks/requirements/check_docker_network.sh +++ b/scripts/checks/requirements/check_docker_network.sh @@ -18,8 +18,8 @@ checkDockerNetworkRequirement() ((preinstallneeded++)) fi elif [[ $CFG_DOCKER_INSTALL_TYPE == "rooted" ]]; then - if docker network inspect $CFG_NETWORK_NAME > /dev/null 2>&1; then - local current_subnet=$(docker network inspect $CFG_NETWORK_NAME --format '{{range .IPAM.Config}}{{.Subnet}}{{end}}' 2>/dev/null) + if runFileOp docker network inspect $CFG_NETWORK_NAME > /dev/null 2>&1; then + local current_subnet=$(runFileOp docker network inspect $CFG_NETWORK_NAME --format '{{range .IPAM.Config}}{{.Subnet}}{{end}}' 2>/dev/null) if [[ "$current_subnet" == "$CFG_NETWORK_SUBNET" ]]; then isSuccessful "Docker Network $CFG_NETWORK_NAME exists with matching subnet" diff --git a/scripts/checks/requirements/check_root.sh b/scripts/checks/requirements/check_root.sh index 5b8c235..19d47dc 100755 --- a/scripts/checks/requirements/check_root.sh +++ b/scripts/checks/requirements/check_root.sh @@ -1,14 +1,17 @@ #!/bin/bash checkRootRequirement() -{ +{ if [[ $CFG_REQUIREMENT_ROOT == "true" ]]; then - # Check if script is run as root - if [[ $EUID -ne 0 ]]; then - echo "This script must be run as root." - exit 1 + # Model A least-privilege: the app runs AS the manager user and escalates + # only specific commands via runSystem, so accept the manager as well as + # root — not root-only. (init.sh keeps its own install-time root check.) + local mgr="${sudo_user_name:-libreportal}" + if [[ $EUID -eq 0 || "$(id -un)" == "$mgr" ]]; then + isSuccessful "Running as $(id -un)." else - isSuccessful "Script ran under root user." + echo "This script must be run as root or the manager user ($mgr)." + exit 1 fi fi -} \ No newline at end of file +} \ No newline at end of file diff --git a/scripts/crontab/task/crontab_check_processor.sh b/scripts/crontab/task/crontab_check_processor.sh index 503deba..ea0c082 100755 --- a/scripts/crontab/task/crontab_check_processor.sh +++ b/scripts/crontab/task/crontab_check_processor.sh @@ -204,7 +204,7 @@ validateDockerService() { } validateContainerHealth() { - local containerInfo=$(docker ps -a --filter "name=libreportal-service" --format "{{.Status}}|{{.Names}}" 2>/dev/null) + local containerInfo=$(runFileOp docker ps -a --filter "name=libreportal-service" --format "{{.Status}}|{{.Names}}" 2>/dev/null) if [ -z "$containerInfo" ]; then healthLogError "LibrePortal WebUI container not found" @@ -214,7 +214,7 @@ validateContainerHealth() { if echo "$status" | grep -q "Up"; then # Check if container is responsive - if docker exec libreportal-service pgrep -f "node.*webui" >/dev/null 2>&1; then + if runFileOp docker exec libreportal-service pgrep -f "node.*webui" >/dev/null 2>&1; then healthLogSuccess "Container is running and responsive" else healthLogWarning "Container is running but WebUI process not found" @@ -226,7 +226,7 @@ validateContainerHealth() { validateWebUIReadiness() { # Get the actual port mapping from the container - local portMapping=$(docker port libreportal-service 2>/dev/null | head -1) + local portMapping=$(runFileOp docker port libreportal-service 2>/dev/null | head -1) if [ -z "$portMapping" ]; then healthLogWarning "No port mapping found for libreportal-service container" diff --git a/scripts/docker/checks/app_health_details.sh b/scripts/docker/checks/app_health_details.sh index c4c85ef..483a17f 100755 --- a/scripts/docker/checks/app_health_details.sh +++ b/scripts/docker/checks/app_health_details.sh @@ -4,6 +4,6 @@ dockerCheckAppHealthDetails() { local app_name="$1" - result=$(docker inspect --format "{{json .State.Health }}" $app_name | jq) + result=$(runFileOp docker inspect --format "{{json .State.Health }}" $app_name | jq) checkSuccess "Getting $app_name health details." } diff --git a/scripts/docker/checks/app_health_status.sh b/scripts/docker/checks/app_health_status.sh index e4fb372..cc82414 100755 --- a/scripts/docker/checks/app_health_status.sh +++ b/scripts/docker/checks/app_health_status.sh @@ -4,6 +4,6 @@ dockerCheckAppHealthStatus() { local app_name="$1" - result=$(docker inspect --format "{{json .State.Health.Status }}" $app_name) + result=$(runFileOp docker inspect --format "{{json .State.Health.Status }}" $app_name) checkSuccess "Getting $app_name health status." } diff --git a/scripts/docker/network/network_prune.sh b/scripts/docker/network/network_prune.sh index 7fb5169..8503353 100755 --- a/scripts/docker/network/network_prune.sh +++ b/scripts/docker/network/network_prune.sh @@ -7,11 +7,11 @@ dockerPruneAppNetworks() if [ ! -z "$app_name" ]; then local networks_found=false # Prune all networks except those containing the specified app_name - for network_id in $(docker network ls --quiet); do - network_name=$(docker network inspect --format '{{.Name}}' "$network_id") + for network_id in $(runFileOp docker network ls --quiet); do + network_name=$(runFileOp docker network inspect --format '{{.Name}}' "$network_id") if [[ "$network_name" == *"$app_name"* ]]; then local result=$(dockerCommandRun "docker network rm "$network_id"") - checkSuccess "Removing the unused docker network - $network_name" + checkSuccess "Removing the unused runFileOp docker network - $network_name" networks_found=true fi done diff --git a/scripts/network/ip/ip_is_available.sh b/scripts/network/ip/ip_is_available.sh index 49820f6..63cf68f 100755 --- a/scripts/network/ip/ip_is_available.sh +++ b/scripts/network/ip/ip_is_available.sh @@ -14,7 +14,7 @@ ipIsAvailable() local existing=$(sqlite3 "$docker_dir/$db_file" "SELECT resource_value FROM network_resources WHERE resource_type = 'ip' AND resource_value = '$test_ip' AND status = 'active';" 2>/dev/null) if [[ -n "$existing" ]]; then ip_available="" - elif docker network inspect $CFG_NETWORK_NAME --format '{{range .Containers}}{{.IPv4Address}} {{end}}' 2>/dev/null | grep -q "$test_ip"; then + elif runFileOp docker network inspect $CFG_NETWORK_NAME --format '{{range .Containers}}{{.IPv4Address}} {{end}}' 2>/dev/null | grep -q "$test_ip"; then ip_available="" else ip_available="$test_ip"