Към съдържанието
PN Scripts

Сигурност

Защитена продукционна среда за Laravel с Docker и архиви, които можете да възстановите

  • PN Scripts
  • 7 мин четене

Laravel приложение, което работи безупречно с php artisan serve, в продукция може да е на една грешна настройка от проблем: порт на базата данни, отворен към интернет, забравен включен APP_DEBUG, файл .env, който се чете през HTTP, архив, който никога не е бил възстановяван. Docker прави чистата настройка по-лесна за повтаряне, но сам по себе си не я прави защитена. В тази статия минаваме през продукционна среда за едно Laravel приложение на един сървър, слой по слой, с настройките, които имат значение, и грешките, които виждаме най-често.

#Как изглежда средата

Типично Laravel приложение в продукция има нужда от няколко неща: нещо, което обслужва HTTPS, уеб сървър за статичните файлове, PHP-FPM за приложението, работник за опашката, планировчик, база данни и обикновено Redis. Най-безопасната подредба има точно един публичен вход:

  • Обратно прокси като Caddy заема портове 80 и 443, взима и подновява сертификатите автоматично и добавя HSTS.
  • Зад него стои nginx, който сервира public/ и файловете от Vite и подава PHP заявките към PHP-FPM.
  • Работникът за опашката и планировчикът вървят от същия образ като приложението, така че кодът и разширенията винаги са едни и същи.
  • MySQL и Redis са във вътрешна мрежа без публикувани портове и без достъп до интернет.

#Заключете контейнерите

Контейнерите споделят ядрото на хоста, затова давайте на всеки възможно най-малко. В Compose това са няколко ключа:

services:
  app:
    build: .
    read_only: true
    tmpfs: [/tmp]
    cap_drop: [ALL]
    security_opt: ["no-new-privileges:true"]
    volumes:
      - storage:/var/www/html/storage
    networks: [backend, data]
    healthcheck:
      test: ["CMD", "php-fpm-healthcheck"]
  mysql:
    image: mysql:8.4
    networks: [data]          # no "ports:" at all
networks:
  backend: {}
  data:
    internal: true            # no route to the internet
volumes:
  storage: {}

Файлова система само за четене, в която може да се пише единствено в storage/ и /tmp, означава, че нападател, открил грешка при качването на файлове, не може да остави PHP файл до вашия код. Премахването на всички Linux права и забраната за повишаване на привилегиите отнемат повечето от това, което би могъл да направи пробит процес. Ползвайте проверката на здравето, която пасва на образа ви; важното е всяка услуга да има такава, за да може Docker да рестартира заседналото.

#Не разчитайте защитната стена да скрие публикуваните портове

Тук се хващат и опитни хора. Docker записва собствени правила в iptables за всеки публикуван порт и те се прилагат, преди защитна стена като ufw да види трафика. Ред ports: ["3306:3306"] следователно отваря MySQL към интернет, дори когато ufw status казва, че са разрешени само 22, 80 и 443. Решението не е правило в защитната стена, а да не публикувате порта. Публикувайте само проксито, а ако ви трябва порт на базата за SSH тунел, вържете го към 127.0.0.1:3306:3306.

#Продукционните настройки на самия Laravel

  • APP_ENV=production и APP_DEBUG=false. Страницата за отстраняване на грешки показва променливите на средата на всеки, който предизвика грешка.
  • Файлът .env никога не е в образа (сложете го в .dockerignore) или в Git, а на сървъра се чете само от собственика си.
  • Зад прокси кажете на Laravel да му се доверява в bootstrap/app.php с $middleware->trustProxies(at: '*'), за да ползват адресите и защитените бисквитки HTTPS. Това е безопасно само ако нищо освен вашето прокси не може да стигне до приложението.
  • Пускайте php artisan migrate --force и php artisan optimize при всяко внедряване.
  • Пускайте работника с ограничения: php artisan queue:work --tries=3 --max-time=3600, така че заседнала задача или изтичане на памет да не продължават вечно. Пускайте планировчика с php artisan schedule:work в собствен контейнер.

#Уеб сървърът: малки правила, голям ефект

  • Връщайте 404 за файловете с точка (.env, .git) и за всеки PHP файл освен index.php.
  • Добавете X-Content-Type-Options, X-Frame-Options, Referrer-Policy и Permissions-Policy и скрийте версиите на сървъра.
  • Ограничете честотата на заявките към входа, регистрацията и смяната на парола.
  • Кеширайте файловете на Vite с хеш в името за година; когато съдържанието им се промени, се променя и името.

#Защитете и сървъра отдолу

Docker на небрежно настроен сървър си остава небрежно настроен сървър. Минимумът: SSH само с ключове и без вход като root; защитна стена, която пропуска SSH, 80 и 443; fail2ban срещу налучкване на пароли; автоматични обновления за сигурност; Docker CE от собственото хранилище на Docker. Ако го опишете като Ansible playbook вместо като списък с команди, вторият сървър ще е настроен точно като първия.

#Архиви, които наистина сте възстановявали

