Стадия жизненного цикла модуля: Общедоступная версия
У модуля есть требования для установки
Как сконфигурировать балансировщик нагрузки для проверки доступности IngressNginxController?
В ситуации, когда Ingress-контроллер размещен за балансировщиком нагрузки, рекомендуется сконфигурировать балансировщик для проверки доступности узлов Ingress-контроллер с помощью HTTP-запросов или TCP-подключений. В то время как тестирование с помощью TCP-подключений представляет собой простой и универсальный механизм проверки доступности, мы рекомендуем использовать проверку на основе HTTP-запросов со следующими параметрами:
- протокол:
HTTP; - путь:
/healthz; - порт:
80(в случае использования инлетаHostPortнужно указать номер порта, соответствующий параметруhttpPort).
Как настроить работу через MetalLB с доступом только из внутренней сети?
Пример MetalLB с настройками доступа только из внутренней сети:
apiVersion: deckhouse.io/v2
kind: IngressNginxController
metadata:
name: main
spec:
ingressClass: "nginx"
inlet: "LoadBalancer"
loadBalancer:
sourceRanges:
- 192.168.0.0/24Для работы необходимо включить параметр svcSourceRangeCheck в модуле cni-cilium.
Как добавить дополнительные поля для логирования в nginx-controller?
Пример добавления дополнительных полей:
apiVersion: deckhouse.io/v2
kind: IngressNginxController
metadata:
name: main
spec:
ingressClass: "nginx"
inlet: "LoadBalancer"
additionalLogFields:
my-cookie: "$cookie_MY_COOKIE"Как включить HorizontalPodAutoscaling для IngressNginxController?
Режим HPA возможен только для контроллеров с инлетом LoadBalancer или LoadBalancerWithProxyProtocol.
Режим HPA возможен только при minReplicas != maxReplicas, в противном случае deployment hpa-scaler не создается.
Для включения HPA используйте атрибуты minReplicas и maxReplicas в IngressNginxController CR.
IngressNginxController разворачивается с помощью DaemonSet. DaemonSet не предоставляет возможности горизонтального масштабирования, поэтому создается дополнительный deployment hpa-scaler и HPA resource, который следит за предварительно созданной метрикой prometheus-metrics-adapter-d8-ingress-nginx-cpu-utilization-for-hpa. Если CPU utilization превысит 50%, HPA закажет новую реплику для hpa-scaler (с учетом minReplicas и maxReplicas).
Deployment hpa-scaler обладает HardPodAntiAffinity (запрет на размещение подов с одинаковыми лейблами на одном узле), поэтому он попытается выделить для себя новый узел (если это возможно
в рамках своей группы узлов), куда автоматически будет размещен еще один instance Ingress-контроллера.
- Минимальное реальное количество реплик IngressNginxController не может быть меньше минимального количества узлов в группе узлов, куда он разворачивается.
- Максимальное реальное количество реплик IngressNginxController не может быть больше максимального количества узлов в группе узлов, куда он разворачивается.
Как использовать IngressClass с установленными IngressClassParameters?
Начиная с версии 1.1 IngressNginxController, Deckhouse создает объект IngressClass самостоятельно. Если вы хотите использовать свой IngressClass с установленными IngressClassParameters, достаточно добавить к нему label ingress-class.deckhouse.io/external: "true":
apiVersion: networking.k8s.io/v1
kind: IngressClass
metadata:
labels:
ingress-class.deckhouse.io/external: "true"
name: my-super-ingress
spec:
controller: ingress-nginx.deckhouse.io/my-super-ingress
parameters:
apiGroup: elbv2.k8s.aws
kind: IngressClassParams
name: awesome-class-cfgВ этом случае, при указании данного IngressClass в CRD IngressNginxController, Deckhouse не будет создавать объект, а использует существующий.
Как отключить сборку детализированной статистики Ingress-ресурсов?
По умолчанию Deckhouse собирает подробную статистику со всех Ingress-ресурсов в кластере. Этот процесс может приводить к высокой нагрузке системы мониторинга.
Для отключения сбора статистики добавьте лейбл ingress.deckhouse.io/discard-metrics: "true" к соответствующему пространству имён или Ingress-ресурсу.
Пример отключения сбора статистики (метрик) для всех Ingress-ресурсов в пространстве имен review-1:
d8 k label ns review-1 ingress.deckhouse.io/discard-metrics=trueПример отключения сбора статистики (метрик) для всех Ingress-ресурсов test-site в пространстве имен development:
d8 k label ingress test-site -n development ingress.deckhouse.io/discard-metrics=trueКак корректно вывести из эксплуатации (drain) узел с запущенным IngressNginxController?
Доступно два способа корректного вывода из эксплуатации узла, на котором запущен IngressNginxController.
-
С помощью аннотации.
Аннотация будет автоматически удалена после завершения операции.
d8 k annotate node <node_name> update.node.deckhouse.io/draining=user -
С помощью d8 k drain.
При использовании стандартной команды d8 k drain необходимо указать флаг
--forceдаже при наличии--ignore-daemonsets, поскольку IngressNginxController развёрнут с использованием Advanced DaemonSet:d8 k drain <node_name> --delete-emptydir-data --ignore-daemonsets --force
Как включить Web Application Firewall (WAF)?
Для защиты веб-приложений от L7-атак используется программное обеспечение известное как Web Application Firewall (WAF).
В ingress-nginx контроллер встроен WAF под названием ModSecurity (проект Open Worldwide Application Security).
По умолчанию ModSecurity выключен.
Включение ModSecurity
Для включения ModSecurity необходимо задать параметры в кастомном ресурсе IngressNginxController, в секции config:
apiVersion: deckhouse.io/v2
kind: IngressNginxController
metadata:
name: <имя_контроллера>
spec:
config:
enable-modsecurity: "true"
modsecurity-snippet: |
Include /etc/nginx/modsecurity/modsecurity.confПосле применения настроек ModSecurity начнет работать для всего трафика, проходящего через данный ingress-nginx контроллер.
При этом используется режим аудита (DetectionOnly) и базовая рекомендуемая конфигурация.
Настройка ModSecurity
ModSecurity можно настраивать двумя способами:
- Для всего ingress-nginx контроллера
- необходимые директивы описываются в секции
config.modsecurity-snippetв кастомном ресурсе IngressNginxController, как в примере выше.
- необходимые директивы описываются в секции
- Для каждого кастомного ресурса Ingress по отдельности
- необходимые директивы описываются в аннотации
nginx.ingress.kubernetes.io/modsecurity-snippet: |непосредственно в манифестах Ingress.
- необходимые директивы описываются в аннотации
Использование snippet-аннотаций не рекомендуется из-за уязвимостей безопасности. Аннотация nginx.ingress.kubernetes.io/modsecurity-snippet относится к ним и не работает, если snippet-аннотации отключены. Ограничения можно задать с помощью параметра annotationPolicy, например установив allowSnippetAnnotations: false.
Чтобы включить выполнение правил (а не только логирование), добавьте директиву SecRuleEngine On по примеру ниже:
apiVersion: deckhouse.io/v2
kind: IngressNginxController
metadata:
name: <имя_контролера>
spec:
config:
enable-modsecurity: "true"
modsecurity-snippet: |
Include /etc/nginx/modsecurity/modsecurity.conf
SecRuleEngine OnДобавление SecRule
Пример правила для ограничения количества аргументов в URL-адресе запроса. Если количество аргументов превышает 10, сервер отклоняет запрос с кодом ошибки 400 (Bad request).
apiVersion: deckhouse.io/v2
kind: IngressNginxController
metadata:
name: <имя_контролера>
spec:
config:
enable-modsecurity: "true"
modsecurity-snippet: |
Include /etc/nginx/modsecurity/modsecurity.conf
SecRuleEngine On
SecRule &ARGS "@gt 10" "id:100100,phase:2,deny,status:400,log,auditlog,severity:WARNING,msg:\"too many args\""Полный перечень и описание директив вы можете найти в официальной документации.
Включение OWASP Core Rule Set (CRS)
Для включения OWASP Core Rule Set (CRS) необходимо добавить config.enable-owasp-modsecurity-crs в кастомном ресурсе IngressNginxController, как показано на примере ниже.
apiVersion: deckhouse.io/v2
kind: IngressNginxController
metadata:
name: <name_of_the_controller>
spec:
config:
enable-modsecurity: "true"
enable-owasp-modsecurity-crs: "true"
modsecurity-snippet: |
Include /etc/nginx/modsecurity/modsecurity.conf
SecRuleEngine OnДополнительные аннотации Ingress
Зачем нужны дополнительные аннотации?
Snippet-аннотации (nginx.ingress.kubernetes.io/configuration-snippet, nginx.ingress.kubernetes.io/server-snippet, nginx.ingress.kubernetes.io/auth-snippet) предназначены для вставки произвольных директив в конфигурацию NGINX: их содержимое переносится в неё как есть, без какой-либо проверки. Контроллер не отличает корректные изменения от ошибочных или злонамеренных — любое из них попадает в общую конфигурацию NGINX, которая обслуживает все Ingress-ресурсы контроллера. Ошибка в snippet-аннотации одного Ingress-ресурса может нарушить работу других, а пользователь с правом создавать Ingress-ресурсы может выполнить произвольные директивы NGINX и код на Lua: прочитать файлы контроллера, включая токен его ServiceAccount и TLS-ключи всех Ingress-ресурсов, или обратиться к внутренним сервисам кластера. На этом основана, в частности, уязвимость CVE-2021-25742. По этим причинам в upstream-версии ingress-nginx snippet-аннотации по умолчанию отключены.
В модуле snippet-аннотации по умолчанию разрешены: если в annotationPolicy не указано иное, контроллер получает параметры allow-snippet-annotations: "true" и annotations-risk-level: Critical. Описанные ниже аннотации покрывают наиболее распространённые сценарии использования snippet-аннотаций и позволяют отказаться от них, установив annotationPolicy.allowSnippetAnnotations: false. В отличие от snippet-аннотаций:
- значения аннотаций проходят проверку, а Ingress-ресурс с некорректным значением отклоняется;
- конфигурация NGINX формируется по фиксированным шаблонам, и значения аннотаций не попадают в неё в исходном виде;
- аннотации продолжают работать при
allowSnippetAnnotations: false.
Если snippet-аннотации необходимо оставить включёнными, их содержимое можно ограничить параметром annotationPolicy.valueWordBlocklist: контроллер отклоняет Ingress-ресурс, если значение любой его аннотации nginx.ingress.kubernetes.io/ содержит одно из перечисленных слов, например load_module, lua_package, _by_lua, location, root, proxy_pass или serviceaccount. Такой список отсеивает заведомо опасные конструкции, но не делает snippet-аннотации безопасными. Он применяется ко всем аннотациям, включая описанные ниже, и не должен содержать слов, которые могут понадобиться в их значениях.
Аннотации реализованы в Deckhouse и отсутствуют в upstream-версии ingress-nginx; их поддерживают все версии контроллера, доступные в модуле.
Уровни риска
При заданном параметре annotationPolicy.riskLevel контроллер отклоняет Ingress-ресурсы, содержащие аннотации с более высоким уровнем риска; если параметр не задан, допускаются аннотации любого уровня.
| Аннотация | Уровень риска |
|---|---|
nginx.ingress.kubernetes.io/ingress-nginx-hsts |
Не проверяется |
nginx.ingress.kubernetes.io/ingress-nginx-hsts-max-age, nginx.ingress.kubernetes.io/ingress-nginx-hsts-include-subdomains, nginx.ingress.kubernetes.io/ingress-nginx-hsts-preload |
Low |
nginx.ingress.kubernetes.io/proxy-ssl-use-controller-certificate |
Не проверяется |
nginx.ingress.kubernetes.io/auth-proxy-ssl-use-controller-certificate |
Medium |
nginx.ingress.kubernetes.io/auth-proxy-ssl-protocols, nginx.ingress.kubernetes.io/auth-proxy-ssl-server-name, nginx.ingress.kubernetes.io/auth-proxy-ssl-name |
Low |
nginx.ingress.kubernetes.io/auth-proxy-connect-timeout, nginx.ingress.kubernetes.io/auth-proxy-send-timeout, nginx.ingress.kubernetes.io/auth-proxy-read-timeout |
Low |
nginx.ingress.kubernetes.io/auth-response-headers-to-client |
Medium |
nginx.ingress.kubernetes.io/auth-error-status-codes |
Low |
nginx.ingress.kubernetes.io/proxy-request-headers |
Medium |
nginx.ingress.kubernetes.io/response-headers-by-status |
Medium |
nginx.ingress.kubernetes.io/limit-req-status-code, nginx.ingress.kubernetes.io/limit-conn-status-code |
Low |
nginx.ingress.kubernetes.io/fetch-metadata-protection, nginx.ingress.kubernetes.io/fetch-metadata-sites, nginx.ingress.kubernetes.io/fetch-metadata-deny-modes, nginx.ingress.kubernetes.io/fetch-metadata-allowed-methods, nginx.ingress.kubernetes.io/fetch-metadata-status-code |
Low |
nginx.ingress.kubernetes.io/redirect-preserve |
Low |
nginx.ingress.kubernetes.io/response-html-head-injection |
High |
Для каждого примера ниже в скобках указан наибольший уровень риска среди всех его аннотаций, включая upstream-аннотации, то есть минимальное значение riskLevel, при котором пример работает.
Директивы, которые не нужно заменять
Некоторые директивы дублируют действия самого контроллера, и их достаточно удалить:
proxy_set_header Authorization $http_authorization;иproxy_set_header X-Auth-Token $http_x_auth_token;вnginx.ingress.kubernetes.io/auth-snippet— контроллер передаёт сервису аутентификации все заголовки исходного запроса;proxy_set_header X-Real-IP $remote_addr;вnginx.ingress.kubernetes.io/auth-snippet— контроллер устанавливает этот заголовок самостоятельно.
Статические заголовки, которые добавляются во все ответы (например, X-Frame-Options или Content-Security-Policy), задаются upstream-аннотацией nginx.ingress.kubernetes.io/custom-headers со ссылкой на ConfigMap. Заголовки X-Frame-Options, X-Content-Type-Options и Content-Security-Policy разрешены по умолчанию; остальные необходимо перечислить в параметре global-allowed-response-headers в spec.config, значение которого дополняет список по умолчанию.
Совместное использование со snippet-аннотациями
Ingress-ресурс отклоняется, если одна из аннотаций ниже и snippet-аннотация того же ресурса задают одну и ту же директиву. Повторное задание большинства директив в одном location NGINX считает ошибкой конфигурации, а для остальных объединяет значения или игнорирует второе из них.
| Аннотация | Snippet-аннотация | Директивы, которые в ней не допускаются |
|---|---|---|
nginx.ingress.kubernetes.io/proxy-ssl-use-controller-certificate |
configuration-snippet |
proxy_ssl_certificate, proxy_ssl_certificate_key, proxy_ssl_protocols, proxy_ssl_session_reuse |
nginx.ingress.kubernetes.io/auth-proxy-ssl-use-controller-certificate |
auth-snippet |
proxy_ssl_certificate, proxy_ssl_certificate_key |
nginx.ingress.kubernetes.io/auth-proxy-ssl-protocols, nginx.ingress.kubernetes.io/auth-proxy-ssl-server-name, nginx.ingress.kubernetes.io/auth-proxy-ssl-name |
auth-snippet |
proxy_ssl_protocols, proxy_ssl_server_name, proxy_ssl_name соответственно |
nginx.ingress.kubernetes.io/auth-proxy-connect-timeout, nginx.ingress.kubernetes.io/auth-proxy-send-timeout, nginx.ingress.kubernetes.io/auth-proxy-read-timeout |
auth-snippet |
proxy_connect_timeout, proxy_send_timeout, proxy_read_timeout соответственно |
nginx.ingress.kubernetes.io/limit-req-status-code, nginx.ingress.kubernetes.io/limit-conn-status-code |
configuration-snippet |
limit_req_status, limit_conn_status соответственно |
nginx.ingress.kubernetes.io/response-html-head-injection |
configuration-snippet |
sub_filter_once |
Комментарии в snippet-аннотациях не учитываются, однако имя директивы внутри значения в кавычках принимается за директиву. Параметр location-snippet из spec.config вставляется в каждый location без такой проверки, и задавать в нём эти директивы также нельзя.
HSTS: nginx.ingress.kubernetes.io/ingress-nginx-hsts
Было (Critical):
nginx.ingress.kubernetes.io/configuration-snippet: |
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;Стало (уровень риска не проверяется):
nginx.ingress.kubernetes.io/ingress-nginx-hsts: "true"Аннотация добавляет заголовок Strict-Transport-Security: max-age=31536000; includeSubDomains во все ответы location-блоков Ingress-ресурса. Параметры политики задаются следующими аннотациями (уровень Low), добавленными в версии модуля v1.3.0; при отсутствии ingress-nginx-hsts: "true" Ingress-ресурс с этими аннотациями отклоняется:
nginx.ingress.kubernetes.io/ingress-nginx-hsts-max-age— значениеmax-ageв секундах, от0до63072000;nginx.ingress.kubernetes.io/ingress-nginx-hsts-include-subdomains— по умолчаниюtrue;nginx.ingress.kubernetes.io/ingress-nginx-hsts-preload— по умолчаниюfalse; для значенияtrueнужныincludeSubDomainsиmax-ageне меньше31536000.
nginx.ingress.kubernetes.io/ingress-nginx-hsts: "true"
nginx.ingress.kubernetes.io/ingress-nginx-hsts-max-age: "63072000"
nginx.ingress.kubernetes.io/ingress-nginx-hsts-preload: "true"Политика HSTS действует для всего хоста, и во всех его Ingress-ресурсах она должна быть одинаковой.
Аннотация имеет приоритет над общим для контроллера параметром hsts: в location-блоках Ingress-ресурса он не применяется, а заголовок Strict-Transport-Security, полученный от бэкенда или заданный аннотацией nginx.ingress.kubernetes.io/custom-headers, заменяется. Для отдельных кодов ответа другое значение по-прежнему можно задать аннотацией nginx.ingress.kubernetes.io/response-headers-by-status.
Клиентский сертификат контроллера для бэкенда: nginx.ingress.kubernetes.io/proxy-ssl-use-controller-certificate
Было (Critical):
nginx.ingress.kubernetes.io/configuration-snippet: |
proxy_ssl_certificate /etc/nginx/ssl/client.crt;
proxy_ssl_certificate_key /etc/nginx/ssl/client.key;
proxy_ssl_protocols TLSv1.2;
proxy_ssl_session_reuse on;Стало (Low — из-за backend-protocol; уровень риска самой аннотации не проверяется):
nginx.ingress.kubernetes.io/backend-protocol: "HTTPS"
nginx.ingress.kubernetes.io/proxy-ssl-use-controller-certificate: "true"Аннотация добавляет эти четыре директивы в location-блоки Ingress-ресурса: контроллер предъявляет HTTPS-бэкенду клиентский сертификат, выпущенный для него модулем. Поскольку сертификат удостоверяет контроллер, аннотация допускается только в Ingress-ресурсах модулей Deckhouse, то есть если имя пространства имён начинается с d8-, а у Ingress-ресурса есть метка heritage: deckhouse и непустая метка module. Аннотацию нельзя сочетать с nginx.ingress.kubernetes.io/proxy-ssl-secret, которая задаёт собственные параметры TLS для соединения с бэкендом: такой Ingress-ресурс отклоняется.
Клиентский сертификат контроллера для внешней аутентификации: nginx.ingress.kubernetes.io/auth-proxy-ssl-use-controller-certificate
Добавлена в версии модуля v1.3.0.
Было (Critical):
nginx.ingress.kubernetes.io/auth-snippet: |
proxy_ssl_certificate /etc/nginx/ssl/client.crt;
proxy_ssl_certificate_key /etc/nginx/ssl/client.key;Стало (High — из-за auth-url; уровень самой аннотации — Medium):
nginx.ingress.kubernetes.io/auth-url: "https://auth.example.com/check"
nginx.ingress.kubernetes.io/auth-proxy-ssl-use-controller-certificate: "true"Аннотация добавляет директивы proxy_ssl_certificate и proxy_ssl_certificate_key с сертификатом контроллера во внутренний location, через который отправляется запрос аутентификации; на соединение с бэкендом она не влияет. Для аннотации требуется nginx.ingress.kubernetes.io/auth-url со схемой https. Как и nginx.ingress.kubernetes.io/proxy-ssl-use-controller-certificate, аннотация допускается только в Ingress-ресурсах модулей Deckhouse.
TLS при внешней аутентификации: nginx.ingress.kubernetes.io/auth-proxy-ssl-protocols, nginx.ingress.kubernetes.io/auth-proxy-ssl-server-name, nginx.ingress.kubernetes.io/auth-proxy-ssl-name
Добавлены в версии модуля v1.3.0.
Было (Critical):
nginx.ingress.kubernetes.io/auth-snippet: |
proxy_ssl_protocols TLSv1.2 TLSv1.3;
proxy_ssl_server_name off;
proxy_ssl_name auth.internal;Стало (High — из-за auth-url; уровень самих аннотаций — Low):
nginx.ingress.kubernetes.io/auth-url: "https://auth.example.com/check"
nginx.ingress.kubernetes.io/auth-proxy-ssl-protocols: "TLSv1.2 TLSv1.3"
nginx.ingress.kubernetes.io/auth-proxy-ssl-server-name: "false"
nginx.ingress.kubernetes.io/auth-proxy-ssl-name: "auth.internal"Аннотации задают директивы proxy_ssl_protocols, proxy_ssl_server_name (по умолчанию on) и proxy_ssl_name в location аутентификации. Допустимые протоколы — TLSv1, TLSv1.1, TLSv1.2 и TLSv1.3; имя должно быть корректным DNS-именем. Для аннотаций требуется nginx.ingress.kubernetes.io/auth-url со схемой https.
Таймауты внешней аутентификации: nginx.ingress.kubernetes.io/auth-proxy-connect-timeout, nginx.ingress.kubernetes.io/auth-proxy-send-timeout, nginx.ingress.kubernetes.io/auth-proxy-read-timeout
Добавлены в версии модуля v1.3.0.
Было (Critical):
nginx.ingress.kubernetes.io/auth-snippet: |
proxy_connect_timeout 2;
proxy_send_timeout 2;
proxy_read_timeout 2;Стало (Low):
nginx.ingress.kubernetes.io/auth-proxy-connect-timeout: "2"
nginx.ingress.kubernetes.io/auth-proxy-send-timeout: "2"
nginx.ingress.kubernetes.io/auth-proxy-read-timeout: "2"Аннотации задают директивы proxy_connect_timeout, proxy_send_timeout и proxy_read_timeout в location аутентификации (в секундах, от 1 до 3600). По умолчанию таймаут установки соединения — 15 секунд, таймауты отправки и чтения — 60 секунд. Аннотации nginx.ingress.kubernetes.io/proxy-*-timeout действуют только на соединение с бэкендом. При превышении таймаута клиент получает ответ 500.
Передача клиенту заголовков ответа аутентификации: nginx.ingress.kubernetes.io/auth-response-headers-to-client
Добавлена в версии модуля v1.3.0.
Было (Critical):
nginx.ingress.kubernetes.io/configuration-snippet: |
auth_request_set $token_rotation $upstream_http_x_token_rotation_pending;
add_header X-Token-Rotation-Pending $token_rotation always;Стало (Medium):
nginx.ingress.kubernetes.io/auth-response-headers-to-client: "X-Token-Rotation-Pending"Аннотация принимает перечисленные через запятую заголовки ответа сервиса аутентификации и добавляет их в ответ клиенту при любом коде ответа. Заголовки, которых нет в ответе сервиса аутентификации, не добавляются. Для передачи заголовков бэкенду предназначена стандартная аннотация nginx.ingress.kubernetes.io/auth-response-headers. Заголовки добавляются директивой add_header: если заголовок с тем же именем задан аннотацией nginx.ingress.kubernetes.io/custom-headers или nginx.ingress.kubernetes.io/response-headers-by-status, клиент получает оба значения.
Коды ошибок аутентификации: nginx.ingress.kubernetes.io/auth-error-status-codes
Добавлена в версии модуля v1.3.0.
Было (Critical):
nginx.ingress.kubernetes.io/configuration-snippet: |
auth_request_set $auth_status $upstream_status;
error_page 500 = @auth_error;
nginx.ingress.kubernetes.io/server-snippet: |
location @auth_error {
internal;
if ($auth_status = 429) { return 429; }
return 500;
}Стало (Low):
nginx.ingress.kubernetes.io/auth-error-status-codes: "429"Если сервис аутентификации возвращает код, отличный от 2xx, 401 и 403, NGINX отвечает клиенту кодом 500. Аннотация задаёт список кодов через запятую (от 400 до 599, кроме 401 и 403), которые передаются клиенту без изменений. Необходимые переменную, директиву error_page и внутренний location контроллер создаёт автоматически. К таким ответам также добавляются заголовки из nginx.ingress.kubernetes.io/response-headers-by-status и nginx.ingress.kubernetes.io/ingress-nginx-hsts. Собственные страницы ошибок продолжают работать: если возвращаемый таким образом код, в том числе 500 для остальных кодов, указан в nginx.ingress.kubernetes.io/custom-http-errors или, при отсутствии этой аннотации, в customErrors, клиент получает соответствующую страницу ошибки, но уже без этих заголовков.
Заголовки запроса к бэкенду: nginx.ingress.kubernetes.io/proxy-request-headers
Добавлена в версии модуля v1.3.0.
Было (Critical):
nginx.ingress.kubernetes.io/configuration-snippet: |
proxy_set_header X-Cluster-Id 00ce4b6f-5031-4483-836e-0a10db23fb21;
proxy_set_header X-Request-Context /$1/$2;Стало (Medium):
nginx.ingress.kubernetes.io/use-regex: "true"
nginx.ingress.kubernetes.io/proxy-request-headers: |
X-Cluster-Id: 00ce4b6f-5031-4483-836e-0a10db23fb21
X-Request-Context: /$1/$2Аннотация принимает заголовки в формате Name: value, по одному на строку; для каждой строки добавляется директива proxy_set_header со значением в кавычках. Значения могут содержать только печатаемые символы ASCII, кроме " и \. Символ $ допускается только в ссылках $1–$9 на захваты регулярного выражения из пути location; другие переменные NGINX использовать нельзя. Захваты требуют use-regex: "true" или nginx.ingress.kubernetes.io/rewrite-target и должны присутствовать в каждом пути Ingress-ресурса. Значения захватов сохраняются сразу при входе запроса в location и не меняются при последующих сопоставлениях с регулярными выражениями (например, в nginx.ingress.kubernetes.io/rewrite-target). Не допускаются заголовки, которые устанавливает сам контроллер: Host, Connection, X-Forwarded-*, X-Original-Forwarded-Host, X-Real-IP, X-Request-ID, заголовки ssl-client-* аннотации nginx.ingress.kubernetes.io/auth-tls-secret, заголовок Accept-Encoding, который очищает nginx.ingress.kubernetes.io/response-html-head-injection, и другие. Не допускаются и заголовки, перечисленные в nginx.ingress.kubernetes.io/auth-response-headers того же Ingress-ресурса, так как их значения передаёт сервис аутентификации. Если заголовок с тем же именем передаёт внешняя аутентификация, заданная параметром global-auth-url в spec.config, заголовок из аннотации не отправляется. Заголовок из additionalHeaders с тем же именем в location-блоках Ingress-ресурса заменяется значением из аннотации.
Заголовки ответа для отдельных кодов: nginx.ingress.kubernetes.io/response-headers-by-status
Добавлена в версии модуля v1.3.0.
Было (Critical):
nginx.ingress.kubernetes.io/configuration-snippet: |
more_set_headers -s 429 "Retry-After: 60";Стало (Medium):
nginx.ingress.kubernetes.io/response-headers-by-status: |
429 Retry-After: 60
503 Retry-After: 30Аннотация принимает строки в формате <код> Name: value, по одной на заголовок, с кодом от 200 до 599; для каждой строки добавляется директива more_set_headers -s <код> "Name: value";. Требования к значениям те же, что и в nginx.ingress.kubernetes.io/proxy-request-headers, но символ $ запрещён. Для заголовков, которые нужно добавлять во все ответы, используйте nginx.ingress.kubernetes.io/custom-headers. Заголовки устанавливаются после заголовков nginx.ingress.kubernetes.io/custom-headers и nginx.ingress.kubernetes.io/ingress-nginx-hsts, и для указанных кодов ответа приоритет имеют значения этой аннотации.
Коды ответа при ограничении запросов: nginx.ingress.kubernetes.io/limit-req-status-code, nginx.ingress.kubernetes.io/limit-conn-status-code
Добавлены в версии модуля v1.3.0.
Было (Critical):
nginx.ingress.kubernetes.io/configuration-snippet: |
limit_req_status 429;
limit_conn_status 429;Стало (Low):
nginx.ingress.kubernetes.io/limit-rps: "10"
nginx.ingress.kubernetes.io/limit-req-status-code: "429"
nginx.ingress.kubernetes.io/limit-conn-status-code: "429"Аннотации задают код ответа на запросы, отклонённые ограничениями Ingress-ресурса: limit-req-status-code — для запросов сверх лимита limit-rps или limit-rpm, limit-conn-status-code — для соединений сверх лимита limit-connections. Допустимы коды от 400 до 599. Если аннотации не заданы, действуют общие для контроллера параметры limit-req-status-code и limit-conn-status-code из spec.config (по умолчанию 503). На nginx.ingress.kubernetes.io/global-rate-limit (только в версии контроллера 1.10) аннотации не влияют: код ответа для неё задаётся параметром global-rate-limit-status-code в spec.config.
Защита Fetch Metadata: nginx.ingress.kubernetes.io/fetch-metadata-protection
Добавлена в версии модуля v1.3.0.
Было (Critical):
nginx.ingress.kubernetes.io/configuration-snippet: |
set $deny_no_cors "";
if ($http_sec_fetch_mode = "no-cors") { set $deny_no_cors "N"; }
if ($http_sec_fetch_site ~ ^(same-site|cross-site)$) { set $deny_no_cors "${deny_no_cors}S"; }
if ($deny_no_cors = "NS") { return 403; }
set $deny_cross_site "";
if ($request_method !~ ^(GET|HEAD|OPTIONS)$) { set $deny_cross_site "M"; }
if ($http_sec_fetch_site ~ ^(same-site|cross-site)$) { set $deny_cross_site "${deny_cross_site}S"; }
if ($deny_cross_site = "MS") { return 403; }Стало (Low):
nginx.ingress.kubernetes.io/fetch-metadata-protection: "true"Аннотация отклоняет запрос, если значение заголовка Sec-Fetch-Site входит в список сайтов и при этом либо значение Sec-Fetch-Mode входит в список запрещённых режимов, либо метод запроса не входит в список разрешённых. Значения по умолчанию воспроизводят поведение snippet-аннотации: запросы с другого сайта (same-site или cross-site), которые выполняются в режиме no-cors или используют метод, отличный от GET, HEAD и OPTIONS, получают ответ 403, а запросы с того же источника (same-origin) и переходы, инициированные пользователем (Sec-Fetch-Site: none), не затрагиваются. Запросы без заголовка Sec-Fetch-Site не отклоняются.
Политику можно изменить следующими аннотациями. Каждая принимает список значений через запятую, значения не должны повторяться; при отсутствии fetch-metadata-protection: "true" Ingress-ресурс с этими аннотациями отклоняется:
nginx.ingress.kubernetes.io/fetch-metadata-sites— значенияSec-Fetch-Site, к которым применяется политика:same-origin,same-site,cross-site,none. По умолчанию —same-site, cross-site.nginx.ingress.kubernetes.io/fetch-metadata-deny-modes— запрещённые значенияSec-Fetch-Mode:cors,navigate,no-cors,same-origin,websocket. По умолчанию —no-cors; пустая строка""означает, что режим не проверяется.nginx.ingress.kubernetes.io/fetch-metadata-allowed-methods— методы, которые не отклоняются:GET,HEAD,POST,PUT,DELETE,CONNECT,OPTIONS,TRACE,PATCH. По умолчанию —GET, HEAD, OPTIONS; пустая строка""означает, что отклоняются запросы с любым методом, а*— что метод не проверяется. Сочетание*с пустым значениемfetch-metadata-deny-modesнедопустимо, так как такая политика ничего не запрещает.nginx.ingress.kubernetes.io/fetch-metadata-status-code— код ответа от400до499. По умолчанию —403.
Политика, которая отклоняет переходы с других сайтов с любым методом запроса и возвращает код 429:
nginx.ingress.kubernetes.io/fetch-metadata-protection: "true"
nginx.ingress.kubernetes.io/fetch-metadata-sites: "cross-site"
nginx.ingress.kubernetes.io/fetch-metadata-deny-modes: "navigate"
nginx.ingress.kubernetes.io/fetch-metadata-allowed-methods: "*"
nginx.ingress.kubernetes.io/fetch-metadata-status-code: "429"Политика, которая отклоняет все запросы с других сайтов:
nginx.ingress.kubernetes.io/fetch-metadata-protection: "true"
nginx.ingress.kubernetes.io/fetch-metadata-sites: "cross-site"
nginx.ingress.kubernetes.io/fetch-metadata-allowed-methods: ""Для каждой уникальной политики контроллер генерирует блоки map один раз и использует их во всех Ingress-ресурсах с этой политикой; в location выполняется только проверка результата: if (...) { return <код>; }. В конфигурацию NGINX попадают только значения из перечисленных выше фиксированных списков.
Проверка выполняется до списков разрешённых адресов, ограничений частоты запросов, аутентификации и перенаправлений permanent-redirect, temporal-redirect и ssl-redirect, и отклонённый запрос получает код политики, даже если на этих этапах ответ был бы иным. На предварительный CORS-запрос (preflight) аннотация nginx.ingress.kubernetes.io/enable-cors отвечает до проверки, и такой запрос не отклоняется.
Перенаправления с сохранением запроса: nginx.ingress.kubernetes.io/redirect-preserve
Добавлена в версии модуля v1.3.0.
Было (Critical):
nginx.ingress.kubernetes.io/configuration-snippet: |
if ($host = 'old.example.com') {
rewrite ^ https://new.example.com$request_uri permanent;
}Стало — отдельный Ingress-ресурс для old.example.com (Medium — из-за permanent-redirect; уровень самой аннотации — Low):
nginx.ingress.kubernetes.io/permanent-redirect: "https://new.example.com"
nginx.ingress.kubernetes.io/redirect-preserve: "request-uri"Аннотация дополняет адрес перенаправления из nginx.ingress.kubernetes.io/permanent-redirect или nginx.ingress.kubernetes.io/temporal-redirect частями исходного запроса:
request-uri— путь и строку запроса (return 301 https://new.example.com$request_uri;); адрес перенаправления не должен содержать путь, даже/;query— только строку запроса ($is_args$args); адрес перенаправления не должен содержать строку запроса.
Пример перенаправления для точного пути /login с сохранением строки запроса:
# path: /login, pathType: Exact
nginx.ingress.kubernetes.io/permanent-redirect: "https://new.example.com/login"
nginx.ingress.kubernetes.io/redirect-preserve: "query"Вставка HTML-кода в страницу: nginx.ingress.kubernetes.io/response-html-head-injection
Добавлена в версии модуля v1.3.0.
Было (Critical):
nginx.ingress.kubernetes.io/configuration-snippet: |
sub_filter "</head>" "<script src=\"https://counter.example.com/tag.js\" async></script></head>";
sub_filter_once on;
sub_filter_types text/html;
proxy_set_header Accept-Encoding "";Стало (High):
apiVersion: v1
kind: ConfigMap
metadata:
name: metrics-injection
data:
content.html: |
<script src="https://counter.example.com/tag.js" async></script>
---
# Аннотации Ingress
nginx.ingress.kubernetes.io/response-html-head-injection: "metrics-injection"Аннотация принимает имя ConfigMap из пространства имён Ingress-ресурса или namespace/name ConfigMap из другого пространства имён и вставляет содержимое её ключей в ответы с типом text/html:
head-start.html— сразу после первого тега<head>;head-end.htmlилиcontent.html— перед первым тегом</head>; можно задать только один из этих ключей;body-end.html— перед первым тегом</body>.
Ключи можно задавать одновременно; остальные ключи ConfigMap игнорируются. Пример со счётчиком в разделе <head> и виджетом чата в конце страницы:
apiVersion: v1
kind: ConfigMap
metadata:
name: page-injection
data:
head-end.html: |
<script src="https://counter.example.com/tag.js" async></script>
body-end.html: |
<script src="https://chat.example.com/widget.js" async></script>Теги ищутся без учёта регистра, причём тег <head> распознаётся только без атрибутов. Если нужного тега на странице нет, соответствующий фрагмент не вставляется. Аннотация предназначена для приложений, HTML-код которых нельзя изменить, например для добавления счётчика веб-аналитики.
HTML-код не попадает в конфигурацию NGINX: контроллер сохраняет его в файлы, Lua-модуль контроллера читает их, а директива sub_filter вставляет содержимое через переменную. NGINX может только читать эти файлы, но не изменять их. Изменения в ConfigMap применяются автоматически; файлы, которые не использует ни одна конфигурация, удаляются через час. Суммарный размер всех ключей ограничен 64 КиБ текста в кодировке UTF-8. Для вставки у бэкенда запрашивается ответ без сжатия. Страница отдаётся без изменений, если ConfigMap отсутствует, в ней не задан ни один из ключей, одновременно заданы head-end.html и content.html или превышен допустимый размер. ConfigMap из другого пространства имён используется, только если включён параметр annotationPolicy.allowCrossNamespaceResources; в противном случае контроллер её не читает, и страница также отдаётся без изменений.
Уровень риска аннотации — High: вставленный HTML-код выполняется в контексте источника (origin) приложения со всеми его правами. Если приложение отправляет заголовок Content-Security-Policy, политика должна разрешать вставленный скрипт.