mirror of
https://github.com/StuffAnThings/qbit_manage.git
synced 2025-10-08 21:06:28 +08:00
feat(entrypoint): enhance script with validation.
- Add structured logging function for consistent output - Implement robust numeric validation for PUID/PGID environment variables - Improve safe_copy with atomic operations and comprehensive error handling - Optimize permission fixing with better performance and root user checks - Restructure code with main function for improved organization - Remove default ENV vars from Dockerfile to simplify conf - Default PUID, PGID, and UMASK environment variables removed from Dockerfile - users must now explicitly set these if needed
This commit is contained in:
parent
cc43a83508
commit
385d0c633d
3 changed files with 153 additions and 79 deletions
|
@ -49,10 +49,6 @@ LABEL org.opencontainers.image.base.name="python:3.13-alpine"
|
|||
|
||||
ENV TINI_VERSION=v0.19.0
|
||||
|
||||
# Default runtime identity and umask (overridable)
|
||||
ENV PUID=1000
|
||||
ENV PGID=1000
|
||||
ENV UMASK=022
|
||||
|
||||
# Runtime dependencies (smaller than build stage)
|
||||
RUN apk add --no-cache \
|
||||
|
|
2
VERSION
2
VERSION
|
@ -1 +1 @@
|
|||
4.5.6-develop12
|
||||
4.5.6-develop13
|
||||
|
|
226
entrypoint.sh
226
entrypoint.sh
|
@ -1,101 +1,179 @@
|
|||
#!/bin/bash
|
||||
|
||||
# Exit on any error
|
||||
set -e
|
||||
|
||||
# Runtime identity and permissions
|
||||
PUID="${PUID:-1000}"
|
||||
PGID="${PGID:-1000}"
|
||||
UMASK="${UMASK:-022}"
|
||||
|
||||
# Validate inputs (numeric PUID/PGID)
|
||||
if ! [[ "$PUID" =~ ^[0-9]+$ ]] || ! [[ "$PGID" =~ ^[0-9]+$ ]]; then
|
||||
echo "ERROR: PUID and PGID must be numeric. Got PUID='$PUID' PGID='$PGID'"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
umask "$UMASK"
|
||||
set -euo pipefail # Exit on error, undefined vars, pipe failures
|
||||
|
||||
# Configuration
|
||||
SOURCE_FILE="/app/config/config.yml.sample"
|
||||
DEST_FILE="/config/config.yml.sample"
|
||||
readonly SOURCE_FILE="/app/config/config.yml.sample"
|
||||
readonly DEST_FILE="/config/config.yml.sample"
|
||||
|
||||
# Function to safely copy with atomic operation
|
||||
# Logging function for consistent output
|
||||
log() {
|
||||
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" >&2
|
||||
}
|
||||
|
||||
# Validate numeric environment variables
|
||||
validate_numeric_env() {
|
||||
local var_name="$1"
|
||||
local var_value="$2"
|
||||
|
||||
if [[ -n "$var_value" ]] && ! [[ "$var_value" =~ ^[0-9]+$ ]]; then
|
||||
log "Warning: $var_name must be numeric. Got $var_name='$var_value' - ignoring PUID/PGID and running as root"
|
||||
return 1
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
# Validate and set PUID/PGID
|
||||
validate_user_group_ids() {
|
||||
local puid_valid=0
|
||||
local pgid_valid=0
|
||||
|
||||
if ! validate_numeric_env "PUID" "${PUID:-}"; then
|
||||
puid_valid=1
|
||||
fi
|
||||
|
||||
if ! validate_numeric_env "PGID" "${PGID:-}"; then
|
||||
pgid_valid=1
|
||||
fi
|
||||
|
||||
# If either is invalid, clear both
|
||||
if [[ $puid_valid -eq 1 ]] || [[ $pgid_valid -eq 1 ]]; then
|
||||
PUID=""
|
||||
PGID=""
|
||||
return 1
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# Safely copy file with atomic operation and error handling
|
||||
safe_copy() {
|
||||
local src="$1"
|
||||
local dest="$2"
|
||||
local temp_file="${dest}.tmp"
|
||||
|
||||
# Create a temporary file first
|
||||
cp "$src" "$temp_file"
|
||||
# Validate source file exists and is readable
|
||||
if [[ ! -f "$src" ]] || [[ ! -r "$src" ]]; then
|
||||
log "Error: Source file '$src' does not exist or is not readable"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Atomic move to final destination
|
||||
mv "$temp_file" "$dest"
|
||||
# Create parent directory if it doesn't exist
|
||||
local dest_dir
|
||||
dest_dir="$(dirname "$dest")"
|
||||
if [[ ! -d "$dest_dir" ]]; then
|
||||
mkdir -p "$dest_dir" || {
|
||||
log "Error: Could not create destination directory '$dest_dir'"
|
||||
return 1
|
||||
}
|
||||
fi
|
||||
|
||||
echo "Successfully copied $src to $dest"
|
||||
# Atomic copy operation
|
||||
if cp "$src" "$temp_file" && mv "$temp_file" "$dest"; then
|
||||
log "Successfully copied $src to $dest"
|
||||
return 0
|
||||
else
|
||||
# Clean up temp file on failure
|
||||
[[ -f "$temp_file" ]] && rm -f "$temp_file"
|
||||
log "Error: Failed to copy $src to $dest"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Function to fix permissions (only when needed; quiet if no changes)
|
||||
# Optimized permission fixing with better performance
|
||||
fix_permissions() {
|
||||
local path="$1"
|
||||
local uid="${PUID:-1000}"
|
||||
local gid="${PGID:-1000}"
|
||||
|
||||
if [ -d "$path" ]; then
|
||||
if find "$path" -xdev \( -not -user "$uid" -o -not -group "$gid" \) -print -quit | grep -q .; then
|
||||
if chown -R "$uid:$gid" "$path" 2>/dev/null; then
|
||||
echo "Corrected ownership of directory $path to $uid:$gid"
|
||||
else
|
||||
echo "Warning: Could not change ownership of directory $path"
|
||||
fi
|
||||
# Skip if PUID or PGID are not set
|
||||
if [[ -z "${PUID:-}" ]] || [[ -z "${PGID:-}" ]]; then
|
||||
log "Skipping permission fix for $path - PUID or PGID not set"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Check if we're running as root
|
||||
if [[ "$(id -u)" != "0" ]]; then
|
||||
log "Skipping permission fix for $path - not running as root"
|
||||
return 0
|
||||
fi
|
||||
|
||||
local needs_fix=0
|
||||
|
||||
if [[ -d "$path" ]]; then
|
||||
# Check if any files in directory need ownership change
|
||||
if find "$path" -xdev \( -not -user "$PUID" -o -not -group "$PGID" \) -print -quit 2>/dev/null | grep -q .; then
|
||||
needs_fix=1
|
||||
fi
|
||||
elif [[ -e "$path" ]]; then
|
||||
# Check if file needs ownership change
|
||||
if [[ "$(stat -c '%u:%g' "$path" 2>/dev/null || echo "0:0")" != "$PUID:$PGID" ]]; then
|
||||
needs_fix=1
|
||||
fi
|
||||
else
|
||||
if ! find "$path" -maxdepth 0 -user "$uid" -a -group "$gid" -print -quit | grep -q .; then
|
||||
if chown "$uid:$gid" "$path" 2>/dev/null; then
|
||||
echo "Corrected ownership of $path to $uid:$gid"
|
||||
else
|
||||
echo "Warning: Could not change ownership of $path"
|
||||
fi
|
||||
log "Warning: Path '$path' does not exist, skipping permission fix"
|
||||
return 0
|
||||
fi
|
||||
|
||||
if [[ $needs_fix -eq 1 ]]; then
|
||||
if chown -R "$PUID:$PGID" "$path" 2>/dev/null; then
|
||||
local type_msg="file"
|
||||
[[ -d "$path" ]] && type_msg="directory"
|
||||
log "Corrected ownership of $type_msg $path to $PUID:$PGID"
|
||||
return 0
|
||||
else
|
||||
log "Warning: Could not change ownership of $path"
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# Execute command with appropriate privilege level
|
||||
execute_command() {
|
||||
local current_uid
|
||||
current_uid="$(id -u)"
|
||||
|
||||
if [[ "$current_uid" = "0" ]]; then
|
||||
if [[ -n "${PUID:-}" ]] && [[ -n "${PGID:-}" ]]; then
|
||||
log "Changing privileges to PUID:PGID = $PUID:$PGID"
|
||||
exec /sbin/su-exec "${PUID}:${PGID}" "$@" || {
|
||||
log "Warning: Could not drop privileges to ${PUID}:${PGID}, continuing as root"
|
||||
exec "$@"
|
||||
}
|
||||
else
|
||||
log "PUID/PGID not set, running as root"
|
||||
exec "$@"
|
||||
fi
|
||||
else
|
||||
log "Already running as non-root user (UID: $current_uid), executing command"
|
||||
exec "$@"
|
||||
fi
|
||||
}
|
||||
|
||||
# Main logic
|
||||
if [ -d "/config" ]; then
|
||||
if [ -f "$SOURCE_FILE" ] && [ -s "$SOURCE_FILE" ]; then
|
||||
if [ ! -f "$DEST_FILE" ] || ! cmp -s "$SOURCE_FILE" "$DEST_FILE"; then
|
||||
# Safely copy the file (logs only when copy occurs)
|
||||
safe_copy "$SOURCE_FILE" "$DEST_FILE"
|
||||
# Fix permissions (logs only if changes made) when running as root
|
||||
if [ "$(id -u)" = "0" ]; then
|
||||
fix_permissions "$DEST_FILE"
|
||||
# Main execution
|
||||
main() {
|
||||
# Validate user/group IDs
|
||||
validate_user_group_ids
|
||||
|
||||
# Handle config file setup
|
||||
if [[ -d "/config" ]]; then
|
||||
if [[ -f "$SOURCE_FILE" ]] && [[ -s "$SOURCE_FILE" ]]; then
|
||||
# Check if destination needs updating
|
||||
if [[ ! -f "$DEST_FILE" ]] || ! cmp -s "$SOURCE_FILE" "$DEST_FILE" 2>/dev/null; then
|
||||
if safe_copy "$SOURCE_FILE" "$DEST_FILE"; then
|
||||
# Fix permissions if running as root and IDs are set
|
||||
if [[ "$(id -u)" = "0" ]] && [[ -n "${PUID:-}" ]] && [[ -n "${PGID:-}" ]]; then
|
||||
fix_permissions "$DEST_FILE"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
elif [[ ! -f "$SOURCE_FILE" ]]; then
|
||||
log "Warning: Source file $SOURCE_FILE does not exist, skipping config setup"
|
||||
fi
|
||||
elif [ ! -f "$SOURCE_FILE" ]; then
|
||||
echo "ERROR: Source file $SOURCE_FILE does not exist"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# Set HOME if /config exists
|
||||
if [ -d "/config" ]; then
|
||||
export HOME=/config
|
||||
fi
|
||||
# Execute the main command
|
||||
execute_command "$@"
|
||||
}
|
||||
|
||||
# Execute the main command:
|
||||
# - If running as root, drop privileges to PUID:PGID via su-exec
|
||||
# - If already non-root (e.g., docker-compose sets user:), run as-is
|
||||
set +e # Temporarily disable exit on error for su-exec handling
|
||||
if [ "$(id -u)" = "0" ]; then
|
||||
/sbin/su-exec "${PUID}:${PGID}" "$@"
|
||||
if [ $? -eq 0 ]; then
|
||||
# Won't reach here if su-exec succeeds
|
||||
true
|
||||
else
|
||||
echo "Warning: Could not drop privileges to ${PUID}:${PGID}, continuing as root"
|
||||
exec "$@"
|
||||
fi
|
||||
else
|
||||
exec "$@"
|
||||
fi
|
||||
set -e # Re-enable exit on error
|
||||
# Run main function with all arguments
|
||||
main "$@"
|
||||
|
|
Loading…
Add table
Reference in a new issue