From 9753221e31e3bbeba4a9abdb9a0bd1cc9b6c33b5 Mon Sep 17 00:00:00 2001 From: Anthony Stirling <77850077+Frooodle@users.noreply.github.com.> Date: Tue, 20 May 2025 21:37:03 +0100 Subject: [PATCH] rootless init --- Dockerfile | 22 ++- Dockerfile.fat | 22 ++- .../docker-compose-latest-rootless.yml | 35 ++++ scripts/init-without-ocr.sh | 89 +++++++--- scripts/prepare-for-rootless.sh | 160 ++++++++++++++++++ 5 files changed, 301 insertions(+), 27 deletions(-) create mode 100644 exampleYmlFiles/docker-compose-latest-rootless.yml create mode 100644 scripts/prepare-for-rootless.sh diff --git a/Dockerfile b/Dockerfile index 6a854f35e..9314fd740 100644 --- a/Dockerfile +++ b/Dockerfile @@ -66,6 +66,7 @@ RUN echo "@main https://dl-cdn.alpinelinux.org/alpine/edge/main" | tee -a /etc/a tesseract-ocr-data-deu \ tesseract-ocr-data-fra \ tesseract-ocr-data-por \ + font-terminus font-dejavu font-noto font-noto-cjk font-awesome font-noto-extra font-liberation font-linux-libertine \ # CV py3-opencv \ python3 \ @@ -78,15 +79,30 @@ RUN echo "@main https://dl-cdn.alpinelinux.org/alpine/edge/main" | tee -a /etc/a ln -s /usr/lib/libreoffice/program/uno.py /opt/venv/lib/python3.12/site-packages/ && \ ln -s /usr/lib/libreoffice/program/unohelper.py /opt/venv/lib/python3.12/site-packages/ && \ ln -s /usr/lib/libreoffice/program /opt/venv/lib/python3.12/site-packages/LibreOffice && \ - mv /usr/share/tessdata /usr/share/tessdata-original && \ - mkdir -p $HOME /configs /logs /customFiles /pipeline/watchedFolders /pipeline/finishedFolders && \ + # Prepare tessdata - copy to permanent location for rootless mode support + mkdir -p /usr/share/tessdata && \ + cp -r /usr/share/tesseract-ocr/*/tessdata/* /usr/share/tessdata/ || true && \ + # Keep a copy of original tessdata files for non-rootless mode + mkdir -p /usr/share/tessdata-original && \ + cp -r /usr/share/tessdata/* /usr/share/tessdata-original/ || true && \ + # Create required directories with proper permissions + mkdir -p $HOME /configs /logs /customFiles/signatures /customFiles/templates /pipeline/watchedFolders /pipeline/finishedFolders /tmp/stirling-pdf && \ + # Font cache fc-cache -f -v && \ chmod +x /scripts/* && \ chmod +x /scripts/init.sh && \ # User permissions addgroup -S stirlingpdfgroup && adduser -S stirlingpdfuser -G stirlingpdfgroup && \ chown -R stirlingpdfuser:stirlingpdfgroup $HOME /scripts /usr/share/fonts/opentype/noto /configs /customFiles /pipeline && \ - chown stirlingpdfuser:stirlingpdfgroup /app.jar + chown stirlingpdfuser:stirlingpdfgroup /app.jar && \ + # Make critical directories and files world-accessible for rootless execution + chmod -R 755 /opt/venv /usr/lib/libreoffice && \ + chmod -R 1777 /configs /logs /customFiles /pipeline /usr/share/tessdata /tmp/stirling-pdf && \ + chmod 755 /app.jar && \ + # Ensure specific content directories are accessible + mkdir -p /tmp/stirling-pdf && chmod 1777 /tmp/stirling-pdf && \ + # Set proper permissions for the Java temp dir + chmod 1777 /tmp EXPOSE 8080/tcp diff --git a/Dockerfile.fat b/Dockerfile.fat index ef19ebde7..300bc2e95 100644 --- a/Dockerfile.fat +++ b/Dockerfile.fat @@ -88,15 +88,31 @@ RUN echo "@main https://dl-cdn.alpinelinux.org/alpine/edge/main" | tee -a /etc/a ln -s /usr/lib/libreoffice/program/uno.py /opt/venv/lib/python3.12/site-packages/ && \ ln -s /usr/lib/libreoffice/program/unohelper.py /opt/venv/lib/python3.12/site-packages/ && \ ln -s /usr/lib/libreoffice/program /opt/venv/lib/python3.12/site-packages/LibreOffice && \ - mv /usr/share/tessdata /usr/share/tessdata-original && \ - mkdir -p $HOME /configs /logs /customFiles /pipeline/watchedFolders /pipeline/finishedFolders && \ + # Prepare tessdata - copy to permanent location for rootless mode support + mkdir -p /usr/share/tessdata && \ + cp -r /usr/share/tesseract-ocr/*/tessdata/* /usr/share/tessdata/ || true && \ + # Keep a copy of original tessdata files for non-rootless mode + mkdir -p /usr/share/tessdata-original && \ + cp -r /usr/share/tessdata/* /usr/share/tessdata-original/ || true && \ + # Create required directories with proper permissions + mkdir -p $HOME /configs /logs /customFiles/signatures /customFiles/templates /pipeline/watchedFolders /pipeline/finishedFolders /tmp/stirling-pdf && \ + # Font cache fc-cache -f -v && \ + # Make scripts executable chmod +x /scripts/* && \ chmod +x /scripts/init.sh && \ # User permissions addgroup -S stirlingpdfgroup && adduser -S stirlingpdfuser -G stirlingpdfgroup && \ chown -R stirlingpdfuser:stirlingpdfgroup $HOME /scripts /usr/share/fonts/opentype/noto /configs /customFiles /pipeline && \ - chown stirlingpdfuser:stirlingpdfgroup /app.jar + chown stirlingpdfuser:stirlingpdfgroup /app.jar && \ + # Make critical directories and files world-accessible for rootless execution + chmod -R 755 /opt/venv /usr/lib/libreoffice && \ + chmod -R 1777 /configs /logs /customFiles /pipeline /usr/share/tessdata /tmp/stirling-pdf && \ + chmod 755 /app.jar && \ + # Ensure specific content directories are accessible + mkdir -p /tmp/stirling-pdf && chmod 1777 /tmp/stirling-pdf && \ + # Set proper permissions for the Java temp dir + chmod 1777 /tmp EXPOSE 8080/tcp # Set user and run command diff --git a/exampleYmlFiles/docker-compose-latest-rootless.yml b/exampleYmlFiles/docker-compose-latest-rootless.yml new file mode 100644 index 000000000..943c606d7 --- /dev/null +++ b/exampleYmlFiles/docker-compose-latest-rootless.yml @@ -0,0 +1,35 @@ +services: + stirling-pdf: + container_name: Stirling-PDF + # Use the fat version for rootless operation as it includes all dependencies + image: docker.stirlingpdf.com/stirlingtools/stirling-pdf:latest-fat + # Run as a non-root user - this is the key part for rootless operation + # Use the user directive to specify the numerical UID:GID + # Use the UID of your host user to ensure proper file permissions + # Example: user: "1000:1000" + user: "1000:1000" + healthcheck: + test: ["CMD-SHELL", "curl -f http://localhost:8080/api/v1/info/status | grep -q 'UP' && curl -fL http://localhost:8080/ | grep -qv 'Please sign in'"] + interval: 5s + timeout: 10s + retries: 16 + ports: + - "8080:8080" + volumes: + # When running rootless, make sure these directories: + # 1. Exist on the host + # 2. Are owned by the same UID as specified in the user directive above + # 3. Have appropriate permissions (at least 755 or 777 for directories) + - /stirling/rootless/data:/usr/share/tessdata:rw + - /stirling/rootless/config:/configs:rw + - /stirling/rootless/logs:/logs:rw + - /stirling/rootless/customFiles:/customFiles:rw + - /stirling/rootless/pipeline:/pipeline:rw + environment: + DOCKER_ENABLE_SECURITY: "false" + SECURITY_ENABLELOGIN: "false" + LANGS: "en_GB,en_US,ar_AR,de_DE,fr_FR,es_ES,zh_CN,zh_TW,ca_CA,it_IT,sv_SE,pl_PL,ro_RO,ko_KR,pt_BR,ru_RU,el_GR,hi_IN,hu_HU,tr_TR,id_ID" + SYSTEM_DEFAULTLOCALE: en-US + SYSTEM_MAXFILESIZE: "100" + SYSTEM_GOOGLEVISIBILITY: "true" + restart: on-failure:5 \ No newline at end of file diff --git a/scripts/init-without-ocr.sh b/scripts/init-without-ocr.sh index 934c995a3..77f1e4a9c 100644 --- a/scripts/init-without-ocr.sh +++ b/scripts/init-without-ocr.sh @@ -3,38 +3,85 @@ export JAVA_TOOL_OPTIONS="${JAVA_BASE_OPTS} ${JAVA_CUSTOM_OPTS}" echo "running with JAVA_TOOL_OPTIONS ${JAVA_BASE_OPTS} ${JAVA_CUSTOM_OPTS}" -# Update the user and group IDs as per environment variables -if [ ! -z "$PUID" ] && [ "$PUID" != "$(id -u stirlingpdfuser)" ]; then - usermod -o -u "$PUID" stirlingpdfuser || true +# Detect if we're running as root (UID 0) +RUNNING_AS_ROOT=false +if [ "$(id -u)" -eq 0 ]; then + RUNNING_AS_ROOT=true + echo "Running container as root, will attempt to drop privileges" fi +# Only attempt user/group modifications if running as root +if [ "$RUNNING_AS_ROOT" = true ]; then + # Update the user and group IDs as per environment variables + if [ ! -z "$PUID" ] && [ "$PUID" != "$(id -u stirlingpdfuser)" ]; then + usermod -o -u "$PUID" stirlingpdfuser || echo "[WARN] Failed to update UID for stirlingpdfuser" + fi -if [ ! -z "$PGID" ] && [ "$PGID" != "$(getent group stirlingpdfgroup | cut -d: -f3)" ]; then - groupmod -o -g "$PGID" stirlingpdfgroup || true + if [ ! -z "$PGID" ] && [ "$PGID" != "$(getent group stirlingpdfgroup | cut -d: -f3)" ]; then + groupmod -o -g "$PGID" stirlingpdfgroup || echo "[WARN] Failed to update GID for stirlingpdfgroup" + fi fi + +# Apply umask in either case umask "$UMASK" || true -if [[ "$INSTALL_BOOK_AND_ADVANCED_HTML_OPS" == "true" && "$FAT_DOCKER" != "true" ]]; then - echo "issue with calibre in current version, feature currently disabled on Stirling-PDF" - #apk add --no-cache calibre@testing + +# Skip download for fat Docker (already has security jar) +if [[ "$FAT_DOCKER" != "true" && "$RUNNING_AS_ROOT" = true ]]; then + echo "Downloading security JAR (not necessary in fat Docker image)..." + /scripts/download-security-jar.sh +elif [[ "$FAT_DOCKER" != "true" && "$RUNNING_AS_ROOT" != true ]]; then + echo "[INFO] Skipping security JAR download in rootless mode" fi -if [[ "$FAT_DOCKER" != "true" ]]; then - /scripts/download-security-jar.sh -fi - -if [[ -n "$LANGS" ]]; then +# Handle font installation +if [[ -n "$LANGS" && "$RUNNING_AS_ROOT" = true ]]; then + echo "Installing fonts for languages: $LANGS" /scripts/installFonts.sh $LANGS +elif [[ -n "$LANGS" && "$RUNNING_AS_ROOT" != true ]]; then + echo "[INFO] Skipping font installation in rootless mode" fi -echo "Setting permissions and ownership for necessary directories..." -# Attempt to change ownership of directories and files -if chown -R stirlingpdfuser:stirlingpdfgroup $HOME /logs /scripts /usr/share/fonts/opentype/noto /configs /customFiles /pipeline /app.jar; then - chmod -R 755 /logs /scripts /usr/share/fonts/opentype/noto /configs /customFiles /pipeline /app.jar || true - # If chown succeeds, execute the command as stirlingpdfuser - exec su-exec stirlingpdfuser "$@" +# Directory list we need to ensure are accessible +DIRS_TO_CHECK="$HOME /logs /scripts /usr/share/fonts/opentype/noto /configs /customFiles /customFiles/signatures /customFiles/templates /pipeline /pipeline/watchedFolders /pipeline/finishedFolders /usr/share/tessdata /tmp /tmp/stirling-pdf" +FILES_TO_CHECK="/app.jar" + +# Skip copying tessdata files in rootless mode to avoid the error message +if [ "$RUNNING_AS_ROOT" = true ]; then + # We're running as root, so try to copy tessdata files if they exist + if [ -d "/usr/share/tessdata-original" ]; then + echo "Copying original files without overwriting existing files" + cp -n /usr/share/tessdata-original/* /usr/share/tessdata/ 2>/dev/null || true + fi + + echo "Setting permissions and ownership for necessary directories..." + # Attempt to change ownership of directories and files if running as root + if chown -R stirlingpdfuser:stirlingpdfgroup $DIRS_TO_CHECK $FILES_TO_CHECK; then + chmod -R 755 $DIRS_TO_CHECK $FILES_TO_CHECK || echo "[WARN] Failed to set directory permissions, but continuing" + # If chown succeeds, execute the command as stirlingpdfuser + echo "Running as stirlingpdfuser" + exec su-exec stirlingpdfuser "$@" + else + # If chown fails, still try to make files accessible + echo "[WARN] Chown failed, but will attempt to make files world-accessible" + chmod -R 1777 /logs /configs /customFiles /pipeline || true + echo "[WARN] Running as root user - could not drop privileges" + exec "$@" + fi else - # If chown fails, execute the command without changing the user context - echo "[WARN] Chown failed, running as host user" + # Already running as non-root (rootless mode) + echo "Running in rootless mode" + + # In rootless mode, we'll only check critical paths that must be writable + CRITICAL_DIRS="/configs /logs /customFiles /customFiles/signatures /customFiles/templates /pipeline/watchedFolders /pipeline/finishedFolders" + + for DIR in $CRITICAL_DIRS; do + if [ -d "$DIR" ] && [ ! -w "$DIR" ]; then + echo "[WARN] Cannot write to $DIR in rootless mode. Some functionality may be limited." + fi + done + + # Just execute the command as the current user + echo "Executing as current user (UID: $(id -u))" exec "$@" fi diff --git a/scripts/prepare-for-rootless.sh b/scripts/prepare-for-rootless.sh new file mode 100644 index 000000000..bacaa7da7 --- /dev/null +++ b/scripts/prepare-for-rootless.sh @@ -0,0 +1,160 @@ +#!/bin/bash + +# This script helps prepare your host environment for running Stirling-PDF in rootless mode +# It creates the necessary directories with appropriate permissions + +# Set text colors +GREEN='\033[0;32m' +YELLOW='\033[0;33m' +NC='\033[0m' # No Color + +# Usage information +print_usage() { + echo "Usage: $0 [OPTIONS]" + echo "" + echo "Options:" + echo " -d, --base-dir DIR Base directory for Stirling-PDF data (default: /stirling/rootless)" + echo " -u, --uid UID User ID to set as owner (default: current user ID)" + echo " -g, --gid GID Group ID to set as owner (default: current group ID)" + echo " -h, --help Show this help message" + echo "" + echo "Example:" + echo " $0 --base-dir ~/stirling-data --uid 1000 --gid 1000" +} + +# Default values +BASE_DIR="/stirling/rootless" +UID_VAL=$(id -u) +GID_VAL=$(id -g) + +# Parse command line arguments +while [[ $# -gt 0 ]]; do + case $1 in + -d|--base-dir) + BASE_DIR="$2" + shift 2 + ;; + -u|--uid) + UID_VAL="$2" + shift 2 + ;; + -g|--gid) + GID_VAL="$2" + shift 2 + ;; + -h|--help) + print_usage + exit 0 + ;; + *) + echo "Error: Unknown option: $1" + print_usage + exit 1 + ;; + esac +done + +# Validate inputs +if [[ ! "$UID_VAL" =~ ^[0-9]+$ ]]; then + echo "Error: UID must be a number" + exit 1 +fi + +if [[ ! "$GID_VAL" =~ ^[0-9]+$ ]]; then + echo "Error: GID must be a number" + exit 1 +fi + +echo -e "${YELLOW}Setting up directories for Stirling-PDF rootless mode${NC}" +echo "===============================================" +echo "UID: $UID_VAL" +echo "GID: $GID_VAL" +echo "Base directory: $BASE_DIR" +echo + +# Create base directory if it doesn't exist +if [ ! -d "$BASE_DIR" ]; then + echo "Creating base directory: $BASE_DIR" + mkdir -p "$BASE_DIR" || { echo "Failed to create base directory"; exit 1; } +fi + +# Create necessary subdirectories +DIRS=( + "data" + "config" + "logs" + "customFiles" + "customFiles/signatures" + "customFiles/templates" + "pipeline/watchedFolders" + "pipeline/finishedFolders" +) + +for DIR in "${DIRS[@]}"; do + FULL_PATH="$BASE_DIR/$DIR" + echo "Creating directory: $FULL_PATH" + mkdir -p "$FULL_PATH" || { echo "Failed to create directory: $FULL_PATH"; exit 1; } + + echo "Setting ownership to $UID_VAL:$GID_VAL for $FULL_PATH" + chown -R "$UID_VAL:$GID_VAL" "$FULL_PATH" || { echo "Warning: Failed to change ownership for $FULL_PATH"; } + + echo "Setting permissions for $FULL_PATH" + chmod -R 1777 "$FULL_PATH" || { echo "Warning: Failed to set permissions for $FULL_PATH"; } +done + +# Create a Docker Compose file for rootless mode if it doesn't exist +COMPOSE_FILE="$BASE_DIR/docker-compose-rootless.yml" +if [ ! -f "$COMPOSE_FILE" ]; then + echo "Creating Docker Compose file for rootless mode: $COMPOSE_FILE" + cat > "$COMPOSE_FILE" << EOL +services: + stirling-pdf: + container_name: Stirling-PDF-Rootless + # Use the fat version for rootless operation as it includes all dependencies + image: docker.stirlingpdf.com/stirlingtools/stirling-pdf:latest-fat + user: "$UID_VAL:$GID_VAL" + ports: + - "8080:8080" + volumes: + - $BASE_DIR/data:/usr/share/tessdata:rw + - $BASE_DIR/config:/configs:rw + - $BASE_DIR/logs:/logs:rw + - $BASE_DIR/customFiles:/customFiles:rw + - $BASE_DIR/pipeline:/pipeline:rw + environment: + DOCKER_ENABLE_SECURITY: "false" + SECURITY_ENABLELOGIN: "false" + SYSTEM_DEFAULTLOCALE: en-US + UI_APPNAME: Stirling-PDF + UI_HOMEDESCRIPTION: Stirling-PDF Rootless + UI_APPNAMENAVBAR: Stirling-PDF Rootless + restart: unless-stopped +EOL + echo "Docker Compose file created" +fi + +echo -e "${GREEN}" +echo "===============================================" +echo "Preparation complete!" +echo "===============================================${NC}" +echo "" +echo "To run Stirling-PDF in rootless mode:" +echo "" +echo -e "${YELLOW}Option 1: Using the generated docker-compose file:${NC}" +echo "cd $BASE_DIR" +echo "docker-compose -f docker-compose-rootless.yml up -d" +echo "" +echo -e "${YELLOW}Option 2: Manual docker run command:${NC}" +echo "docker run -d \\" +echo " --name stirling-pdf-rootless \\" +echo " --user \"$UID_VAL:$GID_VAL\" \\" +echo " -p 8080:8080 \\" +echo " -v $BASE_DIR/data:/usr/share/tessdata:rw \\" +echo " -v $BASE_DIR/config:/configs:rw \\" +echo " -v $BASE_DIR/logs:/logs:rw \\" +echo " -v $BASE_DIR/customFiles:/customFiles:rw \\" +echo " -v $BASE_DIR/pipeline:/pipeline:rw \\" +echo " docker.stirlingpdf.com/stirlingtools/stirling-pdf:latest-fat" +echo "" +echo -e "${YELLOW}IMPORTANT:${NC} For rootless mode, always use the ${YELLOW}:latest-fat${NC} image tag" +echo "which includes all dependencies pre-installed." \ No newline at end of file