175 lines
5.1 KiB
Docker
175 lines
5.1 KiB
Docker
###############################################################################
|
|
# EspoCRM - Minimal Secure Build from Source
|
|
# Multi-stage build with Alpine base images
|
|
###############################################################################
|
|
|
|
# Stage 1: Build frontend assets
|
|
FROM node:20-alpine AS frontend
|
|
WORKDIR /build
|
|
COPY package.json package-lock.json ./
|
|
RUN npm ci --ignore-scripts
|
|
COPY . .
|
|
RUN npx grunt offline
|
|
|
|
# Stage 2: Install PHP dependencies
|
|
FROM composer:2 AS composer
|
|
WORKDIR /build
|
|
COPY composer.json composer.lock ./
|
|
COPY dev/ ./dev/
|
|
RUN composer install \
|
|
--no-dev \
|
|
--no-interaction \
|
|
--no-progress \
|
|
--prefer-dist \
|
|
--optimize-autoloader \
|
|
--ignore-platform-reqs
|
|
|
|
# Stage 3: Minimal runtime
|
|
FROM php:8.4-fpm-alpine
|
|
|
|
RUN set -ex; \
|
|
apk add --no-cache \
|
|
nginx \
|
|
freetype \
|
|
libjpeg-turbo \
|
|
libwebp \
|
|
libpng \
|
|
libzip \
|
|
libpq \
|
|
libldap \
|
|
icu-libs \
|
|
unzip \
|
|
; \
|
|
apk add --no-cache --virtual .build-deps \
|
|
freetype-dev \
|
|
libjpeg-turbo-dev \
|
|
libwebp-dev \
|
|
libpng-dev \
|
|
libzip-dev \
|
|
postgresql-dev \
|
|
openldap-dev \
|
|
icu-dev \
|
|
; \
|
|
docker-php-ext-configure gd \
|
|
--with-freetype --with-jpeg --with-webp \
|
|
; \
|
|
docker-php-ext-install -j$(nproc) \
|
|
gd pdo_mysql pdo_pgsql zip ldap \
|
|
exif pcntl posix bcmath intl \
|
|
; \
|
|
apk del .build-deps; \
|
|
rm -rf /tmp/* /var/cache/apk/*
|
|
|
|
# PHP hardened config
|
|
RUN { \
|
|
echo 'expose_php = Off'; \
|
|
echo 'display_errors = Off'; \
|
|
echo 'display_startup_errors = Off'; \
|
|
echo 'log_errors = On'; \
|
|
echo 'memory_limit = 256M'; \
|
|
echo 'max_execution_time = 180'; \
|
|
echo 'max_input_time = 180'; \
|
|
echo 'post_max_size = 50M'; \
|
|
echo 'upload_max_filesize = 50M'; \
|
|
echo 'session.cookie_httponly = 1'; \
|
|
echo 'session.cookie_secure = 1'; \
|
|
echo 'session.use_strict_mode = 1'; \
|
|
} > "$PHP_INI_DIR/conf.d/espocrm.ini"
|
|
|
|
# Nginx config
|
|
RUN cat > /etc/nginx/http.d/default.conf <<'NGINX'
|
|
server {
|
|
listen 80;
|
|
server_name _;
|
|
root /var/www/html;
|
|
index index.php index.html;
|
|
|
|
server_tokens off;
|
|
add_header X-Content-Type-Options "nosniff" always;
|
|
add_header X-Frame-Options "SAMEORIGIN" always;
|
|
add_header X-XSS-Protection "1; mode=block" always;
|
|
add_header Referrer-Policy "same-origin" always;
|
|
|
|
client_max_body_size 50M;
|
|
|
|
location / {
|
|
try_files $uri $uri/ /index.php?$query_string;
|
|
}
|
|
|
|
location ~ \.php$ {
|
|
fastcgi_pass 127.0.0.1:9000;
|
|
fastcgi_index index.php;
|
|
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
|
include fastcgi_params;
|
|
fastcgi_read_timeout 180;
|
|
}
|
|
|
|
location ~ /\. { deny all; }
|
|
location ~ ^/(data|application|custom|vendor)/ { deny all; }
|
|
location = /data/config.php { deny all; }
|
|
}
|
|
NGINX
|
|
|
|
WORKDIR /var/www/html
|
|
|
|
# Copy application
|
|
COPY --chown=82:82 . .
|
|
COPY --chown=82:82 --from=composer /build/vendor ./vendor
|
|
COPY --chown=82:82 --from=frontend /build/client ./client
|
|
|
|
# Remove dev/build files
|
|
RUN rm -rf node_modules .git .github tests dev \
|
|
Gruntfile.js package.json package-lock.json \
|
|
phpstan.neon phpunit.xml .idea .vscode \
|
|
.editorconfig .gitattributes .gitignore .npmrc \
|
|
diff.js lang.js po.js jsconfig.json
|
|
|
|
# Set permissions
|
|
RUN set -ex; \
|
|
find . -type d -exec chmod 755 {} +; \
|
|
find . -type f -exec chmod 644 {} +; \
|
|
chmod 754 bin/command 2>/dev/null || true; \
|
|
for dir in data custom client/custom; do \
|
|
mkdir -p "$dir"; \
|
|
chown -R 82:82 "$dir"; \
|
|
done
|
|
|
|
# Entrypoint
|
|
COPY --chmod=755 <<'EOF' /entrypoint.sh
|
|
#!/bin/sh
|
|
set -e
|
|
|
|
if [ -n "$ESPOCRM_DATABASE_HOST" ] && [ ! -f "data/config.php" ]; then
|
|
echo "Waiting for database..."
|
|
i=0
|
|
while [ $i -lt 30 ]; do
|
|
if php -r "new PDO('mysql:host=${ESPOCRM_DATABASE_HOST};port=${ESPOCRM_DATABASE_PORT:-3306}','${ESPOCRM_DATABASE_USER:-root}','${ESPOCRM_DATABASE_PASSWORD}');" 2>/dev/null; then
|
|
echo "Database ready. Running installer..."
|
|
php bin/command install \
|
|
--databasePlatform="${ESPOCRM_DATABASE_PLATFORM:-Mysql}" \
|
|
--databaseHost="$ESPOCRM_DATABASE_HOST" \
|
|
--databasePort="${ESPOCRM_DATABASE_PORT:-3306}" \
|
|
--databaseName="${ESPOCRM_DATABASE_NAME:-espocrm}" \
|
|
--databaseUser="${ESPOCRM_DATABASE_USER:-root}" \
|
|
--databasePassword="$ESPOCRM_DATABASE_PASSWORD" \
|
|
--adminUsername="${ESPOCRM_ADMIN_USERNAME:-admin}" \
|
|
--adminPassword="${ESPOCRM_ADMIN_PASSWORD:-admin}" \
|
|
--siteUrl="${ESPOCRM_SITE_URL:-http://localhost}" \
|
|
&& echo "Install complete." \
|
|
|| echo "CLI install failed. Use web installer."
|
|
break
|
|
fi
|
|
i=$((i + 1))
|
|
sleep 2
|
|
done
|
|
fi
|
|
|
|
php-fpm -D
|
|
exec nginx -g 'daemon off;'
|
|
EOF
|
|
|
|
VOLUME /var/www/html/data
|
|
EXPOSE 80
|
|
|
|
ENTRYPOINT ["/entrypoint.sh"]
|