diff --git a/contrib/docker/Dockerfile b/contrib/docker/Dockerfile index e432216ed..d3cbca764 100644 --- a/contrib/docker/Dockerfile +++ b/contrib/docker/Dockerfile @@ -86,10 +86,10 @@ WORKDIR /var/www/ ENV APT_PACKAGES_EXTRA=${APT_PACKAGES_EXTRA} # Install and configure base layer -COPY contrib/docker/install/base.sh /install/base.sh +COPY contrib/docker/shared/root/docker/install/base.sh /docker/install/base.sh RUN --mount=type=cache,id=pixelfed-apt-${PHP_VERSION}-${PHP_DEBIAN_RELEASE}-${TARGETPLATFORM},sharing=locked,target=/var/lib/apt \ --mount=type=cache,id=pixelfed-apt-cache-${PHP_VERSION}-${PHP_DEBIAN_RELEASE}-${TARGETPLATFORM},sharing=locked,target=/var/cache/apt \ - /install/base.sh + /docker/install/base.sh ####################################################### # PHP: extensions @@ -115,11 +115,11 @@ ENV PHP_PECL_EXTENSIONS=${PHP_PECL_EXTENSIONS} ENV PHP_VERSION=${PHP_VERSION} ENV TARGETPLATFORM=${TARGETPLATFORM} -COPY contrib/docker/install/php-extensions.sh /install/php-extensions.sh +COPY contrib/docker/shared/root/docker/install/php-extensions.sh /docker/install/php-extensions.sh RUN --mount=type=cache,id=pixelfed-php-${PHP_VERSION}-${PHP_DEBIAN_RELEASE}-${TARGETPLATFORM},sharing=locked,target=/usr/src/php/ \ --mount=type=cache,id=pixelfed-apt-${PHP_VERSION}-${PHP_DEBIAN_RELEASE}-${TARGETPLATFORM},sharing=locked,target=/var/lib/apt \ --mount=type=cache,id=pixelfed-apt-cache-${PHP_VERSION}-${PHP_DEBIAN_RELEASE}-${TARGETPLATFORM},sharing=locked,target=/var/cache/apt \ - /install/php-extensions.sh + /docker/install/php-extensions.sh ####################################################### # PHP: composer and source code @@ -183,18 +183,15 @@ COPY --link --from=php-extensions /usr/local/lib/php/extensions /usr/local/lib/p COPY --link --from=php-extensions /usr/local/etc/php /usr/local/etc/php COPY --link --from=composer-and-src --chown=${RUNTIME_UID}:${RUNTIME_GID} /var/www /var/www COPY --link --from=forego-image /usr/local/bin/forego /usr/local/bin/forego -COPY --link contrib/docker/php.production.ini "$PHP_INI_DIR/php.ini" # for detail why storage is copied this way, pls refer to https://github.com/pixelfed/pixelfed/pull/2137#discussion_r434468862 RUN set -ex \ && cp --recursive --link --preserve=all storage storage.skel \ && rm -rf html && ln -s public html -COPY contrib/docker/docker-entrypoint.sh /docker-entrypoint.sh -COPY contrib/docker/shared/lib.sh /lib.sh -COPY contrib/docker/shared/docker-entrypoint.d /docker-entrypoint.d/ +COPY contrib/docker/shared/root / -ENTRYPOINT ["/docker-entrypoint.sh"] +ENTRYPOINT ["/docker/entrypoint.sh"] VOLUME /var/www/storage /var/www/bootstrap @@ -204,8 +201,7 @@ VOLUME /var/www/storage /var/www/bootstrap FROM shared-runtime AS apache-runtime -COPY --link contrib/docker/apache/conf-available/remoteip.conf /etc/apache2/conf-available/remoteip.conf -COPY --link contrib/docker/apache/docker-entrypoint.d /docker-entrypoint.d/ +COPY contrib/docker/apache/root / RUN set -ex \ && a2enmod rewrite remoteip proxy proxy_http \ @@ -221,7 +217,7 @@ EXPOSE 80 FROM shared-runtime AS fpm-runtime -COPY --link contrib/docker/fpm/docker-entrypoint.d /docker-entrypoint.d/ +COPY contrib/docker/fpm/root / CMD ["php-fpm"] @@ -252,10 +248,9 @@ RUN --mount=type=cache,id=pixelfed-apt-lists-${PHP_VERSION}-${PHP_DEBIAN_RELEASE nginx=${NGINX_VERSION}* # copy docker entrypoints from the *real* nginx image directly -COPY --link --from=nginx-image /docker-entrypoint.d /docker-entrypoint.d/ -COPY --link contrib/docker/nginx/docker-entrypoint.d /docker-entrypoint.d/ -COPY --link contrib/docker/nginx/default-http.conf /etc/nginx/templates/default.conf.template -COPY --link contrib/docker/nginx/Procfile . +COPY --link --from=nginx-image /docker-entrypoint.d /docker/entrypoint.d/ +COPY contrib/docker/nginx/root / +COPY contrib/docker/nginx/Procfile . EXPOSE 80 diff --git a/contrib/docker/apache/conf-available/remoteip.conf b/contrib/docker/apache/root/etc/apache2/conf-available/remoteip.conf similarity index 56% rename from contrib/docker/apache/conf-available/remoteip.conf rename to contrib/docker/apache/root/etc/apache2/conf-available/remoteip.conf index 1632f8e43..516da9f5d 100644 --- a/contrib/docker/apache/conf-available/remoteip.conf +++ b/contrib/docker/apache/root/etc/apache2/conf-available/remoteip.conf @@ -1,4 +1,8 @@ RemoteIPHeader X-Real-IP + +# All private IPs as outlined in rfc1918 +# +# See: https://datatracker.ietf.org/doc/html/rfc1918 RemoteIPTrustedProxy 10.0.0.0/8 RemoteIPTrustedProxy 172.16.0.0/12 RemoteIPTrustedProxy 192.168.0.0/16 diff --git a/contrib/docker/docker-entrypoint.sh b/contrib/docker/docker-entrypoint.sh deleted file mode 100755 index 706c38317..000000000 --- a/contrib/docker/docker-entrypoint.sh +++ /dev/null @@ -1,45 +0,0 @@ -#!/bin/bash -# vim:sw=4:ts=4:et - -set -e - -source /lib.sh - -mkdir -p /docker-entrypoint.d/ - -if /usr/bin/find "/docker-entrypoint.d/" -mindepth 1 -maxdepth 1 -type f -print -quit 2>/dev/null | read v; then - entrypoint_log "/docker-entrypoint.d/ is not empty, will attempt to perform configuration" - - entrypoint_log "looking for shell scripts in /docker-entrypoint.d/" - find "/docker-entrypoint.d/" -follow -type f -print | sort -V | while read -r f; do - case "$f" in - *.envsh) - if [ -x "$f" ]; then - entrypoint_log "Sourcing $f"; - . "$f" - else - # warn on shell scripts without exec bit - entrypoint_log "Ignoring $f, not executable"; - fi - ;; - - *.sh) - if [ -x "$f" ]; then - entrypoint_log "Launching $f"; - "$f" - else - # warn on shell scripts without exec bit - entrypoint_log "Ignoring $f, not executable"; - fi - ;; - - *) entrypoint_log "Ignoring $f";; - esac - done - - entrypoint_log "Configuration complete; ready for start up" -else - entrypoint_log "No files found in /docker-entrypoint.d/, skipping configuration" -fi - -exec "$@" diff --git a/contrib/docker/fpm/docker-entrypoint.d/.gitkeep b/contrib/docker/fpm/docker-entrypoint.d/.gitkeep deleted file mode 100644 index e69de29bb..000000000 diff --git a/contrib/docker/apache/docker-entrypoint.d/.gitkeep b/contrib/docker/fpm/root/.gitkeep similarity index 100% rename from contrib/docker/apache/docker-entrypoint.d/.gitkeep rename to contrib/docker/fpm/root/.gitkeep diff --git a/contrib/docker/nginx/default-http.conf b/contrib/docker/nginx/default-http.conf deleted file mode 100644 index 8182dba7f..000000000 --- a/contrib/docker/nginx/default-http.conf +++ /dev/null @@ -1,49 +0,0 @@ -server { - listen 80 default_server; - - server_name ${APP_DOMAIN}; - root /var/www/public; - - add_header X-Frame-Options "SAMEORIGIN"; - add_header X-XSS-Protection "1; mode=block"; - add_header X-Content-Type-Options "nosniff"; - - access_log /dev/stdout; - error_log /dev/stderr warn; - - index index.html index.htm index.php; - - charset utf-8; - client_max_body_size 100M; - - location / { - try_files $uri $uri/ /index.php?$query_string; - } - - location = /favicon.ico { - access_log off; - log_not_found off; - } - - location = /robots.txt { - access_log off; - log_not_found off; - } - - error_page 404 /index.php; - - location ~ \.php$ { - fastcgi_split_path_info ^(.+\.php)(/.+)$; - - fastcgi_pass 127.0.0.1:9000; - fastcgi_index index.php; - - include fastcgi_params; - - fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; - } - - location ~ /\.(?!well-known).* { - deny all; - } -} diff --git a/contrib/docker/nginx/docker-entrypoint.d/.gitkeep b/contrib/docker/nginx/docker-entrypoint.d/.gitkeep deleted file mode 100644 index e69de29bb..000000000 diff --git a/contrib/docker/nginx/root/etc/nginx/conf.d/default.conf b/contrib/docker/nginx/root/etc/nginx/conf.d/default.conf new file mode 100644 index 000000000..af5a66b77 --- /dev/null +++ b/contrib/docker/nginx/root/etc/nginx/conf.d/default.conf @@ -0,0 +1,49 @@ +server { + listen 80 default_server; + + server_name ${APP_DOMAIN}; + root /var/www/public; + + add_header X-Frame-Options "SAMEORIGIN"; + add_header X-XSS-Protection "1; mode=block"; + add_header X-Content-Type-Options "nosniff"; + + access_log /dev/stdout; + error_log /dev/stderr warn; + + index index.html index.htm index.php; + + charset utf-8; + client_max_body_size ${POST_MAX_SIZE}; + + location / { + try_files $uri $uri/ /index.php?$query_string; + } + + location = /favicon.ico { + access_log off; + log_not_found off; + } + + location = /robots.txt { + access_log off; + log_not_found off; + } + + error_page 404 /index.php; + + location ~ \.php$ { + fastcgi_split_path_info ^(.+\.php)(/.+)$; + + fastcgi_pass 127.0.0.1:9000; + fastcgi_index index.php; + + include fastcgi_params; + + fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; + } + + location ~ /\.(?!well-known).* { + deny all; + } +} diff --git a/contrib/docker/shared/docker-entrypoint.d/10-storage.sh b/contrib/docker/shared/docker-entrypoint.d/10-storage.sh deleted file mode 100755 index 8357688c1..000000000 --- a/contrib/docker/shared/docker-entrypoint.d/10-storage.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/bash -set -o errexit -o nounset -o pipefail - -source /lib.sh - -entrypoint_log "==> Create the storage tree if needed" -as_runtime_user cp --recursive storage.skel/* storage/ - -entrypoint_log "==> Ensure storage is linked" -as_runtime_user php artisan storage:link - -entrypoint_log "==> Ensure permissions are correct" -chown --recursive ${RUNTIME_UID}:${RUNTIME_GID} storage/ bootstrap/ diff --git a/contrib/docker/shared/docker-entrypoint.d/30-cache.sh b/contrib/docker/shared/docker-entrypoint.d/30-cache.sh deleted file mode 100755 index 3eb87b6bb..000000000 --- a/contrib/docker/shared/docker-entrypoint.d/30-cache.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/bash -set -o errexit -o nounset -o pipefail - -source /lib.sh - -entrypoint_log "==> route:cache" -as_runtime_user php artisan route:cache - -entrypoint_log "==> view:cache" -as_runtime_user php artisan view:cache - -entrypoint_log "==> config:cache" -as_runtime_user php artisan config:cache diff --git a/contrib/docker/shared/lib.sh b/contrib/docker/shared/lib.sh deleted file mode 100644 index 8253ed085..000000000 --- a/contrib/docker/shared/lib.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/bash - -set -e - -function entrypoint_log() { - if [ -z "${ENTRYPOINT_QUIET_LOGS:-}" ]; then - echo "/docker-entrypoint.sh: $@" - fi -} - -function as_runtime_user() { - su --preserve-environment ${RUNTIME_UID} --shell /bin/bash --command "${*}" -} diff --git a/contrib/docker/shared/root/docker/entrypoint.d/04-defaults.envsh b/contrib/docker/shared/root/docker/entrypoint.d/04-defaults.envsh new file mode 100755 index 000000000..2244be9b8 --- /dev/null +++ b/contrib/docker/shared/root/docker/entrypoint.d/04-defaults.envsh @@ -0,0 +1,26 @@ +#!/bin/bash + +# NOTE: +# +# this file is *sourced* not run by the entrypoint runner +# so any environment values set here will be accessible to all sub-processes +# and future entrypoint.d scripts +# + +set_identity "${BASH_SOURCE[0]}" + +load-config-files + +: ${POST_MAX_SIZE_BUFFER:=1M} +log "POST_MAX_SIZE_BUFFER is set to [${POST_MAX_SIZE_BUFFER}]" +buffer=$(numfmt --invalid=fail --from=auto --to=none --to-unit=K "${POST_MAX_SIZE_BUFFER}") +log "POST_MAX_SIZE_BUFFER converted to KB is [${buffer}]" + +log "POST_MAX_SIZE will be calculated by [({MAX_PHOTO_SIZE} * {MAX_ALBUM_LENGTH}) + {POST_MAX_SIZE_BUFFER}]" +log " MAX_PHOTO_SIZE=${MAX_PHOTO_SIZE}" +log " MAX_ALBUM_LENGTH=${MAX_ALBUM_LENGTH}" +log " POST_MAX_SIZE_BUFFER=${buffer}" +: ${POST_MAX_SIZE:=$(numfmt --invalid=fail --from=auto --from-unit=K --to=si $(((${MAX_PHOTO_SIZE} * ${MAX_ALBUM_LENGTH}) + ${buffer})))} +log "POST_MAX_SIZE was calculated to [${POST_MAX_SIZE}]" + +export POST_MAX_SIZE diff --git a/contrib/docker/shared/root/docker/entrypoint.d/05-templating.sh b/contrib/docker/shared/root/docker/entrypoint.d/05-templating.sh new file mode 100755 index 000000000..4b9f9014a --- /dev/null +++ b/contrib/docker/shared/root/docker/entrypoint.d/05-templating.sh @@ -0,0 +1,40 @@ +#!/bin/bash +source /docker/helpers.sh + +set_identity "$0" + +auto_envsubst() { + local template_dir="${ENVSUBST_TEMPLATE_DIR:-/docker/templates}" + local output_dir="${ENVSUBST_OUTPUT_DIR:-}" + local filter="${ENVSUBST_FILTER:-}" + local template defined_envs relative_path output_path output_dir subdir + + # load all dot-env files + load-config-files + + # export all dot-env variables so they are available in templating + export ${seen_dot_env_variables[@]} + + defined_envs=$(printf '${%s} ' $(awk "END { for (name in ENVIRON) { print ( name ~ /${filter}/ ) ? name : \"\" } }" "$output_path" + done +} + +auto_envsubst + +exit 0 diff --git a/contrib/docker/shared/root/docker/entrypoint.d/10-storage.sh b/contrib/docker/shared/root/docker/entrypoint.d/10-storage.sh new file mode 100755 index 000000000..c814a3df4 --- /dev/null +++ b/contrib/docker/shared/root/docker/entrypoint.d/10-storage.sh @@ -0,0 +1,13 @@ +#!/bin/bash +source /docker/helpers.sh + +set_identity "$0" + +log "Create the storage tree if needed" +as_runtime_user cp --recursive storage.skel/* storage/ + +log "Ensure storage is linked" +as_runtime_user php artisan storage:link + +log "Ensure permissions are correct" +chown --recursive ${RUNTIME_UID}:${RUNTIME_GID} storage/ bootstrap/ diff --git a/contrib/docker/shared/docker-entrypoint.d/20-horizon.sh b/contrib/docker/shared/root/docker/entrypoint.d/20-horizon.sh similarity index 52% rename from contrib/docker/shared/docker-entrypoint.d/20-horizon.sh rename to contrib/docker/shared/root/docker/entrypoint.d/20-horizon.sh index 9db54ba35..7c1d8fdc6 100755 --- a/contrib/docker/shared/docker-entrypoint.d/20-horizon.sh +++ b/contrib/docker/shared/root/docker/entrypoint.d/20-horizon.sh @@ -1,6 +1,6 @@ #!/bin/bash -set -o errexit -o nounset -o pipefail +source /docker/helpers.sh -source /lib.sh +set_identity "$0" as_runtime_user php artisan horizon:publish diff --git a/contrib/docker/shared/root/docker/entrypoint.d/30-cache.sh b/contrib/docker/shared/root/docker/entrypoint.d/30-cache.sh new file mode 100755 index 000000000..e561daef9 --- /dev/null +++ b/contrib/docker/shared/root/docker/entrypoint.d/30-cache.sh @@ -0,0 +1,13 @@ +#!/bin/bash +source /docker/helpers.sh + +set_identity "$0" + +log "==> route:cache" +as_runtime_user php artisan route:cache + +log "==> view:cache" +as_runtime_user php artisan view:cache + +log "==> config:cache" +as_runtime_user php artisan config:cache diff --git a/contrib/docker/shared/root/docker/entrypoint.sh b/contrib/docker/shared/root/docker/entrypoint.sh new file mode 100755 index 000000000..0964980bc --- /dev/null +++ b/contrib/docker/shared/root/docker/entrypoint.sh @@ -0,0 +1,50 @@ +#!/bin/bash +set -e -o errexit -o nounset -o pipefail + +[[ -n ${ENTRYPOINT_DEBUG:-} ]] && set -x + +declare -g ME="$0" +declare -gr ENTRYPOINT_ROOT=/docker/entrypoint.d/ + +source /docker/helpers.sh + +# ensure the entrypoint folder exists +mkdir -p "${ENTRYPOINT_ROOT}" + +if /usr/bin/find "${ENTRYPOINT_ROOT}" -mindepth 1 -maxdepth 1 -type f -print -quit 2>/dev/null | read v; then + log "looking for shell scripts in /docker/entrypoint.d/" + find "${ENTRYPOINT_ROOT}" -follow -type f -print | sort -V | while read -r f; do + case "$f" in + *.envsh) + if [ -x "$f" ]; then + log "Sourcing $f" + source "$f" + resetore_identity + else + # warn on shell scripts without exec bit + log_warning "Ignoring $f, not executable" + fi + ;; + + *.sh) + if [ -x "$f" ]; then + log "Launching $f" + "$f" + else + # warn on shell scripts without exec bit + log_warning "Ignoring $f, not executable" + fi + ;; + + *) + log_warning "Ignoring $f" + ;; + esac + done + + log "Configuration complete; ready for start up" +else + log_warning "No files found in ${ENTRYPOINT_ROOT}, skipping configuration" +fi + +exec "$@" diff --git a/contrib/docker/shared/root/docker/helpers.sh b/contrib/docker/shared/root/docker/helpers.sh new file mode 100644 index 000000000..fc8691324 --- /dev/null +++ b/contrib/docker/shared/root/docker/helpers.sh @@ -0,0 +1,75 @@ +#!/bin/bash +set -e -o errexit -o nounset -o pipefail + +declare -g error_message_color="\033[1;31m" +declare -g warn_message_color="\033[1;34m" +declare -g color_clear="\033[1;0m" +declare -g log_prefix= +declare -g old_log_prefix= +declare -ra dot_env_files=( + /var/www/.env.docker + /var/www/.env +) +declare -a seen_dot_env_variables=() + +function set_identity() { + old_log_prefix="${log_prefix}" + log_prefix="ENTRYPOINT - [${1}] - " +} + +function resetore_identity() { + log_prefix="${old_log_prefix}" +} + +function as_runtime_user() { + su --preserve-environment $(id -un ${RUNTIME_UID}) --shell /bin/bash --command "${*}" +} + +# @description Display the given error message with its line number on stderr and exit with error. +# @arg $message string A error message. +function log_error() { + echo -e "${error_message_color}${log_prefix}ERROR - ${1}${color_clear}" >/dev/stderr +} + +# @description Display the given error message with its line number on stderr and exit with error. +# @arg $message string A error message. +# @exitcode 1 +function log_error_and_exit() { + log_error "$1" + + exit 1 +} + +# @description Display the given warning message with its line number on stderr. +# @arg $message string A warning message. +function log_warning() { + echo -e "${warn_message_color}${log_prefix}WARNING - ${1}${color_clear}" >/dev/stderr +} + +function log() { + if [ -z "${ENTRYPOINT_QUIET_LOGS:-}" ]; then + echo "${log_prefix}$@" + fi +} + +function load-config-files() { + # Associative array (aka map/disctrionary) holding the unique keys found in dot-env files + local -A _tmp_dot_env_keys + + for f in "${dot_env_files[@]}"; do + if [ ! -e "$f" ]; then + log_warning "Could not source file [${f}]: does not exists" + continue + fi + + log "Sourcing ${f}" + source "${f}" + + # find all keys in the dot-env file and store them in our temp associative array + for k in "$(grep -v '^#' "${f}" | sed -E 's/(.*)=.*/\1/' | xargs)"; do + _tmp_dot_env_keys[$k]=1 + done + done + + seen_dot_env_variables=(${!_tmp_dot_env_keys[@]}) +} diff --git a/contrib/docker/install/base.sh b/contrib/docker/shared/root/docker/install/base.sh similarity index 100% rename from contrib/docker/install/base.sh rename to contrib/docker/shared/root/docker/install/base.sh diff --git a/contrib/docker/install/php-extensions.sh b/contrib/docker/shared/root/docker/install/php-extensions.sh similarity index 100% rename from contrib/docker/install/php-extensions.sh rename to contrib/docker/shared/root/docker/install/php-extensions.sh diff --git a/contrib/docker/php.production.ini b/contrib/docker/shared/root/docker/templates/usr/local/etc/php/php.ini similarity index 99% rename from contrib/docker/php.production.ini rename to contrib/docker/shared/root/docker/templates/usr/local/etc/php/php.ini index 2a1df3988..1a9f6b598 100644 --- a/contrib/docker/php.production.ini +++ b/contrib/docker/shared/root/docker/templates/usr/local/etc/php/php.ini @@ -679,7 +679,7 @@ auto_globals_jit = On ; Its value may be 0 to disable the limit. It is ignored if POST data reading ; is disabled through enable_post_data_reading. ; http://php.net/post-max-size -post_max_size = 95M +post_max_size = ${POST_MAX_SIZE} ; Automatically add files before PHP document. ; http://php.net/auto-prepend-file @@ -831,10 +831,10 @@ file_uploads = On ; Maximum allowed size for uploaded files. ; http://php.net/upload-max-filesize -upload_max_filesize = 95M +upload_max_filesize = ${POST_MAX_SIZE} ; Maximum number of files that can be uploaded via a single request -max_file_uploads = 20 +max_file_uploads = ${MAX_ALBUM_LENGTH} ;;;;;;;;;;;;;;;;;; ; Fopen wrappers ;