OIDC-розширення для Kong API Gateway

🌐 Цей документ доступний українською та англійською мовами. Використовуйте перемикач у правому верхньому куті, щоб змінити версію.

1. Загальний опис компонента

kong-oidc це розширення для Kong API Gateway, що імплементує функціональність Довірчої Сторони (Relying Party) протоколу OpenID Connect

Дане розширення використовується як зворотний проксі-сервер, завершуючий OAuth/OpenID Connect перед цільовим сервісом, щоб цільовий сервіс можна було захистити відповідними стандартами, не імплементуючи їх на самому сервісі.

2. Функції компоненту

Розширення автентифікує користувачів через OpenID Connect Provider (KeyCloak IAM) використовуючи OpenID Connect Discovery і базовий профіль клієнта (Authorization Code flow).

Також воно підтримує сеанси для автентифікованих користувачів використовуючи бібліотеку lua-resty-openidc. Таким чином пропонуючи настроюваний вибір між збереженням стану сеансу на стороні клієнта в cookie браузера та використанням одного з доступних механізмів збереження на стороні сервера: shared-memory|memcache|redis.

Функція перевірки JWT токену доступу додає можливість для вже автентифікованих користувачів та/або програм, які вже мають токен доступу для проходу через kong. Фактична перевірка токена в такому випадку виконується Сервером Ресурсів (Resource Server).

3. Принципи реалізації компоненту

Даний компонент є розширенням nokia/kong-oidc плагіна який в свою чергу базується на бібліотеці lua-resty-openidc.

Діаграма нижче ілюструє обмін повідомленнями між залученими сторонами в процесі автентифікації.

kong_oidc_flow

Після автентифікації та перед тим як направити запит до цільового сервісу kong-oidc плагін додає до запиту наступні заголовки:

  • X-Access-Token - містить токен доступу

  • X-Id-Token - містить OIDC токен

  • X-Userinfo - містить тіло відповіді з /userinfo ендпоінта.

X-Userinfo: {"preferred_username":"alice","id":"60f65308-3510-40ca-83f0-e9c0151cc680","sub":"60f65308-3510-40ca-83f0-e9c0151cc680"}

Плагін також встановлює змінну ngx.ctx.authenticated_consumer яка може бути використана в інших Kong плагінах:

ngx.ctx.authenticated_consumer = {
    id = "60f65308-3510-40ca-83f0-e9c0151cc680",   -- sub field from Userinfo
    username = "alice"                             -- preferred_username from Userinfo
}

5. Використання

Плагін підтримує наступні налаштування:

Параметр

Значення за замовчуванням

Обов’язковий

Опис

name

так

ім’я плагіну, повинно бути oidc

config.client_id

так

OIDC Client ID

config.client_secret_var_name

ні

Ім’я змінної оточення в якій міститься OIDC Client secret.

config.client_secret

ні

OIDC Client secret. Може також встановлюватися через змінну оточення. В такому випадку має бути встановлено config.client_secret_var_name

config.discovery

https://.well-known/openid-configuration

ні

OIDC Discovery Endpoint (/.well-known/openid-configuration)

config.scope

openid

ні

OAuth2 Token scope. Для того щоб використовувати OIDC, має містити openid.

config.ssl_verify

false

ні

Вмикає перевірку SSL до OIDC провайдера

config.introspection_endpoint

ні

Token introspection endpoint

config.allow_token_auth

false

ні

Дозволяє доступ з Bearer токеном. Якщо дозволений, introspection_endpoint має бути встановлений.

config.timeout

ні

таймаут виклику OIDC endpoint

config.introspection_endpoint_auth_method

client_secret_basic

ні

метод автентифікації для Token introspection. resty-openidc підтримує client_secret_(basic|post).

config.bearer_only

no

ні

Лише перевірка токена без переадресації.

config.realm

kong

ні

Рілм що використовується в WWW-Authenticate заголовку відповіді.

config.logout_path

/logout

ні

абсолютний шлях для виходу (logout) з OIDC RP

config.unauth_action

deny

так

Поведінка якщо користувач неавтентифікований:nil - переадресація на сторінку автентифікації, pass - не переадресовувати, deny - не переадресовувати але повернути помилку натомість.

config.access_token_header_name

X-Access-Token

так

Ім’я для заголовку з токеном доступу.

