Модул 7 · 7.4
Caddy, HSTS и security.txt
Модул 7: Домейн, уеб сървър и https · Време за четене: 12 минути
Цел на урока: След този урок ще можете да използвате Caddy като лесен уеб сървър с автоматичен https, да задавате HSTS и най-важните защитни headers (в Caddy и nginx), без сайтът Ви да стане недостъпен, и да публикувате валиден security.txt.
Основни понятия: Caddy, Caddyfile, автоматичен https, reverse_proxy, file_server, header, HSTS (Strict-Transport-Security), max-age, includeSubDomains, preload, X-Content-Type-Options, frame-ancestors, Referrer-Policy, security.txt, Contact, Expires, /.well-known/
Caddy: https без грижи
В уроци 7.2 и 7.3 изградихте сайт с https с помощта на nginx и certbot. Това са две програми, няколко файла и един таймер. Caddy прави всичко това наведнъж: той е уеб сървър с вграден ACME клиент. Ако дадено име е записано в конфигурацията, сочи към сървъра и портове 80 и 443 са достъпни, Caddy сам получава сертификат, подновява го навреме и пренасочва http към https.
Caddy е наличен в хранилището с пакети на Debian и Ubuntu (в малко по-стара версия); създателите му предлагат и собствено apt хранилище с най-новата версия, със собствен ключ и Signed-By, както в урок 4.1. В модул 9 Caddy работи като контейнер за Вашите Docker услуги (урок 9.4). Тук ще го използвате като обикновена услуга.
anna@web1:~$ sudo apt install caddy
anna@web1:~$ systemctl status caddyИзползвайте или nginx, или Caddy на портове 80 и 443, но не и двете: само една програма може да слуша на даден порт.
Caddyfile
Конфигурацията се намира в /etc/caddy/Caddyfile. Статичен сайт и приложение зад reverse proxy:
example.com, www.example.com {
root * /var/www/example.com
file_server
encode zstd gzip
}
app.example.com {
reverse_proxy 127.0.0.1:3000
}Това е всичко. Caddy автоматично слуша на IPv4 и IPv6, заявява сертификати за трите имена, пренасочва http към https и при reverse_proxy сам предава заглавните редове X-Forwarded-For и X-Forwarded-Proto (нещо, което в nginx правехте ръчно, урок 7.2).
Проверката и презареждането стават както при nginx:
anna@web1:~$ caddy validate --config /etc/caddy/Caddyfile
Valid configuration
anna@web1:~$ sudo systemctl reload caddy
anna@web1:~$ sudo journalctl -u caddy -fВ журнала ще видите получаването на сертификатите. Ако нещо се обърка, причината е записана там: обикновено е същата като при certbot (порт 80 е затворен, името сочи другаде, IPv6 не работи; урок 7.3).
HSTS: браузърът да запомни, че сайтът е https
Пренасочването от http към https има един слаб момент: най-първата заявка по http все още не е криптирана. Някой в същата мрежа може да я прихване и да задържи посетителя на http. HSTS (HTTP Strict Transport Security, RFC 6797) решава този проблем. Вашият сайт изпраща по https следния заглавен ред:
Strict-Transport-Security: max-age=31536000Тогава браузърът запомня за една година (31.536.000 секунди), че това име може да се отваря само по https. Ако по-късно посетителят напише http://, браузърът сам го превръща в https, още преди нещо да бъде изпратено по мрежата. HSTS заглавие, получено по http, браузърът пренебрегва; то важи само по https.
Има и обратна страна. Ако браузърът е запомнил HSTS и види грешка в сертификата (изтекъл, грешно име), посетителят не получава бутон, с който да продължи въпреки това. Тогава сайтът Ви е недостъпен за тези посетители, докато сертификатът не бъде наред. Затова:
- Включете HSTS едва когато https работи навсякъде и подновяването става автоматично (урок 7.3).
- Започнете с кратък
max-age, например един ден (86400), и го увеличавайте постепенно до една година. - Бъдете внимателни с
includeSubDomains: той важи за всеки поддомейн, включително стар поддомейн с устройство или услуга без валиден сертификат. preloadе заявка за включване в списък, вграден в браузърите (hstspreload.org). За целта се изискват, наред с други неща,max-ageот поне една година,includeSubDomainsи https на всички поддомейни. Премахването от списъка отнема месеци. Направете го само ако сте сигурни, че целият домейн ще бъде завинаги на https.
Другите защитни headers
Освен HSTS има няколко заглавни реда, които с малко усилия помагат много:
| Заглавен ред | Срещу какво | Начална стойност |
|---|---|---|
X-Content-Type-Options | браузърът се опитва да познае какъв е даден файл, така че качена "картинка" може да се изпълни като скрипт | nosniff |
Content-Security-Policy: frame-ancestors (или по-старият X-Frame-Options) | clickjacking: друг сайт зарежда Вашата страница невидимо във frame | frame-ancestors 'self' (или X-Frame-Options: SAMEORIGIN) |
Referrer-Policy | адресът на Вашата страница (с думи за търсене или номера на поръчки) изтича към други сайтове | strict-origin-when-cross-origin |
Пълният Content-Security-Policy (кои скриптове и стилове могат да се зареждат) се настройва индивидуално за всеки сайт и е извън обхвата на този курс; започнете само с frame-ancestors.
В Caddy ги задавате с директивата header в блока на даден сайт:
example.com, www.example.com {
root * /var/www/example.com
file_server
header {
Strict-Transport-Security "max-age=31536000"
X-Content-Type-Options "nosniff"
Content-Security-Policy "frame-ancestors 'self'"
Referrer-Policy "strict-origin-when-cross-origin"
-Server
}
}Caddy не задава HSTS и тези заглавни редове сам; затова трябва наистина да ги добавите. -Server премахва заглавния ред Server. В урок 9.4 в същия вид блок ще зададете собствен заглавен ред X-Weboke-Cursus за задачата.
В nginx задавате същите редове в блока server за порт 443:
add_header Strict-Transport-Security "max-age=31536000" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Content-Security-Policy "frame-ancestors 'self'" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;always гарантира, че те се изпращат и при страници за грешки. Внимавайте с един известен капан на nginx: ако в блок location има дори един собствен add_header, редовете add_header от блока server вече не важат за този location. Затова винаги проверявайте резултата:
anna@laptop:~$ curl -sI https://example.com | grep -iE 'strict|nosniff|frame|referrer'
strict-transport-security: max-age=31536000
x-content-type-options: nosniff
content-security-policy: frame-ancestors 'self'
referrer-policy: strict-origin-when-cross-originsecurity.txt: как някой може да Ви съобщи за уязвимост
Да приемем, че някой открие уязвимост на Вашия сървър, например отворена папка или остаряла услуга. Как този човек да разбере кого да предупреди? За това служи security.txt (RFC 9116): малък текстов файл на определено място, https://<jouwdomein>/.well-known/security.txt.
Contact: mailto:security@example.com
Expires: 2027-10-31T23:00:00Z
Preferred-Languages: nl, en
Canonical: https://example.com/.well-known/security.txtПравилата:
Contactе задължителен:mailto:,https://илиtel:адрес, на който пристигат съобщенията. Използвайте адрес, който някой наистина чете.Expiresе задължителен и може да се среща само веднъж: датата, след която файлът се счита за остарял. RFC препоръчва дата в рамките на една година. Така не остава файл с отдавна забравен адрес. Отбележете в календара си кога трябва да го подновите.Preferred-Languages,CanonicalиPolicy(връзка към Вашата политика за съобщения) са по избор.- Файлът трябва да се предоставя по https, като обикновен текст (
text/plain, UTF-8). Старото място/security.txtможе да се използва допълнително, но/.well-known/има предимство.
Поставяне при статичен сайт:
anna@web1:~$ sudo mkdir -p /var/www/example.com/.well-known
anna@web1:~$ sudoedit /var/www/example.com/.well-known/security.txt
anna@laptop:~$ curl -sI https://example.com/.well-known/security.txt | grep -i content-type
content-type: text/plain; charset=utf-8Ако в nginx използвате предпазната мрежа location ~ /\. { deny all; } от урок 7.2, добавете преди нея изключение: location ^~ /.well-known/ { }. За приложение зад reverse proxy в Caddy можете да насочите пътя /.well-known/security.txt към файл с блок handle и file_server.
Честа грешка: сайт или приложение, което отговаря на всеки непознат път с началната страница и статус 200. Тогава security.txt "съществува", но е HTML. Затова проверявайте и съдържанието.
На AlmaLinux и Rocky Linux
Там Caddy се инсталира от хранилището на създателите или от EPEL/COPR. Както при nginx, SELinux трябва да разреши reverse proxy (setsebool -P httpd_can_network_connect 1). Caddyfile и headers са същите.
Обобщение
- Caddy сам се грижи за сертификатите, подновяването и пренасочването към https;
reverse_proxyавтоматично предава правилните заглавни редове. - HSTS кара браузърите да запомнят, че сайтът Ви е само https; увеличавайте
max-ageпостепенно, бъдете внимателни сincludeSubDomainsи много внимателни сpreload. - Задайте освен това
X-Content-Type-Options: nosniff,frame-ancestors 'self'иReferrer-Policy; в Caddy сheader, в nginx сadd_header ... always, и проверявайте сcurl -I. - security.txt се намира на
/.well-known/security.txt, по https, като обикновен текст, със задължителенContactи единExpiresв рамките на една година.
Тест (quiz)
Задача
Задача Z7.4: HSTS, headers и security.txt. Задайте за Вашия сайт HSTS (поне 180 дни, след като https е работил добре известно време), X-Content-Type-Options: nosniff и frame-ancestors 'self' (или X-Frame-Options) и публикувайте security.txt с Contact и Expires в рамките на една година. Въведете името на Вашия хост. Програмата зарежда https://<hostnaam>/ и проверява заглавните редове, а след това зарежда https://<hostnaam>/.well-known/security.txt и проверява полетата. Тя не съхранява нищо освен резултата и датата в Expires.
Още за четене
- HSTS и защитни headers за Вашия уебсайт
- Caddy, Automatic HTTPS: https://caddyserver.com/docs/automatic-https
- Caddy, директивата header: https://caddyserver.com/docs/caddyfile/directives/header
- RFC 6797, HTTP Strict Transport Security: https://www.rfc-editor.org/rfc/rfc6797.html
- Списъкът за HSTS preload и изискванията: https://hstspreload.org/
- RFC 9116, security.txt: https://www.rfc-editor.org/rfc/rfc9116.html
- securitytxt.org (създаване и проверка): https://securitytxt.org/
