Как разрешить доступ к приложению внутри кластера только от Ingress-контроллера?

Если необходимо ограничить доступ к вашему приложению внутри кластера исключительно от подов Ingress-контроллера, необходимо в под с приложением добавить контейнер с kube-rbac-proxy, как показано в примере ниже:

1apiVersion: apps/v1
2kind: Deployment
3metadata:
4  name: my-app
5  namespace: my-namespace
6spec:
7  selector:
8    matchLabels:
9      app: my-app
10  replicas: 1
11  template:
12    metadata:
13      labels:
14        app: my-app
15    spec:
16      serviceAccountName: my-sa
17      containers:
18      - name: my-cool-app
19        image: mycompany/my-app:v0.5.3
20        args:
21        - "--listen=127.0.0.1:8080"
22        livenessProbe:
23          httpGet:
24            path: /healthz
25            port: 443
26            scheme: HTTPS
27      - name: kube-rbac-proxy
28        image: flant/kube-rbac-proxy:v0.1.0
29        # Рекомендуется использовать прокси из репозитория Deckhouse.
30        args:
31        - "--secure-listen-address=0.0.0.0:443"
32        - "--config-file=/etc/kube-rbac-proxy/config-file.yaml"
33        - "--v=2"
34        - "--logtostderr=true"
35        # Если kube-apiserver недоступен, аутентификация и авторизация пользователей невозможна.
36        # Stale Cache хранит результаты успешной авторизации и используется лишь в случае, если apiserver недоступен.
37        - "--stale-cache-interval=1h30m"
38        ports:
39        - containerPort: 443
40          name: https
41        volumeMounts:
42        - name: kube-rbac-proxy
43          mountPath: /etc/kube-rbac-proxy
44      volumes:
45      - name: kube-rbac-proxy
46        configMap:
47          name: kube-rbac-proxy

Приложение принимает запросы на адресе 127.0.0.1, это означает, что по незащищенному соединению к нему можно подключиться только внутри пода. Прокси прослушивает порт на адресе 0.0.0.0 и перехватывает весь внешний трафик к поду.

Как выдать минимальные права для ServiceAccount?

Чтобы аутентифицировать и авторизовывать пользователей с помощью kube-apiserver, у прокси должны быть права на создание TokenReview и SubjectAccessReview.

В кластерах DKP уже есть готовая ClusterRoled8-rbac-proxy, создавать её самостоятельно не требуется! Свяжите её с ServiceAccount вашего Deployment’а, как показано в примере ниже.

1---
2apiVersion: v1
3kind: ServiceAccount
4metadata:
5  name: my-sa
6  namespace: my-namespace
7---
8apiVersion: rbac.authorization.k8s.io/v1
9kind: ClusterRoleBinding
10metadata:
11  name: my-namespace:my-sa:d8-rbac-proxy
12roleRef:
13  apiGroup: rbac.authorization.k8s.io
14  kind: ClusterRole
15  name: d8:rbac-proxy
16subjects:
17- kind: ServiceAccount
18  name: my-sa
19  namespace: my-namespace

Конфигурация Kube-RBAC-Proxy

1apiVersion: v1
2kind: ConfigMap
3metadata:
4  name: kube-rbac-proxy
5data:
6  config-file.yaml: |+
7    excludePaths:
8    - /healthz 
9  # Не требуем авторизацию для liveness пробы.
10    upstreams:
11    - upstream: http://127.0.0.1:8081/
12  # Адрес upstream-сервиса, на который будет перенаправлен входящий трафик.
13      path: / 
14  # Путь, обрабатываемый прокси, по которому принимаются запросы и перенаправляются на upstream.
15      authorization:
16        resourceAttributes:
17          namespace: my-namespace
18          apiGroup: apps
19          apiVersion: v1
20          resource: deployments
21          subresource: http
22          name: my-app

Согласно конфигурации, у пользователя должны быть права доступа к Deployment с именем my-app и его дополнительному ресурсу http в пространстве имён my-namespace.

Выглядят такие права в виде RBAC следующим образом:

1---
2apiVersion: rbac.authorization.k8s.io/v1
3kind: Role
4metadata:
5  name: kube-rbac-proxy:my-app
6  namespace: my-namespace
7rules:
8- apiGroups: ["apps"]
9  resources: ["deployments/http"]
10  resourceNames: ["my-app"]
11  verbs: ["get", "create", "update", "patch", "delete"]
12---
13apiVersion: rbac.authorization.k8s.io/v1
14kind: RoleBinding
15metadata:
16  name: kube-rbac-proxy:my-app
17  namespace: my-namespace
18roleRef:
19  apiGroup: rbac.authorization.k8s.io
20  kind: Role
21  name: kube-rbac-proxy:my-app
22subjects:
23# Все пользовательские сертификаты ingress-контроллеров выписаны для одной конкретной группы.
24- kind: Group
25  name: ingress-nginx:auth

Для Ingress-ресурса добавьте параметры:

1nginx.ingress.kubernetes.io/backend-protocol: HTTPS
2nginx.ingress.kubernetes.io/configuration-snippet: |
3  proxy_ssl_certificate /etc/nginx/ssl/client.crt;
4  proxy_ssl_certificate_key /etc/nginx/ssl/client.key;
5  proxy_ssl_protocols TLSv1.2;
6  proxy_ssl_session_reuse on;

