背景
在平常工作,相信大家都会使用到一些常用的开源软件,像 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-proxy
docker-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
更多请参考:oauth2-proxy.github.io/oauth2-prox…
配置 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.gif
打开浏览器开发者模式,可以看到请求Prometheus 是带有 ouath2_proxy cookies 认证。
最后
keycloak + oauth2_proxy 真的基本上可以开箱即用,无需额外开发,我们可以使用 keycloak 做统一用户管理,当然 keycloak 还不止上面这些功能,有兴趣的朋友可以自行查看官档。