diff --git a/Dockerfile.goreleaser b/Dockerfile.goreleaser index 91577e122..f368aa105 100644 --- a/Dockerfile.goreleaser +++ b/Dockerfile.goreleaser @@ -6,7 +6,9 @@ ENV GOPATH=/go ENV CGO_ENABLED=0 ARG MC_REPO=pgsty/mc -ARG MC_VERSION=latest +ARG MC_VERSION=RELEASE.2026-08-04T00-00-00Z +ARG MC_AMD64_SHA256=4aa1fa83ea153025a3f60684e13f453749dd17587257ccb7f0dde42b560120be +ARG MC_ARM64_SHA256=18f7bd442aec24c5eb5ffd1ef368eb44f94045e2da109a344ad184f34641e273 RUN apk add -U --no-cache \ ca-certificates \ @@ -14,8 +16,8 @@ RUN apk add -U --no-cache \ curl \ jq && \ case "${TARGETARCH}" in \ - amd64) MC_ARCH=amd64 ;; \ - arm64) MC_ARCH=arm64 ;; \ + amd64) MC_ARCH=amd64; MC_PINNED_SHA256="${MC_AMD64_SHA256}" ;; \ + arm64) MC_ARCH=arm64; MC_PINNED_SHA256="${MC_ARM64_SHA256}" ;; \ *) echo "Unsupported TARGETARCH=${TARGETARCH}"; exit 1 ;; \ esac && \ if [ "${MC_VERSION}" = "latest" ]; then \ @@ -39,7 +41,8 @@ RUN apk add -U --no-cache \ EXPECTED=$(grep " ${ARCHIVE_NAME}$" /tmp/mcli_checksums.txt | awk '{print $1}') && \ ACTUAL=$(sha256sum /tmp/mcli.tar.gz | awk '{print $1}') && \ [ -n "${EXPECTED}" ] || { echo "Checksum entry not found for ${ARCHIVE_NAME}"; exit 1; } && \ - [ "${EXPECTED}" = "${ACTUAL}" ] || { echo "Checksum mismatch: expected ${EXPECTED}, got ${ACTUAL}"; exit 1; } && \ + [ "${EXPECTED}" = "${MC_PINNED_SHA256}" ] || { echo "Published checksum drift for ${ARCHIVE_NAME}"; exit 1; } && \ + [ "${MC_PINNED_SHA256}" = "${ACTUAL}" ] || { echo "Checksum mismatch: expected ${MC_PINNED_SHA256}, got ${ACTUAL}"; exit 1; } && \ echo "Checksum OK: ${ACTUAL}" && \ mkdir -p /tmp/mcli-extract && \ tar -xzf /tmp/mcli.tar.gz -C /tmp/mcli-extract/ && \ @@ -66,31 +69,35 @@ RUN dnf -y install ca-certificates && \ FROM registry.access.redhat.com/ubi9/ubi-micro:latest -LABEL maintainer="pgsty " \ - description="MinIO community fork, build by pgsty" +LABEL org.opencontainers.image.title="Silo" \ + org.opencontainers.image.description="S3-Interface Libre Object Storage" \ + org.opencontainers.image.url="https://silo.pgsty.com" \ + org.opencontainers.image.source="https://github.com/pgsty/minio" \ + org.opencontainers.image.licenses="AGPL-3.0-or-later" \ + maintainer="PGSTY " ENV MINIO_ACCESS_KEY_FILE=access_key \ MINIO_SECRET_KEY_FILE=secret_key \ MINIO_ROOT_USER_FILE=access_key \ MINIO_ROOT_PASSWORD_FILE=secret_key \ MINIO_KMS_SECRET_KEY_FILE=kms_master_key \ - MINIO_UPDATE_MINISIGN_PUBKEY="RWTx5Zr1tiHQLwG9keckT0c45M3AGeHD6IvimQHpyRywVWGbP1aVSGav" \ MINIO_CONFIG_ENV_FILE=config.env \ + HOME=/tmp \ MC_CONFIG_DIR=/tmp/.mc COPY --from=certs /tmp/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt -COPY minio /usr/bin/minio +COPY silo /usr/bin/silo COPY --from=build /go/bin/mcli /usr/bin/mcli COPY --from=build /go/bin/curl* /usr/bin/ COPY dockerscripts/docker-entrypoint.sh /usr/bin/docker-entrypoint.sh COPY LICENSE /licenses/LICENSE COPY CREDITS /licenses/CREDITS -RUN chmod +x /usr/bin/minio /usr/bin/mcli /usr/bin/docker-entrypoint.sh && \ +RUN chmod +x /usr/bin/silo /usr/bin/mcli /usr/bin/docker-entrypoint.sh && \ ln -sf mcli /usr/bin/mc EXPOSE 9000 VOLUME ["/data"] ENTRYPOINT ["/usr/bin/docker-entrypoint.sh"] -CMD ["minio"] +CMD ["silo"] diff --git a/dockerscripts/docker-entrypoint.sh b/dockerscripts/docker-entrypoint.sh index 8e19b665f..88f0c3ac4 100755 --- a/dockerscripts/docker-entrypoint.sh +++ b/dockerscripts/docker-entrypoint.sh @@ -1,21 +1,43 @@ #!/bin/sh # -# If command starts with an option, prepend minio. -if [ "${1}" != "minio" ]; then - if [ -n "${1}" ]; then - set -- minio "$@" +# Run Silo by default, while translating the legacy argv-level command name. +# An explicitly supplied shell or utility remains an explicit entrypoint command. +case "${1:-}" in + "") + set -- silo + ;; + minio) + shift + set -- silo "$@" + ;; + silo) + ;; + -*|server|fmt-gen) + set -- silo "$@" + ;; +esac + +ensure_writable_home() { + # The image is commonly run with an arbitrary UID or with the legacy + # MINIO_USERNAME drop-user path. Do not leave those processes pointing at + # root's inaccessible home: Silo probes its default configuration directory + # during process initialization. + if [ -z "${HOME:-}" ] || [ "${HOME}" = /root ] || [ ! -d "${HOME}" ] || [ ! -w "${HOME}" ]; then + HOME=/tmp + export HOME fi -fi +} docker_switch_user() { + ensure_writable_home if [ -n "${MINIO_USERNAME}" ] && [ -n "${MINIO_GROUPNAME}" ]; then if [ -n "${MINIO_UID}" ] && [ -n "${MINIO_GID}" ]; then - exec chroot --userspec=${MINIO_UID}:${MINIO_GID} / "$@" + exec chroot --userspec="${MINIO_UID}:${MINIO_GID}" / "$@" else echo "${MINIO_USERNAME}:x:1000:1000:${MINIO_USERNAME}:/:/sbin/nologin" >>/etc/passwd echo "${MINIO_GROUPNAME}:x:1000" >>/etc/group - exec chroot --userspec=${MINIO_USERNAME}:${MINIO_GROUPNAME} / "$@" + exec chroot --userspec="${MINIO_USERNAME}:${MINIO_GROUPNAME}" / "$@" fi else exec "$@" diff --git a/dockerscripts/docker-entrypoint_test.sh b/dockerscripts/docker-entrypoint_test.sh new file mode 100755 index 000000000..bfb7ffa8c --- /dev/null +++ b/dockerscripts/docker-entrypoint_test.sh @@ -0,0 +1,75 @@ +#!/usr/bin/env bash + +set -euo pipefail + +script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +work_dir="$(mktemp -d)" +trap 'rm -rf "${work_dir}"' EXIT + +mkdir -p "${work_dir}/bin" +cat >"${work_dir}/bin/silo" <<'EOF' +#!/bin/sh +printf '%s\n' silo "$@" >"${ENTRYPOINT_CAPTURE}" +if [ -n "${ENTRYPOINT_HOME_CAPTURE:-}" ]; then + printf '%s\n' "${HOME:-}" >"${ENTRYPOINT_HOME_CAPTURE}" +fi +EOF +chmod +x "${work_dir}/bin/silo" + +cat >"${work_dir}/bin/chroot" <<'EOF' +#!/bin/sh +printf '%s\n' "$1" "$2" >"${ENTRYPOINT_CHROOT_CAPTURE}" +shift 2 +exec "$@" +EOF +chmod +x "${work_dir}/bin/chroot" + +run_case() { + local name="$1" + local expected="$2" + shift 2 + local capture="${work_dir}/${name}.actual" + local expected_file="${work_dir}/${name}.expected" + + ENTRYPOINT_CAPTURE="${capture}" PATH="${work_dir}/bin:${PATH}" \ + "${script_dir}/docker-entrypoint.sh" "$@" + printf '%s' "${expected}" >"${expected_file}" + diff -u "${expected_file}" "${capture}" +} + +run_case default $'silo\n' +run_case server $'silo\nserver\n/data\n' server /data +run_case option $'silo\n--version\n' --version +run_case explicit-silo $'silo\nserver\n/data\n' silo server /data +run_case legacy-minio $'silo\nserver\n/data\n' minio server /data + +shell_capture="${work_dir}/shell.actual" +# shellcheck disable=SC2016 # ENTRYPOINT_CAPTURE is expanded by the child shell. +ENTRYPOINT_CAPTURE="${shell_capture}" PATH="${work_dir}/bin:${PATH}" \ + "${script_dir}/docker-entrypoint.sh" sh -c 'printf "%s\n" shell >"${ENTRYPOINT_CAPTURE}"' +test "$(cat "${shell_capture}")" = shell + +rootless_capture="${work_dir}/rootless.actual" +rootless_home_capture="${work_dir}/rootless-home.actual" +rootless_chroot_capture="${work_dir}/rootless-chroot.actual" +ENTRYPOINT_CAPTURE="${rootless_capture}" \ + ENTRYPOINT_HOME_CAPTURE="${rootless_home_capture}" \ + ENTRYPOINT_CHROOT_CAPTURE="${rootless_chroot_capture}" \ + HOME=/root MINIO_USERNAME=silo-user MINIO_GROUPNAME=silo-group \ + MINIO_UID=1001 MINIO_GID=1002 PATH="${work_dir}/bin:${PATH}" \ + "${script_dir}/docker-entrypoint.sh" silo --version +test "$(cat "${rootless_home_capture}")" = /tmp +test "$(sed -n '1p' "${rootless_chroot_capture}")" = --userspec=1001:1002 +test "$(sed -n '2p' "${rootless_chroot_capture}")" = / +test "$(cat "${rootless_capture}")" = $'silo\n--version' + +arbitrary_uid_capture="${work_dir}/arbitrary-uid.actual" +arbitrary_uid_home_capture="${work_dir}/arbitrary-uid-home.actual" +ENTRYPOINT_CAPTURE="${arbitrary_uid_capture}" \ + ENTRYPOINT_HOME_CAPTURE="${arbitrary_uid_home_capture}" \ + HOME="${work_dir}/missing-home" PATH="${work_dir}/bin:${PATH}" \ + "${script_dir}/docker-entrypoint.sh" silo --version +test "$(cat "${arbitrary_uid_home_capture}")" = /tmp +test "$(cat "${arbitrary_uid_capture}")" = $'silo\n--version' + +echo "docker entrypoint argv compatibility tests passed" diff --git a/dockerscripts/download-static-curl.sh b/dockerscripts/download-static-curl.sh index b855967ee..aff4cf738 100644 --- a/dockerscripts/download-static-curl.sh +++ b/dockerscripts/download-static-curl.sh @@ -4,21 +4,23 @@ # v8.17.0 (current latest) dropped the aarch64 build. STATIC_CURL_VERSION="v8.11.0" -function download_arch_specific_executable { - curl -f -L -s -q \ - "https://github.com/moparisthebest/static-curl/releases/download/${STATIC_CURL_VERSION}/curl-$1" \ - -o /go/bin/curl || exit 1 - chmod +x /go/bin/curl -} - -case $TARGETARCH in -"arm64") - download_arch_specific_executable aarch64 +case ${TARGETARCH:?TARGETARCH is required} in +amd64) + asset=curl-amd64 + expected=d18aa1f4e03b50b649491ca2c401cd8c5e89e72be91ff758952ad2ab5a83135d ;; -"s390x") - echo "Not downloading static cURL because it does not exist for the $TARGETARCH architecture." +arm64) + asset=curl-aarch64 + expected=1b050abd1669f9a2ac29b34eb022cdeafb271dce5a4fb57d8ef8fadff6d7be1f ;; *) - download_arch_specific_executable "$TARGETARCH" + echo "Unsupported static cURL architecture: ${TARGETARCH}" >&2 + exit 1 ;; esac + +curl --fail --location --silent --show-error --retry 3 \ + "https://github.com/moparisthebest/static-curl/releases/download/${STATIC_CURL_VERSION}/${asset}" \ + --output /go/bin/curl +printf '%s %s\n' "${expected}" /go/bin/curl | sha256sum -c +chmod +x /go/bin/curl