Начал писать свой пэт проект и дошел до момента, когда мне понадобилось авторизовывать пользователя в своем приложении. До этого был опыт использования спринга с кейклок, но поскольку сейчас развернул приложение в облаке, решил узнать, как принято использовать кейклок в облаке. Может можно использовать кейклок как авторизационную прокси?
Для начала, у меня были приложение развернутое в кубере и ingress сервис. Для начала решил использовать kubectl, а не helm, чтобы разобраться что-за что отвечает, а после по надобности перейти на helm. Собственно этой командой у меня был настроен ингрес.
kubectl --namespace default apply -f - <<< ' apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: ingress spec: rules: - host: somehost.ru http: paths: - path: /app pathType: Prefix backend: service: name: app port: number: 80 ingressClassName: nginx '
Покопавшись немного в интернете нашел, что ингресс может делегировать авторизацию c помощью аннотаций, а сервис которому можно так делегировать называется oauth2-proxy.
oauth2-proxy
Сам по себе oauth2-proxy не умеет авторизовывать пользователей. Он по сути является адаптером между ingress и сервисами которые можно использовать для авторизации. В документации у них есть инструкции как использовать для авторизации гугл, гитхаб, гитлаб... Для начала решил не настраивать сервис кеклока, а поэкспериментировать с чем-то готовым, например авторизовывать через гитхаб.
По сути, действуя по инструкции настроил oauth2-proxy, добавил только две настройки:
1) --set-xauthrequest - добавляет в запрос хэдеры X-Auth-Request-User, X-Auth-Request-Groups, X-Auth-Request-Email и X-Auth-Request-Preferred-Username.
2) --set-authorization-header - добавляет хедер Authorization: Bearer...
kubectl --namespace default apply -f - <<< ' apiVersion: apps/v1 kind: Deployment metadata: labels: app: oauth2-proxy name: oauth2-proxy spec: replicas: 1 selector: matchLabels: app: oauth2-proxy template: metadata: labels: app: oauth2-proxy spec: containers: - name: oauth2-proxy image: quay.io/oauth2-proxy/oauth2-proxy:latest imagePullPolicy: Always ports: - containerPort: 4180 protocol: TCP args: - --provider=github - --email-domain=* - --upstream=file:///dev/null - --http-address=0.0.0.0:4180 - --scope=user:email - --set-xauthrequest=true - --set-authorization-header=true env: - name: OAUTH2_PROXY_CLIENT_ID value: acb0636a63a40efab6d - name: OAUTH2_PROXY_CLIENT_SECRET value: 4076962096c9b3d1774e9cb6850b67eef23ad88e - name: OAUTH2_PROXY_COOKIE_SECRET value: 6Pf1nzU4nV2syd8e0JAkmw== --- apiVersion: v1 kind: Service metadata: labels: app: oauth2-proxy name: oauth2-proxy spec: ports: - name: http port: 4180 protocol: TCP targetPort: 4180 selector: app: oauth2-proxy --- apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: ingress-oauth2-proxy annotations: nginx.ingress.kubernetes.io/proxy-buffer-size: "8k" nginx.ingress.kubernetes.io/proxy-buffers-number: "4" spec: rules: - host: somehost.ru http: paths: - path: /oauth2 pathType: Prefix backend: service: name: oauth2-proxy port: number: 4180 ingressClassName: nginx '
Дальше надо проставить переменные окружения (в целом можно так же в аргументах передать):
OAUTH2_PROXY_CLIENT_ID - берется и приложения в git-hub
OAUTH2_PROXY_CLIENT_SECRET - так же берется из приложения, но показывается только один раз, после нужно создавать новый
OAUTH2_PROXY_COOKIE_SECRET - можно сгенерировать так python ‑c 'import os,base64; print(base64.urlsafe_b64encode(os.urandom(32)).decode())'

