diff --git a/2024.06-rc/apache/Dockerfile b/2024.06-rc/apache/Dockerfile new file mode 100644 index 0000000..a1535d9 --- /dev/null +++ b/2024.06-rc/apache/Dockerfile @@ -0,0 +1,184 @@ +# DO NOT EDIT: created by update.sh from Dockerfile-debian.template +FROM php:8.1-apache-bullseye + +# entrypoint.sh and cron.sh dependencies +RUN set -ex; \ + \ + apt-get update; \ + apt-get install -y --no-install-recommends \ + rsync \ + bzip2 \ +# For mail() support + msmtp \ + tini \ + ; + +ENV GOSU_VERSION 1.14 +RUN set -eux; \ +# save list of currently installed packages for later so we can clean up + savedAptMark="$(apt-mark showmanual)"; \ + apt-get update; \ + apt-get install -y --no-install-recommends ca-certificates wget; \ + if ! command -v gpg; then \ + apt-get install -y --no-install-recommends gnupg2 dirmngr; \ + elif gpg --version | grep -q '^gpg (GnuPG) 1\.'; then \ +# "This package provides support for HKPS keyservers." (GnuPG 1.x only) + apt-get install -y --no-install-recommends gnupg-curl; \ + fi; \ + rm -rf /var/lib/apt/lists/*; \ + \ + dpkgArch="$(dpkg --print-architecture | awk -F- '{ print $NF }')"; \ + wget -O /usr/local/bin/gosu "https://github.com/tianon/gosu/releases/download/$GOSU_VERSION/gosu-$dpkgArch"; \ + wget -O /usr/local/bin/gosu.asc "https://github.com/tianon/gosu/releases/download/$GOSU_VERSION/gosu-$dpkgArch.asc"; \ + \ +# verify the signature + export GNUPGHOME="$(mktemp -d)"; \ + gpg --batch --keyserver hkps://keys.openpgp.org --recv-keys B42F6819007F00F88E364FD4036A9C25BF357DD4; \ + gpg --batch --verify /usr/local/bin/gosu.asc /usr/local/bin/gosu; \ + command -v gpgconf && gpgconf --kill all || :; \ + rm -rf "$GNUPGHOME" /usr/local/bin/gosu.asc; \ + \ +# clean up fetch dependencies + apt-mark auto '.*' > /dev/null; \ + [ -z "$savedAptMark" ] || apt-mark manual $savedAptMark; \ + apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false; \ + \ + chmod +x /usr/local/bin/gosu; \ +# verify that the binary works + gosu --version; \ + gosu nobody true + +# install the PHP extensions we need +# see https://friendi.ca/resources/requirements/ +RUN set -ex; \ + \ + savedAptMark="$(apt-mark showmanual)"; \ + \ + apt-get update; \ + apt-get install -y --no-install-recommends \ + mariadb-client \ + bash \ + libpng-dev \ + libjpeg62-turbo-dev \ + libtool \ + libmagick++-dev \ + libmemcached-dev \ + libgraphicsmagick1-dev \ + libfreetype6-dev \ + libwebp-dev \ + librsvg2-2 \ + libzip-dev \ + libldap2-dev \ + libgmp-dev \ + libmagickcore-6.q16-6-extra \ + ; \ + \ + debMultiarch="$(dpkg-architecture --query DEB_BUILD_MULTIARCH)"; \ + \ + docker-php-ext-configure gd \ + --with-freetype \ + --with-jpeg \ + --with-webp \ + ; \ + docker-php-ext-configure ldap \ + --with-libdir=lib/$debMultiarch/ \ + ;\ + docker-php-ext-install -j "$(nproc)" \ + pdo_mysql \ + gd \ + exif \ + zip \ + opcache \ + ctype \ + pcntl \ + ldap \ + gmp \ + intl \ + ; \ + \ +# pecl will claim success even if one install fails, so we need to perform each install separately + pecl install apcu-5.1.23; \ + pecl install memcached-3.2.0RC2; \ + pecl install redis-6.0.2; \ + pecl install imagick-3.7.0; \ + \ + docker-php-ext-enable \ + apcu \ + memcached \ + redis \ + imagick \ + ; \ + \ +# reset apt-mark's "manual" list so that "purge --auto-remove" will remove all build dependencies + apt-mark auto '.*' > /dev/null; \ + apt-mark manual $savedAptMark; \ + ldd "$(php -r 'echo ini_get("extension_dir");')"/*.so \ + | awk '/=>/ { print $3 }' \ + | sort -u \ + | xargs -r dpkg-query -S \ + | cut -d: -f1 \ + | sort -u \ + | xargs -rt apt-mark manual; \ + \ + apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false; \ + rm -rf /var/lib/apt/lists/* + +# set recommended PHP.ini settings +ENV PHP_MEMORY_LIMIT 512M +ENV PHP_UPLOAD_LIMIT 512M +RUN set -ex; \ + { \ + echo 'opcache.enable=1' ; \ + echo 'opcache.interned_strings_buffer=8'; \ + echo 'opcache.max_accelerated_files=10000'; \ + echo 'opcache.memory_consumption=128'; \ + echo 'opcache.save_comments=1'; \ + echo 'opcache.revalidte_freq=1'; \ + } > /usr/local/etc/php/conf.d/opcache-recommended.ini; \ + \ + { \ + echo sendmail_path = "/usr/bin/msmtp -t"; \ + } > /usr/local/etc/php/conf.d/sendmail.ini; \ + \ + echo 'apc.enable_cli=1' >> /usr/local/etc/php/conf.d/docker-php-ext-apcu.ini; \ + \ + { \ + echo 'memory_limit=${PHP_MEMORY_LIMIT}'; \ + echo 'upload_max_filesize=${PHP_UPLOAD_LIMIT}'; \ + echo 'post_max_size=${PHP_UPLOAD_LIMIT}'; \ + } > /usr/local/etc/php/conf.d/friendica.ini; \ + ln -s /usr/local/etc/php/php.ini-production /usr/local/etc/php/php.ini; \ + \ + mkdir /var/www/data; \ + chown -R www-data:root /var/www; \ + chmod -R g=u /var/www + +VOLUME /var/www/html + +RUN set -ex;\ + a2enmod rewrite remoteip ;\ + {\ + echo RemoteIPHeader X-Real-IP ;\ + echo RemoteIPTrustedProxy 10.0.0.0/8 ;\ + echo RemoteIPTrustedProxy 172.16.0.0/12 ;\ + echo RemoteIPTrustedProxy 192.168.0.0/16 ;\ + } > /etc/apache2/conf-available/remoteip.conf;\ + a2enconf remoteip + +# 39 = LOG_PID | LOG_ODELAY | LOG_CONS | LOG_PERROR +ENV FRIENDICA_SYSLOG_FLAGS 39 +ENV FRIENDICA_VERSION "2024.06-rc" +ENV FRIENDICA_ADDONS "2024.06-rc" + +RUN set -ex; \ + fetchDeps=" \ + gnupg \ + "; \ + apt-get update; \ + apt-get install -y --no-install-recommends $fetchDeps; + +COPY *.sh upgrade.exclude / +COPY config/* /usr/src/friendica/config/ + +ENTRYPOINT ["/entrypoint-dev.sh"] +CMD ["apache2-foreground"] diff --git a/2024.06-rc/apache/config/00apcu.config.php b/2024.06-rc/apache/config/00apcu.config.php new file mode 100644 index 0000000..2e5ebcf --- /dev/null +++ b/2024.06-rc/apache/config/00apcu.config.php @@ -0,0 +1,11 @@ + [ + 'cache_driver' => 'apcu', + ], +]; diff --git a/2024.06-rc/apache/config/01redis.config.php b/2024.06-rc/apache/config/01redis.config.php new file mode 100644 index 0000000..2ea29bd --- /dev/null +++ b/2024.06-rc/apache/config/01redis.config.php @@ -0,0 +1,17 @@ + [ + 'session_handler' => 'cache', + 'distributed_cache_driver' => 'redis', + 'lock_driver' => 'redis', + 'redis_host' => getenv('REDIS_HOST'), + 'redis_port' => (getenv('REDIS_PORT') ?: ''), + 'redis_password' => (getenv('REDIS_PW') ?: ''), + 'redis_db' => (getenv('REDIS_DB') ?: 0), + ], + ]; +} else { + return []; +} diff --git a/2024.06-rc/apache/config/zz-docker.config.php b/2024.06-rc/apache/config/zz-docker.config.php new file mode 100644 index 0000000..946fe81 --- /dev/null +++ b/2024.06-rc/apache/config/zz-docker.config.php @@ -0,0 +1,34 @@ + [ + // Necessary because otherwise the daemon isn't working + 'pidfile' => '/var/run/friendica.pid', + + 'logfile' => '/var/www/html/friendica.log', + 'loglevel' => 'notice', + ], + 'storage' => [ + 'filesystem_path' => '/var/www/html/storage', + ], +]; + +if (!empty(getenv('FRIENDICA_NO_VALIDATION'))) { + $config['system']['disable_url_validation'] = true; + $config['system']['disable_email_validation'] = true; +} + +if (!empty(getenv('SMTP_DOMAIN'))) { + $smtp_from = !empty(getenv('SMTP_FROM')) ? getenv('SMTP_FROM') : 'no-reply'; + + $config['config']['sender_email'] = $smtp_from . "@" . getenv('SMTP_DOMAIN'); +} + +return $config; diff --git a/2024.06-rc/apache/cron.sh b/2024.06-rc/apache/cron.sh new file mode 100755 index 0000000..18dced0 --- /dev/null +++ b/2024.06-rc/apache/cron.sh @@ -0,0 +1,14 @@ +#!/bin/sh +trap "break;exit" HUP INT TERM + +while [ ! -f /var/www/html/bin/daemon.php ]; do + sleep 1 +done + +echo "Waiting for MySQL $MYSQL_HOST initialization..." +if php /var/www/html/bin/wait-for-connection "$MYSQL_HOST" "${MYSQL_PORT:-3306}" 300; then + sh /setup_msmtp.sh + exec gosu www-data:www-data tini -- php /var/www/html/bin/daemon.php -f start +else + echo "[ERROR] Waited 300 seconds, no response" >&2 +fi diff --git a/2024.06-rc/apache/entrypoint-dev.sh b/2024.06-rc/apache/entrypoint-dev.sh new file mode 100755 index 0000000..8b34c21 --- /dev/null +++ b/2024.06-rc/apache/entrypoint-dev.sh @@ -0,0 +1,54 @@ +#!/bin/sh +set -eu + +# just check if we execute apache or php-fpm +if (expr "$1" : "apache" 1>/dev/null || [ "$1" = "php-fpm" ]) && [ "${FRIENDICA_UPGRADE:-false}" = "true" ]; then + curl -fsSL -o "/usr/src/friendica-full-${FRIENDICA_VERSION}.tar.gz.sum256" "https://files.friendi.ca/friendica-full-${FRIENDICA_VERSION}.tar.gz.sum256" + curl -fsSL -o "/usr/src/friendica-addons-${FRIENDICA_ADDONS}.tar.gz.sum256" "https://files.friendi.ca/friendica-full-${FRIENDICA_ADDONS}.tar.gz.sum256" + + # Don't download already latest sources + if [ -f "/usr/src/friendica.tar.gz.sum256" ] && [ -f "/usr/src/friendica-addons.tar.gz.sum256" ] && \ + cmp -s "/usr/src/friendica-full-${FRIENDICA_VERSION}.tar.gz.sum256" "/usr/src/friendica.tar.gz.sum256" && \ + cmp -s "/usr/src/friendica-addons-${FRIENDICA_ADDONS}.tar.gz.sum256" "/usr/src/friendica-addons.tar.gz.sum256"; then + echo "Already latest sources - skipped download" + else + + echo "Download sources for ${FRIENDICA_VERSION} (Addon: ${FRIENDICA_ADDONS})" + + # Removing the whole directory first + rm -fr /usr/src/friendica + export GNUPGHOME="$(mktemp -d)" + + gpg --batch --logger-fd=1 --no-tty --quiet --keyserver keyserver.ubuntu.com --recv-keys 08656443618E6567A39524083EE197EF3F9E4287 + + curl -fsSL -o friendica-full-${FRIENDICA_VERSION}.tar.gz "https://files.friendi.ca/friendica-full-${FRIENDICA_VERSION}.tar.gz" + curl -fsSL -o friendica-full-${FRIENDICA_VERSION}.tar.gz.asc "https://files.friendi.ca/friendica-full-${FRIENDICA_VERSION}.tar.gz.asc"; + gpg --batch --logger-fd=1 --no-tty --quiet --verify friendica-full-${FRIENDICA_VERSION}.tar.gz.asc friendica-full-${FRIENDICA_VERSION}.tar.gz + echo "Core sources (${FRIENDICA_VERSION}) verified" + + tar -xzf friendica-full-${FRIENDICA_VERSION}.tar.gz -C /usr/src/ + rm friendica-full-${FRIENDICA_VERSION}.tar.gz friendica-full-${FRIENDICA_VERSION}.tar.gz.asc + mv -f /usr/src/friendica-full-${FRIENDICA_VERSION}/ /usr/src/friendica + echo "Core sources (${FRIENDICA_VERSION}) extracted" + + chmod 777 /usr/src/friendica/view/smarty3 + + curl -fsSL -o friendica-addons-${FRIENDICA_ADDONS}.tar.gz "https://files.friendi.ca/friendica-addons-${FRIENDICA_ADDONS}.tar.gz" + curl -fsSL -o friendica-addons-${FRIENDICA_ADDONS}.tar.gz.asc "https://files.friendi.ca/friendica-addons-${FRIENDICA_ADDONS}.tar.gz.asc" + gpg --batch --logger-fd=1 --no-tty --quiet --verify friendica-addons-${FRIENDICA_ADDONS}.tar.gz.asc friendica-addons-${FRIENDICA_ADDONS}.tar.gz + echo "Addon sources (${FRIENDICA_ADDONS}) verified" + + mkdir -p /usr/src/friendica/addon + tar -xzf friendica-addons-${FRIENDICA_ADDONS}.tar.gz -C /usr/src/friendica/addon --strip-components=1 + rm friendica-addons-${FRIENDICA_ADDONS}.tar.gz friendica-addons-${FRIENDICA_ADDONS}.tar.gz.asc + echo "Addon sources (${FRIENDICA_ADDONS}) extracted" + + gpgconf --kill all + rm -rf "$GNUPGHOME" + + mv -f /usr/src/friendica-full-${FRIENDICA_VERSION}.tar.gz.sum256 /usr/src/friendica.tar.gz.sum256 + mv -f /usr/src/friendica-addons-${FRIENDICA_ADDONS}.tar.gz.sum256 /usr/src/friendica-addons.tar.gz.sum256 + fi +fi + +exec /entrypoint.sh "$@" diff --git a/2024.06-rc/apache/entrypoint.sh b/2024.06-rc/apache/entrypoint.sh new file mode 100755 index 0000000..b080cef --- /dev/null +++ b/2024.06-rc/apache/entrypoint.sh @@ -0,0 +1,185 @@ +#!/bin/sh +set -eu + +# run an command with the www-data user +run_as() { + set -- sh -c "cd /var/www/html; $*" + if [ "$(id -u)" -eq 0 ]; then + set -- gosu www-data "$@" + fi + "$@" +} + +# checks if the the first parameter is greater than the second parameter +version_greater() { + [ "$(printf '%s\n' "$@" | sed -e 's/-rc/.1/' | sed -e 's/-dev/.2/' | sort -t '.' -k1,1n -k2,2n -k3,3nbr | head -n 1)" != "$(printf "$1" | sed -e 's/-rc/.1/' | sed -e 's/-dev/.2/')" ] +} + +# usage: file_env VAR [DEFAULT] +# ie: file_env '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) +file_env() { + var="$1" + fileVar="${var}_FILE" + def="${2:-}" + varValue=$(env | grep -E "^${var}=" | sed -E -e "s/^${var}=//") + fileVarValue=$(env | grep -E "^${fileVar}=" | sed -E -e "s/^${fileVar}=//") + if [ -n "${varValue}" ] && [ -n "${fileVarValue}" ]; then + echo >&2 "error: both $var and $fileVar are set (but are exclusive)" + exit 1 + fi + if [ -n "${varValue}" ]; then + export "$var"="${varValue}" + elif [ -n "${fileVarValue}" ]; then + export "$var"="$(cat "${fileVarValue}")" + elif [ -n "${def}" ]; then + export "$var"="$def" + fi + unset "$fileVar" +} + +sh /setup_msmtp.sh + +# just check if we execute apache or php-fpm +if expr "$1" : "apache" 1>/dev/null || [ "$1" = "php-fpm" ]; then + if [ -n "${REDIS_HOST+x}" ]; then + echo "Configuring Redis as session handler" + { + file_env REDIS_PW + echo 'session.save_handler = redis' + # check if redis host is an unix socket path + if expr "${REDIS_HOST}" : "/" 1>/dev/null; then + if [ -n "${REDIS_PW+x}" ]; then + echo "session.save_path = \"unix://${REDIS_HOST}?auth=${REDIS_PW}\"" + else + echo "session.save_path = \"unix://${REDIS_HOST}\"" + fi + # check if redis password has been set + elif [ -n "${REDIS_PW+x}" ]; then + echo "session.save_path = \"tcp://${REDIS_HOST}:${REDIS_PORT:=6379}?auth=${REDIS_PW}\"" + else + echo "session.save_path = \"tcp://${REDIS_HOST}:${REDIS_PORT:=6379}\"" + fi + echo "redis.session.locking_enabled = 1" + echo "redis.session.lock_retries = -1" + # redis.session.lock_wait_time is specified in microseconds. + # Wait 10ms before retrying the lock rather than the default 2ms. + echo "redis.session.lock_wait_time = 10000" + } > /usr/local/etc/php/conf.d/redis-session.ini + fi + + # If another process is syncing the html folder, wait for + # it to be done, then escape initialization. + ( + if ! flock -n 9; then + # If we couldn't get it immediately, show a message, then wait for real + echo "Another process is initializing Friendica. Waiting..." + flock 9 + fi + + installed_version="0.0.0.0" + if [ -f /var/www/html/VERSION ]; then + installed_version="$(cat /var/www/html/VERSION)" + fi + + image_version="0.0.0.0" + if [ -f /usr/src/friendica/VERSION ]; then + image_version="$(cat /usr/src/friendica/VERSION)" + else + echo "No new Friendica sources found (enable FRIENDICA_UPGRADE for new sources)" + fi + + # no downgrading possible + if version_greater "$installed_version" "$image_version"; then + echo "Can't copy Friendica sources because the version of the data ($installed_version) is higher than the docker image ($image_version)" + exit 1 + fi + + # check it just in case the version is greater or if we force the upgrade + if version_greater "$image_version" "$installed_version" || [ "${FRIENDICA_UPGRADE:-false}" = "true" ]; then + echo "Initializing Friendica $image_version ..." + + if [ "$installed_version" != "0.0.0.0" ]; then + echo "Upgrading Friendica from $installed_version ..." + fi + + if [ "$(id -u)" -eq 0 ]; then + rsync_options="-rlDog --chown=www-data:www-data" + else + rsync_options="-rlD" + fi + + rsync $rsync_options --delete --exclude-from=/upgrade.exclude /usr/src/friendica/ /var/www/html/ + + # Update docker-based config files, but never delete other config files + rsync $rsync_options --update --exclude=/addon.config.php --exclude=/local.config.php /usr/src/friendica/config/ /var/www/html/config/ + + # In case there is no .htaccess, copy it from the default dist file + if [ ! -f "/var/www/html/.htaccess" ]; then + cp "/var/www/html/.htaccess-dist" "/var/www/html/.htaccess" + fi + + if [ -d /var/www/html/view/smarty3 ]; then + chmod -R 777 /var/www/html/view/smarty3 + fi + echo "Initializing finished" + + # install + if [ "$installed_version" = "0.0.0.0" ]; then + echo "New Friendica instance" + + file_env FRIENDICA_ADMIN_MAIL + + file_env MYSQL_DATABASE + file_env MYSQL_USER + file_env MYSQL_PASSWORD + + install=false + if [ -n "${MYSQL_DATABASE+x}" ] && [ -n "${MYSQL_PASSWORD+x}" ] && [ -n "${MYSQL_HOST+x}" ] && [ -n "${MYSQL_USER+x}" ] && [ -n "${FRIENDICA_ADMIN_MAIL+x}" ] && [ -n "${FRIENDICA_URL+x}" ]; then + echo "Installation with environment variables" + + FRIENDICA_TZ=${FRIENDICA_TZ:-America/New_York} + FRIENDICA_LANG=${FRIENDICA_LANG:-en} + MYSQL_PORT=${MYSQL_PORT:-3306} + + # shellcheck disable=SC2016 + install_options='-s --dbhost "'$MYSQL_HOST'" --dbport "'$MYSQL_PORT'" --dbdata "'$MYSQL_DATABASE'" --dbuser "'$MYSQL_USER'" --dbpass "'$MYSQL_PASSWORD'"' + + # shellcheck disable=SC2016 + install_options=$install_options' --admin "'$FRIENDICA_ADMIN_MAIL'" --tz "'$FRIENDICA_TZ'" --lang "'$FRIENDICA_LANG'" --url "'$FRIENDICA_URL'"' + install=true + fi + + if [ "$install" = true ]; then + echo "Waiting for MySQL $MYSQL_HOST initialization..." + if run_as "php /var/www/html/bin/wait-for-connection $MYSQL_HOST ${MYSQL_PORT:-3306} 300"; then + + echo "Starting Friendica installation ..." + run_as "php /var/www/html/bin/console.php autoinstall $install_options" + + rm -fr /var/www/html/view/smarty3/compiled + + # load other config files (*.config.php) to the config folder + if [ -d "/usr/src/config" ]; then + rsync $rsync_options --ignore-existing /usr/src/friendica/config/ /var/www/html/config/ + fi + + echo "Installation finished" + else + echo "[ERROR] Waited 300 seconds, no response" >&2 + fi + else + echo "Running web-based installer on first connect!" + fi + # upgrade + else + echo "Upgrading Friendica ..." + run_as 'php /var/www/html/bin/console.php dbstructure update -f' + echo "Upgrading finished" + fi + fi + ) 9> /var/www/html/friendica-init-sync.lock +fi + +exec "$@" diff --git a/2024.06-rc/apache/setup_msmtp.sh b/2024.06-rc/apache/setup_msmtp.sh new file mode 100644 index 0000000..c902b6d --- /dev/null +++ b/2024.06-rc/apache/setup_msmtp.sh @@ -0,0 +1,40 @@ +#!/bin/sh +set -eu + +if [ -n "${SMTP_DOMAIN+x}" ] && [ -n "${SMTP+x}" ] && [ "${SMTP}" != "localhost" ]; then + SITENAME="${FRIENDICA_SITENAME:-Friendica Social Network}" + echo "Setup MSMTP for '$SITENAME' with '$SMTP' ..." + + smtp_from="${SMTP_FROM:=no-reply}" + smtp_auth="${SMTP_AUTH:=on}" + # https://github.com/friendica/docker/issues/233 + smtp_starttls="${SMTP_STARTTLS:=on}" + + # Setup MSMTP + usermod --comment "$(echo "$SITENAME" | tr -dc '[:print:]')" root + usermod --comment "$(echo "$SITENAME" | tr -dc '[:print:]')" www-data + + # add possible mail-senders + { + echo "www-data: $smtp_from@$SMTP_DOMAIN" + echo "root: $smtp_from@$SMTP_DOMAIN" + } >/etc/aliases + + # create msmtp settings + { + echo "account default" + echo "host $SMTP" + if [ -n "${SMTP_PORT+x}" ]; then echo "port $SMTP_PORT"; else echo "port 587"; fi + echo "from \"$smtp_from@$SMTP_DOMAIN\"" + echo "tls_certcheck off" # No certcheck because of internal docker mail-hostnames + if [ -n "${SMTP_TLS+x}" ]; then echo "tls on"; fi + echo "tls_starttls $smtp_starttls"; + if [ -n "${SMTP_AUTH_USER+x}" ]; then echo "auth $smtp_auth"; fi + if [ -n "${SMTP_AUTH_USER+x}" ]; then echo "user \"$SMTP_AUTH_USER\""; fi + if [ -n "${SMTP_AUTH_PASS+x}" ]; then echo "password \"$SMTP_AUTH_PASS\""; fi + echo "logfile -" + echo "aliases /etc/aliases" + } >/etc/msmtprc + + echo "Setup finished" +fi diff --git a/2024.06-rc/apache/upgrade.exclude b/2024.06-rc/apache/upgrade.exclude new file mode 100644 index 0000000..4f94596 --- /dev/null +++ b/2024.06-rc/apache/upgrade.exclude @@ -0,0 +1,10 @@ +/photo/ +/proxy/ +/.htconfig.php +/.htaccess +/home.* +/config/ +/storage/ +/log/ +*.log +/friendica-init-sync.lock diff --git a/2024.06-rc/fpm-alpine/Dockerfile b/2024.06-rc/fpm-alpine/Dockerfile new file mode 100644 index 0000000..993e3dd --- /dev/null +++ b/2024.06-rc/fpm-alpine/Dockerfile @@ -0,0 +1,154 @@ +# DO NOT EDIT: created by update.sh from Dockerfile-alpine.template +FROM php:8.1-fpm-alpine + +# entrypoint.sh and cron.sh dependencies +RUN set -ex; \ + apk add --no-cache \ + rsync \ + imagemagick \ +# For mail() support + msmtp \ + shadow \ + tini; + +ENV GOSU_VERSION 1.14 +RUN set -eux; \ + \ + apk add --no-cache --virtual .gosu-deps \ + ca-certificates \ + dpkg \ + gnupg \ + ; \ + \ + dpkgArch="$(dpkg --print-architecture | awk -F- '{ print $NF }')"; \ + wget -O /usr/local/bin/gosu "https://github.com/tianon/gosu/releases/download/$GOSU_VERSION/gosu-$dpkgArch"; \ + wget -O /usr/local/bin/gosu.asc "https://github.com/tianon/gosu/releases/download/$GOSU_VERSION/gosu-$dpkgArch.asc"; \ + \ +# verify the signature + export GNUPGHOME="$(mktemp -d)"; \ + gpg --batch --keyserver hkps://keys.openpgp.org --recv-keys B42F6819007F00F88E364FD4036A9C25BF357DD4; \ + gpg --batch --verify /usr/local/bin/gosu.asc /usr/local/bin/gosu; \ + command -v gpgconf && gpgconf --kill all || :; \ + rm -rf "$GNUPGHOME" /usr/local/bin/gosu.asc; \ + \ +# clean up fetch dependencies + apk del --no-network .gosu-deps; \ + \ + chmod +x /usr/local/bin/gosu; \ +# verify that the binary works + gosu --version; \ + gosu nobody true + +# install the PHP extensions we need +# see https://friendi.ca/resources/requirements/ +RUN set -ex; \ + \ + apk add --no-cache --virtual .build-deps \ + mariadb-client \ + bash \ + $PHPIZE_DEPS \ + libpng-dev \ + libjpeg-turbo-dev \ + imagemagick-dev \ + libtool \ + libmemcached-dev \ + cyrus-sasl-dev \ + libjpeg-turbo-dev \ + freetype-dev \ + libwebp-dev \ + librsvg \ + pcre-dev \ + libzip-dev \ + icu-dev \ + openldap-dev \ + gmp-dev \ + ; \ + \ + docker-php-ext-configure gd \ + --with-freetype \ + --with-jpeg \ + --with-webp \ + ; \ + \ + docker-php-ext-install -j "$(nproc)" \ + pdo_mysql \ + exif \ + gd \ + zip \ + opcache \ + pcntl \ + ldap \ + gmp \ + intl \ + ; \ + \ +# pecl will claim success even if one install fails, so we need to perform each install separately + pecl install APCu-5.1.23; \ + pecl install memcached-3.2.0RC2; \ + pecl install redis-6.0.2; \ + pecl install imagick-3.7.0; \ + \ + docker-php-ext-enable \ + apcu \ + memcached \ + redis \ + imagick \ + ; \ + \ + runDeps="$( \ + scanelf --needed --nobanner --format '%n#p' --recursive /usr/local/lib/php/extensions \ + | tr ',' '\n' \ + | sort -u \ + | awk 'system("[ -e /usr/local/lib" $1 " ]") == 0 { next } { print "so:" $1 }' \ + )"; \ + apk add --no-network --virtual .friendica-phpext-rundeps $runDeps; \ + apk del --no-network .build-deps; + +# set recommended PHP.ini settings +ENV PHP_MEMORY_LIMIT 512M +ENV PHP_UPLOAD_LIMIT 512M +RUN set -ex; \ + { \ + echo 'opcache.enable=1' ; \ + echo 'opcache.interned_strings_buffer=8'; \ + echo 'opcache.max_accelerated_files=10000'; \ + echo 'opcache.memory_consumption=128'; \ + echo 'opcache.save_comments=1'; \ + echo 'opcache.revalidte_freq=1'; \ + } > /usr/local/etc/php/conf.d/opcache-recommended.ini; \ + \ + { \ + echo sendmail_path = "/usr/bin/msmtp -t"; \ + } > /usr/local/etc/php/conf.d/sendmail.ini; \ + \ + echo 'apc.enable_cli=1' >> /usr/local/etc/php/conf.d/docker-php-ext-apcu.ini; \ + \ + { \ + echo 'memory_limit=${PHP_MEMORY_LIMIT}'; \ + echo 'upload_max_filesize=${PHP_UPLOAD_LIMIT}'; \ + echo 'post_max_size=${PHP_UPLOAD_LIMIT}'; \ + } > /usr/local/etc/php/conf.d/friendica.ini; \ + ln -s /usr/local/etc/php/php.ini-production /usr/local/etc/php/php.ini; \ + \ + mkdir /var/www/data; \ + chown -R www-data:root /var/www; \ + chmod -R g=u /var/www + +VOLUME /var/www/html + + +# 39 = LOG_PID | LOG_ODELAY | LOG_CONS | LOG_PERROR +ENV FRIENDICA_SYSLOG_FLAGS 39 +ENV FRIENDICA_VERSION "2024.06-rc" +ENV FRIENDICA_ADDONS "2024.06-rc" + +RUN set -ex; \ + apk add --no-cache --virtual .fetch-deps \ + gnupg \ + ; + +COPY *.sh upgrade.exclude / +COPY config/* /usr/src/friendica/config/ + +ENTRYPOINT ["/entrypoint-dev.sh"] +CMD ["php-fpm"] diff --git a/2024.06-rc/fpm-alpine/config/00apcu.config.php b/2024.06-rc/fpm-alpine/config/00apcu.config.php new file mode 100644 index 0000000..2e5ebcf --- /dev/null +++ b/2024.06-rc/fpm-alpine/config/00apcu.config.php @@ -0,0 +1,11 @@ + [ + 'cache_driver' => 'apcu', + ], +]; diff --git a/2024.06-rc/fpm-alpine/config/01redis.config.php b/2024.06-rc/fpm-alpine/config/01redis.config.php new file mode 100644 index 0000000..2ea29bd --- /dev/null +++ b/2024.06-rc/fpm-alpine/config/01redis.config.php @@ -0,0 +1,17 @@ + [ + 'session_handler' => 'cache', + 'distributed_cache_driver' => 'redis', + 'lock_driver' => 'redis', + 'redis_host' => getenv('REDIS_HOST'), + 'redis_port' => (getenv('REDIS_PORT') ?: ''), + 'redis_password' => (getenv('REDIS_PW') ?: ''), + 'redis_db' => (getenv('REDIS_DB') ?: 0), + ], + ]; +} else { + return []; +} diff --git a/2024.06-rc/fpm-alpine/config/zz-docker.config.php b/2024.06-rc/fpm-alpine/config/zz-docker.config.php new file mode 100644 index 0000000..946fe81 --- /dev/null +++ b/2024.06-rc/fpm-alpine/config/zz-docker.config.php @@ -0,0 +1,34 @@ + [ + // Necessary because otherwise the daemon isn't working + 'pidfile' => '/var/run/friendica.pid', + + 'logfile' => '/var/www/html/friendica.log', + 'loglevel' => 'notice', + ], + 'storage' => [ + 'filesystem_path' => '/var/www/html/storage', + ], +]; + +if (!empty(getenv('FRIENDICA_NO_VALIDATION'))) { + $config['system']['disable_url_validation'] = true; + $config['system']['disable_email_validation'] = true; +} + +if (!empty(getenv('SMTP_DOMAIN'))) { + $smtp_from = !empty(getenv('SMTP_FROM')) ? getenv('SMTP_FROM') : 'no-reply'; + + $config['config']['sender_email'] = $smtp_from . "@" . getenv('SMTP_DOMAIN'); +} + +return $config; diff --git a/2024.06-rc/fpm-alpine/cron.sh b/2024.06-rc/fpm-alpine/cron.sh new file mode 100755 index 0000000..18dced0 --- /dev/null +++ b/2024.06-rc/fpm-alpine/cron.sh @@ -0,0 +1,14 @@ +#!/bin/sh +trap "break;exit" HUP INT TERM + +while [ ! -f /var/www/html/bin/daemon.php ]; do + sleep 1 +done + +echo "Waiting for MySQL $MYSQL_HOST initialization..." +if php /var/www/html/bin/wait-for-connection "$MYSQL_HOST" "${MYSQL_PORT:-3306}" 300; then + sh /setup_msmtp.sh + exec gosu www-data:www-data tini -- php /var/www/html/bin/daemon.php -f start +else + echo "[ERROR] Waited 300 seconds, no response" >&2 +fi diff --git a/2024.06-rc/fpm-alpine/entrypoint-dev.sh b/2024.06-rc/fpm-alpine/entrypoint-dev.sh new file mode 100755 index 0000000..8b34c21 --- /dev/null +++ b/2024.06-rc/fpm-alpine/entrypoint-dev.sh @@ -0,0 +1,54 @@ +#!/bin/sh +set -eu + +# just check if we execute apache or php-fpm +if (expr "$1" : "apache" 1>/dev/null || [ "$1" = "php-fpm" ]) && [ "${FRIENDICA_UPGRADE:-false}" = "true" ]; then + curl -fsSL -o "/usr/src/friendica-full-${FRIENDICA_VERSION}.tar.gz.sum256" "https://files.friendi.ca/friendica-full-${FRIENDICA_VERSION}.tar.gz.sum256" + curl -fsSL -o "/usr/src/friendica-addons-${FRIENDICA_ADDONS}.tar.gz.sum256" "https://files.friendi.ca/friendica-full-${FRIENDICA_ADDONS}.tar.gz.sum256" + + # Don't download already latest sources + if [ -f "/usr/src/friendica.tar.gz.sum256" ] && [ -f "/usr/src/friendica-addons.tar.gz.sum256" ] && \ + cmp -s "/usr/src/friendica-full-${FRIENDICA_VERSION}.tar.gz.sum256" "/usr/src/friendica.tar.gz.sum256" && \ + cmp -s "/usr/src/friendica-addons-${FRIENDICA_ADDONS}.tar.gz.sum256" "/usr/src/friendica-addons.tar.gz.sum256"; then + echo "Already latest sources - skipped download" + else + + echo "Download sources for ${FRIENDICA_VERSION} (Addon: ${FRIENDICA_ADDONS})" + + # Removing the whole directory first + rm -fr /usr/src/friendica + export GNUPGHOME="$(mktemp -d)" + + gpg --batch --logger-fd=1 --no-tty --quiet --keyserver keyserver.ubuntu.com --recv-keys 08656443618E6567A39524083EE197EF3F9E4287 + + curl -fsSL -o friendica-full-${FRIENDICA_VERSION}.tar.gz "https://files.friendi.ca/friendica-full-${FRIENDICA_VERSION}.tar.gz" + curl -fsSL -o friendica-full-${FRIENDICA_VERSION}.tar.gz.asc "https://files.friendi.ca/friendica-full-${FRIENDICA_VERSION}.tar.gz.asc"; + gpg --batch --logger-fd=1 --no-tty --quiet --verify friendica-full-${FRIENDICA_VERSION}.tar.gz.asc friendica-full-${FRIENDICA_VERSION}.tar.gz + echo "Core sources (${FRIENDICA_VERSION}) verified" + + tar -xzf friendica-full-${FRIENDICA_VERSION}.tar.gz -C /usr/src/ + rm friendica-full-${FRIENDICA_VERSION}.tar.gz friendica-full-${FRIENDICA_VERSION}.tar.gz.asc + mv -f /usr/src/friendica-full-${FRIENDICA_VERSION}/ /usr/src/friendica + echo "Core sources (${FRIENDICA_VERSION}) extracted" + + chmod 777 /usr/src/friendica/view/smarty3 + + curl -fsSL -o friendica-addons-${FRIENDICA_ADDONS}.tar.gz "https://files.friendi.ca/friendica-addons-${FRIENDICA_ADDONS}.tar.gz" + curl -fsSL -o friendica-addons-${FRIENDICA_ADDONS}.tar.gz.asc "https://files.friendi.ca/friendica-addons-${FRIENDICA_ADDONS}.tar.gz.asc" + gpg --batch --logger-fd=1 --no-tty --quiet --verify friendica-addons-${FRIENDICA_ADDONS}.tar.gz.asc friendica-addons-${FRIENDICA_ADDONS}.tar.gz + echo "Addon sources (${FRIENDICA_ADDONS}) verified" + + mkdir -p /usr/src/friendica/addon + tar -xzf friendica-addons-${FRIENDICA_ADDONS}.tar.gz -C /usr/src/friendica/addon --strip-components=1 + rm friendica-addons-${FRIENDICA_ADDONS}.tar.gz friendica-addons-${FRIENDICA_ADDONS}.tar.gz.asc + echo "Addon sources (${FRIENDICA_ADDONS}) extracted" + + gpgconf --kill all + rm -rf "$GNUPGHOME" + + mv -f /usr/src/friendica-full-${FRIENDICA_VERSION}.tar.gz.sum256 /usr/src/friendica.tar.gz.sum256 + mv -f /usr/src/friendica-addons-${FRIENDICA_ADDONS}.tar.gz.sum256 /usr/src/friendica-addons.tar.gz.sum256 + fi +fi + +exec /entrypoint.sh "$@" diff --git a/2024.06-rc/fpm-alpine/entrypoint.sh b/2024.06-rc/fpm-alpine/entrypoint.sh new file mode 100755 index 0000000..b080cef --- /dev/null +++ b/2024.06-rc/fpm-alpine/entrypoint.sh @@ -0,0 +1,185 @@ +#!/bin/sh +set -eu + +# run an command with the www-data user +run_as() { + set -- sh -c "cd /var/www/html; $*" + if [ "$(id -u)" -eq 0 ]; then + set -- gosu www-data "$@" + fi + "$@" +} + +# checks if the the first parameter is greater than the second parameter +version_greater() { + [ "$(printf '%s\n' "$@" | sed -e 's/-rc/.1/' | sed -e 's/-dev/.2/' | sort -t '.' -k1,1n -k2,2n -k3,3nbr | head -n 1)" != "$(printf "$1" | sed -e 's/-rc/.1/' | sed -e 's/-dev/.2/')" ] +} + +# usage: file_env VAR [DEFAULT] +# ie: file_env '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) +file_env() { + var="$1" + fileVar="${var}_FILE" + def="${2:-}" + varValue=$(env | grep -E "^${var}=" | sed -E -e "s/^${var}=//") + fileVarValue=$(env | grep -E "^${fileVar}=" | sed -E -e "s/^${fileVar}=//") + if [ -n "${varValue}" ] && [ -n "${fileVarValue}" ]; then + echo >&2 "error: both $var and $fileVar are set (but are exclusive)" + exit 1 + fi + if [ -n "${varValue}" ]; then + export "$var"="${varValue}" + elif [ -n "${fileVarValue}" ]; then + export "$var"="$(cat "${fileVarValue}")" + elif [ -n "${def}" ]; then + export "$var"="$def" + fi + unset "$fileVar" +} + +sh /setup_msmtp.sh + +# just check if we execute apache or php-fpm +if expr "$1" : "apache" 1>/dev/null || [ "$1" = "php-fpm" ]; then + if [ -n "${REDIS_HOST+x}" ]; then + echo "Configuring Redis as session handler" + { + file_env REDIS_PW + echo 'session.save_handler = redis' + # check if redis host is an unix socket path + if expr "${REDIS_HOST}" : "/" 1>/dev/null; then + if [ -n "${REDIS_PW+x}" ]; then + echo "session.save_path = \"unix://${REDIS_HOST}?auth=${REDIS_PW}\"" + else + echo "session.save_path = \"unix://${REDIS_HOST}\"" + fi + # check if redis password has been set + elif [ -n "${REDIS_PW+x}" ]; then + echo "session.save_path = \"tcp://${REDIS_HOST}:${REDIS_PORT:=6379}?auth=${REDIS_PW}\"" + else + echo "session.save_path = \"tcp://${REDIS_HOST}:${REDIS_PORT:=6379}\"" + fi + echo "redis.session.locking_enabled = 1" + echo "redis.session.lock_retries = -1" + # redis.session.lock_wait_time is specified in microseconds. + # Wait 10ms before retrying the lock rather than the default 2ms. + echo "redis.session.lock_wait_time = 10000" + } > /usr/local/etc/php/conf.d/redis-session.ini + fi + + # If another process is syncing the html folder, wait for + # it to be done, then escape initialization. + ( + if ! flock -n 9; then + # If we couldn't get it immediately, show a message, then wait for real + echo "Another process is initializing Friendica. Waiting..." + flock 9 + fi + + installed_version="0.0.0.0" + if [ -f /var/www/html/VERSION ]; then + installed_version="$(cat /var/www/html/VERSION)" + fi + + image_version="0.0.0.0" + if [ -f /usr/src/friendica/VERSION ]; then + image_version="$(cat /usr/src/friendica/VERSION)" + else + echo "No new Friendica sources found (enable FRIENDICA_UPGRADE for new sources)" + fi + + # no downgrading possible + if version_greater "$installed_version" "$image_version"; then + echo "Can't copy Friendica sources because the version of the data ($installed_version) is higher than the docker image ($image_version)" + exit 1 + fi + + # check it just in case the version is greater or if we force the upgrade + if version_greater "$image_version" "$installed_version" || [ "${FRIENDICA_UPGRADE:-false}" = "true" ]; then + echo "Initializing Friendica $image_version ..." + + if [ "$installed_version" != "0.0.0.0" ]; then + echo "Upgrading Friendica from $installed_version ..." + fi + + if [ "$(id -u)" -eq 0 ]; then + rsync_options="-rlDog --chown=www-data:www-data" + else + rsync_options="-rlD" + fi + + rsync $rsync_options --delete --exclude-from=/upgrade.exclude /usr/src/friendica/ /var/www/html/ + + # Update docker-based config files, but never delete other config files + rsync $rsync_options --update --exclude=/addon.config.php --exclude=/local.config.php /usr/src/friendica/config/ /var/www/html/config/ + + # In case there is no .htaccess, copy it from the default dist file + if [ ! -f "/var/www/html/.htaccess" ]; then + cp "/var/www/html/.htaccess-dist" "/var/www/html/.htaccess" + fi + + if [ -d /var/www/html/view/smarty3 ]; then + chmod -R 777 /var/www/html/view/smarty3 + fi + echo "Initializing finished" + + # install + if [ "$installed_version" = "0.0.0.0" ]; then + echo "New Friendica instance" + + file_env FRIENDICA_ADMIN_MAIL + + file_env MYSQL_DATABASE + file_env MYSQL_USER + file_env MYSQL_PASSWORD + + install=false + if [ -n "${MYSQL_DATABASE+x}" ] && [ -n "${MYSQL_PASSWORD+x}" ] && [ -n "${MYSQL_HOST+x}" ] && [ -n "${MYSQL_USER+x}" ] && [ -n "${FRIENDICA_ADMIN_MAIL+x}" ] && [ -n "${FRIENDICA_URL+x}" ]; then + echo "Installation with environment variables" + + FRIENDICA_TZ=${FRIENDICA_TZ:-America/New_York} + FRIENDICA_LANG=${FRIENDICA_LANG:-en} + MYSQL_PORT=${MYSQL_PORT:-3306} + + # shellcheck disable=SC2016 + install_options='-s --dbhost "'$MYSQL_HOST'" --dbport "'$MYSQL_PORT'" --dbdata "'$MYSQL_DATABASE'" --dbuser "'$MYSQL_USER'" --dbpass "'$MYSQL_PASSWORD'"' + + # shellcheck disable=SC2016 + install_options=$install_options' --admin "'$FRIENDICA_ADMIN_MAIL'" --tz "'$FRIENDICA_TZ'" --lang "'$FRIENDICA_LANG'" --url "'$FRIENDICA_URL'"' + install=true + fi + + if [ "$install" = true ]; then + echo "Waiting for MySQL $MYSQL_HOST initialization..." + if run_as "php /var/www/html/bin/wait-for-connection $MYSQL_HOST ${MYSQL_PORT:-3306} 300"; then + + echo "Starting Friendica installation ..." + run_as "php /var/www/html/bin/console.php autoinstall $install_options" + + rm -fr /var/www/html/view/smarty3/compiled + + # load other config files (*.config.php) to the config folder + if [ -d "/usr/src/config" ]; then + rsync $rsync_options --ignore-existing /usr/src/friendica/config/ /var/www/html/config/ + fi + + echo "Installation finished" + else + echo "[ERROR] Waited 300 seconds, no response" >&2 + fi + else + echo "Running web-based installer on first connect!" + fi + # upgrade + else + echo "Upgrading Friendica ..." + run_as 'php /var/www/html/bin/console.php dbstructure update -f' + echo "Upgrading finished" + fi + fi + ) 9> /var/www/html/friendica-init-sync.lock +fi + +exec "$@" diff --git a/2024.06-rc/fpm-alpine/setup_msmtp.sh b/2024.06-rc/fpm-alpine/setup_msmtp.sh new file mode 100644 index 0000000..c902b6d --- /dev/null +++ b/2024.06-rc/fpm-alpine/setup_msmtp.sh @@ -0,0 +1,40 @@ +#!/bin/sh +set -eu + +if [ -n "${SMTP_DOMAIN+x}" ] && [ -n "${SMTP+x}" ] && [ "${SMTP}" != "localhost" ]; then + SITENAME="${FRIENDICA_SITENAME:-Friendica Social Network}" + echo "Setup MSMTP for '$SITENAME' with '$SMTP' ..." + + smtp_from="${SMTP_FROM:=no-reply}" + smtp_auth="${SMTP_AUTH:=on}" + # https://github.com/friendica/docker/issues/233 + smtp_starttls="${SMTP_STARTTLS:=on}" + + # Setup MSMTP + usermod --comment "$(echo "$SITENAME" | tr -dc '[:print:]')" root + usermod --comment "$(echo "$SITENAME" | tr -dc '[:print:]')" www-data + + # add possible mail-senders + { + echo "www-data: $smtp_from@$SMTP_DOMAIN" + echo "root: $smtp_from@$SMTP_DOMAIN" + } >/etc/aliases + + # create msmtp settings + { + echo "account default" + echo "host $SMTP" + if [ -n "${SMTP_PORT+x}" ]; then echo "port $SMTP_PORT"; else echo "port 587"; fi + echo "from \"$smtp_from@$SMTP_DOMAIN\"" + echo "tls_certcheck off" # No certcheck because of internal docker mail-hostnames + if [ -n "${SMTP_TLS+x}" ]; then echo "tls on"; fi + echo "tls_starttls $smtp_starttls"; + if [ -n "${SMTP_AUTH_USER+x}" ]; then echo "auth $smtp_auth"; fi + if [ -n "${SMTP_AUTH_USER+x}" ]; then echo "user \"$SMTP_AUTH_USER\""; fi + if [ -n "${SMTP_AUTH_PASS+x}" ]; then echo "password \"$SMTP_AUTH_PASS\""; fi + echo "logfile -" + echo "aliases /etc/aliases" + } >/etc/msmtprc + + echo "Setup finished" +fi diff --git a/2024.06-rc/fpm-alpine/upgrade.exclude b/2024.06-rc/fpm-alpine/upgrade.exclude new file mode 100644 index 0000000..4f94596 --- /dev/null +++ b/2024.06-rc/fpm-alpine/upgrade.exclude @@ -0,0 +1,10 @@ +/photo/ +/proxy/ +/.htconfig.php +/.htaccess +/home.* +/config/ +/storage/ +/log/ +*.log +/friendica-init-sync.lock diff --git a/2024.06-rc/fpm/Dockerfile b/2024.06-rc/fpm/Dockerfile new file mode 100644 index 0000000..d4bda52 --- /dev/null +++ b/2024.06-rc/fpm/Dockerfile @@ -0,0 +1,175 @@ +# DO NOT EDIT: created by update.sh from Dockerfile-debian.template +FROM php:8.1-fpm-bullseye + +# entrypoint.sh and cron.sh dependencies +RUN set -ex; \ + \ + apt-get update; \ + apt-get install -y --no-install-recommends \ + rsync \ + bzip2 \ +# For mail() support + msmtp \ + tini \ + ; + +ENV GOSU_VERSION 1.14 +RUN set -eux; \ +# save list of currently installed packages for later so we can clean up + savedAptMark="$(apt-mark showmanual)"; \ + apt-get update; \ + apt-get install -y --no-install-recommends ca-certificates wget; \ + if ! command -v gpg; then \ + apt-get install -y --no-install-recommends gnupg2 dirmngr; \ + elif gpg --version | grep -q '^gpg (GnuPG) 1\.'; then \ +# "This package provides support for HKPS keyservers." (GnuPG 1.x only) + apt-get install -y --no-install-recommends gnupg-curl; \ + fi; \ + rm -rf /var/lib/apt/lists/*; \ + \ + dpkgArch="$(dpkg --print-architecture | awk -F- '{ print $NF }')"; \ + wget -O /usr/local/bin/gosu "https://github.com/tianon/gosu/releases/download/$GOSU_VERSION/gosu-$dpkgArch"; \ + wget -O /usr/local/bin/gosu.asc "https://github.com/tianon/gosu/releases/download/$GOSU_VERSION/gosu-$dpkgArch.asc"; \ + \ +# verify the signature + export GNUPGHOME="$(mktemp -d)"; \ + gpg --batch --keyserver hkps://keys.openpgp.org --recv-keys B42F6819007F00F88E364FD4036A9C25BF357DD4; \ + gpg --batch --verify /usr/local/bin/gosu.asc /usr/local/bin/gosu; \ + command -v gpgconf && gpgconf --kill all || :; \ + rm -rf "$GNUPGHOME" /usr/local/bin/gosu.asc; \ + \ +# clean up fetch dependencies + apt-mark auto '.*' > /dev/null; \ + [ -z "$savedAptMark" ] || apt-mark manual $savedAptMark; \ + apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false; \ + \ + chmod +x /usr/local/bin/gosu; \ +# verify that the binary works + gosu --version; \ + gosu nobody true + +# install the PHP extensions we need +# see https://friendi.ca/resources/requirements/ +RUN set -ex; \ + \ + savedAptMark="$(apt-mark showmanual)"; \ + \ + apt-get update; \ + apt-get install -y --no-install-recommends \ + mariadb-client \ + bash \ + libpng-dev \ + libjpeg62-turbo-dev \ + libtool \ + libmagick++-dev \ + libmemcached-dev \ + libgraphicsmagick1-dev \ + libfreetype6-dev \ + libwebp-dev \ + librsvg2-2 \ + libzip-dev \ + libldap2-dev \ + libgmp-dev \ + libmagickcore-6.q16-6-extra \ + ; \ + \ + debMultiarch="$(dpkg-architecture --query DEB_BUILD_MULTIARCH)"; \ + \ + docker-php-ext-configure gd \ + --with-freetype \ + --with-jpeg \ + --with-webp \ + ; \ + docker-php-ext-configure ldap \ + --with-libdir=lib/$debMultiarch/ \ + ;\ + docker-php-ext-install -j "$(nproc)" \ + pdo_mysql \ + gd \ + exif \ + zip \ + opcache \ + ctype \ + pcntl \ + ldap \ + gmp \ + intl \ + ; \ + \ +# pecl will claim success even if one install fails, so we need to perform each install separately + pecl install apcu-5.1.23; \ + pecl install memcached-3.2.0RC2; \ + pecl install redis-6.0.2; \ + pecl install imagick-3.7.0; \ + \ + docker-php-ext-enable \ + apcu \ + memcached \ + redis \ + imagick \ + ; \ + \ +# reset apt-mark's "manual" list so that "purge --auto-remove" will remove all build dependencies + apt-mark auto '.*' > /dev/null; \ + apt-mark manual $savedAptMark; \ + ldd "$(php -r 'echo ini_get("extension_dir");')"/*.so \ + | awk '/=>/ { print $3 }' \ + | sort -u \ + | xargs -r dpkg-query -S \ + | cut -d: -f1 \ + | sort -u \ + | xargs -rt apt-mark manual; \ + \ + apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false; \ + rm -rf /var/lib/apt/lists/* + +# set recommended PHP.ini settings +ENV PHP_MEMORY_LIMIT 512M +ENV PHP_UPLOAD_LIMIT 512M +RUN set -ex; \ + { \ + echo 'opcache.enable=1' ; \ + echo 'opcache.interned_strings_buffer=8'; \ + echo 'opcache.max_accelerated_files=10000'; \ + echo 'opcache.memory_consumption=128'; \ + echo 'opcache.save_comments=1'; \ + echo 'opcache.revalidte_freq=1'; \ + } > /usr/local/etc/php/conf.d/opcache-recommended.ini; \ + \ + { \ + echo sendmail_path = "/usr/bin/msmtp -t"; \ + } > /usr/local/etc/php/conf.d/sendmail.ini; \ + \ + echo 'apc.enable_cli=1' >> /usr/local/etc/php/conf.d/docker-php-ext-apcu.ini; \ + \ + { \ + echo 'memory_limit=${PHP_MEMORY_LIMIT}'; \ + echo 'upload_max_filesize=${PHP_UPLOAD_LIMIT}'; \ + echo 'post_max_size=${PHP_UPLOAD_LIMIT}'; \ + } > /usr/local/etc/php/conf.d/friendica.ini; \ + ln -s /usr/local/etc/php/php.ini-production /usr/local/etc/php/php.ini; \ + \ + mkdir /var/www/data; \ + chown -R www-data:root /var/www; \ + chmod -R g=u /var/www + +VOLUME /var/www/html + + +# 39 = LOG_PID | LOG_ODELAY | LOG_CONS | LOG_PERROR +ENV FRIENDICA_SYSLOG_FLAGS 39 +ENV FRIENDICA_VERSION "2024.06-rc" +ENV FRIENDICA_ADDONS "2024.06-rc" + +RUN set -ex; \ + fetchDeps=" \ + gnupg \ + "; \ + apt-get update; \ + apt-get install -y --no-install-recommends $fetchDeps; + +COPY *.sh upgrade.exclude / +COPY config/* /usr/src/friendica/config/ + +ENTRYPOINT ["/entrypoint-dev.sh"] +CMD ["php-fpm"] diff --git a/2024.06-rc/fpm/config/00apcu.config.php b/2024.06-rc/fpm/config/00apcu.config.php new file mode 100644 index 0000000..2e5ebcf --- /dev/null +++ b/2024.06-rc/fpm/config/00apcu.config.php @@ -0,0 +1,11 @@ + [ + 'cache_driver' => 'apcu', + ], +]; diff --git a/2024.06-rc/fpm/config/01redis.config.php b/2024.06-rc/fpm/config/01redis.config.php new file mode 100644 index 0000000..2ea29bd --- /dev/null +++ b/2024.06-rc/fpm/config/01redis.config.php @@ -0,0 +1,17 @@ + [ + 'session_handler' => 'cache', + 'distributed_cache_driver' => 'redis', + 'lock_driver' => 'redis', + 'redis_host' => getenv('REDIS_HOST'), + 'redis_port' => (getenv('REDIS_PORT') ?: ''), + 'redis_password' => (getenv('REDIS_PW') ?: ''), + 'redis_db' => (getenv('REDIS_DB') ?: 0), + ], + ]; +} else { + return []; +} diff --git a/2024.06-rc/fpm/config/zz-docker.config.php b/2024.06-rc/fpm/config/zz-docker.config.php new file mode 100644 index 0000000..946fe81 --- /dev/null +++ b/2024.06-rc/fpm/config/zz-docker.config.php @@ -0,0 +1,34 @@ + [ + // Necessary because otherwise the daemon isn't working + 'pidfile' => '/var/run/friendica.pid', + + 'logfile' => '/var/www/html/friendica.log', + 'loglevel' => 'notice', + ], + 'storage' => [ + 'filesystem_path' => '/var/www/html/storage', + ], +]; + +if (!empty(getenv('FRIENDICA_NO_VALIDATION'))) { + $config['system']['disable_url_validation'] = true; + $config['system']['disable_email_validation'] = true; +} + +if (!empty(getenv('SMTP_DOMAIN'))) { + $smtp_from = !empty(getenv('SMTP_FROM')) ? getenv('SMTP_FROM') : 'no-reply'; + + $config['config']['sender_email'] = $smtp_from . "@" . getenv('SMTP_DOMAIN'); +} + +return $config; diff --git a/2024.06-rc/fpm/cron.sh b/2024.06-rc/fpm/cron.sh new file mode 100755 index 0000000..18dced0 --- /dev/null +++ b/2024.06-rc/fpm/cron.sh @@ -0,0 +1,14 @@ +#!/bin/sh +trap "break;exit" HUP INT TERM + +while [ ! -f /var/www/html/bin/daemon.php ]; do + sleep 1 +done + +echo "Waiting for MySQL $MYSQL_HOST initialization..." +if php /var/www/html/bin/wait-for-connection "$MYSQL_HOST" "${MYSQL_PORT:-3306}" 300; then + sh /setup_msmtp.sh + exec gosu www-data:www-data tini -- php /var/www/html/bin/daemon.php -f start +else + echo "[ERROR] Waited 300 seconds, no response" >&2 +fi diff --git a/2024.06-rc/fpm/entrypoint-dev.sh b/2024.06-rc/fpm/entrypoint-dev.sh new file mode 100755 index 0000000..8b34c21 --- /dev/null +++ b/2024.06-rc/fpm/entrypoint-dev.sh @@ -0,0 +1,54 @@ +#!/bin/sh +set -eu + +# just check if we execute apache or php-fpm +if (expr "$1" : "apache" 1>/dev/null || [ "$1" = "php-fpm" ]) && [ "${FRIENDICA_UPGRADE:-false}" = "true" ]; then + curl -fsSL -o "/usr/src/friendica-full-${FRIENDICA_VERSION}.tar.gz.sum256" "https://files.friendi.ca/friendica-full-${FRIENDICA_VERSION}.tar.gz.sum256" + curl -fsSL -o "/usr/src/friendica-addons-${FRIENDICA_ADDONS}.tar.gz.sum256" "https://files.friendi.ca/friendica-full-${FRIENDICA_ADDONS}.tar.gz.sum256" + + # Don't download already latest sources + if [ -f "/usr/src/friendica.tar.gz.sum256" ] && [ -f "/usr/src/friendica-addons.tar.gz.sum256" ] && \ + cmp -s "/usr/src/friendica-full-${FRIENDICA_VERSION}.tar.gz.sum256" "/usr/src/friendica.tar.gz.sum256" && \ + cmp -s "/usr/src/friendica-addons-${FRIENDICA_ADDONS}.tar.gz.sum256" "/usr/src/friendica-addons.tar.gz.sum256"; then + echo "Already latest sources - skipped download" + else + + echo "Download sources for ${FRIENDICA_VERSION} (Addon: ${FRIENDICA_ADDONS})" + + # Removing the whole directory first + rm -fr /usr/src/friendica + export GNUPGHOME="$(mktemp -d)" + + gpg --batch --logger-fd=1 --no-tty --quiet --keyserver keyserver.ubuntu.com --recv-keys 08656443618E6567A39524083EE197EF3F9E4287 + + curl -fsSL -o friendica-full-${FRIENDICA_VERSION}.tar.gz "https://files.friendi.ca/friendica-full-${FRIENDICA_VERSION}.tar.gz" + curl -fsSL -o friendica-full-${FRIENDICA_VERSION}.tar.gz.asc "https://files.friendi.ca/friendica-full-${FRIENDICA_VERSION}.tar.gz.asc"; + gpg --batch --logger-fd=1 --no-tty --quiet --verify friendica-full-${FRIENDICA_VERSION}.tar.gz.asc friendica-full-${FRIENDICA_VERSION}.tar.gz + echo "Core sources (${FRIENDICA_VERSION}) verified" + + tar -xzf friendica-full-${FRIENDICA_VERSION}.tar.gz -C /usr/src/ + rm friendica-full-${FRIENDICA_VERSION}.tar.gz friendica-full-${FRIENDICA_VERSION}.tar.gz.asc + mv -f /usr/src/friendica-full-${FRIENDICA_VERSION}/ /usr/src/friendica + echo "Core sources (${FRIENDICA_VERSION}) extracted" + + chmod 777 /usr/src/friendica/view/smarty3 + + curl -fsSL -o friendica-addons-${FRIENDICA_ADDONS}.tar.gz "https://files.friendi.ca/friendica-addons-${FRIENDICA_ADDONS}.tar.gz" + curl -fsSL -o friendica-addons-${FRIENDICA_ADDONS}.tar.gz.asc "https://files.friendi.ca/friendica-addons-${FRIENDICA_ADDONS}.tar.gz.asc" + gpg --batch --logger-fd=1 --no-tty --quiet --verify friendica-addons-${FRIENDICA_ADDONS}.tar.gz.asc friendica-addons-${FRIENDICA_ADDONS}.tar.gz + echo "Addon sources (${FRIENDICA_ADDONS}) verified" + + mkdir -p /usr/src/friendica/addon + tar -xzf friendica-addons-${FRIENDICA_ADDONS}.tar.gz -C /usr/src/friendica/addon --strip-components=1 + rm friendica-addons-${FRIENDICA_ADDONS}.tar.gz friendica-addons-${FRIENDICA_ADDONS}.tar.gz.asc + echo "Addon sources (${FRIENDICA_ADDONS}) extracted" + + gpgconf --kill all + rm -rf "$GNUPGHOME" + + mv -f /usr/src/friendica-full-${FRIENDICA_VERSION}.tar.gz.sum256 /usr/src/friendica.tar.gz.sum256 + mv -f /usr/src/friendica-addons-${FRIENDICA_ADDONS}.tar.gz.sum256 /usr/src/friendica-addons.tar.gz.sum256 + fi +fi + +exec /entrypoint.sh "$@" diff --git a/2024.06-rc/fpm/entrypoint.sh b/2024.06-rc/fpm/entrypoint.sh new file mode 100755 index 0000000..b080cef --- /dev/null +++ b/2024.06-rc/fpm/entrypoint.sh @@ -0,0 +1,185 @@ +#!/bin/sh +set -eu + +# run an command with the www-data user +run_as() { + set -- sh -c "cd /var/www/html; $*" + if [ "$(id -u)" -eq 0 ]; then + set -- gosu www-data "$@" + fi + "$@" +} + +# checks if the the first parameter is greater than the second parameter +version_greater() { + [ "$(printf '%s\n' "$@" | sed -e 's/-rc/.1/' | sed -e 's/-dev/.2/' | sort -t '.' -k1,1n -k2,2n -k3,3nbr | head -n 1)" != "$(printf "$1" | sed -e 's/-rc/.1/' | sed -e 's/-dev/.2/')" ] +} + +# usage: file_env VAR [DEFAULT] +# ie: file_env '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) +file_env() { + var="$1" + fileVar="${var}_FILE" + def="${2:-}" + varValue=$(env | grep -E "^${var}=" | sed -E -e "s/^${var}=//") + fileVarValue=$(env | grep -E "^${fileVar}=" | sed -E -e "s/^${fileVar}=//") + if [ -n "${varValue}" ] && [ -n "${fileVarValue}" ]; then + echo >&2 "error: both $var and $fileVar are set (but are exclusive)" + exit 1 + fi + if [ -n "${varValue}" ]; then + export "$var"="${varValue}" + elif [ -n "${fileVarValue}" ]; then + export "$var"="$(cat "${fileVarValue}")" + elif [ -n "${def}" ]; then + export "$var"="$def" + fi + unset "$fileVar" +} + +sh /setup_msmtp.sh + +# just check if we execute apache or php-fpm +if expr "$1" : "apache" 1>/dev/null || [ "$1" = "php-fpm" ]; then + if [ -n "${REDIS_HOST+x}" ]; then + echo "Configuring Redis as session handler" + { + file_env REDIS_PW + echo 'session.save_handler = redis' + # check if redis host is an unix socket path + if expr "${REDIS_HOST}" : "/" 1>/dev/null; then + if [ -n "${REDIS_PW+x}" ]; then + echo "session.save_path = \"unix://${REDIS_HOST}?auth=${REDIS_PW}\"" + else + echo "session.save_path = \"unix://${REDIS_HOST}\"" + fi + # check if redis password has been set + elif [ -n "${REDIS_PW+x}" ]; then + echo "session.save_path = \"tcp://${REDIS_HOST}:${REDIS_PORT:=6379}?auth=${REDIS_PW}\"" + else + echo "session.save_path = \"tcp://${REDIS_HOST}:${REDIS_PORT:=6379}\"" + fi + echo "redis.session.locking_enabled = 1" + echo "redis.session.lock_retries = -1" + # redis.session.lock_wait_time is specified in microseconds. + # Wait 10ms before retrying the lock rather than the default 2ms. + echo "redis.session.lock_wait_time = 10000" + } > /usr/local/etc/php/conf.d/redis-session.ini + fi + + # If another process is syncing the html folder, wait for + # it to be done, then escape initialization. + ( + if ! flock -n 9; then + # If we couldn't get it immediately, show a message, then wait for real + echo "Another process is initializing Friendica. Waiting..." + flock 9 + fi + + installed_version="0.0.0.0" + if [ -f /var/www/html/VERSION ]; then + installed_version="$(cat /var/www/html/VERSION)" + fi + + image_version="0.0.0.0" + if [ -f /usr/src/friendica/VERSION ]; then + image_version="$(cat /usr/src/friendica/VERSION)" + else + echo "No new Friendica sources found (enable FRIENDICA_UPGRADE for new sources)" + fi + + # no downgrading possible + if version_greater "$installed_version" "$image_version"; then + echo "Can't copy Friendica sources because the version of the data ($installed_version) is higher than the docker image ($image_version)" + exit 1 + fi + + # check it just in case the version is greater or if we force the upgrade + if version_greater "$image_version" "$installed_version" || [ "${FRIENDICA_UPGRADE:-false}" = "true" ]; then + echo "Initializing Friendica $image_version ..." + + if [ "$installed_version" != "0.0.0.0" ]; then + echo "Upgrading Friendica from $installed_version ..." + fi + + if [ "$(id -u)" -eq 0 ]; then + rsync_options="-rlDog --chown=www-data:www-data" + else + rsync_options="-rlD" + fi + + rsync $rsync_options --delete --exclude-from=/upgrade.exclude /usr/src/friendica/ /var/www/html/ + + # Update docker-based config files, but never delete other config files + rsync $rsync_options --update --exclude=/addon.config.php --exclude=/local.config.php /usr/src/friendica/config/ /var/www/html/config/ + + # In case there is no .htaccess, copy it from the default dist file + if [ ! -f "/var/www/html/.htaccess" ]; then + cp "/var/www/html/.htaccess-dist" "/var/www/html/.htaccess" + fi + + if [ -d /var/www/html/view/smarty3 ]; then + chmod -R 777 /var/www/html/view/smarty3 + fi + echo "Initializing finished" + + # install + if [ "$installed_version" = "0.0.0.0" ]; then + echo "New Friendica instance" + + file_env FRIENDICA_ADMIN_MAIL + + file_env MYSQL_DATABASE + file_env MYSQL_USER + file_env MYSQL_PASSWORD + + install=false + if [ -n "${MYSQL_DATABASE+x}" ] && [ -n "${MYSQL_PASSWORD+x}" ] && [ -n "${MYSQL_HOST+x}" ] && [ -n "${MYSQL_USER+x}" ] && [ -n "${FRIENDICA_ADMIN_MAIL+x}" ] && [ -n "${FRIENDICA_URL+x}" ]; then + echo "Installation with environment variables" + + FRIENDICA_TZ=${FRIENDICA_TZ:-America/New_York} + FRIENDICA_LANG=${FRIENDICA_LANG:-en} + MYSQL_PORT=${MYSQL_PORT:-3306} + + # shellcheck disable=SC2016 + install_options='-s --dbhost "'$MYSQL_HOST'" --dbport "'$MYSQL_PORT'" --dbdata "'$MYSQL_DATABASE'" --dbuser "'$MYSQL_USER'" --dbpass "'$MYSQL_PASSWORD'"' + + # shellcheck disable=SC2016 + install_options=$install_options' --admin "'$FRIENDICA_ADMIN_MAIL'" --tz "'$FRIENDICA_TZ'" --lang "'$FRIENDICA_LANG'" --url "'$FRIENDICA_URL'"' + install=true + fi + + if [ "$install" = true ]; then + echo "Waiting for MySQL $MYSQL_HOST initialization..." + if run_as "php /var/www/html/bin/wait-for-connection $MYSQL_HOST ${MYSQL_PORT:-3306} 300"; then + + echo "Starting Friendica installation ..." + run_as "php /var/www/html/bin/console.php autoinstall $install_options" + + rm -fr /var/www/html/view/smarty3/compiled + + # load other config files (*.config.php) to the config folder + if [ -d "/usr/src/config" ]; then + rsync $rsync_options --ignore-existing /usr/src/friendica/config/ /var/www/html/config/ + fi + + echo "Installation finished" + else + echo "[ERROR] Waited 300 seconds, no response" >&2 + fi + else + echo "Running web-based installer on first connect!" + fi + # upgrade + else + echo "Upgrading Friendica ..." + run_as 'php /var/www/html/bin/console.php dbstructure update -f' + echo "Upgrading finished" + fi + fi + ) 9> /var/www/html/friendica-init-sync.lock +fi + +exec "$@" diff --git a/2024.06-rc/fpm/setup_msmtp.sh b/2024.06-rc/fpm/setup_msmtp.sh new file mode 100644 index 0000000..c902b6d --- /dev/null +++ b/2024.06-rc/fpm/setup_msmtp.sh @@ -0,0 +1,40 @@ +#!/bin/sh +set -eu + +if [ -n "${SMTP_DOMAIN+x}" ] && [ -n "${SMTP+x}" ] && [ "${SMTP}" != "localhost" ]; then + SITENAME="${FRIENDICA_SITENAME:-Friendica Social Network}" + echo "Setup MSMTP for '$SITENAME' with '$SMTP' ..." + + smtp_from="${SMTP_FROM:=no-reply}" + smtp_auth="${SMTP_AUTH:=on}" + # https://github.com/friendica/docker/issues/233 + smtp_starttls="${SMTP_STARTTLS:=on}" + + # Setup MSMTP + usermod --comment "$(echo "$SITENAME" | tr -dc '[:print:]')" root + usermod --comment "$(echo "$SITENAME" | tr -dc '[:print:]')" www-data + + # add possible mail-senders + { + echo "www-data: $smtp_from@$SMTP_DOMAIN" + echo "root: $smtp_from@$SMTP_DOMAIN" + } >/etc/aliases + + # create msmtp settings + { + echo "account default" + echo "host $SMTP" + if [ -n "${SMTP_PORT+x}" ]; then echo "port $SMTP_PORT"; else echo "port 587"; fi + echo "from \"$smtp_from@$SMTP_DOMAIN\"" + echo "tls_certcheck off" # No certcheck because of internal docker mail-hostnames + if [ -n "${SMTP_TLS+x}" ]; then echo "tls on"; fi + echo "tls_starttls $smtp_starttls"; + if [ -n "${SMTP_AUTH_USER+x}" ]; then echo "auth $smtp_auth"; fi + if [ -n "${SMTP_AUTH_USER+x}" ]; then echo "user \"$SMTP_AUTH_USER\""; fi + if [ -n "${SMTP_AUTH_PASS+x}" ]; then echo "password \"$SMTP_AUTH_PASS\""; fi + echo "logfile -" + echo "aliases /etc/aliases" + } >/etc/msmtprc + + echo "Setup finished" +fi diff --git a/2024.06-rc/fpm/upgrade.exclude b/2024.06-rc/fpm/upgrade.exclude new file mode 100644 index 0000000..4f94596 --- /dev/null +++ b/2024.06-rc/fpm/upgrade.exclude @@ -0,0 +1,10 @@ +/photo/ +/proxy/ +/.htconfig.php +/.htaccess +/home.* +/config/ +/storage/ +/log/ +*.log +/friendica-init-sync.lock