From f27ee96014b8d73314f4b2807b881fb983df7677 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AE=89=E6=AD=A3=E8=B6=85?= Date: Sat, 19 Jul 2025 11:48:46 +0800 Subject: [PATCH] feat: enhance entrypoint and Dockerfiles for flexible volume and permission management (#260) * feat: enhance entrypoint and Dockerfiles for flexible volume and permission management\n\n- Support batch mount and permission fix in entrypoint.sh\n- Add coreutils/shadow (alpine) and coreutils/passwd (ubuntu) for UID/GID/ownership\n- Use ENTRYPOINT for unified startup\n- Make local dev and prod Dockerfile behavior consistent\n- Improve security and user experience\n\nBREAKING CHANGE: entrypoint.sh and Dockerfile now require additional packages for permission management, and support batch volume mount via RUSTFS_VOLUMES. * chore: update Dockerfile comments to English only * fix(entrypoint): improve local/remote volume detection and permission logic in entrypoint.sh * Update entrypoint.sh Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update entrypoint.sh Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update Dockerfile Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- Dockerfile | 26 ++++----- Dockerfile.source | 10 +++- entrypoint.sh | 137 ++++++++++++++++++++++++++++++++-------------- 3 files changed, 114 insertions(+), 59 deletions(-) diff --git a/Dockerfile b/Dockerfile index e932ebe9..85b39754 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,8 +4,7 @@ FROM alpine:3.22 AS build # Build arguments for platform and release -ARG TARGETPLATFORM -ARG BUILDPLATFORM +ARG TARGETARCH ARG RELEASE=latest # Install minimal dependencies for downloading and extracting @@ -14,18 +13,14 @@ RUN apk add --no-cache ca-certificates curl unzip # Create build directory WORKDIR /build -# Map TARGETPLATFORM to architecture -RUN case "${TARGETPLATFORM}" in \ - "linux/amd64") ARCH="x86_64" ;; \ - "linux/arm64") ARCH="aarch64" ;; \ - *) echo "Unsupported platform: ${TARGETPLATFORM}" >&2 && exit 1 ;; \ +# Detect architecture and download corresponding binary +RUN case "${TARGETARCH}" in \ + amd64) ARCH="x86_64" ;; \ + arm64) ARCH="aarch64" ;; \ + *) echo "Unsupported architecture: ${TARGETARCH}" >&2 && exit 1 ;; \ esac && \ - echo "ARCH=${ARCH}" > /build/arch.env - -# Download and extract RustFS binary -RUN . /build/arch.env && \ BASE_URL="https://dl.rustfs.com/artifacts/rustfs/release" && \ - PACKAGE_NAME="rustfs-linux-${ARCH}-${RELEASE}.zip" && \ + PACKAGE_NAME="rustfs-linux-${ARCH}-${RELEASE#v}.zip" && \ DOWNLOAD_URL="${BASE_URL}/${PACKAGE_NAME}" && \ echo "Downloading ${PACKAGE_NAME} from ${DOWNLOAD_URL}" >&2 && \ curl -f -L "${DOWNLOAD_URL}" -o rustfs.zip && \ @@ -56,7 +51,7 @@ LABEL name="RustFS" \ # Install runtime dependencies RUN echo "https://dl-cdn.alpinelinux.org/alpine/v3.20/community" >> /etc/apk/repositories && \ apk update && \ - apk add --no-cache ca-certificates bash gosu && \ + apk add --no-cache ca-certificates bash gosu coreutils shadow && \ addgroup -g 1000 rustfs && \ adduser -u 1000 -G rustfs -s /bin/bash -D rustfs @@ -74,7 +69,7 @@ RUN chmod +x /usr/bin/rustfs /entrypoint.sh && \ chmod 700 /data /logs # Environment variables (credentials should be set via environment or secrets) -ENV RUSTFS_ADDRESS=":9000" \ +ENV RUSTFS_ADDRESS=:9000 \ RUSTFS_ACCESS_KEY=rustfsadmin \ RUSTFS_SECRET_KEY=rustfsadmin \ RUSTFS_CONSOLE_ENABLE=true \ @@ -91,4 +86,5 @@ VOLUME ["/data", "/logs"] # Set entry point ENTRYPOINT ["/entrypoint.sh"] -CMD ["/usr/bin/rustfs"] \ No newline at end of file +CMD ["/usr/bin/rustfs"] + diff --git a/Dockerfile.source b/Dockerfile.source index 24180e6e..ede0177e 100644 --- a/Dockerfile.source +++ b/Dockerfile.source @@ -112,6 +112,8 @@ RUN apt-get update && apt-get install -y \ ca-certificates \ tzdata \ wget \ + coreutils \ + passwd \ && rm -rf /var/lib/apt/lists/* # Create rustfs user and group @@ -128,6 +130,10 @@ RUN mkdir -p /data/rustfs{0,1,2,3} && \ COPY --from=builder /usr/local/bin/rustfs /app/rustfs RUN chmod +x /app/rustfs && chown rustfs:rustfs /app/rustfs +# Copy entrypoint script +COPY entrypoint.sh /entrypoint.sh +RUN chmod +x /entrypoint.sh + # Switch to non-root user USER rustfs @@ -142,9 +148,9 @@ ENV RUSTFS_ACCESS_KEY=rustfsadmin \ RUSTFS_VOLUMES=/data \ RUST_LOG=warn - # Volume for data VOLUME ["/data"] -# Set default command +# Set entrypoint and default command +ENTRYPOINT ["/entrypoint.sh"] CMD ["/app/rustfs"] diff --git a/entrypoint.sh b/entrypoint.sh index 6ea3c5bb..25bcf6bc 100644 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -1,51 +1,104 @@ #!/bin/bash set -e -# Function to adjust rustfs user/group to match mounted directory ownership -fix_permissions() { - local dir="$1" - local user="rustfs" - local group="rustfs" +APP_USER=rustfs +APP_GROUP=rustfs +APP_UID=${PUID:-1000} +APP_GID=${PGID:-1000} - # Skip if directory doesn't exist or isn't mounted - if [ ! -d "$dir" ]; then - echo "Directory $dir does not exist, creating it" - mkdir -p "$dir" - chown "$user:$group" "$dir" - chmod 700 "$dir" - return +# Parse RUSTFS_VOLUMES into array (support space, comma, tab as separator) +VOLUME_RAW="${RUSTFS_VOLUMES:-/data}" +# Replace comma and tab with space, then split +VOLUME_RAW=$(echo "$VOLUME_RAW" | tr ',\t' ' ') +read -ra ALL_VOLUMES <<< "$VOLUME_RAW" + +# Only keep local volumes (start with /, not http/https) +LOCAL_VOLUMES=() +for vol in "${ALL_VOLUMES[@]}"; do + if [[ "$vol" =~ ^/ ]] && [[ ! "$vol" =~ ^https?:// ]]; then + # Not a URL (http/https), just a local path + LOCAL_VOLUMES+=("$vol") + fi + # If it's a URL (http/https), skip + # If it's an empty string, skip + # If it's a local path, keep + # (We don't support other protocols here) +done + +# Always ensure /logs is included for permission fix +include_logs=1 +for vol in "${LOCAL_VOLUMES[@]}"; do + if [ "$vol" = "/logs" ]; then + include_logs=0 + break + fi +done +if [ $include_logs -eq 1 ]; then + LOCAL_VOLUMES+=("/logs") +fi + +# Try to update rustfs UID/GID if needed (requires root and shadow tools) +update_user_group_ids() { + local uid="$1" + local gid="$2" + local user="$3" + local group="$4" + local updated=0 + if [ "$(id -u "$user")" != "$uid" ]; then + if command -v usermod >/dev/null 2>&1; then + echo "🔧 Updating UID of $user to $uid" + usermod -u "$uid" "$user" + updated=1 fi - - # Get directory ownership - local dir_uid=$(stat -c %u "$dir") - local dir_gid=$(stat -c %g "$dir") - - # If directory is owned by root or inaccessible, skip adjustment - if [ "$dir_uid" = "0" ] || [ "$dir_gid" = "0" ]; then - echo "Warning: Directory $dir is owned by root, skipping UID/GID adjustment" - chown "$user:$group" "$dir" - chmod 700 "$dir" - return + fi + if [ "$(id -g "$group")" != "$gid" ]; then + if command -v groupmod >/dev/null 2>&1; then + echo "🔧 Updating GID of $group to $gid" + groupmod -g "$gid" "$group" + updated=1 fi - - # Adjust rustfs user/group to match directory ownership - if [ "$dir_uid" != "$(id -u $user)" ]; then - echo "Adjusting UID of $user to $dir_uid for $dir" - usermod -u "$dir_uid" "$user" - fi - if [ "$dir_gid" != "$(id -g $group)" ]; then - echo "Adjusting GID of $group to $dir_gid for $dir" - groupmod -g "$dir_gid" "$group" - fi - - # Ensure permissions are correct - chown "$user:$group" "$dir" - chmod 700 "$dir" + fi + return $updated } -# Fix permissions for /data and /logs -fix_permissions "/data" -fix_permissions "/logs" +echo "📦 Initializing mount directories: ${LOCAL_VOLUMES[*]}" -# Run RustFS as the rustfs user -exec gosu rustfs:rustfs "$@" \ No newline at end of file +for vol in "${LOCAL_VOLUMES[@]}"; do + if [ ! -d "$vol" ]; then + echo "📁 Creating directory: $vol" + mkdir -p "$vol" + fi + + # Alpine busybox stat does not support -c, coreutils is required + dir_uid=$(stat -c '%u' "$vol") + dir_gid=$(stat -c '%g' "$vol") + + if [ "$dir_uid" != "$APP_UID" ] || [ "$dir_gid" != "$APP_GID" ]; then + if [[ "$SKIP_CHOWN" != "true" ]]; then + # Prefer to update rustfs user/group UID/GID + update_user_group_ids "$dir_uid" "$dir_gid" "$APP_USER" "$APP_GROUP" || \ + { + echo "🔧 Fixing ownership for: $vol → $APP_USER:$APP_GROUP" + if [[ -n "$CHOWN_RECURSION_DEPTH" ]]; then + echo "🔧 Applying ownership fix with recursion depth: $CHOWN_RECURSION_DEPTH" + find "$vol" -mindepth 0 -maxdepth "$CHOWN_RECURSION_DEPTH" -exec chown "$APP_USER:$APP_GROUP" {} \; + else + echo "🔧 Applying ownership fix recursively (full depth)" + chown -R "$APP_USER:$APP_GROUP" "$vol" + fi + } + else + echo "⚠️ SKIP_CHOWN is enabled. Skipping ownership fix for: $vol" + fi + fi + chmod 700 "$vol" +done + +# Warn if default credentials are used +if [[ "$RUSTFS_ACCESS_KEY" == "rustfsadmin" || "$RUSTFS_SECRET_KEY" == "rustfsadmin" ]]; then + echo "⚠️ WARNING: Using default RUSTFS_ACCESS_KEY or RUSTFS_SECRET_KEY" + echo "⚠️ It is strongly recommended to override these values in production!" +fi + +echo "🚀 Starting application: $*" +exec gosu "$APP_USER" "$@"