# syntax=docker/dockerfile:1.7 # ---------- composer stage ---------- FROM composer:2 AS deps WORKDIR /app COPY composer.json composer.lock* ./ RUN composer install --no-dev --no-interaction --no-scripts --no-progress --optimize-autoloader # ---------- runtime ---------- FROM dunglas/frankenphp:1-php8.3-alpine ENV PHP_INI_SCAN_DIR=/usr/local/etc/php/conf.d # System deps for PHP extensions RUN apk add --no-cache \ icu-dev \ oniguruma-dev \ sqlite-dev \ bash \ && install-php-extensions \ pdo_sqlite \ pdo_mysql \ mbstring \ intl \ opcache \ bcmath WORKDIR /app COPY --from=deps /app/vendor ./vendor COPY . ./ # Caddyfile and entrypoint COPY docker/Caddyfile /etc/Caddyfile COPY docker/entrypoint.sh /usr/local/bin/entrypoint.sh # SEC_REVIEW F18: drop root. /app stays root-owned and world-readable # (the runtime only needs to read source); /data is app-owned so the # SQLite db, attached volume, and `auth:bootstrap-service-token` # writes succeed without root. /home/app/.{config,local/share} are # pre-created and owned so FrankenPHP/Caddy XDG state has somewhere # to land. apk + install-php-extensions still ran above as root # because they touch /usr/local; no further root-owned work remains. RUN addgroup -S -g 1000 app \ && adduser -S -u 1000 -G app -h /home/app app \ && chmod +x /usr/local/bin/entrypoint.sh \ && chmod +x bin/console \ && mkdir -p /data /home/app/.config /home/app/.local/share \ && chown -R app:app /data /home/app ENV XDG_CONFIG_HOME=/home/app/.config \ XDG_DATA_HOME=/home/app/.local/share USER app EXPOSE 8081 ENTRYPOINT ["/usr/local/bin/entrypoint.sh"] CMD ["api"]