first commit

This commit is contained in:
brusnitsyn
2026-06-24 17:20:43 +09:00
commit 43499acf1c
165 changed files with 25929 additions and 0 deletions

40
deploy/Dockerfile Normal file
View File

@@ -0,0 +1,40 @@
# ============================================================================
# Образ PHP-FPM для защищённого Laravel-приложения (УЗ-1 / К1)
# Меры: УКФ.1 (фиксированные версии), ОДТ (минимизация образа).
# ============================================================================
FROM php:8.3-fpm-alpine AS base
# Системные зависимости и расширения PHP (включая pgsql, redis, gd для проверок).
RUN apk add --no-cache postgresql-dev oniguruma-dev icu-dev libzip-dev $PHPIZE_DEPS \
&& docker-php-ext-install pdo_pgsql mbstring intl bcmath opcache zip \
&& pecl install redis && docker-php-ext-enable redis \
&& apk del $PHPIZE_DEPS
# Рекомендуемая базовая конфигурация PHP (УКФ.1, ОДТ.3).
RUN { \
echo 'expose_php=Off'; \
echo 'display_errors=Off'; \
echo 'log_errors=On'; \
echo 'session.cookie_httponly=On'; \
echo 'session.cookie_secure=On'; \
echo 'session.use_strict_mode=On'; \
echo 'opcache.enable=1'; \
echo 'opcache.validate_timestamps=0'; \
} > /usr/local/etc/php/conf.d/zz-security.ini
WORKDIR /var/www/app
COPY --from=composer:2 /usr/bin/composer /usr/bin/composer
# Установка зависимостей по зафиксированным версиям (мера ОПС.1, УКФ.1).
COPY composer.json composer.lock ./
RUN composer install --no-dev --no-interaction --no-progress --prefer-dist --no-scripts
COPY . .
RUN composer dump-autoload --optimize \
# Веб-сервер не должен иметь прав записи вне storage и bootstrap/cache (ЗИС).
&& chown -R www-data:www-data storage bootstrap/cache \
&& chmod -R 775 storage bootstrap/cache
USER www-data

72
deploy/docker-compose.yml Normal file
View File

@@ -0,0 +1,72 @@
# ============================================================================
# Пример docker-compose для защищённого развёртывания (УЗ-1 / К1)
# Сегментация (раздел 10 гайда): веб в одной сети, БД/Redis — во внутренней
# сети без доступа из интернета (мера ЗИС.1).
# Это ОБРАЗЕЦ для стенда; в проде используйте управляемые БД и секрет-хранилище.
# ============================================================================
services:
app:
build:
context: ..
dockerfile: deploy/Dockerfile
restart: unless-stopped
env_file: ../.env
depends_on:
- pgsql
- pgsql-audit
- redis
networks: [frontend, backend]
volumes:
- app-storage:/var/www/app/storage
nginx:
image: nginx:1.27-alpine
restart: unless-stopped
depends_on: [app]
ports:
- "443:443"
- "80:80"
volumes:
- ./nginx/app.conf:/etc/nginx/conf.d/default.conf:ro
- ./certs:/etc/ssl/app:ro
networks: [frontend]
# Основная БД с ПДн — только во внутренней сети (ЗИС.1).
pgsql:
image: postgres:17-alpine
restart: unless-stopped
environment:
POSTGRES_DB: ${DB_DATABASE}
POSTGRES_USER: ${DB_USERNAME}
POSTGRES_PASSWORD: ${DB_PASSWORD}
volumes:
- pgsql-data:/var/lib/postgresql/data
networks: [backend]
# Отдельная БД журнала аудита (РСБ.3) — изолированный экземпляр.
pgsql-audit:
image: postgres:17-alpine
restart: unless-stopped
environment:
POSTGRES_DB: ${AUDIT_DB_DATABASE}
POSTGRES_USER: ${AUDIT_DB_USERNAME}
POSTGRES_PASSWORD: ${AUDIT_DB_PASSWORD}
volumes:
- pgsql-audit-data:/var/lib/postgresql/data
networks: [backend]
redis:
image: redis:7-alpine
restart: unless-stopped
command: ["redis-server", "--requirepass", "${REDIS_PASSWORD}"]
networks: [backend]
networks:
frontend:
backend:
internal: true # Нет доступа в интернет из сегмента БД (ЗИС.1).
volumes:
app-storage:
pgsql-data:
pgsql-audit-data:

View File

@@ -0,0 +1,24 @@
# ============================================================================
# fail2ban: блокировка перебора входа на уровне сети (мера ИАФ.6, ЗИС.2)
# Дополняет блокировку учётной записи в приложении блокировкой IP.
#
# Установка:
# 1) filter -> /etc/fail2ban/filter.d/laravel-auth.conf (секция [Definition])
# 2) jail -> /etc/fail2ban/jail.d/laravel-auth.conf (секция [laravel-auth])
# ============================================================================
[Definition]
# Срабатывает на записи о неудачном входе в логах приложения/nginx.
# Настройте под формат вашего лога (RSB: auth.login.failed).
failregex = ^.*"event_type":"auth\.login\.failed".*"ip":"<HOST>".*$
^<HOST> .* "POST /login HTTP.*" 4(0[39]|22|29) .*$
ignoreregex =
[laravel-auth]
enabled = true
port = http,https
logpath = /var/www/app/storage/logs/laravel.log
/var/log/nginx/access.log
maxretry = 5
findtime = 600
bantime = 1800

67
deploy/nginx/app.conf Normal file
View File

@@ -0,0 +1,67 @@
# ============================================================================
# Пример конфигурации Nginx для защищённого Laravel-приложения (УЗ-1 / К1)
# Меры ФСТЭК: ЗИС.9 (TLS), ЗИС (заголовки), ЗИС.2 (защита периметра).
# Подставьте домен, пути к сертификатам и при необходимости — ГОСТ TLS.
# ============================================================================
# Ограничение частоты запросов к форме входа (защита от перебора, ИАФ.6).
limit_req_zone $binary_remote_addr zone=login:10m rate=10r/m;
server {
listen 80;
server_name example.local;
# Принудительный редирект на HTTPS (ЗИС.9).
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
http2 on;
server_name example.local;
root /var/www/app/public;
index index.php;
# ---- TLS (ЗИС.9). Только TLS 1.2/1.3 ----------------------------------
ssl_certificate /etc/ssl/app/fullchain.pem;
ssl_certificate_key /etc/ssl/app/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305;
ssl_prefer_server_ciphers on;
ssl_session_timeout 10m;
ssl_session_cache shared:SSL:10m;
ssl_session_tickets off;
# Для государственных ИС: ГОСТ TLS через КриптоПро (ЗИС.16) — модуль
# nginx с поддержкой ГОСТ-шифронаборов либо stunnel/КриптоПро.
# ---- Заголовки безопасности (дублируют middleware приложения) ---------
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
add_header X-Frame-Options "DENY" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
server_tokens off;
# ---- Ограничение размера загрузок (ЗИС, защита от исчерпания ресурсов) -
client_max_body_size 10m;
location = /login {
limit_req zone=login burst=5 nodelay;
try_files $uri /index.php?$query_string;
}
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
fastcgi_pass unix:/run/php/php8.3-fpm.sock;
fastcgi_hide_header X-Powered-By;
}
# Запрет доступа к служебным и скрытым файлам.
location ~ /\.(?!well-known).* { deny all; }
location ~ /(storage|bootstrap|config|database|tests)/ { deny all; }
}