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