Подробнее о том, как работает аутентификация по сертификатам, можно прочитать в документации Kubernetes.

Как сконфигурировать балансировщик нагрузки для проверки доступности IngressNginxController?

В ситуации, когда IngressNginxController размещен за балансировщиком нагрузки, рекомендуется сконфигурировать балансировщик для проверки доступности узлов IngressNginxController с помощью HTTP-запросов или TCP-подключений. В то время как тестирование с помощью TCP-подключений представляет собой простой и универсальный механизм проверки доступности, мы рекомендуем использовать проверку на основе HTTP-запросов со следующими параметрами:

  • протокол: HTTP;
  • путь: /healthz;
  • порт: 80 (в случае использования инлета HostPort нужно указать номер порта, соответствующий параметру httpPort.

Как настроить работу через MetalLB с доступом только из внутренней сети?

Пример MetalLB с настройками доступа только из внутренней сети:

1apiVersion: deckhouse.io/v1
2kind: IngressNginxController
3metadata:
4  name: main
5spec:
6  ingressClass: "nginx"
7  inlet: "LoadBalancer"
8  loadBalancer:
9    sourceRanges:
10    - 192.168.0.0/24

Для работы необходимо включить параметр svcSourceRangeCheck в модуле cni-cilium.

Как добавить дополнительные поля для логирования в nginx-controller?

Пример добавления дополнительных полей:

1apiVersion: deckhouse.io/v1
2kind: IngressNginxController
3metadata:
4  name: main
5spec:
6  ingressClass: "nginx"
7  inlet: "LoadBalancer"
8  additionalLogFields:
9    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":

1apiVersion: networking.k8s.io/v1
2kind: IngressClass
3metadata:
4  labels:
5    ingress-class.deckhouse.io/external: "true"
6  name: my-super-ingress
7spec:
8  controller: ingress-nginx.deckhouse.io/my-super-ingress
9  parameters:
10    apiGroup: elbv2.k8s.aws
11    kind: IngressClassParams
12    name: awesome-class-cfg

В этом случае, при указании данного IngressClass в CRD IngressNginxController, Deckhouse не будет создавать объект, а использует существующий.

Как отключить сборку детализированной статистики Ingress-ресурсов?

По умолчанию Deckhouse собирает подробную статистику со всех Ingress-ресурсов в кластере. Этот процесс может приводить к высокой нагрузке системы мониторинга.

Для отключения сбора статистики добавьте лейбл ingress.deckhouse.io/discard-metrics: "true" к соответствующему пространству имён или Ingress-ресурсу.

Пример отключения сбора статистики (метрик) для всех Ingress-ресурсов в пространстве имен review-1:

1kubectl label ns review-1 ingress.deckhouse.io/discard-metrics=true

Пример отключения сбора статистики (метрик) для всех Ingress-ресурсов test-site в пространстве имен development:

1kubectl label ingress test-site -n development ingress.deckhouse.io/discard-metrics=true

Как корректно вывести из эксплуатации (drain) узел с запущенным IngressNginxController?

Доступно два способа корректного вывода из эксплуатации узла, на котором запущен IngressNginxController.

  1. С помощью аннотации.

    Аннотация будет автоматически удалена после завершения операции.

    1 kubectl annotate node <node_name> update.node.deckhouse.io/draining=user
    
  2. С помощью kubectl drain.

    При использовании стандартной команды kubectl drain необходимо указать флаг --force даже при наличии --ignore-daemonsets, поскольку IngressNginxController развёрнут с использованием Advanced DaemonSet:

    1 kubectl 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:

1apiVersion: deckhouse.io/v1
2kind: IngressNginxController
3metadata:
4  name: <имя_контроллера>
5spec:
6  config:
7    enable-modsecurity: "true"
8    modsecurity-snippet: |
9      Include /etc/nginx/modsecurity/modsecurity.conf

После применения настроек ModSecurity начнет работать для всего трафика, проходящего через данный ingress-nginx контроллер. При этом используется режим аудита (DetectionOnly) и базовая рекомендуемая конфигурация.

Настройка ModSecurity

ModSecurity можно настраивать двумя способами:

  1. Для всего ingress-nginx контроллера
    • необходимые директивы описываются в секции config.modsecurity-snippet в кастомном ресурсе IngressNginxController, как в примере выше.
  2. Для каждого кастомного ресурса Ingress по отдельности
    • необходимые директивы описываются в аннотации nginx.ingress.kubernetes.io/modsecurity-snippet: | непосредственно в манифестах Ingress.

Чтобы включить выполнение правил (а не только логирование), добавьте директиву SecRuleEngine On по примеру ниже:

1apiVersion: deckhouse.io/v1
2kind: IngressNginxController
3metadata:
4  name: <имя_контролера>
5spec:
6  config:
7    enable-modsecurity: "true"
8    modsecurity-snippet: |
9      Include /etc/nginx/modsecurity/modsecurity.conf
10      SecRuleEngine On

Полный перечень и описание директив вы можете найти в официальной документации.

На данный момент использование набора правил OWASP Core Rule Set (CRS) недоступно.