Остается только добавить аннотации в ingress сервис и авторизация заработает.
1) nginx.ingress.kubernetes.io/auth-url - судя по инструкция в интернетах обычно можно указать через https://$host... но у меня не получалось ни в какую. Появлялась проблема ssl сертификата, возможно он не совсем правильно настроен был. Поэтому я этот url прописываю внутрекуберовский.
2 nginx.ingress.kubernetes.io/auth-signin: - этот url вернется пользователю, на неудачную авторизацию.
3) nginx.ingress.kubernetes.io/auth-response-headers: - здесь ингрес добавляет указанные хэдеры из ответа oauth2-proxy в запрос
4) nginx.ingress.kubernetes.io/proxy-buffer-size: - размер кэша, нужен, чтобы на каждый запрос не ходить за авторизацией.
kubectl --namespace default apply -f - <<< ' apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: ingress annotations: nginx.ingress.kubernetes.io/auth-url: "http://oauth2-proxy.default.svc.cluster.local:4180/oauth2/auth" nginx.ingress.kubernetes.io/auth-signin: "https://$host/oauth2/start?rd=$escaped_request_uri" nginx.ingress.kubernetes.io/auth-response-headers: "x-auth-request-user, x-auth-request-email, authorization" nginx.ingress.kubernetes.io/proxy-buffer-size: "16k" spec: rules: - host: somehost.ru http: paths: - path: /app pathType: Prefix backend: service: name: app port: number: 80 ingressClassName: nginx '
Заходим теперь в https://somehost.ru/app и нас перенаправляет на страничку авторизации в git-hub.
Keycloak
Так, теперь у меня есть какая-никакая авторизация, но хочется ее самому контролировать. Пришло время перевести oauth2-proxy на кейклок. И сначала я чуть-чуть ошибся. Очень много инструкций в интернете рассказывают, как настраивать кейклок до 16й версии. Я его настроил, посмотрел как подключить русских провайдеров типа вк яндекс итд и понял, что нужно настроить 19ю версию. В общем обратите на это внимание, 19 версия лучше, легче быстрее и всякое такое. По крайней мере в документации так написано.
Собственно настройка кейклок у меня выглядит так:
kubectl --namespace default apply -f - <<< ' apiVersion: v1 kind: Service metadata: name: keycloak labels: app: keycloak spec: type: LoadBalancer ports: - name: http port: 80 targetPort: 8080 selector: app: keycloak --- apiVersion: apps/v1 kind: Deployment metadata: name: keycloak namespace: default labels: app: keycloak spec: replicas: 1 selector: matchLabels: app: keycloak template: metadata: labels: app: keycloak spec: containers: - name: keycloak image: playaru/keycloak-russian args: [ "start" ] env: - name: KC_HOSTNAME_URL value: "https://somehost.ru" - name: KC_HOSTNAME_ADMIN_URL value: "https://somehost.ru" - name: KC_HOSTNAME_STRICT value: "false" - name: KC_HTTP_ENABLED value: "true" - name: KEYCLOAK_ADMIN value: "keycloak" - name: KEYCLOAK_ADMIN_PASSWORD value: "keycloak" - name: KC_PROXY value: "edge" - name: KC_HEALTH_ENABLED value: "true" - name: KC_DB value: "postgres" - name: KC_DB_URL value: "" - name: KC_DB_URL_DATABASE value: "keycloak" - name: KC_DB_USERNAME value: "" - name: KC_DB_PASSWORD value: "" ports: - name: http containerPort: 8080 readinessProbe: httpGet: path: /realms/master port: 8080 --- apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: nginx-test namespace: default spec: tls: - hosts: - somehost.ru secretName: k8s-secret ingressClassName: nginx rules: - host: somehost.ru http: paths: - path: /admin pathType: Prefix backend: service: name: keycloak port: number: 80 - path: /realms/ pathType: Prefix backend: service: name: keycloak port: number: 80 - path: /resources/ pathType: Prefix backend: service: name: keycloak port: number: 80 - path: /robots.txt pathType: ImplementationSpecific backend: service: name: keycloak port: number: 80 - path: /js/ pathType: Prefix backend: service: name: keycloak port: number: 80 '
Если не указывать настройки базы данных, то кейклок создаст базу сам, а мне нужно было подключиться к своей. И image я взял playaru/keycloak-russian, чтобы у меня сразу были русские провайдеры и не мучиться с их подключением.
Помимо настроек сервиса надо прокинуть через ингрес путь в keycloak, поскольку теперь страничку авторизации будет возвращать он. Путь /admin лучше не пробрасывать, можно например добавить vpn чтобы подключаться к сервисам внутри кубера, но я так не научился еще.
Теперь нужно поменять настройки oauth2-proxy.
kubectl --namespace default apply -f - <<< ' apiVersion: apps/v1 kind: Deployment metadata: labels: app: oauth2-proxy name: oauth2-proxy spec: replicas: 1 selector: matchLabels: app: oauth2-proxy template: metadata: labels: app: oauth2-proxy spec: containers: - name: oauth2-proxy image: quay.io/oauth2-proxy/oauth2-proxy:latest imagePullPolicy: Always ports: - containerPort: 4180 protocol: TCP args: - --provider=keycloak-oidc - --upstream=file:///dev/null - --http-address=0.0.0.0:4180 - --whitelist-domain=somehost.ru - --cookie-domain=somehost.ru - --set-xauthrequest=true - --reverse-proxy=true - --set-authorization-header=true - --oidc-issuer-url=https://somehost.ru/realms/master - --ssl-insecure-skip-verify=true - --silence-ping-logging=true - --email-domain=* env: - name: OAUTH2_PROXY_CLIENT_ID value: ingress - name: OAUTH2_PROXY_CLIENT_SECRET value: <client-secret> - name: OAUTH2_PROXY_COOKIE_SECRET value: zTBum01F+fZmVntQutehXw== --- apiVersion: v1 kind: Service metadata: labels: app: oauth2-proxy name: oauth2-proxy spec: ports: - name: http port: 4180 protocol: TCP targetPort: 4180 selector: app: oauth2-proxy --- apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: ingress-oauth2-proxy annotations: nginx.ingress.kubernetes.io/proxy-buffer-size: "8k" nginx.ingress.kubernetes.io/proxy-buffers-number: "4" spec: rules: - host: somehost.ru http: paths: - path: /oauth2 pathType: Prefix backend: service: name: oauth2-proxy port: number: 4180 ingressClassName: nginx '
Здесь настройки такие добавились:
1) --oidc-issuer-url - url по которому кейклок вернет настройки для авторизации, регистрации и подобное.
2) --ssl-insecure-skip-verify - как раз здесь я выключаю проверку сертификата, потому что не получалось настроить удачный поход внутри кубера. С моим сертификатом явно что-то не так.
3) --silence-ping-logging - буквально "не пиши в логи пинг кубера"
Как настроить кейклок и где взять client-secret хорошо описано тут.
Теперь при входе на страницу https://somehost.ru/app нам откроется авторизация в кейклоке.
Выводы
В целом, получилось сделать то что хотелось, авторизация работает. Но нужно еще разобраться с сертификатом да и наверняка много с чем еще.
Ну и как всегда телега, хотя в прошлые разы подписываться на нее не было никакого смысла, но я стараюсь)