diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 0000000..321e94a --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,120 @@ +name: Build and publish Docker images + +on: + pull_request: + paths: + - 'apache/**' + - 'fpm/**' + - 'fpm-alpine/**' + - 'frankenphp/**' + - 'docker-entrypoint.sh' + - '.github/workflows/publish-images.yml' + + push: + branches: + - master + tags: + - 'v*' + paths: + - 'apache/**' + - 'fpm/**' + - 'fpm-alpine/**' + - 'frankenphp/**' + - 'docker-entrypoint.sh' + - '.github/workflows/publish-images.yml' + + workflow_dispatch: + +env: + REGISTRY: ghcr.io + IMAGE_NAME: ${{ github.repository_owner }}/postfixadmin + +permissions: + contents: read + packages: write + +jobs: + build: + name: Build ${{ matrix.variant }} + runs-on: ubuntu-latest + + strategy: + fail-fast: false + matrix: + include: + - variant: apache + context: ./apache + suffix: -apache + latest: 'true' + + - variant: fpm + context: ./fpm + suffix: -fpm + latest: 'false' + + - variant: fpm-alpine + context: ./fpm-alpine + suffix: -fpm-alpine + latest: 'false' + + - variant: frankenphp + context: ./frankenphp + suffix: -frankenphp + latest: 'false' + + steps: + - name: Checkout repository + uses: actions/checkout@v6 + + - name: Set up QEMU + uses: docker/setup-qemu-action@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v4 + + - name: Log in to GHCR + if: github.event_name != 'pull_request' + uses: docker/login-action@v4 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract Docker metadata + id: meta + uses: docker/metadata-action@v6 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + + tags: | + # type=raw,value=dev-${{ matrix.variant }},enable={{is_default_branch}} + # type=raw,value=dev,enable=${{ matrix.variant == 'apache' && github.ref == format('refs/heads/{0}', github.event.repository.default_branch) }} + + type=semver,pattern={{version}},suffix=-${{ matrix.variant }} + type=semver,pattern={{major}}.{{minor}},suffix=-${{ matrix.variant }} + type=semver,pattern={{major}},suffix=-${{ matrix.variant }} + + type=semver,pattern={{version}},enable=${{ matrix.variant == 'apache' }} + type=semver,pattern={{major}}.{{minor}},enable=${{ matrix.variant == 'apache' }} + type=semver,pattern={{major}},enable=${{ matrix.variant == 'apache' }} + type=raw,value=latest,enable=${{ matrix.variant == 'apache' && startsWith(github.ref, 'refs/tags/v') }} + + labels: | + org.opencontainers.image.title=PostfixAdmin ${{ matrix.variant }} + org.opencontainers.image.description=PostfixAdmin Docker image using the ${{ matrix.variant }} variant + org.opencontainers.image.source=${{ github.server_url }}/${{ github.repository }} + + - name: Build and push image + uses: docker/build-push-action@v7 + with: + context: ${{ matrix.context }} + file: ${{ matrix.context }}/Dockerfile + platforms: linux/amd64,linux/arm64 + push: ${{ github.event_name != 'pull_request' }} + pull: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=gha,scope=${{ matrix.variant }} + cache-to: type=gha,mode=max,scope=${{ matrix.variant }} + provenance: mode=max + sbom: true diff --git a/Dockerfile-alpine.template b/Dockerfile-alpine.template index fbe24c5..41e1e5b 100644 --- a/Dockerfile-alpine.template +++ b/Dockerfile-alpine.template @@ -1,4 +1,4 @@ -FROM php:8.3-%%VARIANT%% +FROM php:%%PHPVERSION%%-%%VARIANT%% LABEL maintainer="David Goodwin (@DavidGoodwin)" # docker-entrypoint.sh dependencies @@ -12,12 +12,14 @@ RUN set -ex; \ apk add --no-cache --virtual .build-deps \ postgresql-dev \ sqlite-dev \ + libpng-dev \ ; \ docker-php-ext-install -j "$(nproc)" \ pdo_mysql \ pdo_pgsql \ pdo_sqlite \ pgsql \ + gd \ ; \ runDeps="$( \ scanelf --needed --nobanner --format '%n#p' --recursive /usr/local/lib/php/extensions \ @@ -28,11 +30,11 @@ RUN set -ex; \ apk add --no-network --virtual .postfixadmin-phpexts-rundeps $runDeps; \ apk del --no-network .build-deps -ARG POSTFIXADMIN_VERSION=4.0.5 -ARG POSTFIXADMIN_SHA512=b9549137e5bb6cf69fe76aef2711092a7e74ec223ee272d0303430b03da16ba8cfe704047f8e07ee5bd1ef6b632cac9ae2c0830271629b193c395197e1f4d93d +ARG POSTFIXADMIN_VERSION=%%POSTFIXADMIN_VERSION%% +ARG POSTFIXADMIN_SHA512=%%POSTFIXADMIN_SHA512%% -ENV POSTFIXADMIN_VERSION $POSTFIXADMIN_VERSION -ENV POSTFIXADMIN_SHA512 $POSTFIXADMIN_SHA512 +ENV POSTFIXADMIN_VERSION=$POSTFIXADMIN_VERSION +ENV POSTFIXADMIN_SHA512=$POSTFIXADMIN_SHA512 RUN set -eu; \ curl -fsSL -o postfixadmin.tar.gz "https://github.com/postfixadmin/postfixadmin/archive/v${POSTFIXADMIN_VERSION}.tar.gz"; \ @@ -52,7 +54,7 @@ COPY --from=composer:2 /usr/bin/composer /usr/local/bin/ # https://github.com/moby/buildkit/issues/4503 # https://github.com/composer/composer/issues/11839 # https://github.com/composer/composer/blob/94fe2945456df51e122a492b8d14ac4b54c1d2ce/src/Composer/Console/Application.php#L217-L218 -ENV COMPOSER_ALLOW_SUPERUSER 1 +ENV COMPOSER_ALLOW_SUPERUSER=1 RUN set -eux; \ export COMPOSER_HOME="$(mktemp -d)"; \ diff --git a/Dockerfile-debian.template b/Dockerfile-debian.template index 367896b..294baec 100644 --- a/Dockerfile-debian.template +++ b/Dockerfile-debian.template @@ -1,4 +1,4 @@ -FROM php:8.3-%%VARIANT%% +FROM php:%%PHPVERSION%%-%%VARIANT%% LABEL maintainer="David Goodwin (@DavidGoodwin)" # docker-entrypoint.sh dependencies @@ -18,12 +18,14 @@ RUN set -ex; \ apt-get install -y --no-install-recommends \ libpq-dev \ libsqlite3-dev \ + libpng-dev \ ; \ docker-php-ext-install -j "$(nproc)" \ pdo_mysql \ pdo_pgsql \ pdo_sqlite \ pgsql \ + gd \ ; \ # reset apt-mark's "manual" list so that "purge --auto-remove" will remove all build dependencies apt-mark auto '.*' > /dev/null; \ @@ -40,12 +42,12 @@ RUN set -ex; \ apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false; \ apt-get dist-clean -ARG POSTFIXADMIN_VERSION=4.0.5 -ARG POSTFIXADMIN_SHA512=b9549137e5bb6cf69fe76aef2711092a7e74ec223ee272d0303430b03da16ba8cfe704047f8e07ee5bd1ef6b632cac9ae2c0830271629b193c395197e1f4d93d +ARG POSTFIXADMIN_VERSION=%%POSTFIXADMIN_VERSION%% +ARG POSTFIXADMIN_SHA512=%%POSTFIXADMIN_SHA512%% -ENV POSTFIXADMIN_VERSION $POSTFIXADMIN_VERSION -ENV POSTFIXADMIN_SHA512 $POSTFIXADMIN_SHA512 -ENV APACHE_DOCUMENT_ROOT /var/www/html/public +ENV POSTFIXADMIN_VERSION=$POSTFIXADMIN_VERSION +ENV POSTFIXADMIN_SHA512=$POSTFIXADMIN_SHA512 +ENV APACHE_DOCUMENT_ROOT=/var/www/html/public RUN set -eu; sed -ri -e 's!/var/www/html!${APACHE_DOCUMENT_ROOT}!g' /etc/apache2/sites-available/*.conf; \ sed -ri -e 's!/var/www/!${APACHE_DOCUMENT_ROOT}!g' /etc/apache2/apache2.conf /etc/apache2/conf-available/*.conf @@ -68,7 +70,7 @@ COPY --from=composer:2 /usr/bin/composer /usr/local/bin/ # https://github.com/moby/buildkit/issues/4503 # https://github.com/composer/composer/issues/11839 # https://github.com/composer/composer/blob/94fe2945456df51e122a492b8d14ac4b54c1d2ce/src/Composer/Console/Application.php#L217-L218 -ENV COMPOSER_ALLOW_SUPERUSER 1 +ENV COMPOSER_ALLOW_SUPERUSER=1 RUN set -eux; \ composerDeps=" \ diff --git a/Dockerfile-frankenphp.template b/Dockerfile-frankenphp.template new file mode 100644 index 0000000..4e72f8f --- /dev/null +++ b/Dockerfile-frankenphp.template @@ -0,0 +1,101 @@ +FROM dunglas/frankenphp:1-php%%PHPVERSION%% + +LABEL maintainer="David Goodwin (@DavidGoodwin)" + +ARG POSTFIXADMIN_VERSION=%%POSTFIXADMIN_VERSION%% +ARG POSTFIXADMIN_SHA512=%%POSTFIXADMIN_SHA512%% + +ENV POSTFIXADMIN_VERSION="${POSTFIXADMIN_VERSION}" \ + POSTFIXADMIN_SHA512="${POSTFIXADMIN_SHA512}" \ + COMPOSER_ALLOW_SUPERUSER=1 \ + SERVER_NAME=:80 \ + SERVER_ROOT=/var/www/html/public + +WORKDIR /var/www/html + +# +# Runtime dependencies used by docker-entrypoint.sh. +# +RUN set -eux; \ + apt-get update; \ + apt-get install -y --no-install-recommends \ + gosu \ + ; \ + rm -rf /var/lib/apt/lists/* + +# +# PHP extensions required by PostfixAdmin. +# +# install-php-extensions automatically installs and removes the necessary +# system build dependencies. +# +RUN install-php-extensions \ + pdo_mysql \ + pdo_pgsql \ + pdo_sqlite \ + pgsql \ + gd \ + ; + +RUN printf '%s\n' \ + 'output_buffering = 4096' \ + 'session.use_strict_mode = 1' \ + 'expose_php = Off' \ + > /usr/local/etc/php/conf.d/postfixadmin.ini + +# +# Download PostfixAdmin. +# +RUN set -eux; \ + curl -fsSL \ + -o /tmp/postfixadmin.tar.gz \ + "https://github.com/postfixadmin/postfixadmin/archive/v${POSTFIXADMIN_VERSION}.tar.gz"; \ + \ + echo "${POSTFIXADMIN_SHA512} */tmp/postfixadmin.tar.gz" \ + | sha512sum -c -; \ + \ + mkdir -p /var/www/html; \ + tar \ + -xf /tmp/postfixadmin.tar.gz \ + -C /var/www/html \ + --strip-components=1; \ + rm /tmp/postfixadmin.tar.gz; \ + \ + mkdir -p /var/www/html/templates_c; \ + chown -R www-data:www-data /var/www/html + +# +# Install Composer dependencies. +# +COPY --from=composer:2 /usr/bin/composer /usr/local/bin/composer + +RUN set -eux; \ + apt-get update; \ + apt-get install -y --no-install-recommends \ + unzip \ + ; \ + \ + export COMPOSER_HOME="$(mktemp -d)"; \ + composer install \ + --ignore-platform-req=ext-mysqli \ + --no-dev \ + --no-interaction \ + --no-progress \ + --prefer-dist \ + --working-dir=/var/www/html; \ + rm -rf "${COMPOSER_HOME}"; \ + \ + apt-get purge -y --auto-remove \ + -o APT::AutoRemove::RecommendsImportant=false \ + unzip; \ + rm -rf /var/lib/apt/lists/* + +COPY docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh + +RUN chmod +x /usr/local/bin/docker-entrypoint.sh + +EXPOSE 80 + +ENTRYPOINT ["/usr/local/bin/docker-entrypoint.sh"] + +CMD ["%%CMD%%"] diff --git a/apache/Dockerfile b/apache/Dockerfile index 8e26bce..f5cd8bb 100644 --- a/apache/Dockerfile +++ b/apache/Dockerfile @@ -18,12 +18,14 @@ RUN set -ex; \ apt-get install -y --no-install-recommends \ libpq-dev \ libsqlite3-dev \ + libpng-dev \ ; \ docker-php-ext-install -j "$(nproc)" \ pdo_mysql \ pdo_pgsql \ pdo_sqlite \ pgsql \ + gd \ ; \ # reset apt-mark's "manual" list so that "purge --auto-remove" will remove all build dependencies apt-mark auto '.*' > /dev/null; \ @@ -43,9 +45,9 @@ RUN set -ex; \ ARG POSTFIXADMIN_VERSION=4.0.5 ARG POSTFIXADMIN_SHA512=b9549137e5bb6cf69fe76aef2711092a7e74ec223ee272d0303430b03da16ba8cfe704047f8e07ee5bd1ef6b632cac9ae2c0830271629b193c395197e1f4d93d -ENV POSTFIXADMIN_VERSION $POSTFIXADMIN_VERSION -ENV POSTFIXADMIN_SHA512 $POSTFIXADMIN_SHA512 -ENV APACHE_DOCUMENT_ROOT /var/www/html/public +ENV POSTFIXADMIN_VERSION=$POSTFIXADMIN_VERSION +ENV POSTFIXADMIN_SHA512=$POSTFIXADMIN_SHA512 +ENV APACHE_DOCUMENT_ROOT=/var/www/html/public RUN set -eu; sed -ri -e 's!/var/www/html!${APACHE_DOCUMENT_ROOT}!g' /etc/apache2/sites-available/*.conf; \ sed -ri -e 's!/var/www/!${APACHE_DOCUMENT_ROOT}!g' /etc/apache2/apache2.conf /etc/apache2/conf-available/*.conf @@ -68,7 +70,7 @@ COPY --from=composer:2 /usr/bin/composer /usr/local/bin/ # https://github.com/moby/buildkit/issues/4503 # https://github.com/composer/composer/issues/11839 # https://github.com/composer/composer/blob/94fe2945456df51e122a492b8d14ac4b54c1d2ce/src/Composer/Console/Application.php#L217-L218 -ENV COMPOSER_ALLOW_SUPERUSER 1 +ENV COMPOSER_ALLOW_SUPERUSER=1 RUN set -eux; \ composerDeps=" \ diff --git a/apache/docker-entrypoint.sh b/apache/docker-entrypoint.sh index 9c6a528..ece00cb 100755 --- a/apache/docker-entrypoint.sh +++ b/apache/docker-entrypoint.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eo pipefail # usage: get_env_value VAR [DEFAULT] @@ -26,22 +26,84 @@ function get_env_value() { exit 0 } +function get_database_env_value() { + local suffix="$1" + local default_value="${2-}" + + local primary_name="POSTFIXADMIN_DB_${suffix}" + local fallback_name="POSTFIXADMIN_DATABASE_${suffix}" + + local primary_file_name="${primary_name}_FILE" + local fallback_file_name="${fallback_name}_FILE" + + if [[ -n "${!primary_name:-}" && -n "${!fallback_name:-}" ]]; then + echo >&2 "Error : both ${primary_name} and ${fallback_name} are set (but are exclusive)" + exit 1 + fi + + if [[ -n "${!primary_file_name:-}" && -n "${!fallback_file_name:-}" ]]; then + echo >&2 "Error : both ${primary_file_name} and ${fallback_file_name} are set (but are exclusive)" + exit 1 + fi + + # Priority to POSTFIXADMIN_DB_* + if [[ -n "${!primary_name:-}" || -n "${!primary_file_name:-}" ]]; then + get_env_value "${primary_name}" "${default_value}" + else + get_env_value "${fallback_name}" "${default_value}" + fi +} + +function php_escape() { + local value="$1" + + # value=${value//\\/\\\\} + # value=${value//\'/\\\'} + + printf '%s' "${value}" +} + +function config_name_from_env() { + local env_name="$1" + + case "${env_name}" in + POSTFIXADMIN_DB_TYPE) + printf '%s' 'database_type' + ;; + POSTFIXADMIN_DB_HOST) + printf '%s' 'database_host' + ;; + POSTFIXADMIN_DB_PORT) + printf '%s' 'database_port' + ;; + POSTFIXADMIN_DB_USER) + printf '%s' 'database_user' + ;; + POSTFIXADMIN_DB_PASSWORD) + printf '%s' 'database_password' + ;; + POSTFIXADMIN_DB_NAME) + printf '%s' 'database_name' + ;; + *) + local config_name="${env_name#POSTFIXADMIN_}" + printf '%s' "${config_name,,}" + ;; + esac +} + # Init vars for running script -POSTFIXADMIN_DB_TYPE=$(get_env_value 'POSTFIXADMIN_DB_TYPE' 'sqlite') -POSTFIXADMIN_DB_HOST=$(get_env_value "POSTFIXADMIN_DB_HOST" "") -POSTFIXADMIN_DB_PORT=$(get_env_value "POSTFIXADMIN_DB_PORT" "") -POSTFIXADMIN_DB_USER=$(get_env_value "POSTFIXADMIN_DB_USER" "") -POSTFIXADMIN_DB_PASSWORD=$(get_env_value "POSTFIXADMIN_DB_PASSWORD" "") -POSTFIXADMIN_SMTP_SERVER=$(get_env_value "POSTFIXADMIN_SMTP_SERVER" "localhost") -POSTFIXADMIN_SMTP_PORT=$(get_env_value "POSTFIXADMIN_SMTP_PORT" "25") -POSTFIXADMIN_ENCRYPT=$(get_env_value "POSTFIXADMIN_ENCRYPT" "md5crypt") -POSTFIXADMIN_DKIM=$(get_env_value "POSTFIXADMIN_DKIM" "NO") -POSTFIXADMIN_DKIM_ALL_ADMINS=$(get_env_value "POSTFIXADMIN_DKIM_ALL_ADMINS" "NO") +export POSTFIXADMIN_DB_TYPE=$(get_database_env_value 'TYPE' 'sqlite') +export POSTFIXADMIN_DB_HOST=$(get_database_env_value "HOST" "") +export POSTFIXADMIN_DB_NAME=$(get_database_env_value "NAME" "") +export POSTFIXADMIN_DB_PORT=$(get_database_env_value "PORT" "") +export POSTFIXADMIN_DB_USER=$(get_database_env_value "USER" "") +export POSTFIXADMIN_DB_PASSWORD=$(get_database_env_value "PASSWORD" "") DEFAULT_SETUP_PASSWORD="changeme" -POSTFIXADMIN_SETUP_PASSWORD=$(get_env_value "POSTFIXADMIN_SETUP_PASSWORD" "${DEFAULT_SETUP_PASSWORD}") +export POSTFIXADMIN_SETUP_PASSWORD=$(get_env_value "POSTFIXADMIN_SETUP_PASSWORD" "${DEFAULT_SETUP_PASSWORD}") -if [[ "$1" == apache2* ]] || [ "$1" == php-fpm ]; then +if [[ "$1" == apache2* ]] || [ "$1" == php-fpm ] || [ "$1" == frankenphp ]; then if [ "${POSTFIXADMIN_SETUP_PASSWORD}" = "${DEFAULT_SETUP_PASSWORD}" ]; then echo >&2 "WARNING: setup.php password not set" @@ -72,14 +134,6 @@ if [[ "$1" == apache2* ]] || [ "$1" == php-fpm ]; then ;; esac - if [ "${POSTFIXADMIN_DB_TYPE}" != "sqlite" ]; then - if [ -z "${POSTFIXADMIN_DB_USER}" ] || [ -z "${POSTFIXADMIN_DB_PASSWORD}" ]; then - echo >&2 'Error: POSTFIXADMIN_DB_USER and POSTFIXADMIN_DB_PASSWORD must be specified. ' - exit 1 - fi - timeout 15 bash -c "until echo > /dev/tcp/${POSTFIXADMIN_DB_HOST}/${POSTFIXADMIN_DB_PORT}; do sleep 0.5; done" - fi - if [ "${POSTFIXADMIN_DB_TYPE}" = 'sqlite' ]; then : "${POSTFIXADMIN_DB_NAME:=/var/tmp/postfixadmin.db}" @@ -89,26 +143,52 @@ if [[ "$1" == apache2* ]] || [ "$1" == php-fpm ]; then chown www-data:www-data $POSTFIXADMIN_DB_NAME chmod 0700 $POSTFIXADMIN_DB_NAME fi + else + if [ -z "${POSTFIXADMIN_DB_USER}" ] || [ -z "${POSTFIXADMIN_DB_PASSWORD}" ]; then + echo >&2 'Error: POSTFIXADMIN_DB_USER and POSTFIXADMIN_DB_PASSWORD must be specified. ' + exit 1 + fi + timeout 15 bash -c "until echo > /dev/tcp/${POSTFIXADMIN_DB_HOST}/${POSTFIXADMIN_DB_PORT}; do sleep 0.5; done" fi if [ ! -e config.local.php ]; then touch config.local.php echo "Write config to $PWD/config.local.php" - echo "" | tee config.local.php + { + printf '%s\n' '"$PWD/config.local.php" + cat "$PWD/config.local.php" else echo "WARNING: $PWD/config.local.php already exists." echo "Postfixadmin related environment variables have been ignored." diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh index 9c6a528..ece00cb 100755 --- a/docker-entrypoint.sh +++ b/docker-entrypoint.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eo pipefail # usage: get_env_value VAR [DEFAULT] @@ -26,22 +26,84 @@ function get_env_value() { exit 0 } +function get_database_env_value() { + local suffix="$1" + local default_value="${2-}" + + local primary_name="POSTFIXADMIN_DB_${suffix}" + local fallback_name="POSTFIXADMIN_DATABASE_${suffix}" + + local primary_file_name="${primary_name}_FILE" + local fallback_file_name="${fallback_name}_FILE" + + if [[ -n "${!primary_name:-}" && -n "${!fallback_name:-}" ]]; then + echo >&2 "Error : both ${primary_name} and ${fallback_name} are set (but are exclusive)" + exit 1 + fi + + if [[ -n "${!primary_file_name:-}" && -n "${!fallback_file_name:-}" ]]; then + echo >&2 "Error : both ${primary_file_name} and ${fallback_file_name} are set (but are exclusive)" + exit 1 + fi + + # Priority to POSTFIXADMIN_DB_* + if [[ -n "${!primary_name:-}" || -n "${!primary_file_name:-}" ]]; then + get_env_value "${primary_name}" "${default_value}" + else + get_env_value "${fallback_name}" "${default_value}" + fi +} + +function php_escape() { + local value="$1" + + # value=${value//\\/\\\\} + # value=${value//\'/\\\'} + + printf '%s' "${value}" +} + +function config_name_from_env() { + local env_name="$1" + + case "${env_name}" in + POSTFIXADMIN_DB_TYPE) + printf '%s' 'database_type' + ;; + POSTFIXADMIN_DB_HOST) + printf '%s' 'database_host' + ;; + POSTFIXADMIN_DB_PORT) + printf '%s' 'database_port' + ;; + POSTFIXADMIN_DB_USER) + printf '%s' 'database_user' + ;; + POSTFIXADMIN_DB_PASSWORD) + printf '%s' 'database_password' + ;; + POSTFIXADMIN_DB_NAME) + printf '%s' 'database_name' + ;; + *) + local config_name="${env_name#POSTFIXADMIN_}" + printf '%s' "${config_name,,}" + ;; + esac +} + # Init vars for running script -POSTFIXADMIN_DB_TYPE=$(get_env_value 'POSTFIXADMIN_DB_TYPE' 'sqlite') -POSTFIXADMIN_DB_HOST=$(get_env_value "POSTFIXADMIN_DB_HOST" "") -POSTFIXADMIN_DB_PORT=$(get_env_value "POSTFIXADMIN_DB_PORT" "") -POSTFIXADMIN_DB_USER=$(get_env_value "POSTFIXADMIN_DB_USER" "") -POSTFIXADMIN_DB_PASSWORD=$(get_env_value "POSTFIXADMIN_DB_PASSWORD" "") -POSTFIXADMIN_SMTP_SERVER=$(get_env_value "POSTFIXADMIN_SMTP_SERVER" "localhost") -POSTFIXADMIN_SMTP_PORT=$(get_env_value "POSTFIXADMIN_SMTP_PORT" "25") -POSTFIXADMIN_ENCRYPT=$(get_env_value "POSTFIXADMIN_ENCRYPT" "md5crypt") -POSTFIXADMIN_DKIM=$(get_env_value "POSTFIXADMIN_DKIM" "NO") -POSTFIXADMIN_DKIM_ALL_ADMINS=$(get_env_value "POSTFIXADMIN_DKIM_ALL_ADMINS" "NO") +export POSTFIXADMIN_DB_TYPE=$(get_database_env_value 'TYPE' 'sqlite') +export POSTFIXADMIN_DB_HOST=$(get_database_env_value "HOST" "") +export POSTFIXADMIN_DB_NAME=$(get_database_env_value "NAME" "") +export POSTFIXADMIN_DB_PORT=$(get_database_env_value "PORT" "") +export POSTFIXADMIN_DB_USER=$(get_database_env_value "USER" "") +export POSTFIXADMIN_DB_PASSWORD=$(get_database_env_value "PASSWORD" "") DEFAULT_SETUP_PASSWORD="changeme" -POSTFIXADMIN_SETUP_PASSWORD=$(get_env_value "POSTFIXADMIN_SETUP_PASSWORD" "${DEFAULT_SETUP_PASSWORD}") +export POSTFIXADMIN_SETUP_PASSWORD=$(get_env_value "POSTFIXADMIN_SETUP_PASSWORD" "${DEFAULT_SETUP_PASSWORD}") -if [[ "$1" == apache2* ]] || [ "$1" == php-fpm ]; then +if [[ "$1" == apache2* ]] || [ "$1" == php-fpm ] || [ "$1" == frankenphp ]; then if [ "${POSTFIXADMIN_SETUP_PASSWORD}" = "${DEFAULT_SETUP_PASSWORD}" ]; then echo >&2 "WARNING: setup.php password not set" @@ -72,14 +134,6 @@ if [[ "$1" == apache2* ]] || [ "$1" == php-fpm ]; then ;; esac - if [ "${POSTFIXADMIN_DB_TYPE}" != "sqlite" ]; then - if [ -z "${POSTFIXADMIN_DB_USER}" ] || [ -z "${POSTFIXADMIN_DB_PASSWORD}" ]; then - echo >&2 'Error: POSTFIXADMIN_DB_USER and POSTFIXADMIN_DB_PASSWORD must be specified. ' - exit 1 - fi - timeout 15 bash -c "until echo > /dev/tcp/${POSTFIXADMIN_DB_HOST}/${POSTFIXADMIN_DB_PORT}; do sleep 0.5; done" - fi - if [ "${POSTFIXADMIN_DB_TYPE}" = 'sqlite' ]; then : "${POSTFIXADMIN_DB_NAME:=/var/tmp/postfixadmin.db}" @@ -89,26 +143,52 @@ if [[ "$1" == apache2* ]] || [ "$1" == php-fpm ]; then chown www-data:www-data $POSTFIXADMIN_DB_NAME chmod 0700 $POSTFIXADMIN_DB_NAME fi + else + if [ -z "${POSTFIXADMIN_DB_USER}" ] || [ -z "${POSTFIXADMIN_DB_PASSWORD}" ]; then + echo >&2 'Error: POSTFIXADMIN_DB_USER and POSTFIXADMIN_DB_PASSWORD must be specified. ' + exit 1 + fi + timeout 15 bash -c "until echo > /dev/tcp/${POSTFIXADMIN_DB_HOST}/${POSTFIXADMIN_DB_PORT}; do sleep 0.5; done" fi if [ ! -e config.local.php ]; then touch config.local.php echo "Write config to $PWD/config.local.php" - echo "" | tee config.local.php + { + printf '%s\n' '"$PWD/config.local.php" + cat "$PWD/config.local.php" else echo "WARNING: $PWD/config.local.php already exists." echo "Postfixadmin related environment variables have been ignored." diff --git a/fpm-alpine/Dockerfile b/fpm-alpine/Dockerfile index c400966..718601c 100644 --- a/fpm-alpine/Dockerfile +++ b/fpm-alpine/Dockerfile @@ -12,12 +12,14 @@ RUN set -ex; \ apk add --no-cache --virtual .build-deps \ postgresql-dev \ sqlite-dev \ + libpng-dev \ ; \ docker-php-ext-install -j "$(nproc)" \ pdo_mysql \ pdo_pgsql \ pdo_sqlite \ pgsql \ + gd \ ; \ runDeps="$( \ scanelf --needed --nobanner --format '%n#p' --recursive /usr/local/lib/php/extensions \ @@ -31,8 +33,8 @@ RUN set -ex; \ ARG POSTFIXADMIN_VERSION=4.0.5 ARG POSTFIXADMIN_SHA512=b9549137e5bb6cf69fe76aef2711092a7e74ec223ee272d0303430b03da16ba8cfe704047f8e07ee5bd1ef6b632cac9ae2c0830271629b193c395197e1f4d93d -ENV POSTFIXADMIN_VERSION $POSTFIXADMIN_VERSION -ENV POSTFIXADMIN_SHA512 $POSTFIXADMIN_SHA512 +ENV POSTFIXADMIN_VERSION=$POSTFIXADMIN_VERSION +ENV POSTFIXADMIN_SHA512=$POSTFIXADMIN_SHA512 RUN set -eu; \ curl -fsSL -o postfixadmin.tar.gz "https://github.com/postfixadmin/postfixadmin/archive/v${POSTFIXADMIN_VERSION}.tar.gz"; \ @@ -52,7 +54,7 @@ COPY --from=composer:2 /usr/bin/composer /usr/local/bin/ # https://github.com/moby/buildkit/issues/4503 # https://github.com/composer/composer/issues/11839 # https://github.com/composer/composer/blob/94fe2945456df51e122a492b8d14ac4b54c1d2ce/src/Composer/Console/Application.php#L217-L218 -ENV COMPOSER_ALLOW_SUPERUSER 1 +ENV COMPOSER_ALLOW_SUPERUSER=1 RUN set -eux; \ export COMPOSER_HOME="$(mktemp -d)"; \ diff --git a/fpm-alpine/docker-entrypoint.sh b/fpm-alpine/docker-entrypoint.sh index e7d2af7..35bdff8 100755 --- a/fpm-alpine/docker-entrypoint.sh +++ b/fpm-alpine/docker-entrypoint.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eo pipefail # usage: get_env_value VAR [DEFAULT] @@ -26,22 +26,84 @@ function get_env_value() { exit 0 } +function get_database_env_value() { + local suffix="$1" + local default_value="${2-}" + + local primary_name="POSTFIXADMIN_DB_${suffix}" + local fallback_name="POSTFIXADMIN_DATABASE_${suffix}" + + local primary_file_name="${primary_name}_FILE" + local fallback_file_name="${fallback_name}_FILE" + + if [[ -n "${!primary_name:-}" && -n "${!fallback_name:-}" ]]; then + echo >&2 "Error : both ${primary_name} and ${fallback_name} are set (but are exclusive)" + exit 1 + fi + + if [[ -n "${!primary_file_name:-}" && -n "${!fallback_file_name:-}" ]]; then + echo >&2 "Error : both ${primary_file_name} and ${fallback_file_name} are set (but are exclusive)" + exit 1 + fi + + # Priority to POSTFIXADMIN_DB_* + if [[ -n "${!primary_name:-}" || -n "${!primary_file_name:-}" ]]; then + get_env_value "${primary_name}" "${default_value}" + else + get_env_value "${fallback_name}" "${default_value}" + fi +} + +function php_escape() { + local value="$1" + + # value=${value//\\/\\\\} + # value=${value//\'/\\\'} + + printf '%s' "${value}" +} + +function config_name_from_env() { + local env_name="$1" + + case "${env_name}" in + POSTFIXADMIN_DB_TYPE) + printf '%s' 'database_type' + ;; + POSTFIXADMIN_DB_HOST) + printf '%s' 'database_host' + ;; + POSTFIXADMIN_DB_PORT) + printf '%s' 'database_port' + ;; + POSTFIXADMIN_DB_USER) + printf '%s' 'database_user' + ;; + POSTFIXADMIN_DB_PASSWORD) + printf '%s' 'database_password' + ;; + POSTFIXADMIN_DB_NAME) + printf '%s' 'database_name' + ;; + *) + local config_name="${env_name#POSTFIXADMIN_}" + printf '%s' "${config_name,,}" + ;; + esac +} + # Init vars for running script -POSTFIXADMIN_DB_TYPE=$(get_env_value 'POSTFIXADMIN_DB_TYPE' 'sqlite') -POSTFIXADMIN_DB_HOST=$(get_env_value "POSTFIXADMIN_DB_HOST" "") -POSTFIXADMIN_DB_PORT=$(get_env_value "POSTFIXADMIN_DB_PORT" "") -POSTFIXADMIN_DB_USER=$(get_env_value "POSTFIXADMIN_DB_USER" "") -POSTFIXADMIN_DB_PASSWORD=$(get_env_value "POSTFIXADMIN_DB_PASSWORD" "") -POSTFIXADMIN_SMTP_SERVER=$(get_env_value "POSTFIXADMIN_SMTP_SERVER" "localhost") -POSTFIXADMIN_SMTP_PORT=$(get_env_value "POSTFIXADMIN_SMTP_PORT" "25") -POSTFIXADMIN_ENCRYPT=$(get_env_value "POSTFIXADMIN_ENCRYPT" "md5crypt") -POSTFIXADMIN_DKIM=$(get_env_value "POSTFIXADMIN_DKIM" "NO") -POSTFIXADMIN_DKIM_ALL_ADMINS=$(get_env_value "POSTFIXADMIN_DKIM_ALL_ADMINS" "NO") +export POSTFIXADMIN_DB_TYPE=$(get_database_env_value 'TYPE' 'sqlite') +export POSTFIXADMIN_DB_HOST=$(get_database_env_value "HOST" "") +export POSTFIXADMIN_DB_NAME=$(get_database_env_value "NAME" "") +export POSTFIXADMIN_DB_PORT=$(get_database_env_value "PORT" "") +export POSTFIXADMIN_DB_USER=$(get_database_env_value "USER" "") +export POSTFIXADMIN_DB_PASSWORD=$(get_database_env_value "PASSWORD" "") DEFAULT_SETUP_PASSWORD="changeme" -POSTFIXADMIN_SETUP_PASSWORD=$(get_env_value "POSTFIXADMIN_SETUP_PASSWORD" "${DEFAULT_SETUP_PASSWORD}") +export POSTFIXADMIN_SETUP_PASSWORD=$(get_env_value "POSTFIXADMIN_SETUP_PASSWORD" "${DEFAULT_SETUP_PASSWORD}") -if [[ "$1" == apache2* ]] || [ "$1" == php-fpm ]; then +if [[ "$1" == apache2* ]] || [ "$1" == php-fpm ] || [ "$1" == frankenphp ]; then if [ "${POSTFIXADMIN_SETUP_PASSWORD}" = "${DEFAULT_SETUP_PASSWORD}" ]; then echo >&2 "WARNING: setup.php password not set" @@ -72,14 +134,6 @@ if [[ "$1" == apache2* ]] || [ "$1" == php-fpm ]; then ;; esac - if [ "${POSTFIXADMIN_DB_TYPE}" != "sqlite" ]; then - if [ -z "${POSTFIXADMIN_DB_USER}" ] || [ -z "${POSTFIXADMIN_DB_PASSWORD}" ]; then - echo >&2 'Error: POSTFIXADMIN_DB_USER and POSTFIXADMIN_DB_PASSWORD must be specified. ' - exit 1 - fi - timeout 15 bash -c "until echo > /dev/tcp/${POSTFIXADMIN_DB_HOST}/${POSTFIXADMIN_DB_PORT}; do sleep 0.5; done" - fi - if [ "${POSTFIXADMIN_DB_TYPE}" = 'sqlite' ]; then : "${POSTFIXADMIN_DB_NAME:=/var/tmp/postfixadmin.db}" @@ -89,26 +143,52 @@ if [[ "$1" == apache2* ]] || [ "$1" == php-fpm ]; then chown www-data:www-data $POSTFIXADMIN_DB_NAME chmod 0700 $POSTFIXADMIN_DB_NAME fi + else + if [ -z "${POSTFIXADMIN_DB_USER}" ] || [ -z "${POSTFIXADMIN_DB_PASSWORD}" ]; then + echo >&2 'Error: POSTFIXADMIN_DB_USER and POSTFIXADMIN_DB_PASSWORD must be specified. ' + exit 1 + fi + timeout 15 bash -c "until echo > /dev/tcp/${POSTFIXADMIN_DB_HOST}/${POSTFIXADMIN_DB_PORT}; do sleep 0.5; done" fi if [ ! -e config.local.php ]; then touch config.local.php echo "Write config to $PWD/config.local.php" - echo "" | tee config.local.php + { + printf '%s\n' '"$PWD/config.local.php" + cat "$PWD/config.local.php" else echo "WARNING: $PWD/config.local.php already exists." echo "Postfixadmin related environment variables have been ignored." diff --git a/fpm/Dockerfile b/fpm/Dockerfile index 4e3a986..c84d7a1 100644 --- a/fpm/Dockerfile +++ b/fpm/Dockerfile @@ -18,12 +18,14 @@ RUN set -ex; \ apt-get install -y --no-install-recommends \ libpq-dev \ libsqlite3-dev \ + libpng-dev \ ; \ docker-php-ext-install -j "$(nproc)" \ pdo_mysql \ pdo_pgsql \ pdo_sqlite \ pgsql \ + gd \ ; \ # reset apt-mark's "manual" list so that "purge --auto-remove" will remove all build dependencies apt-mark auto '.*' > /dev/null; \ @@ -43,8 +45,8 @@ RUN set -ex; \ ARG POSTFIXADMIN_VERSION=4.0.5 ARG POSTFIXADMIN_SHA512=b9549137e5bb6cf69fe76aef2711092a7e74ec223ee272d0303430b03da16ba8cfe704047f8e07ee5bd1ef6b632cac9ae2c0830271629b193c395197e1f4d93d -ENV POSTFIXADMIN_VERSION $POSTFIXADMIN_VERSION -ENV POSTFIXADMIN_SHA512 $POSTFIXADMIN_SHA512 +ENV POSTFIXADMIN_VERSION=$POSTFIXADMIN_VERSION +ENV POSTFIXADMIN_SHA512=$POSTFIXADMIN_SHA512 RUN set -eu; \ @@ -65,7 +67,7 @@ COPY --from=composer:2 /usr/bin/composer /usr/local/bin/ # https://github.com/moby/buildkit/issues/4503 # https://github.com/composer/composer/issues/11839 # https://github.com/composer/composer/blob/94fe2945456df51e122a492b8d14ac4b54c1d2ce/src/Composer/Console/Application.php#L217-L218 -ENV COMPOSER_ALLOW_SUPERUSER 1 +ENV COMPOSER_ALLOW_SUPERUSER=1 RUN set -eux; \ composerDeps=" \ diff --git a/fpm/docker-entrypoint.sh b/fpm/docker-entrypoint.sh index 9c6a528..ece00cb 100755 --- a/fpm/docker-entrypoint.sh +++ b/fpm/docker-entrypoint.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eo pipefail # usage: get_env_value VAR [DEFAULT] @@ -26,22 +26,84 @@ function get_env_value() { exit 0 } +function get_database_env_value() { + local suffix="$1" + local default_value="${2-}" + + local primary_name="POSTFIXADMIN_DB_${suffix}" + local fallback_name="POSTFIXADMIN_DATABASE_${suffix}" + + local primary_file_name="${primary_name}_FILE" + local fallback_file_name="${fallback_name}_FILE" + + if [[ -n "${!primary_name:-}" && -n "${!fallback_name:-}" ]]; then + echo >&2 "Error : both ${primary_name} and ${fallback_name} are set (but are exclusive)" + exit 1 + fi + + if [[ -n "${!primary_file_name:-}" && -n "${!fallback_file_name:-}" ]]; then + echo >&2 "Error : both ${primary_file_name} and ${fallback_file_name} are set (but are exclusive)" + exit 1 + fi + + # Priority to POSTFIXADMIN_DB_* + if [[ -n "${!primary_name:-}" || -n "${!primary_file_name:-}" ]]; then + get_env_value "${primary_name}" "${default_value}" + else + get_env_value "${fallback_name}" "${default_value}" + fi +} + +function php_escape() { + local value="$1" + + # value=${value//\\/\\\\} + # value=${value//\'/\\\'} + + printf '%s' "${value}" +} + +function config_name_from_env() { + local env_name="$1" + + case "${env_name}" in + POSTFIXADMIN_DB_TYPE) + printf '%s' 'database_type' + ;; + POSTFIXADMIN_DB_HOST) + printf '%s' 'database_host' + ;; + POSTFIXADMIN_DB_PORT) + printf '%s' 'database_port' + ;; + POSTFIXADMIN_DB_USER) + printf '%s' 'database_user' + ;; + POSTFIXADMIN_DB_PASSWORD) + printf '%s' 'database_password' + ;; + POSTFIXADMIN_DB_NAME) + printf '%s' 'database_name' + ;; + *) + local config_name="${env_name#POSTFIXADMIN_}" + printf '%s' "${config_name,,}" + ;; + esac +} + # Init vars for running script -POSTFIXADMIN_DB_TYPE=$(get_env_value 'POSTFIXADMIN_DB_TYPE' 'sqlite') -POSTFIXADMIN_DB_HOST=$(get_env_value "POSTFIXADMIN_DB_HOST" "") -POSTFIXADMIN_DB_PORT=$(get_env_value "POSTFIXADMIN_DB_PORT" "") -POSTFIXADMIN_DB_USER=$(get_env_value "POSTFIXADMIN_DB_USER" "") -POSTFIXADMIN_DB_PASSWORD=$(get_env_value "POSTFIXADMIN_DB_PASSWORD" "") -POSTFIXADMIN_SMTP_SERVER=$(get_env_value "POSTFIXADMIN_SMTP_SERVER" "localhost") -POSTFIXADMIN_SMTP_PORT=$(get_env_value "POSTFIXADMIN_SMTP_PORT" "25") -POSTFIXADMIN_ENCRYPT=$(get_env_value "POSTFIXADMIN_ENCRYPT" "md5crypt") -POSTFIXADMIN_DKIM=$(get_env_value "POSTFIXADMIN_DKIM" "NO") -POSTFIXADMIN_DKIM_ALL_ADMINS=$(get_env_value "POSTFIXADMIN_DKIM_ALL_ADMINS" "NO") +export POSTFIXADMIN_DB_TYPE=$(get_database_env_value 'TYPE' 'sqlite') +export POSTFIXADMIN_DB_HOST=$(get_database_env_value "HOST" "") +export POSTFIXADMIN_DB_NAME=$(get_database_env_value "NAME" "") +export POSTFIXADMIN_DB_PORT=$(get_database_env_value "PORT" "") +export POSTFIXADMIN_DB_USER=$(get_database_env_value "USER" "") +export POSTFIXADMIN_DB_PASSWORD=$(get_database_env_value "PASSWORD" "") DEFAULT_SETUP_PASSWORD="changeme" -POSTFIXADMIN_SETUP_PASSWORD=$(get_env_value "POSTFIXADMIN_SETUP_PASSWORD" "${DEFAULT_SETUP_PASSWORD}") +export POSTFIXADMIN_SETUP_PASSWORD=$(get_env_value "POSTFIXADMIN_SETUP_PASSWORD" "${DEFAULT_SETUP_PASSWORD}") -if [[ "$1" == apache2* ]] || [ "$1" == php-fpm ]; then +if [[ "$1" == apache2* ]] || [ "$1" == php-fpm ] || [ "$1" == frankenphp ]; then if [ "${POSTFIXADMIN_SETUP_PASSWORD}" = "${DEFAULT_SETUP_PASSWORD}" ]; then echo >&2 "WARNING: setup.php password not set" @@ -72,14 +134,6 @@ if [[ "$1" == apache2* ]] || [ "$1" == php-fpm ]; then ;; esac - if [ "${POSTFIXADMIN_DB_TYPE}" != "sqlite" ]; then - if [ -z "${POSTFIXADMIN_DB_USER}" ] || [ -z "${POSTFIXADMIN_DB_PASSWORD}" ]; then - echo >&2 'Error: POSTFIXADMIN_DB_USER and POSTFIXADMIN_DB_PASSWORD must be specified. ' - exit 1 - fi - timeout 15 bash -c "until echo > /dev/tcp/${POSTFIXADMIN_DB_HOST}/${POSTFIXADMIN_DB_PORT}; do sleep 0.5; done" - fi - if [ "${POSTFIXADMIN_DB_TYPE}" = 'sqlite' ]; then : "${POSTFIXADMIN_DB_NAME:=/var/tmp/postfixadmin.db}" @@ -89,26 +143,52 @@ if [[ "$1" == apache2* ]] || [ "$1" == php-fpm ]; then chown www-data:www-data $POSTFIXADMIN_DB_NAME chmod 0700 $POSTFIXADMIN_DB_NAME fi + else + if [ -z "${POSTFIXADMIN_DB_USER}" ] || [ -z "${POSTFIXADMIN_DB_PASSWORD}" ]; then + echo >&2 'Error: POSTFIXADMIN_DB_USER and POSTFIXADMIN_DB_PASSWORD must be specified. ' + exit 1 + fi + timeout 15 bash -c "until echo > /dev/tcp/${POSTFIXADMIN_DB_HOST}/${POSTFIXADMIN_DB_PORT}; do sleep 0.5; done" fi if [ ! -e config.local.php ]; then touch config.local.php echo "Write config to $PWD/config.local.php" - echo "" | tee config.local.php + { + printf '%s\n' '"$PWD/config.local.php" + cat "$PWD/config.local.php" else echo "WARNING: $PWD/config.local.php already exists." echo "Postfixadmin related environment variables have been ignored." diff --git a/frankenphp/Dockerfile b/frankenphp/Dockerfile new file mode 100644 index 0000000..f8ac0da --- /dev/null +++ b/frankenphp/Dockerfile @@ -0,0 +1,101 @@ +FROM dunglas/frankenphp:1-php8.3 + +LABEL maintainer="David Goodwin (@DavidGoodwin)" + +ARG POSTFIXADMIN_VERSION=4.0.5 +ARG POSTFIXADMIN_SHA512=b9549137e5bb6cf69fe76aef2711092a7e74ec223ee272d0303430b03da16ba8cfe704047f8e07ee5bd1ef6b632cac9ae2c0830271629b193c395197e1f4d93d + +ENV POSTFIXADMIN_VERSION="${POSTFIXADMIN_VERSION}" \ + POSTFIXADMIN_SHA512="${POSTFIXADMIN_SHA512}" \ + COMPOSER_ALLOW_SUPERUSER=1 \ + SERVER_NAME=:80 \ + SERVER_ROOT=/var/www/html/public + +WORKDIR /var/www/html + +# +# Runtime dependencies used by docker-entrypoint.sh. +# +RUN set -eux; \ + apt-get update; \ + apt-get install -y --no-install-recommends \ + gosu \ + ; \ + rm -rf /var/lib/apt/lists/* + +# +# PHP extensions required by PostfixAdmin. +# +# install-php-extensions automatically installs and removes the necessary +# system build dependencies. +# +RUN install-php-extensions \ + pdo_mysql \ + pdo_pgsql \ + pdo_sqlite \ + pgsql \ + gd \ + ; + +RUN printf '%s\n' \ + 'output_buffering = 4096' \ + 'session.use_strict_mode = 1' \ + 'expose_php = Off' \ + > /usr/local/etc/php/conf.d/postfixadmin.ini + +# +# Download PostfixAdmin. +# +RUN set -eux; \ + curl -fsSL \ + -o /tmp/postfixadmin.tar.gz \ + "https://github.com/postfixadmin/postfixadmin/archive/v${POSTFIXADMIN_VERSION}.tar.gz"; \ + \ + echo "${POSTFIXADMIN_SHA512} */tmp/postfixadmin.tar.gz" \ + | sha512sum -c -; \ + \ + mkdir -p /var/www/html; \ + tar \ + -xf /tmp/postfixadmin.tar.gz \ + -C /var/www/html \ + --strip-components=1; \ + rm /tmp/postfixadmin.tar.gz; \ + \ + mkdir -p /var/www/html/templates_c; \ + chown -R www-data:www-data /var/www/html + +# +# Install Composer dependencies. +# +COPY --from=composer:2 /usr/bin/composer /usr/local/bin/composer + +RUN set -eux; \ + apt-get update; \ + apt-get install -y --no-install-recommends \ + unzip \ + ; \ + \ + export COMPOSER_HOME="$(mktemp -d)"; \ + composer install \ + --ignore-platform-req=ext-mysqli \ + --no-dev \ + --no-interaction \ + --no-progress \ + --prefer-dist \ + --working-dir=/var/www/html; \ + rm -rf "${COMPOSER_HOME}"; \ + \ + apt-get purge -y --auto-remove \ + -o APT::AutoRemove::RecommendsImportant=false \ + unzip; \ + rm -rf /var/lib/apt/lists/* + +COPY docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh + +RUN chmod +x /usr/local/bin/docker-entrypoint.sh + +EXPOSE 80 + +ENTRYPOINT ["/usr/local/bin/docker-entrypoint.sh"] + +CMD ["frankenphp", "run", "--config", "/etc/caddy/Caddyfile"] diff --git a/frankenphp/docker-entrypoint.sh b/frankenphp/docker-entrypoint.sh new file mode 100755 index 0000000..ece00cb --- /dev/null +++ b/frankenphp/docker-entrypoint.sh @@ -0,0 +1,203 @@ +#!/usr/bin/env bash +set -eo pipefail + +# usage: get_env_value VAR [DEFAULT] +# ie: get_env_value 'XYZ_DB_PASSWORD' 'example' +# (will allow for "$XYZ_DB_PASSWORD_FILE" to fill in the value of +# "$XYZ_DB_PASSWORD" from a file, especially for Docker's secrets feature) +function get_env_value() { + local varName="${1}" + local fileVarName="${varName}_FILE" + local defaultValue="${2:-}" + + if [ "${!varName:-}" ] && [ "${!fileVarName:-}" ]; then + echo >&2 "error: both ${varName} and ${fileVarName} are set (but are exclusive)" + exit 1 + fi + + local value="${defaultValue}" + if [ "${!varName:-}" ]; then + value="${!varName}" + elif [ "${!fileVarName:-}" ]; then + value="$(< "${!fileVarName}")" + fi + + echo "${value}" + exit 0 +} + +function get_database_env_value() { + local suffix="$1" + local default_value="${2-}" + + local primary_name="POSTFIXADMIN_DB_${suffix}" + local fallback_name="POSTFIXADMIN_DATABASE_${suffix}" + + local primary_file_name="${primary_name}_FILE" + local fallback_file_name="${fallback_name}_FILE" + + if [[ -n "${!primary_name:-}" && -n "${!fallback_name:-}" ]]; then + echo >&2 "Error : both ${primary_name} and ${fallback_name} are set (but are exclusive)" + exit 1 + fi + + if [[ -n "${!primary_file_name:-}" && -n "${!fallback_file_name:-}" ]]; then + echo >&2 "Error : both ${primary_file_name} and ${fallback_file_name} are set (but are exclusive)" + exit 1 + fi + + # Priority to POSTFIXADMIN_DB_* + if [[ -n "${!primary_name:-}" || -n "${!primary_file_name:-}" ]]; then + get_env_value "${primary_name}" "${default_value}" + else + get_env_value "${fallback_name}" "${default_value}" + fi +} + +function php_escape() { + local value="$1" + + # value=${value//\\/\\\\} + # value=${value//\'/\\\'} + + printf '%s' "${value}" +} + +function config_name_from_env() { + local env_name="$1" + + case "${env_name}" in + POSTFIXADMIN_DB_TYPE) + printf '%s' 'database_type' + ;; + POSTFIXADMIN_DB_HOST) + printf '%s' 'database_host' + ;; + POSTFIXADMIN_DB_PORT) + printf '%s' 'database_port' + ;; + POSTFIXADMIN_DB_USER) + printf '%s' 'database_user' + ;; + POSTFIXADMIN_DB_PASSWORD) + printf '%s' 'database_password' + ;; + POSTFIXADMIN_DB_NAME) + printf '%s' 'database_name' + ;; + *) + local config_name="${env_name#POSTFIXADMIN_}" + printf '%s' "${config_name,,}" + ;; + esac +} + +# Init vars for running script +export POSTFIXADMIN_DB_TYPE=$(get_database_env_value 'TYPE' 'sqlite') +export POSTFIXADMIN_DB_HOST=$(get_database_env_value "HOST" "") +export POSTFIXADMIN_DB_NAME=$(get_database_env_value "NAME" "") +export POSTFIXADMIN_DB_PORT=$(get_database_env_value "PORT" "") +export POSTFIXADMIN_DB_USER=$(get_database_env_value "USER" "") +export POSTFIXADMIN_DB_PASSWORD=$(get_database_env_value "PASSWORD" "") + +DEFAULT_SETUP_PASSWORD="changeme" +export POSTFIXADMIN_SETUP_PASSWORD=$(get_env_value "POSTFIXADMIN_SETUP_PASSWORD" "${DEFAULT_SETUP_PASSWORD}") + +if [[ "$1" == apache2* ]] || [ "$1" == php-fpm ] || [ "$1" == frankenphp ]; then + + if [ "${POSTFIXADMIN_SETUP_PASSWORD}" = "${DEFAULT_SETUP_PASSWORD}" ]; then + echo >&2 "WARNING: setup.php password not set" + fi + + if ! [ -e index.php ] && ! [ -e scripts/postfixadmin-cli.php ]; then + echo >&2 "Postfixadmin not found in $PWD - copying now..." + if [ "$(ls -A)" ]; then + echo >&2 "WARNING: $PWD is not empty - press Ctrl+C now if this is an error!" + ( set -x; ls -A; sleep 10 ) + fi + tar cf - --one-file-system -C /usr/src/postfixadmin . | tar xf - + echo >&2 "Complete! Postfixadmin has been successfully copied to $PWD" + fi + + case "${POSTFIXADMIN_DB_TYPE}" in + sqlite) + ;; + mysqli) + : "${POSTFIXADMIN_DB_PORT:=3306}" + ;; + pgsql) + : "${POSTFIXADMIN_DB_PORT:=5432}" + ;; + *) + echo >&2 "${POSTFIXADMIN_DB_TYPE} is not a supported value." + exit 1 + ;; + esac + + if [ "${POSTFIXADMIN_DB_TYPE}" = 'sqlite' ]; then + : "${POSTFIXADMIN_DB_NAME:=/var/tmp/postfixadmin.db}" + + if [ ! -f "${POSTFIXADMIN_DB_NAME}" ]; then + echo "Creating sqlite db" + touch $POSTFIXADMIN_DB_NAME + chown www-data:www-data $POSTFIXADMIN_DB_NAME + chmod 0700 $POSTFIXADMIN_DB_NAME + fi + else + if [ -z "${POSTFIXADMIN_DB_USER}" ] || [ -z "${POSTFIXADMIN_DB_PASSWORD}" ]; then + echo >&2 'Error: POSTFIXADMIN_DB_USER and POSTFIXADMIN_DB_PASSWORD must be specified. ' + exit 1 + fi + timeout 15 bash -c "until echo > /dev/tcp/${POSTFIXADMIN_DB_HOST}/${POSTFIXADMIN_DB_PORT}; do sleep 0.5; done" + fi + + if [ ! -e config.local.php ]; then + touch config.local.php + echo "Write config to $PWD/config.local.php" + { + printf '%s\n' '"$PWD/config.local.php" + cat "$PWD/config.local.php" + else + echo "WARNING: $PWD/config.local.php already exists." + echo "Postfixadmin related environment variables have been ignored." + fi + + if [ -f public/upgrade.php ]; then + echo " ** Running database / environment upgrade.php " + gosu www-data php public/upgrade.php + fi +fi + +exec "$@" diff --git a/update.sh b/update.sh index d019970..48159e7 100755 --- a/update.sh +++ b/update.sh @@ -1,25 +1,34 @@ #!/bin/bash set -euo pipefail +PHPVERSION="8.3" +POSTFIXADMIN_VERSION=4.0.5 +POSTFIXADMIN_SHA512=b9549137e5bb6cf69fe76aef2711092a7e74ec223ee272d0303430b03da16ba8cfe704047f8e07ee5bd1ef6b632cac9ae2c0830271629b193c395197e1f4d93d + declare -A cmd=( [apache]='apache2-foreground' [fpm]='php-fpm' [fpm-alpine]='php-fpm' + [frankenphp]='frankenphp", "run", "--config", "/etc/caddy/Caddyfile' ) declare -A base=( [apache]='debian' [fpm]='debian' [fpm-alpine]='alpine' + [frankenphp]='frankenphp' ) -for variant in apache fpm fpm-alpine; do +for variant in apache fpm fpm-alpine frankenphp; do dir="$variant" template="Dockerfile-${base[$variant]}.template" mkdir -p "$dir" cp -a "docker-entrypoint.sh" "$dir/docker-entrypoint.sh" sed -r \ -e 's!%%VARIANT%%!'"$variant"'!g' \ + -e 's!%%PHPVERSION%%!'"${PHPVERSION}"'!g' \ + -e 's!%%POSTFIXADMIN_VERSION%%!'"${POSTFIXADMIN_VERSION}"'!g' \ + -e 's!%%POSTFIXADMIN_SHA512%%!'"${POSTFIXADMIN_SHA512}"'!g' \ -e 's!%%CMD%%!'"${cmd[$variant]}"'!g' \ "Dockerfile-${base[$variant]}.template" > "$dir/Dockerfile" if [ $variant != "apache" ]; then