diff --git a/.env.example b/.env.example new file mode 100644 index 000000000..8941526f2 --- /dev/null +++ b/.env.example @@ -0,0 +1,10 @@ +# Copy to .env for local development. Local test values only, never use in production. +APP_PORT=8080 +DB_NAME=qloapps +DB_USER=qloapps +DB_PASSWORD=qlo_local_dev +DB_ROOT_PASSWORD=qlo_local_root + +# Local admin created by the CLI installer (test values only) +ADMIN_EMAIL=admin@hotel.test +ADMIN_PASSWORD=change-me diff --git a/.gitignore b/.gitignore index 9ce95681a..846867e3e 100644 --- a/.gitignore +++ b/.gitignore @@ -255,4 +255,6 @@ admin/themes/default/css/screen.css # MISC **sitemap.xml -robots.txt \ No newline at end of file +robots.txt +# Local docker environment +.env diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 000000000..0e8143bb8 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,42 @@ +# Local development setup for QloApps. +# Start: docker compose up -d --build +# Shop: http://localhost:8080 +# Admin: http://localhost:8080/admin-dev (login: ADMIN_EMAIL / ADMIN_PASSWORD from .env) +# Values come from .env (copy .env.example to .env). +# +# Install (once, as www-data so file permissions stay correct; the web installer is blocked): +# docker compose exec -u www-data app php install-dev/index_cli.php --language=de --country=de \ +# --timezone=Europe/Berlin --domain=localhost:8080 --db_server=db --db_user=qloapps \ +# --db_password= --db_name=qloapps --email= --password= \ +# --newsletter=0 + +services: + app: + build: ./docker + ports: + - "${APP_PORT:-8080}:80" + volumes: + - ./:/var/www/html + depends_on: + db: + condition: service_healthy + restart: unless-stopped + + db: + image: mariadb:11.4 + environment: + MARIADB_DATABASE: ${DB_NAME:-qloapps} + MARIADB_USER: ${DB_USER:-qloapps} + MARIADB_PASSWORD: ${DB_PASSWORD:?set DB_PASSWORD in .env} + MARIADB_ROOT_PASSWORD: ${DB_ROOT_PASSWORD:?set DB_ROOT_PASSWORD in .env} + volumes: + - db_data:/var/lib/mysql + healthcheck: + test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"] + interval: 5s + timeout: 5s + retries: 20 + restart: unless-stopped + +volumes: + db_data: diff --git a/docker/Dockerfile b/docker/Dockerfile new file mode 100644 index 000000000..c7e219f06 --- /dev/null +++ b/docker/Dockerfile @@ -0,0 +1,19 @@ +FROM php:8.3-apache + +# PHP extensions required by QloApps (see README "Requirements") +RUN apt-get update \ + && apt-get install -y --no-install-recommends \ + libfreetype6-dev libjpeg62-turbo-dev libpng-dev libwebp-dev \ + libzip-dev libxml2-dev libicu-dev unzip \ + && docker-php-ext-configure gd --with-freetype --with-jpeg --with-webp \ + && docker-php-ext-install -j"$(nproc)" pdo_mysql mysqli gd zip soap intl opcache \ + && a2enmod rewrite headers \ + && rm -rf /var/lib/apt/lists/* + +COPY php.ini /usr/local/etc/php/conf.d/qloapps.ini +COPY apache-hardening.conf /etc/apache2/conf-enabled/zz-hardening.conf + +# QloApps' .htaccess files need AllowOverride +RUN sed -ri 's!AllowOverride None!AllowOverride All!g' /etc/apache2/apache2.conf + +WORKDIR /var/www/html diff --git a/docker/apache-hardening.conf b/docker/apache-hardening.conf new file mode 100644 index 000000000..7ed04551c --- /dev/null +++ b/docker/apache-hardening.conf @@ -0,0 +1,7 @@ +# The repo is bind-mounted as web root: never serve dotfiles (.env, .git, ...) or docker files. +RedirectMatch 404 "/\.(?!well-known/)" +RedirectMatch 404 "^/(docker/|docker-compose\.yml)" +# Installation is done via CLI (see docker-compose.yml); a web re-install would wipe the database. +RedirectMatch 404 "^/install-dev" +ServerTokens Prod +ServerSignature Off diff --git a/docker/php.ini b/docker/php.ini new file mode 100644 index 000000000..4f411de6d --- /dev/null +++ b/docker/php.ini @@ -0,0 +1,12 @@ +memory_limit = 256M +upload_max_filesize = 16M +post_max_size = 20M +max_execution_time = 500 +max_input_vars = 5000 +allow_url_fopen = On +date.timezone = Europe/Berlin +expose_php = Off + +; Development: re-read changed PHP files immediately +opcache.validate_timestamps = 1 +opcache.revalidate_freq = 0