config.bearer_access_token

"no"

так

Використовувати bearer токен доступу.

config.id_token_header_name

X-ID-Token

так

Ім’я для заголовку з ID токеном.

config.user_header_name

X-Userinfo

так

Ім’я для заголовку з інформацією про користувача.

config.session_opts

ні

JSON стрічка з конфігурацією для сесії користувача. Управління сесією побудовано на базі [lua-resty-session](https://github.com/bungle/lua-resty-session). Детальний опис конфігураційних параметрів для управління сесіями вказано в документації до бібліотеки.

config.enable_authorization

true

ні

Дозволити авторизацію базуючись на таких атрибутах JWT токену доступу як issuer, scope та roles.

config.issuers_allowed

ні

Перелік дозволених видавців токена доступу (iss claim). Приклад: issuers_allowed:\["https://domain/issuer1", "https://domain/issuer2"]

config.scopes_required

nil

ні

Меже (scopes) що мають бути присутні в токені доступу для успішної авторизації.

config.roles_required

nil

ні

Ролі (roles_claim claim) що мають бути присутні (принаймні одна) в токені доступу для успішної авторизації.

config.realm_roles_required

nil

ні

Ролі рілма (realm_access.roles claim) що мають бути присутні (принаймні одна) в токені доступу для успішної авторизації.

config.client_roles_required

nil

ні

Клієнтські ролі (resource_access claim) що мають бути присутні (принаймні одна) в токені доступу для успішної авторизації.

config.token_claim_header_value

ні

Назва атрибуту (token claim) токена доступа чиє значення буде додане в заголовок запиту "token-claim". Значення цього заголовку в подальшому може використовуватися іншими плагінами, наприклад, для обчислення лімітів запитів для автентифікованого користувача чи групи таких користувачів.

При використанні змінних оточення для встановлення конфігураційних параметрів які дозволяють такий механізм, необхідно додатково встановлювати ще одну змінну оточення KONG_NGINX_MAIN_ENV в значеннях якої необхідно перелічити інші змінні оточення читання яких ми хочемо дозволити OIDC плагіну. KONG_NGINX_MAIN_ENV використовує NGINX Injected Directives для того щоб оголосити в nginx.conf змінні оточення що містять секрети через команду env і таким чином дозволити OIDC плагіну прочитати їхні значення.

5.1. Запит до цільового сервісу

Плагін за замовчуванням додає 3 додаткові заголовки до оригінального запиту: X-Userinfo, X-Access-Token та X-Id-Token які можуть використовуватися сервісами. Всі вони закодовані в base64.

GET / HTTP/1.1
Host: netcat:9000
Connection: keep-alive
X-Forwarded-For: 172.19.0.1
X-Forwarded-Proto: http
X-Forwarded-Host: localhost
X-Forwarded-Port: 8000
X-Real-IP: 172.19.0.1
Cache-Control: max-age=0
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36
Upgrade-Insecure-Requests: 1
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Accept-Encoding: gzip, deflate
Accept-Language: pl-PL,pl;q=0.8,en-US;q=0.6,en;q=0.4
Cookie: session=KOn1am4mhQLKazlCA.....
X-Userinfo: eyJnaXZlbl9uYW1lIjoixITEmMWaw5PFgcW7xbnEhiIsInN1YiI6ImM4NThiYzAxLTBiM2ItNDQzNy1hMGVlLWE1ZTY0ODkwMDE5ZCIsInByZWZlcnJlZF91c2VybmFtZSI6ImFkbWluIiwibmFtZSI6IsSExJjFmsOTxYHFu8W5xIYiLCJ1c2VybmFtZSI6ImFkbWluIiwiaWQiOiJjODU4YmMwMS0wYjNiLTQ0MzctYTBlZS1hNWU2NDg5MDAxOWQifQ==
X-Access-Token: eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJGenFSY0N1Ry13dzlrQUJBVng1ZG9sT2ZwTFhBNWZiRGFlVDRiemtnSzZRIn0.eyJqdGkiOiIxYjhmYzlkMC1jMjlmLTQwY2ItYWM4OC1kNzMyY2FkODcxY2IiLCJleHAiOjE1NDg1MTA4MjksIm5iZiI6MCwiaWF0IjoxNTQ4NTEwNzY5LCJpc3MiOiJodHRwOi8vMTkyLjE2OC4wLjk6ODA4MC9hdXRoL3JlYWxtcy9tYXN0ZXIiLCJhdWQiOlsibWFzdGVyLXJlYWxtIiwiYWNjb3VudCJdLCJzdWIiOiJhNmE3OGQ5MS01NDk0LTRjZTMtOTU1NS04NzhhMTg1Y2E0YjkiLCJ0eXAiOiJCZWFyZXIiLCJhenAiOiJrb25nIiwibm9uY2UiOiJmNGRkNDU2YzBjZTY4ZmFmYWJmNGY4ZDA3YjQ0YWE4NiIsImF1dGhfdGltZSI6…IiwibWFuYWdlLWFjY291bnQtbGlua3MiLCJ2aWV3LXByb2ZpbGUiXX19LCJzY29wZSI6Im9wZW5pZCBwcm9maWxlIGVtYWlsIiwiZW1haWxfdmVyaWZpZWQiOmZhbHNlLCJwcmVmZXJyZWRfdXNlcm5hbWUiOiJhZG1pbiJ9.GWuguFjSEDGxw_vbD04UMKxtai15BE2lwBO0YkSzp-NKZ2SxAzl0nyhZxpP0VTzk712nQ8f_If5-mQBf_rqEVnOraDmX5NOXP0B8AoaS1jsdq4EomrhZGqlWmuaV71Cnqrw66iaouBR_6Q0s8bgc1FpCPyACM4VWs57CBdTrAZ2iv8dau5ODkbEvSgIgoLgBbUvjRKz1H0KyeBcXlVSgHJ_2zB9q2HvidBsQEIwTP8sWc6er-5AltLbV8ceBg5OaZ4xHoramMoz2xW-ttjIujS382QQn3iekNByb62O2cssTP3UYC747ehXReCrNZmDA6ecdnv8vOfIem3xNEnEmQw
X-Id-Token: eyJuYmYiOjAsImF6cCI6ImtvbmciLCJpYXQiOjE1NDg1MTA3NjksImlzcyI6Imh0dHA6XC9cLzE5Mi4xNjguMC45OjgwODBcL2F1dGhcL3JlYWxtc1wvbWFzdGVyIiwiYXVkIjoia29uZyIsIm5vbmNlIjoiZjRkZDQ1NmMwY2U2OGZhZmFiZjRmOGQwN2I0NGFhODYiLCJwcmVmZXJyZWRfdXNlcm5hbWUiOiJhZG1pbiIsImF1dGhfdGltZSI6MTU0ODUxMDY5NywiYWNyIjoiMSIsInNlc3Npb25fc3RhdGUiOiJiNDZmODU2Ny0zODA3LTQ0YmMtYmU1Mi1iMTNiNWQzODI5MTQiLCJleHAiOjE1NDg1MTA4MjksImVtYWlsX3ZlcmlmaWVkIjpmYWxzZSwianRpIjoiMjI1ZDRhNDItM2Y3ZC00Y2I2LTkxMmMtOGNkYzM0Y2JiNTk2Iiwic3ViIjoiYTZhNzhkOTEtNTQ5NC00Y2UzLTk1NTUtODc4YTE4NWNhNGI5IiwidHlwIjoiSUQifQ==

Імена цих заголовків можуть бути змінені в налаштуваннях плагіна за допомогою відповідних параметрів: config.access_token_header_name, config.id_token_header_name та config.user_header_name. Додатково, токен доступу можна зробити bearer токеном встановивши значення параметра config.bearer_access_token рівним yes.

Приклад:

config:
...
access_token_header_name: Authorization
bearer_access_token: 'yes'
kind: KongPlugin

Результатом такого налаштування буде додавання до оригінального запиту заголовку Authorization що міститиме beaer токен доступу:

Authorization: bearer eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJGenFSY0N1Ry13dzlrQUJBVng1ZG9sT2ZwTFhBNWZiRGFlVDRiemtnSzZRIn0.eyJqdGkiOiIxYjhmYzlkMC1jMjlmLTQwY2ItYWM4OC1kNzMyY2FkODcxY2IiLCJleHAiOjE1NDg1MTA4MjksIm5iZiI6MCwiaWF0IjoxNTQ4NTEwNzY5LCJpc3MiOiJodHRwOi8vMTkyLjE2OC4wLjk6ODA4MC9hdXRoL3JlYWxtcy9tYXN0ZXIiLCJhdWQiOlsibWFzdGVyLXJlYWxtIiwiYWNjb3VudCJdLCJzdWIiOiJhNmE3OGQ5MS01NDk0LTRjZTMtOTU1NS04NzhhMTg1Y2E0YjkiLCJ0eXAiOiJCZWFyZXIiLCJhenAiOiJrb25nIiwibm9uY2UiOiJmNGRkNDU2YzBjZTY4ZmFmYWJmNGY4ZDA3YjQ0YWE4NiIsImF1dGhfdGltZSI6…IiwibWFuYWdlLWFjY291bnQtbGlua3MiLCJ2aWV3LXByb2ZpbGUiXX19LCJzY29wZSI6Im9wZW5pZCBwcm9maWxlIGVtYWlsIiwiZW1haWxfdmVyaWZpZWQiOmZhbHNlLCJwcmVmZXJyZWRfdXNlcm5hbWUiOiJhZG1pbiJ9.GWuguFjSEDGxw_vbD04UMKxtai15BE2lwBO0YkSzp-NKZ2SxAzl0nyhZxpP0VTzk712nQ8f_If5-mQBf_rqEVnOraDmX5NOXP0B8AoaS1jsdq4EomrhZGqlWmuaV71Cnqrw66iaouBR_6Q0s8bgc1FpCPyACM4VWs57CBdTrAZ2iv8dau5ODkbEvSgIgoLgBbUvjRKz1H0KyeBcXlVSgHJ_2zB9q2HvidBsQEIwTP8sWc6er-5AltLbV8ceBg5OaZ4xHoramMoz2xW-ttjIujS382QQn3iekNByb62O2cssTP3UYC747ehXReCrNZmDA6ecdnv8vOfIem3xNEnEmQw

6. Управління сесіями користувачів

Для управління сесіями користувачів плагін використовує бібліотеку lua-resty-session яка дозволяє зберігати JWT токени користувача як на стороні клієнта в куці так і на стороні сервера в одному з підтримуваних сховищ: shared-memory|memcache|redis(single or cluster). Інструкція по налаштуванню роботи з наведеними сховищами є в репозиторії бібліотеки за посиланням вище.

6.1. Збереження сесій в Redis Sentinel

Додатково функціонал бібліотеки був розширений можливістю зберігати сесію в Redis Sentinel.

Приклад налаштування OIDC плагіна для збереження сесій в Redis Sentinel:

config:
  session_opts:
    {"storage":"sentinel",
        "sentinel":{"sentinel_master_name":"mymaster",
            "sentinel_db":"sessions",
            "sentinel_host":"my-sentinel-host.env-dev.svc",
            "sentinel_port":"26379",
            "uselocking":false}}

Секрет доступу до Redis задається через змінну оточення REDIS_AUTH_SECRET

REDIS_AUTH_SECRET = some_secret_value
KONG_NGINX_MAIN_ENV = REDIS_AUTH_SECRET

6.2. Встановлення секрету сесії

Бібліотека lua-resty-session для управління сесіями та обрахунку HMAC дайджесту за замовчуванням використовує автозгенерований секрет довжиною 32 байти. Проте якщо інсталяція Kong передбачає декілька екземплярів то кожен з них згенерую власний секрет. Це призведе до того, що сесія створена одним екземпляром Kong не зможе бути прочитана іншими екземплярами. Для вирішення цієї проблеми можна або використовувати концепцію sticky session (запити користувача завжди потрапляють на один екземпляр Kong) або явно встановити однаковий секрет на всіх екземплярах Kong. Секрет можна встановити або передавши його в конфігурації OIDC плагіна як додатковий параметр для session_opts:

config:
  session_opts:
    {"secret":"c29tZV9zZWNyZXRfdmFsdWU="}

або через змінну оточення OIDC_SESSION_SECRET:

OIDC_SESSION_SECRET = some_secret_value
KONG_NGINX_MAIN_ENV = OIDC_SESSION_SECRET

В разі використання конфігурації OIDC, значення параметру session_opts.secret має бути закодовано в Base64. При використанні змінної оточення кодувати секрет в Base64 не потрібно.