VictoriaMetrics: VMAuth – проксі, аутентифиікація та авторизація

Автор |  23/08/2023
 

Продовжуємо розвивати наш стек моніторингу. Див. VictoriaMetrics: створення Kubernetes monitoring stack з власним Helm-чартом.

Що хочеться: зробити доступ девелоперам, щоб вони могли в Alertmanager самі виставляти Silence для алертів аби не спамити в Slack, див. Prometheus: Alertmanager Web UI и Silence алертов.

Для того, щоб забезпечити безпечний доступ до нього можна використати рішення від VictoriaMetrics – компонент VMAuth, який дозволяє створити єдиний ендпоінт, через який будуть ходити всі юзери і налаштувати відповідні бекенди для інших компонентів кластеру.

Кратко – що можна з VMAuth:

  • створити єдину точку входу для сервісів з Basic або Bearer user аутентифікацією та авторизацією
  • в залежності від юзера та роута/URI направляти його до відповідного сервіса (фактично, ви можете створити один Ingress і всі запити обслуговувати через нього замість того, щоб створювати Ingress та аутентифікацію для кожного сервіса окремо)
  • мати простий round-robin load balancer
  • налаштувати IP фільтри з Allow та Deny листами
  • керувати додаванням власних хедерів до запитів

Деплоїти будемо у AWS EKS з Helm-чарту victoria-metrics-auth, але можна робити через yaml-маніфести, див. документацію та інші приклади на Authorization and exposing components та VMAuth.

Встановлення чарту VMAuth

Так як ми маємо umbrella-chart, то додаємо в Chart.yaml в блок dependecy новий сабчарт:

...
- name: victoria-metrics-auth
  version: ~0.3.3
  repository: https://victoriametrics.github.io/helm-charts/ 
...

Дефолтні вальюси – values.yaml.

У власних values.yaml описуємо конфіг VMAuth – створення Ingress, ім’я користувача, пароль, та куди перенаправляти його запити – тут це буде Kubernetes Service для Alertmanager:

...
victoria-metrics-auth:
  ingress:
    enabled: true
    annotations:
      kubernetes.io/ingress.class: alb
      alb.ingress.kubernetes.io/target-type: ip
      alb.ingress.kubernetes.io/scheme: internet-facing
      alb.ingress.kubernetes.io/certificate-arn: arn:aws:acm:us-east-1:49***148:certificate/66e3050e-7f27-4f0c-8ad4-0733a6d8071a
      alb.ingress.kubernetes.io/listen-ports: '[{"HTTPS":443}, {"HTTP":80}]'
      alb.ingress.kubernetes.io/load-balancer-attributes: idle_timeout.timeout_seconds=600    
    hosts:
      - name: vmauth.dev.example.co
        path: /
        port: http
  config:
    users:
      - username: "vmadmin"
        password: "p@ssw0rd"
        url_prefix: "http://vmalertmanager-vm-k8s-stack.dev-monitoring-ns.svc:9093"
...

Вновлюмо Helm dependency:

[simterm]

$ helm dependency update

[/simterm]

І деплоїмо чарт:

[simterm]

$ helm -n dev-monitoring-ns upgrade --install atlas-victoriametrics . -f values/dev/atlas-monitoring-dev-values.yaml

[/simterm]

Перевіряємо чи додався Ingress і AWS ALB до нього:

[simterm]

$ kk -n dev-monitoring-ns get ingress
NAME                                          CLASS    HOSTS                   ADDRESS                   PORTS   AGE
atlas-victoriametrics-victoria-metrics-auth   <none>   vmauth.dev.example.co   k8s-***elb.amazonaws.com  80      3m12s

[/simterm]

Чекаємо поки оновляться DNS, і відкриваємо https://vmauth.dev.example.co:

Логінимось, і попадаємо прямо в Алертменеджер:

Конфіг в Kubernetes Secret

Замість того, щоб тримати конфіг в values чарту можно створити Kubernetes Secret. Це додатково дасть можливість передавати пароль, якщо він у вас один, через helm install --set:

apiVersion: v1
kind: Secret
metadata:
  name: vmauth-config-secret
stringData:
  auth.yml: |-
    users:
      - username: vmadmin
        password: {{ .Values.vmauth_password }}
        url_map:
        url_prefix: http://vmalertmanager-vm-k8s-stack.dev-monitoring-ns.svc:9093/

VMAuth, users та routes

Є можливість створити одного користувача, і з url_map йому налаштувати кілька роутів – в залежності від URI запиту, він буде перенаправлений на відповідний бекенд, а з default_url задати URL, куди будуть перенаравлені запроси, для яких не задано роута. При цьому в роутах можна використовувати регулярки.

Наприклад:

...
    users:
      - username: vmadmin
        password: {{ .Values.vmauth_password }}
        url_map:
        - src_paths:
          - /alertmanager.*
          url_prefix: http://vmalertmanager-vm-k8s-stack.dev-monitoring-ns.svc:9093/
        - src_paths:
          - /vmui.*
          url_prefix: http://vmsingle-vm-k8s-stack.dev-monitoring-ns.svc:8429
        default_url:
          - https://google.com

Якщо плануєте додавати доступ до інстансу VMSingle – додайте блок для Prometheus, бо інакше будуть помилки виду:

{“ts”:”2023-08-22T14:37:43.363Z”,”level”:”warn”,”caller”:”VictoriaMetrics/app/vmauth/main.go:159″,”msg”:”remoteAddr: \”10.0.0.74:25806, X-Forwarded-For: 217.***.***.253\”; requestURI: /prometheus/vmui/custom-dashboards; missing route for \”/prometheus/v
mui/custom-dashboards\””}
{“ts”:”2023-08-22T14:37:43.396Z”,”level”:”warn”,”caller”:”VictoriaMetrics/app/vmauth/main.go:159″,”msg”:”remoteAddr: \”10.0.0.74:25806, X-Forwarded-For: ***.***.165.253\”; requestURI: /prometheus/api/v1/label/__name__/values; missing route for \”/promet
heus/api/v1/label/__name__/values\””}

Для Prometheus блок виглядає аналогічно:

...
      - src_paths:
        - /prometheus.*
        url_prefix: http://vmsingle-vm-k8s-stack.dev-monitoring-ns.svc:8429

Для того, щоб сам Alertmanager працював через URI /alertmanager – в його values налаштовуємо routePrefix:

...
  alertmanager:
    enabled: true 
    spec:
      configSecret: "alertmanager-config"
      routePrefix: "/alertmanager"
...

І не забудьте в такому випадку змінити дефолтний URL для VMAlert у його values:

...
  vmalert:
    annotations: {}
    enabled: true
    spec:  
      notifier:
        url: "http://vmalertmanager-vm-k8s-stack.dev-monitoring-ns.svc:9093/alertmanager"
...

Деплоїмо зміни, а щоб застосувати зміни конфіг в самому інстансі VMAuth, виконуємо запит до ендпоінту /-/reload, тобто https://vmauth.dev.example.co/-/reload.

Тепер Alertmanager доступний за адресою https://vmauth.dev.example.co/alertmanager:

Насправді, настройка src_paths може бути трохи геморною, бо, наприклад, в документації роути вказані просто як /uri/path:

url_map:
- src_paths:
  - /api/v1/query
  - /api/v1/query_range

Але коли я почав це робити, то виявилось, що при виконанні редіректу з VMAuth на внутрішній сервіс в кінці додається зайвий слеш, і доступ до Alertmanager не працював.

Саме тому в моїх прикладах вище роути задані з “.*“.

Години дві спілкувався з саппортом в VictoriaMetrcis Slack, намагались знайти причину проблем з доступом к Alertmanager, наче знайшли, завів GitHub issue, подивимось, як воно буде далі.

Взагалі, підтримку VictoriaMetrics варто згадати окремо, бо працює вона чудово і досить швидко. Є Slack, є Telegram-канал.

Basic Auth vs Bearer token

Замість звичайного логіна:пароля можемо використати ServiceAccount токен.