Архивът е архив едва след като сте го възстановили. Добра настройка за един сървър:

  1. Архивирайте базата последователно: mysqldump --single-transaction чете таблиците InnoDB, без да заключва приложението.
  2. Включете качените файлове от storage/app; база без файловете, към които сочи, е половин архив.
  3. Криптирайте, преди архивът да напусне сървъра, например с openssl enc -aes-256-cbc -pbkdf2 -pass env:BACKUP_PASSPHRASE, и пазете паролата някъде другаде, не на същия сървър.
  4. Прочитайте всеки архив обратно веднага след записа и пазете контролна сума до него.
  5. Въртете копията: например 7 дневни, 4 седмични и 6 месечни.
  6. Копирайте извън сървъра: архив на същия диск умира заедно с диска. rclone може да копира в съвместимо с S3 хранилище, Backblaze B2 или SFTP.
  7. Наблюдавайте: сигнал към услуга за наблюдение след всяко успешно архивиране и тревога, когато последният добър архив е твърде стар.
  8. Пробвайте възстановяване в нова, празна база на всеки няколко месеца и сравнявайте броя на редовете или контролните суми с продукцията.

За повече по поддръжката на приложение след пускането му вижте основите на сигурността на уеб приложенията и поддръжката на сайт след пускането му.

#Berthwell: тази среда като файлове, които можете да прочетете

Събрахме тази настройка в Berthwell: комплект с Docker Compose и Ansible за едно Laravel приложение на един сървър. Caddy с автоматичен HTTPS и HSTS, nginx, PHP-FPM 8.4, работник за опашката и планировчик от един образ, MySQL 8.4 и Redis във вътрешна мрежа, контейнери само за четене и без Linux права, проверки на здравето и ротация на логовете, като порт публикува само Caddy. Файловете за nginx и PHP съдържат правилата по-горе. Услуга за архиви всяка нощ архивира базата и качените файлове, криптира ги с AES-256, прочита всеки архив обратно, пази 7 дневни, 4 седмични и 6 месечни копия, може да ги копира с rclone и да съобщава на адрес за наблюдение; скрипт за възстановяване ги зарежда в нова база за проверка. Ansible playbook-ът защитава SSH, настройва защитната стена, fail2ban и автоматичните обновления, инсталира Docker CE и пуска средата като systemd услуга.

Комплектът казва честно докъде стига: едно приложение, един сървър, само MySQL, без внедряване без прекъсване. Неговите 44 автоматични теста проверяват файла на Compose, Dockerfile файловете, настройките на Caddy, nginx и PHP-FPM, пускат файловете на комплекта срещу истинско Laravel 13 приложение с MySQL 8.4 и Redis и архивират и възстановяват база с еднакви контролни суми. При тези тестове образите не са изграждани с Docker, а playbook-ът не е пускан срещу истински сървър, затова го казваме. Berthwell е обявен като „Очаквайте скоро“.

#Често задавани въпроси

По-сигурен ли е Docker от инсталиране на PHP направо на сървъра?

Не автоматично. Docker прави заключената настройка по-лесна за повтаряне и изолира услугите една от друга, но контейнер, който върви като root и публикува порта на базата, е по-малко безопасен от внимателна обикновена инсталация.

Защо ufw не блокира публикувания порт на Docker?

Docker добавя собствени правила в iptables за публикуваните портове и те се прилагат преди тези на ufw. Изобщо не публикувайте вътрешните портове или ги вържете към 127.0.0.1.

Колко често да пробвам възстановяване?

Поне на няколко месеца и след всяка промяна в настройката на архивите. Възстановяване, което никога не сте пробвали, е надежда, а не план.

Мога ли да пусна няколко Laravel приложения на един сървър по този начин?

Да, с едно прокси пред няколко среди, но тогава всяко приложение има нужда от свои мрежи, достъп до база и архиви. Berthwell е написан за едно приложение на сървър.

#Източници

Още по темата

Още по темата

Коментари

Коментари

Напишете първия коментар.

Оставете коментар

Следваща стъпка

pnscripts.com/bg/contact

Пишете на екипа

Попитайте за статия или ни разкажете за проект, който искате да изградим. Отговаряме до един работен ден.

Пишете ни

Семейството PN Scripts

Другите сайтове на PN Scripts

Хостингът, игрите и блогът имат свои сайтове, поддържани от същата компания.

  • pnscripts.com

    PN Scripts

    Софтуерно инженерство

    Уеб и мобилни приложения, API и игри по поръчка, плюс нашите продукти и плъгини с отворен код.

  • games.pnscripts.com

    Игри

    Игри и игрови сървъри

    Домът на игрите на PN Scripts: безплатни браузърни игри, които се играят веднага, а игровите сървъри предстоят.

  • hosting.pnscripts.com

    Хостинг

    Хостинг и инфраструктура

    Споделен хостинг, KVM VPS, самостоятелни сървъри и домейни от същата компания, която изработва проекта ви.

  • blog.pnscripts.com

    Блог

    Статии и бележки

    Практични статии за разработка на софтуер, хостинг, отворен код и игри, писани от хората, които ги създават.

    Вие сте тук