使用 OAuth2-Proxy 和 Keycloak 为系统添加 OAuth2 身份验证
toyiye 2024-09-16 06:07 4 浏览 0 评论
背景
在平常工作,相信大家都会使用到一些常用的开源软件,像 prometheus , rabbitmq , kibanba 等。这些软件很多在权限设计这块就比较简单,或者是没有,如Prometheus 自带的查询面板,早期的 rocketmq dashboard,kibana 等,相信小伙伴有不少基于这块的安全整改,整改措施一般只是配置简单的 http basic 认证,或者 IP 白名单访问。下面介绍基于 Keycloak 和 OAuth2-Proxy 为系统添加 OAuth2 身份验证的方案。
keycloak 简单介绍
KeyCloak 是一种开源身份和访问管理解决方案,以最少的工作量为应用程序添加身份验证和保护服务。Keycloak 提供用户联合、强身份验证、用户管理、精细授权等。目前支持挺多开源软件单点登录,如 jenkins, redash, grafana 等。
OAuth2-Proxy 简单介绍
oauth2-proxy 是一个反向代理和静态文件服务器,它使用提供程序(Google、Keycloak、GitHub 等)提供身份验证,以通过电子邮件、域或组验证帐户。
方案介绍
我们可以使用 OAuth2-Proxy 和 Keycloak 为应用程序中添加 OAuth2 身份验证,特别是那些没有身份验证的系统,下面以 Prometheus 为例,在我的一台服务器上,有 nginx , keycloak , oauth2_proxy 以及 prometheus 4 个服务,其中 keycloak, oauth2_proxy, prometheus 都是通过 nginx vhost 反向代理访问,域名如下图:
配置 Prometheus 基于 OAuth2-Proxy 和 Keycloak 身份验证的请求过程可以参考下图:
nginx , keycloak , oauth2_proxy 以及 prometheus 4 个服务这次全部使用 docker 部署。
keycloak docker 部署
keycloak docker-compose.yml
services:
keycloak:
image: quay.io/keycloak/keycloak:25.0.4
ports:
- "8080:8080"
# restart: always
environment:
KEYCLOAK_ADMIN: admin
KEYCLOAK_ADMIN_PASSWORD: ftLVBgGK9SP
KC_HTTP_ENABLED: true
KC_PROXY_HEADERS: xforwarded # keycloak 访问通过 nginx 反向代理,proxy headers 支持 forwarded, xforwarded
KC_HEALTH_ENABLED: true
KC_HOSTNAME: keycloak.zwade.top
#KC_HOSTNAME_STRICT: false
KC_DB: mysql
KC_DB_URL: jdbc:mysql://mysql:3306/keycloak
KC_DB_USERNAME: keycloak
KC_DB_PASSWORD: Pp123456
command:
- start
networks:
- access-mysql
networks:
access-mysql:
external: true
默认情况下,服务器使用 dev-file 数据库。这是服务器将用于持久数据的默认数据库,并且仅存在用于开发用例的数据库。 dev-file 数据库不适合生产用例,必须在部署到生产之前替换。keycloak 内置支持不同的数据库,具体可以参考官档。
oauth2-proxy docker 部署
Oauth2-proxy 的配置项可以参考 oauth2-proxy 官档,每个命令行参数都可以指定为环境变量,方法是将其前缀 OAUTH2_PROXY_ ,将其大写,并将连字符 ( - ) 替换为下划线 ( _ )。如果可以多次指定参数,则环境变量应为复数(尾随 S )。注意,环境配置项的值千万不要加引号。
生成强 Cookie 密钥,对应环境变量 OAUTH2_PROXY_COOKIE_SECRET
dd if=/dev/urandom bs=32 count=1 2>/dev/null | base64 | tr -d -- '\n' | tr -- '+/' '-_' ; echo
REDIRECT_URL 需要与 keycloak 配置的 client 的 Valid redirect URIs 配置项要对应上,下面是 oauth2-proxydocker-compose.yml
services:
server:
image: quay.io/oauth2-proxy/oauth2-proxy:v7.6.0
ports:
- "4180:4180"
environment:
- OAUTH2_PROXY_PROVIDER=keycloak-oidc
- OAUTH2_PROXY_CLIENT_ID=prometheus
- OAUTH2_PROXY_CLIENT_SECRET=z6rqgRPAZ3QsOIbx02Zq60llHeDGpZnAuo
- OAUTH2_PROXY_REDIRECT_URL=https://prometheus.zwade.top/oauth2/callback
- OAUTH2_PROXY_OIDC_ISSUER_URL=https://keycloak.zwade.top/realms/master
- OAUTH2_PROXY_COOKIE_SECRET=ofonr_QLDW5EaKaVp09P2b46Pycn8pps7bHNpjDEUEk=
- OAUTH2_PROXY_EMAIL_DOMAINS=*
- OAUTH2_PROXY_INSECURE_OIDC_ALLOW_UNVERIFIED_EMAIL=true
- OAUTH2_PROXY_CODE_CHALLENGE_METHOD=S256
- OAUTH2_PROXY_REVERSE_PROXY=true
- OAUTH2_PROXY_HTTP_ADDRESS=0.0.0.0:4180
Nginx 和 Prometheus docker 部署
部署过程略
Keycloak 创建新的 OIDC
在 Keycloak新版控制台按照以下步骤创建:
a. 在 Keycloak realm 下创建新的 OIDC client
Clients -> Create client
Client Type 选择 OpenID Connect,填写 Client ID,* 必填项,其他项也可以进行填写, Next
Client authentication 'On',
Authentication flow ,勾选 Standard flow,取消勾选 Direct access grants, Next
填写Valid redirect URIs,需要与 oauth2_proxy 的 OAUTH2_PROXY_REDIRECT_URL 对应上,Save
b. client 配置专用的 audience mapper
Clients -> <your client's id> -> Client scopes
点击 Configure a new mapper 然后选择 Audience
Name 一般以aud-mapper-<your client's id>格式,Included Client Audience 下拉框选择你对应的 client's id,Add to ID token 'On',Add to access token 'On',其他保持默认就可以,然后 Save
更多请参考:https://oauth2-proxy.github.io/oauth2-proxy/configuration/providers/keycloak_oidc/
配置 Nginx 反向代理
创建关于 prometheus 域名 nginx vhost 配置,使用 Nginx auth_request 指令,该指令允许 Nginx 通过 oauth2-proxy 的/auth endpoint 对请求进行身份验证,该 endpoint 仅返回 202 Accepted 响应或 401 Unauthorized 响应,而不通过代理请求。
upstream prometheus {
server localhost:9090;
keepalive 1;
}
server {
listen 80;
listen 443 ssl http2;
server_name prometheus.zwade.top;
ssl_certificate /etc/nginx/conf.d/ssl/zwade.top/zwade.top.cer;
ssl_certificate_key /etc/nginx/conf.d/ssl/zwade.top/zwade.top.key;
proxy_buffer_size 32k;
proxy_buffers 4 32k;
location /oauth2/ {
proxy_pass http://127.0.0.1:4180;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Auth-Request-Redirect $request_uri;
# or, if you are handling multiple domains:
proxy_set_header X-Auth-Request-Redirect $scheme://$host$request_uri;
}
location = /oauth2/auth {
proxy_pass http://127.0.0.1:4180;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Uri $request_uri;
# nginx auth_request includes headers but not body
proxy_set_header Content-Length "";
proxy_pass_request_body off;
}
location / {
auth_request /oauth2/auth;
error_page 401 =403 /oauth2/sign_in;
# pass information via X-User and X-Email headers to backend,
# requires running with --set-xauthrequest flag
auth_request_set $user $upstream_http_x_auth_request_user;
auth_request_set $email $upstream_http_x_auth_request_email;
proxy_set_header X-User $user;
proxy_set_header X-Email $email;
# if you enabled --pass-access-token, this will pass the token to the backend
auth_request_set $token $upstream_http_x_auth_request_access_token;
proxy_set_header X-Access-Token $token;
# if you enabled --cookie-refresh, this is needed for it to work with auth_request
auth_request_set $auth_cookie $upstream_http_set_cookie;
add_header Set-Cookie $auth_cookie;
# When using the --set-authorization-header flag, some provider's cookies can exceed the 4kb
# limit and so the OAuth2 Proxy splits these into multiple parts.
# Nginx normally only copies the first `Set-Cookie` header from the auth_request to the response,
# so if your cookies are larger than 4kb, you will need to extract additional cookies manually.
auth_request_set $auth_cookie_name_upstream_1 $upstream_cookie_auth_cookie_name_1;
# Extract the Cookie attributes from the first Set-Cookie header and append them
# to the second part ($upstream_cookie_* variables only contain the raw cookie content)
if ($auth_cookie ~* "(; .*)") {
set $auth_cookie_name_0 $auth_cookie;
set $auth_cookie_name_1 "auth_cookie_name_1=$auth_cookie_name_upstream_1$1";
}
# Send both Set-Cookie headers now if there was a second part
if ($auth_cookie_name_upstream_1) {
add_header Set-Cookie $auth_cookie_name_0;
add_header Set-Cookie $auth_cookie_name_1;
}
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_pass http://prometheus/;
}
}
测试验证
打开浏览器开发者模式,可以看到请求Prometheus 是带有 ouath2_proxy cookies 认证。
最后
keycloak + oauth2_proxy 真的基本上可以开箱即用,无需额外开发,我们可以使用 keycloak 做统一用户管理,当然 keycloak 还不止上面这些功能,有兴趣的朋友可以自行查看官档。
相关推荐
- 为何越来越多的编程语言使用JSON(为什么编程)
-
JSON是JavascriptObjectNotation的缩写,意思是Javascript对象表示法,是一种易于人类阅读和对编程友好的文本数据传递方法,是JavaScript语言规范定义的一个子...
- 何时在数据库中使用 JSON(数据库用json格式存储)
-
在本文中,您将了解何时应考虑将JSON数据类型添加到表中以及何时应避免使用它们。每天?分享?最新?软件?开发?,Devops,敏捷?,测试?以及?项目?管理?最新?,最热门?的?文章?,每天?花?...
- MySQL 从零开始:05 数据类型(mysql数据类型有哪些,并举例)
-
前面的讲解中已经接触到了表的创建,表的创建是对字段的声明,比如:上述语句声明了字段的名称、类型、所占空间、默认值和是否可以为空等信息。其中的int、varchar、char和decimal都...
- JSON对象花样进阶(json格式对象)
-
一、引言在现代Web开发中,JSON(JavaScriptObjectNotation)已经成为数据交换的标准格式。无论是从前端向后端发送数据,还是从后端接收数据,JSON都是不可或缺的一部分。...
- 深入理解 JSON 和 Form-data(json和formdata提交区别)
-
在讨论现代网络开发与API设计的语境下,理解客户端和服务器间如何有效且可靠地交换数据变得尤为关键。这里,特别值得关注的是两种主流数据格式:...
- JSON 语法(json 语法 priority)
-
JSON语法是JavaScript语法的子集。JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔花括号保存对象方括号保存数组JS...
- JSON语法详解(json的语法规则)
-
JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔大括号保存对象中括号保存数组注意:json的key是字符串,且必须是双引号,不能是单引号...
- MySQL JSON数据类型操作(mysql的json)
-
概述mysql自5.7.8版本开始,就支持了json结构的数据存储和查询,这表明了mysql也在不断的学习和增加nosql数据库的有点。但mysql毕竟是关系型数据库,在处理json这种非结构化的数据...
- JSON的数据模式(json数据格式示例)
-
像XML模式一样,JSON数据格式也有Schema,这是一个基于JSON格式的规范。JSON模式也以JSON格式编写。它用于验证JSON数据。JSON模式示例以下代码显示了基本的JSON模式。{"...
- 前端学习——JSON格式详解(后端json格式)
-
JSON(JavaScriptObjectNotation)是一种轻量级的数据交换格式。易于人阅读和编写。同时也易于机器解析和生成。它基于JavaScriptProgrammingLa...
- 什么是 JSON:详解 JSON 及其优势(什么叫json)
-
现在程序员还有谁不知道JSON吗?无论对于前端还是后端,JSON都是一种常见的数据格式。那么JSON到底是什么呢?JSON的定义...
- PostgreSQL JSON 类型:处理结构化数据
-
PostgreSQL提供JSON类型,以存储结构化数据。JSON是一种开放的数据格式,可用于存储各种类型的值。什么是JSON类型?JSON类型表示JSON(JavaScriptO...
- JavaScript:JSON、三种包装类(javascript 包)
-
JOSN:我们希望可以将一个对象在不同的语言中进行传递,以达到通信的目的,最佳方式就是将一个对象转换为字符串的形式JSON(JavaScriptObjectNotation)-JS的对象表示法...
- Python数据分析 只要1分钟 教你玩转JSON 全程干货
-
Json简介:Json,全名JavaScriptObjectNotation,JSON(JavaScriptObjectNotation(记号、标记))是一种轻量级的数据交换格式。它基于J...
- 比较一下JSON与XML两种数据格式?(json和xml哪个好)
-
JSON(JavaScriptObjectNotation)和XML(eXtensibleMarkupLanguage)是在日常开发中比较常用的两种数据格式,它们主要的作用就是用来进行数据的传...
你 发表评论:
欢迎- 一周热门
- 最近发表
- 标签列表
-
- r语言矩阵 (127)
- browsererror (114)
- exportexcel (119)
- cv2.bitwise_not (137)
- dump命令 (128)
- es6concat (126)
- heapify (127)
- java.security.egd (130)
- javax.annotation (117)
- jsstringsplit (117)
- js数字 (115)
- maven编译 (132)
- mysqlleft (128)
- nodejsbuffer (149)
- org.apache.commons.httpclient (126)
- org.jsoup (141)
- org.springframework.web (128)
- robotframework-ride (115)
- setnocounton (141)
- socket.gethostbyname (122)
- sqlmid (121)
- time.strptime (133)
- vscode格式化 (125)
- win32con (129)
- window.localstorage (126)