feat(model-a): run app as manager; route bare docker calls through runFileOp
Model A prototype (run start.sh AS the manager, escalate only via helpers): - check_root.sh: accept the manager user, not root-only (init.sh keeps its own install-time root check). - init.sh: guard the top-level root-check + installer entrypoint with BASH_SOURCE!=$0 so it runs ONLY when init.sh is executed directly; when start.sh sources it as the manager the entrypoint (and its root check) no longer fires. Also: convert bare daemon-touching 'docker' calls (no helper -> hit the nonexistent /var/run socket in rootless) to runFileOp docker across app_status, app_health_*, network_prune, ip_is_available, check_docker_network, backup_db (db dumps) and crontab_check_processor. cd&&compose rooted-branches and 'docker compose --version' checks left as-is (rooted-only / no daemon). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> Signed-off-by: librelad <librelad@digitalangels.vip>
This commit is contained in:
parent
c6a27d28c4
commit
a3afb2aeae
6
init.sh
6
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
|
||||
|
||||
@ -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 ""
|
||||
|
||||
@ -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)"
|
||||
|
||||
@ -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"
|
||||
|
||||
@ -3,12 +3,15 @@
|
||||
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
|
||||
}
|
||||
@ -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"
|
||||
|
||||
@ -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."
|
||||
}
|
||||
|
||||
@ -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."
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user