Traefik: заголовки безопасности, ограничение запросов и доступ через middleware

Если перед сервисами стоит Traefik, защитные настройки удобнее задавать в нём: одни и те же правила для всех приложений, без правки каждого. Заголовки безопасности, ограничение частоты запросов, закрытие административных путей по адресам и паролю описываются обработчиками (middleware) и подключаются к маршрутам. Ниже конфигурация, которую можно скопировать, и проверка результата командами curl с реальными ответами.
Статья опирается на логику направления «Сетевая безопасность» из методики зрелости Группы Сбер (источник); это методика для крупных компаний, мы используем её как ориентир и пересказываем своими словами. Конфигурация проверена на Traefik 3.7 в одноразовом контейнере с тестовым сервером nginx за ним, без доступа к рабочим службам. Синтаксис различается между мажорными версиями Traefik (например, ipAllowList появился в третьей версии вместо ipWhiteList): сверяйте с документацией вашей версии. Настройка заголовков на самом веб-сервере: безопасная настройка nginx.
Что должно получиться
| Уровень | Что это значит на практике |
|---|---|
| 2. Гигиенический | Публичные сервисы защищены от типовых атак, доступ к административным интерфейсам ограничен, правила пересматриваются |
Критерии сформулированы нами по мотивам методики, это ориентир, а не оценка соответствия.
Статическая конфигурация
Файл traefik.yml: точка входа, провайдер из каталога файлов с отслеживанием изменений, журнал доступа.
# /etc/traefik/traefik.yml: статическая конфигурация (пример для проверки)
entryPoints:
web:
address: ":80"
providers:
file:
directory: /dynamic
watch: true
api:
dashboard: false
accessLog: {}
log:
level: INFO
В рабочей установке добавляются точка входа websecure с TLS (сертификаты Let’s Encrypt), перенаправление с HTTP на HTTPS и отключённый или защищённый дашборд. Здесь для проверки используется открытый HTTP внутри тестовой сети.
Маршруты и middleware
Файл /dynamic/app.yml (динамическая конфигурация: Traefik перечитывает его без перезапуска). Адрес сервиса, разрешённую сеть и хэш пароля замените своими: хэш создаётся командой openssl passwd -apr1 'ПАРОЛЬ', при записи в Docker Compose символы $ нужно удваивать.
# /dynamic/app.yml: маршруты и промежуточные обработчики (middleware)
http:
routers:
app:
rule: "Host(`example.com`)"
entryPoints: [web]
service: app
middlewares: [secure-headers, rate-limit]
admin:
rule: "Host(`example.com`) && PathPrefix(`/admin`)"
priority: 100
entryPoints: [web]
service: app
middlewares: [admin-only, admin-auth, secure-headers]
services:
app:
loadBalancer:
servers:
- url: "http://backend:80"
middlewares:
secure-headers:
headers:
contentTypeNosniff: true
customFrameOptionsValue: "SAMEORIGIN"
referrerPolicy: "strict-origin-when-cross-origin"
permissionsPolicy: "geolocation=(), microphone=(), camera=()"
stsSeconds: 31536000
forceSTSHeader: true
customResponseHeaders:
X-Powered-By: ""
Server: ""
rate-limit:
rateLimit:
average: 5
burst: 10
period: 1s
admin-only:
ipAllowList:
sourceRange:
- "203.0.113.0/24"
admin-auth:
basicAuth:
users:
- "admin:$apr1$ХЭШ_ИЗ_openssl_passwd"
| Элемент | Что делает |
|---|---|
Маршрут app |
Весь трафик для example.com с заголовками безопасности и ограничением частоты |
Маршрут admin |
Путь /admin: только разрешённые адреса, затем пароль, затем заголовки; высокий priority, чтобы он сработал раньше общего маршрута |
secure-headers |
Заголовки X-Content-Type-Options, X-Frame-Options, Referrer-Policy, Permissions-Policy, Strict-Transport-Security; пустые значения Server и X-Powered-By удаляют эти заголовки из ответа |
rate-limit |
В среднем 5 запросов в секунду с адреса, всплеск до 10 |
admin-only |
Разрешает доступ только из указанной сети (в Traefik 3 это ipAllowList) |
admin-auth |
Пароль через basicAuth (подходит для небольшой команды; для серьёзного доступа используйте единый вход и MFA) |
Порядок в списке middlewares важен: они выполняются слева направо. Ограничение по адресу ставьте раньше пароля, чтобы не давать постороннему возможность подбирать его.
Проверка
В тесте Traefik стоял по адресу контейнера, а запросы шли с заголовком Host: example.com.
Заголовки
curl -sI -H "Host: example.com" http://АДРЕС_TRAEFIK/
Результат: статус 200, заголовки Permissions-Policy, Referrer-Policy, Strict-Transport-Security: max-age=31536000, X-Content-Type-Options: nosniff, X-Frame-Options: SAMEORIGIN. Заголовков Server и X-Powered-By в ответе нет, хотя тестовый сервер nginx их отправляет. Параметр forceSTSHeader заставляет отправлять HSTS и по HTTP: в рабочей среде при TLS он не нужен.
Неизвестное имя
curl -s -o /dev/null -w "%{http_code}\n" -H "Host: other.example" http://АДРЕС_TRAEFIK/
Результат: 404. Traefik не отдаёт сервис по чужому имени: такой ответ удобен как «глухой» ответ на запросы по адресу без имени.
Ограничение частоты
for i in $(seq 1 40); do curl -s -o /dev/null -w "%{http_code}\n" -H "Host: example.com" http://АДРЕС_TRAEFIK/; done | sort | uniq -c
Результат из 40 быстрых запросов: 12 ответов 200 и 28 ответов 429. Первые запросы уместились во всплеск (10) и среднюю скорость, остальные получили 429 Too Many Requests. Для обычных посетителей лимит в 5 запросов в секунду достаточен, для страниц с большим числом ресурсов поднимите значения. Источником адреса считается адрес соединения; за внешним прокси или CDN настройте определение настоящего адреса клиента (параметры ipStrategy и доверенные адреса), иначе лимит распространится на всех посетителей сразу.
Административный путь
| Условие | Результат |
|---|---|
| Адрес клиента не входит в разрешённую сеть | 403 (даже с правильным паролем) |
Сеть разрешена (изменили sourceRange), пароль не передан |
401 |
| Сеть разрешена, пароль неверный | 401 |
| Сеть разрешена, пароль верный | Запрос дошёл до сервиса (в тесте ответ 404, так как на тестовом сервере страницы /admin/ нет: это ответ сервиса, а не защиты) |
Изменение файла динамической конфигурации применилось без перезапуска Traefik за несколько секунд (провайдер файлов с watch: true).
Применение на рабочем Traefik
- Добавьте файл middleware в каталог динамической конфигурации; подключайте их к существующим маршрутам постепенно, начиная с заголовков.
- Для маршрутов, заданных метками контейнеров Docker, подключение выглядит как
traefik.http.routers.ИМЯ.middlewares=secure-headers@file: middleware из файла доступны под суффиксом@file. - Включайте
rateLimitиipAllowListосторожно: ошибка может отрезать вас или пользователей. Держите запасной доступ и проверяйте правила изнутри и снаружи. - Следите за журналом доступа Traefik: он должен попадать в систему сбора: сбор логов, Fail2ban по журналам.
- Изменения оформляйте через карточку: управление изменениями.
Заголовки безопасности можно задавать и в Traefik, и в самом приложении, но не в двух местах с разными значениями: определите, где источник истины.
Диагностика
| Симптом | Причина и что делать |
|---|---|
| Ответ 404 на нужный адрес | Не совпало правило маршрута (имя, путь, точка входа). Проверьте rule и entryPoints |
| Middleware не применяется | Не указан в middlewares маршрута или неверное имя. Для файлового провайдера в метках используйте имя@file |
| Для всех посетителей ответ 429 | За прокси виден один адрес. Настройте ipStrategy и доверенные источники заголовка с настоящим адресом |
| Административный путь открыт всем | Маршрут общий перехватывает путь. Задайте priority выше у маршрута admin |
| Ошибка в журнале Traefik при чтении файла | Синтаксис YAML или устаревшее имя параметра для вашей версии (ipWhiteList и ipAllowList). Смотрите журнал запуска и документацию версии |
| Хэш пароля не работает в Compose | Знак $ нужно удваивать ($$) в файлах Compose |
Где это в модели зрелости
Материал относится к направлению «Сетевая безопасность» (материалы направления, карта зрелости). Уровень 2 требует управляемого периметра и защиты публичных сервисов; централизованные middleware на входе дают единый набор правил для всех приложений.
Как проверить
- В ответах всех публичных маршрутов есть заголовки безопасности, заголовки
ServerиX-Powered-Byотсутствуют. - Ограничение частоты срабатывает при быстрой серии запросов и не блокирует обычного посетителя.
- Административные пути недоступны вне разрешённых адресов и требуют пароля или единого входа.
- Журнал доступа Traefik собирается и разбирается; изменения конфигурации идут через карточки изменений.
Источники
- Методика оценки уровня зрелости кибербезопасности компании, Группа Сбер: sberbank.ru.
- Документация Traefik: HTTP middlewares (
headers,rateLimit,ipAllowList,basicAuth), файловый провайдер: doc.traefik.io. - OWASP Secure Headers Project: owasp.org. Международная практика, не российский норматив.





















