Сеть настроена через реверс-прокси traefik. Сам траефик не умеет работать с аутентификацией, для этого необходимо прокладывать oauth прокси. появилась задача внутри сети прокинуть токены в заголовки всех приложений без авторизации пользователя. Keycloak настроен и отдает токен. Проблема появилась при конфигурации oauth2-proxy, он работает как autentification_code то есть аутентификация происходит при вмешательстве пользователя, мне нужно получить токен без вмешательства пользователя, так называемый M2M обмен, токен должен быть передан как client_credentials.
Конфигурация oauth2-proxy в traefik
oauth2-cred-proxy:
image: quay.io/oauth2-proxy/oauth2-proxy:v7.6.0
container_name: oauth2-cred-proxy
environment:
OAUTH2_PROXY_LOG_LEVEL: "debug"
OAUTH2_PROXY_PROVIDER: "oidc"
OAUTH2_PROXY_CLIENT_ID: "@oauth2-cred-proxy"
OAUTH2_PROXY_CLIENT_SECRET: "@secret-oauth2-cred-proxy"
OAUTH2_PROXY_COOKIE_SECURE: "true"
OAUTH2_PROXY_COOKIE_SECRET: "@cookie-oauth2-cred-proxy"
OAUTH2_PROXY_COOKIE_REFRESH: "600s"
OAUTH2_PROXY_COOKIE_SAMESITE: "none" # Для кросс-доменных запросов
OAUTH2_PROXY_COOKIE_DOMAINS: ".example.com"
OAUTH2_PROXY_PASS_ACCESS_TOKEN: "true" # Важно: разрешаем прокси передавать токен в заголовке
OAUTH2_PROXY_PASS_AUTHORIZATION_HEADER: "true"
OAUTH2_PROXY_SSL_INSECURE_SKIP_VERIFY: "true"
OAUTH2_PROXY_SKIP_PROVIDER_BUTTON: "true"
OAUTH2_PROXY_UPSTREAMS: static://204
OAUTH2_PROXY_EMAIL_DOMAINS: "*"
OAUTH2_PROXY_WHITELIST_DOMAINS: ".example.com"
OAUTH2_PROXY_SET_AUTHORIZATION_HEADER: "true"
OAUTH2_PROXY_SET_XAUTHREQUEST: "true"
OAUTH2_PROXY_CODE_CHALLENGE_METHOD: "S256"
OAUTH2_PROXY_HTTP_ADDRESS: "0.0.0.0:4180"
OAUTH2_PROXY_REVERSE_PROXY: "true"
OAUTH2_PROXY_ALLOWED_REDIRECT_DOMAINS: ".example.com"
OAUTH2_PROXY_SHOW_DEBUG_ON_ERROR: "true"
# Позволяет проверять только токены без редиректа
OAUTH2_PROXY_SKIP_JWT_BEARER_TOKENS: "false"
OAUTH2_PROXY_OIDC_PKCE: "false"
OAUTH2_PROXY_OIDC_ISSUER_URL: "https://keycloak.example.com/realms/example.com-realm"
OAUTH2_PROXY_OIDC_AUTH_URL: "https://keycloak.example.com/realms/example.com-realm/protocol/openid-connect/auth"
OAUTH2_PROXY_OIDC_TOKEN_URL: "https://keycloak.example.com/realms/example.com-realm/protocol/openid-connect/token"
OAUTH2_PROXY_OIDC_USER_INFO_URL: "https://keycloak.example.com/realms/example.com-realm/protocol/openid-connect/userinfo"
expose:
- "4180"
labels:
- "traefik.enable=true"
- "traefik.http.routers.oauth2-cred-proxy-secure.rule=Host(`oauth.example.com`)"
- "traefik.http.routers.oauth2-cred-proxy-secure.entrypoints=websecure"
- "traefik.http.routers.oauth2-cred-proxy-secure.tls=true"
- "traefik.http.routers.oauth2-cred-proxy-secure.tls.certresolver=myresolver" # генерация сертификата
- "traefik.http.routers.oauth2-cred-proxy-secure.service=oauth2-cred-proxy-secure"
- "traefik.http.services.oauth2-cred-proxy-secure.loadbalancer.server.port=4180"
networks:
- traefik_my-network</blockquote>
конфигурация keycloak
{
"clientId": "@oauth2-cred-proxy",
"name": "@oauth2-cred-proxy",
"description": "",
"rootUrl": "",
"adminUrl": "",
"baseUrl": "",
"surrogateAuthRequired": false,
"enabled": true,
"alwaysDisplayInConsole": false,
"clientAuthenticatorType": "client-secret",
"secret": "@secret-oauth2-cred-proxy",
"redirectUris": [
"*"
],
"webOrigins": [
"https://keycloak.example.com",
"https://echo.example.com",
"+"
],
"notBefore": 0,
"bearerOnly": false,
"consentRequired": false,
"standardFlowEnabled": true,
"implicitFlowEnabled": true,
"directAccessGrantsEnabled": false,
"serviceAccountsEnabled": true,
"authorizationServicesEnabled": true,
"publicClient": false,
"frontchannelLogout": true,
"protocol": "openid-connect",
"attributes": {
"realm_client": "false",
"oidc.ciba.grant.enabled": "true",
"client.secret.creation.time": "1753858235",
"backchannel.logout.session.required": "true",
"standard.token.exchange.enabled": "true",
"frontchannel.logout.session.required": "true",
"oauth2.device.authorization.grant.enabled": "true",
"display.on.consent.screen": "false",
"pkce.code.challenge.method": "S256",
"backchannel.logout.revoke.offline.tokens": "false"
},
"authenticationFlowBindingOverrides": {},
"fullScopeAllowed": true,
"nodeReRegistrationTimeout": -1,
"defaultClientScopes": [
"service_account",
"web-origins",
"acr",
"grant_type_scope",
"roles",
"profile",
"basic",
"email"
],
"optionalClientScopes": [
"address",
"phone",
"organization",
"offline_access",
"microprofile-jwt"
],
"access": {
"view": true,
"configure": true,
"manage": true
}
}
запрос токена из keycloak напрямую отдает access_token
docker run --rm curlimages/curl -i -X POST -d "client_id=@oauth2-cred-proxy" -d "client_secret=@secret-oauth2-cred-proxy" -d "grant_type=client_credentials" -d "scope=openid" https://keycloak.example.com/realms/example.com-realm/protocol/openid-connect/token
запрос токена из keykloak через oauth2-proxy отдает заголовки без access_token
docker run --rm --network traefik_my-network curlimages/curl curl -i http://oauth2-cred-proxy:4180
root@www:~# docker run --rm --network traefik_my-network curlimages/curl curl -i http://oauth2-cred-proxy:4180
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0HTTP/1.1 302 Found
Cache-Control: no-cache, no-store, must-revalidate, max-age=0
Content-Type: text/html; charset=utf-8
Expires: Thu, 01 Jan 1970 00:00:00 UTC
Location: https://keycloak.example.com/realms/example.com-realm/protocol/openid-connect/auth?approval_prompt=force&client_id=oauth2-cred-proxy&code_challenge=qldLKr4qsT_idKXnBetxkV4AeY4JBNSvWO9b7bOlX_U&code_challenge_method=S256&redirect_uri=https%3A%2F%2Fkeycloak.example.com%2Frealms%2Fexample.com-realm%2Fprotocol%2Fopenid-connect%2Ftoken&response_type=code&scope=openid+email+profile&state=prEDLzhcPSAgcB6PYBHLzsjnLADT19ARaBotVVABHLo%3A%2F
Set-Cookie: _oauth2_proxy_csrf=Op5su9atPad2h1twvnAlzS8F1QCdCIsCvDTRvlqrZX_QH_rCObPxLO2oQ1qrqiDCH4KQB9y9EJfZQGflJt1z7RXJxdGLdbCvdGCkMQvss70K80P4PSKIB66CU_5MoCDPr-sKFSEUyVnF2MOJXHGTaaqJ9nRX9IVGbSBLTuKUwU-ExTDZWAFmPsgxcFiAU3GxZcUmCgtrpmTqH2XDcLOYeb-3_NRpxQG-1g19Gfgt5zkhSc-NmkHN6IvvCEEh8w==|1753892954|TZMWEOiw8wWGWPSJ4ty__tsYiuePhFsaKYl3fkRHjNQ=; Path=/; Domain=example.com; Expires=Wed, 30 Jul 2025 16:44:14 GMT; HttpOnly; Secure; SameSite=None
100 484 100 484 0 0 10437 0 --:--:-- --:--:-- --:--:-- 10521
X-Accel-Expires: 0
Date: Wed, 30 Jul 2025 16:29:14 GMT
Content-Length: 484
<a href="https://keycloak.example.com/realms/example.com-realm/protocol/openid-connect/auth?approval_prompt=force&client_id=oauth2-cred-proxy&code_challenge=qldLKr4qsT_idKXnBetxkV4AeY4JBNSvWO9b7bOlX_U&code_challenge_method=S256&redirect_uri=https%3A%2F%2Fkeycloak.example.com%2Frealms%2Fexample.com-realm%2Fprotocol%2Fopenid-connect%2Ftoken&response_type=code&scope=openid+email+profile&state=prEDLzhcPSAgcB6PYBHLzsjnLADT19ARaBotVVABHLo%3A%2F">Found</a>.
Думаю, для знатоков oauth2-proxy настройка такой конфигурации должна быть простой.