# syntax=docker/dockerfile:1.4 ############################# # Source Stage – copy your FileRise app ############################# FROM ubuntu:24.04 AS appsource RUN apt-get update && \ apt-get install -y --no-install-recommends ca-certificates && \ rm -rf /var/lib/apt/lists/* # clean up apt cache RUN mkdir -p /var/www && rm -f /var/www/html/index.html COPY . /var/www ############################# # Composer Stage – install PHP dependencies ############################# FROM composer:2 AS composer WORKDIR /app COPY --from=appsource /var/www/composer.json /var/www/composer.lock ./ RUN composer install --no-dev --optimize-autoloader # production-ready autoloader ############################# # Final Stage – runtime image ############################# FROM ubuntu:24.04 LABEL by=error311 ENV DEBIAN_FRONTEND=noninteractive \ HOME=/root \ LC_ALL=C.UTF-8 LANG=en_US.UTF-8 LANGUAGE=en_US.UTF-8 TERM=xterm \ UPLOAD_MAX_FILESIZE=5G POST_MAX_SIZE=5G TOTAL_UPLOAD_SIZE=5G \ PERSISTENT_TOKENS_KEY=default_please_change_this_key \ PUID=99 PGID=100 # Install Apache, PHP, and required extensions RUN apt-get update && \ apt-get upgrade -y && \ apt-get install -y --no-install-recommends \ apache2 php php-json php-curl php-zip php-mbstring php-gd php-xml \ ca-certificates curl git openssl && \ apt-get clean && rm -rf /var/lib/apt/lists/* # slim down image # Remap www-data to the PUID/PGID provided for safe bind mounts RUN set -eux; \ if [ "$(id -u www-data)" != "${PUID}" ]; then usermod -u "${PUID}" www-data; fi; \ if [ "$(id -g www-data)" != "${PGID}" ]; then groupmod -g "${PGID}" www-data 2>/dev/null || true; fi; \ usermod -g "${PGID}" www-data # Copy config, code, and vendor COPY custom-php.ini /etc/php/8.3/apache2/conf.d/99-app-tuning.ini COPY --from=appsource /var/www /var/www COPY --from=composer /app/vendor /var/www/vendor # ── ensure config/ is writable by www-data so sed -i can work ── RUN mkdir -p /var/www/config \ && chown -R www-data:www-data /var/www/config \ && chmod 750 /var/www/config # Secure permissions: code read-only, only data dirs writable RUN chown -R root:www-data /var/www && \ find /var/www -type d -exec chmod 755 {} \; && \ find /var/www -type f -exec chmod 644 {} \; && \ mkdir -p /var/www/public/uploads /var/www/users /var/www/metadata && \ chown -R www-data:www-data /var/www/public/uploads /var/www/users /var/www/metadata && \ chmod -R 775 /var/www/public/uploads /var/www/users /var/www/metadata # writable upload areas # Apache site configuration RUN cat <<'EOF' > /etc/apache2/sites-available/000-default.conf # Global settings TraceEnable off KeepAlive On MaxKeepAliveRequests 100 KeepAliveTimeout 5 Timeout 60 ServerAdmin webmaster@localhost DocumentRoot /var/www/public # Security headers for all responses Header always set X-Frame-Options "SAMEORIGIN" Header always set X-Content-Type-Options "nosniff" Header always set X-XSS-Protection "1; mode=block" Header always set Referrer-Policy "strict-origin-when-cross-origin" Header always set Content-Security-Policy "default-src 'self'; script-src 'self' https://cdnjs.cloudflare.com https://cdn.jsdelivr.net https://stackpath.bootstrapcdn.com; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com https://cdnjs.cloudflare.com https://cdn.jsdelivr.net https://stackpath.bootstrapcdn.com; font-src 'self' https://fonts.gstatic.com; img-src 'self' data: blob:; connect-src 'self'; frame-src 'self'; frame-ancestors 'none'; base-uri 'self'; form-action 'self';" # Compression AddOutputFilterByType DEFLATE text/html text/plain text/css application/javascript application/json # Cache static assets ExpiresActive on ExpiresByType image/jpeg "access plus 1 month" ExpiresByType image/png "access plus 1 month" ExpiresByType text/css "access plus 1 week" ExpiresByType application/javascript "access plus 3 hour" # Protect uploads directory Alias /uploads/ /var/www/uploads/ Options -Indexes AllowOverride None php_flag engine off php_flag engine off Require all granted # Public directory AllowOverride All Require all granted DirectoryIndex index.html index.php # Deny access to hidden files Require all denied Header always set Content-Security-Policy "default-src 'self'; script-src 'self' https://cdn.redoc.ly; style-src 'self' 'unsafe-inline'; worker-src 'self' https://cdn.redoc.ly blob:; connect-src 'self'; img-src 'self' data: blob:; frame-ancestors 'self'; base-uri 'self'; form-action 'self';" ErrorLog /var/www/metadata/log/error.log CustomLog /var/www/metadata/log/access.log combined EOF # Enable required modules RUN a2enmod rewrite headers proxy proxy_fcgi expires deflate ssl EXPOSE 80 443 COPY start.sh /usr/local/bin/start.sh RUN chmod +x /usr/local/bin/start.sh CMD ["/usr/local/bin/start.sh"]