Створюємо ServiceAccount та Secret для нього з типом kubernetes.io/service-account-token:

apiVersion: v1
kind: ServiceAccount
metadata:
  name: vmauth-sa
  namespace: dev-monitoring-ns
secrets:
- name: vmauth-token-secret
---
apiVersion: v1
kind: Secret
metadata:
  name: vmauth-token-secret
  namespace: dev-monitoring-ns
  annotations:
    kubernetes.io/service-account.name: vmauth-sa
type: kubernetes.io/service-account-token

Деплоїмо, отримуємо токен для цього ServicAccount:

[simterm]

$ kk -n dev-monitoring-ns create token vmauth-sa 
eyJhbGciOi***gfeNGWVjJn5-LWd2aslxAwnUTpQ

[/simterm]

Додаємо bearer_token в конфіг VMAuth:

...
    users:
    - username: vmadmin
      password: {{ .Values.vmauth_password }}
      url_map:
      - src_paths:
        - /alertmanager.*
        url_prefix: http://vmalertmanager-vm-k8s-stack.dev-monitoring-ns.svc:9093
      - src_paths:
        - /vmui.*
        url_prefix: http://vmsingle-vm-k8s-stack.dev-monitoring-ns.svc:8429
      - src_paths:
        - /prometheus.*
        url_prefix: http://vmsingle-vm-k8s-stack.dev-monitoring-ns.svc:8429
    - bearer_token: "eyJhbGciOiJSUzI1NiIsImtpZ***gfeNGWVjJn5-LWd2aslxAwnUTpQ"
      url_prefix: http://vmalertmanager-vm-k8s-stack.dev-monitoring-ns.svc:9093

Деплоїмо, знов робимо /-/reload, та перевіряємо доступ.

Заносимо токен в змінну:

[simterm]

$ token="eyJhbGciOiJSUzI1NiIsImt***-LWd2aslxAwnUTpQ"wnUTpQ

[/simterm]

І з curl відкриваємо ендпоінт:

[simterm]

$ curl -H "Authorization: Bearer ${token}" https://vmauth.dev.example.co/
<a href="/alertmanager">Found</a>.

[/simterm]

VMAuth та “AnyService”

Ну і на останнє – VMAuth можна використовувати для аутентифікації не тільки VictoriaMetrics та її сервісів, а (майже) будь-яких.

Наприклад, маємо под з Nginx Demo:

apiVersion: v1
kind: Pod
metadata:
  name: my-pod
  labels:
    app: my-pod
spec:
  containers:
    - name: my-container
      image: nginxdemos/hello
---
apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  selector:
    app: my-pod
  ports:
    - protocol: TCP
      port: 80
      targetPort: 80

У VMAuth додаємо роут:

...
      - src_paths:
        - /nginxdemo.*
        url_prefix: http://my-service.default.svc:80

І тепер за адресою https://vmauth.dev.example.co/nginxdemo попадаемо на Nginx:

А от для стандартної Kubernetes Dashboard так не вийде, бо вона використовує self-signed TLS сертифиікат, і VMAuth не підключається до відповідного сервісу, бо не може провалідувати сертификат. Можливо, є рішення, але не шукав, бо в принципі не потрібно.

VMAuth Self-Security

Див. документацію.

Закрийте аутентифікацією “службові” роути самого VMAuth.

У values.yaml додаємо ключі:

...
  extraArgs:
    reloadAuthKey: password
    flagsAuthKey: password
    metricsAuthKey: password
    pprofAuthKey: password
...

Деплоїмо, і тепер якщо викликати /-/reload без ключа – буде помилка:

[simterm]

$ curl https://vmauth.dev.example.co/-/reload
The provided authKey doesn't match -reloadAuthKey

[/simterm]

Щоб передати ключ для аутентифікації – використовуємо форму /-/reload?authKey=password:

[simterm]

$ curl -I https://vmauth.dev.example.co/-/reload?authKey=password
HTTP/2 200 

[/simterm]

Поки що наче немає можливості передачи ключі через Kubertes Secret, тільки хардкодити у values.yaml, але вже є фіча-реквест.