尝试使用Keycloak+OAuth2代理+OpenLDAP进行Ldap认证
这次实施的组合
环境
-
- WSL Ubuntu 20.04
- Docker version 20.10.14
Keycloak是什么?
Keycloak是一款开源软件。
-
- シングルサインオン
-
- アイデンティティ管理
- アクセス管理の機能
提供等等的服务。
OAuth2 Proxy 是什么?
這是一個用於將認證和授權委託給外部認證基礎設施的反向代理伺服器。(參考:OAuth2 Proxy)
使用OIDC提供程序进行身份验证并进行帐户验证。 OIDC是“OpenID Connect”的缩写,是一种进行身份验证的协议之一。(参考:关于OAuth和OpenID Connect的说明:机制和特点等解释)
将Keycloak构建为Docker容器。
我已经将使用的源代码存放在GitHub上。
- v1tam1nb2/keycloak-oauth2-proxy-ldap
还有一个公式的快速入门指南。
- Get started with Keycloak on Docker
创建Docker网络
创建用于本次验证的容器网络。
docker network create oauth_network
Keycloak的加密协议转换成HTTPS.
cd keycloak
mkdir certs
openssl req -x509 -sha256 -nodes -days 3650 -newkey rsa:2048 -subj /CN=localhost -keyout server.key -out server.crt
省略了一些内容,但文件夹的结构是基于以下状态来构建的。
.
├── docker-compose.yaml
├── .env
├── keycloak
│ ├── certs
│ │ ├── server.crt
│ │ └── server.key
│ └── keycloak.yaml
├── openldap
│ └── testuser.ldif
└── openresty
└── nginx.conf
构建Keycloak容器
version: '3'
services:
keycloak:
image: quay.io/keycloak/keycloak:latest
container_name: keycloak
tty: true
ports:
- "18080:8080"
- "8443:8443"
volumes:
- ./keycloak/data:/opt/keycloak/data
- ./keycloak/certs:/etc/x509/https
environment:
KEYCLOAK_ADMIN: admin
KEYCLOAK_ADMIN_PASSWORD: passwd
KC_HOSTNAME_STRICT_HTTPS: 'false'
KC_HTTPS_CERTIFICATE_FILE: /etc/x509/https/server.crt
KC_HTTPS_CERTIFICATE_KEY_FILE: /etc/x509/https/server.key
command:
- start-dev
networks:
- oauth_network
networks:
oauth_network:
external: true
启动容器。
docker-compose -f keycloak.yaml up -d
然而,如果仍然保持现状,由于音量管理权限问题,启动将失败,因此请执行以下操作。
docker-compose -f keycloak.yaml down
chown -R 1000:0 data/
chown -R 1000:0 certs/
再次启动容器。
docker-compose -f keycloak.yaml up -d
请访问以下内容。
Keycloak的界面会显示出来。
选择管理控制台。
请转到登录界面,并输入在keycloak.yaml文件中设置的以下登录信息。
-
- admin
- passwd
创建领域
创建客户
选择”创建客户”选项。
在输入oauth2-proxy作为客户端ID后,选择下一步。
将客户身份认证设置为开启,并选择”下一步”。
在”Valid redirect URIs”和”Web origins”栏目中输入*,然后选择保存。
复制凭证
保留下来后面会在OAuth2代理中使用。
在中国本土化的情况下,创建OAuth2代理/OpenLDAP/Openresty容器。
version: '3'
services:
webapp:
image: openresty/openresty:latest
container_name: webapp
hostname: webapp
networks:
- oauth_network
openresty:
image: openresty/openresty:latest
container_name: openresty
hostname: openresty
ports:
- '${HTTP_PORT}:80'
env_file:
- ./.env
volumes:
- ./openresty/nginx.conf:/usr/local/openresty/nginx/conf/nginx.conf
# - ./openresty/conf.d:/usr/local/openresty/nginx/conf/conf.d
# - ./openresty/default.d:/usr/local/openresty/nginx/conf/default.d
networks:
- oauth_network
oauth2-proxy:
container_name: oauth2-proxy
hostname: oauth2-proxy
image: quay.io/oauth2-proxy/oauth2-proxy:latest
ports:
- 4180:4180
environment:
OAUTH2_PROXY_PROVIDER: oidc
OAUTH2_PROXY_OIDC_EMAIL_CLAIM: sub
OAUTH2_PROXY_SCOPE: "openid"
# keycloakで設定したClient IDと同じ
OAUTH2_PROXY_CLIENT_ID: oauth2-proxy
# keycloakで取得したCredentials
OAUTH2_PROXY_CLIENT_SECRET: "taAhCUFAemBr3Q0pnTESj6Y2V0JBi6yo"
OAUTH2_PROXY_OIDC_ISSUER_URL: https://keycloak:8443/realms/demo_realm
# OAUTH2_PROXY_COOKIE_SECRETは「python -c 'import os,base64; print(base64.urlsafe_b64encode(os.urandom(32)).decode())'」で生成可。
OAUTH2_PROXY_COOKIE_SECRET: "5JEx-EnDmjnlN-1wd2EQDMrLLEly5NOUUSYsZCiy3Tc="
# keycloakで設定したRealmの名前
OAUTH2_PROXY_COOKIE_NAME: "auth_demo"
OAUTH2_PROXY_EMAIL_DOMAINS: "*"
OAUTH2_PROXY_HTTP_ADDRESS: 0.0.0.0:4180
OAUTH2_PROXY_PASS_AUTHORIZATION_HEADER: "true"
OAUTH2_PROXY_PASS_ACCESS_TOKEN: "true"
OAUTH2_PROXY_PASS_USER_HEADERS: "true"
OAUTH2_PROXY_PASS_AUTHORIZATION_HEADER: "true"
OAUTH2_PROXY_SSL_INSECURE_SKIP_VERIFY: "true"
OAUTH2_PROXY_INSECURE_OIDC_ALLOW_UNVERIFIED_EMAIL: "true"
# 以下、HTTPS ではなく HTTP で動かすための設定
OAUTH2_PROXY_REDIRECT_URL: ""
OAUTH2_PROXY_COOKIE_SECURE: "false"
# OAUTH2_PROXY_COOKIE_EXPIRE: "12h"
# OAUTH2_PROXY_COOKIE_SECURE: "true"
# OAUTH2_PROXY_SET_XAUTHREQUEST: "true"
# OAUTH2_PROXY_REVERSE_PROXY: "true"
# OAUTH2_PROXY_SKIP_PROVIDER_BUTTON: "true"
networks:
- oauth_network
ldap-server:
image: osixia/openldap:latest
restart: always
container_name: ldap-server
environment:
LDAP_ORGANISATION: "My Company"
LDAP_DOMAIN: "my-company.com"
LDAP_ADMIN_PASSWORD: "admin"
LDAP_READONLY_USER: "true"
LDAP_READONLY_USER_USERNAME: "readonly"
LDAP_READONLY_USER_PASSWORD: "readonly_password"
LDAP_TLS_VERIFY_CLIENT: "never"
ports:
- "389:389"
volumes:
- ./openldap/var/lib/ldap:/var/lib/ldap
- ./openldap/etc/ldap/slapd.d:/etc/ldap/slapd.d
- ./openldap/testuser.ldif:/slapd/assets/custome/testuser.ldif
networks:
- oauth_network
# ログイン情報
# Login DN: cn=admin,dc=my-company,dc=com
# Password: admin
ldap-admin:
image: osixia/phpldapadmin:latest
container_name: ldap-admin
restart: always
environment:
PHPLDAPADMIN_LDAP_HOSTS: "ldap"
PHPLDAPADMIN_HTTPS: "false"
ports:
- "8080:80"
links:
- "ldap-server:ldap"
networks:
- oauth_network
networks:
oauth_network:
external: true
在OpenResty中,端口是通过环境变量进行指定的。虽然在这种情况下没有太大意义,但如果有需要从OpenResty访问环境变量的情况出现,这会很有帮助。
HTTP_PORT = "80"
docker-compose up -d
主机的配置
OAUTH2_PROXY_OIDC_ISSUER_URL: https://keycloak:8443/realms/demo_realm 在Docker网络中通过服务名称进行解析,但如果没有以下设置,将无法成功进行访问验证。
127.0.0.1 keycloak
使用 OpenLDAP 对用户进行注册。
这是用于在OpenLDAP中注册测试用户的配置文件。
test001用户的密码是test001。
dn: cn=test001,dc=my-company,dc=com
givenName:test001
sn:test001
cn:test001
mail:test001@my-company.com
userPassword::dGVzdDAwMQ==
objectClass: inetOrgPerson
objectClass: top
docker exec ldap-server ldapadd -x -D "cn=admin,dc=my-company,dc=com" -w admin -f /slapd/assets/custome/testuser.ldif -ZZ
使用Keycloak设置Ldap
选择”添加LDAP提供程序”来进行用户联邦。
进行以下设置。最后按Save按钮保存。
Openresty的配置
几乎是以下文本的复制粘贴。
- Configuring for use with the Nginx auth_request directive
# nginx.conf -- docker-openresty
#
# This file is installed to:
# `/usr/local/openresty/nginx/conf/nginx.conf`
# and is the file loaded by nginx at startup,
# unless the user specifies otherwise.
#
# It tracks the upstream OpenResty's `nginx.conf`, but removes the `server`
# section and adds this directive:
# `include /etc/nginx/conf.d/*.conf;`
#
# The `docker-openresty` file `nginx.vh.default.conf` is copied to
# `/etc/nginx/conf.d/default.conf`. It contains the `server section
# of the upstream `nginx.conf`.
#
# See https://github.com/openresty/docker-openresty/blob/master/README.md#nginx-config-files
#
# 環境変数(envに設定したポート番号を取得したい場合は利用する)
# env HTTP_PORT;
#user nobody;
worker_processes 1;
# Enables the use of JIT for regular expressions to speed-up their processing.
pcre_jit on;
# error_log /etc/nginx/lua/error.log;
#error_log logs/error.log;
#error_log logs/error.log notice;
#error_log logs/error.log info;
#pid logs/nginx.pid;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
charset UTF-8;
# Enables or disables the use of underscores in client request header fields.
# When the use of underscores is disabled, request header fields whose names contain underscores are marked as invalid and become subject to the ignore_invalid_headers directive.
# underscores_in_headers off;
#log_format main '$remote_addr - $remote_user [$time_local] "$request" '
# '$status $body_bytes_sent "$http_referer" '
# '"$http_user_agent" "$http_x_forwarded_for"';
#access_log logs/access.log main;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
#access_log /var/log/nginx/access.log main;
access_log /dev/stdout;
# Log in JSON Format
# log_format nginxlog_json escape=json '{ "timestamp": "$time_iso8601", '
# '"remote_addr": "$remote_addr", '
# '"body_bytes_sent": $body_bytes_sent, '
# '"request_time": $request_time, '
# '"response_status": $status, '
# '"request": "$request", '
# '"request_method": "$request_method", '
# '"host": "$host",'
# '"upstream_addr": "$upstream_addr",'
# '"http_x_forwarded_for": "$http_x_forwarded_for",'
# '"http_referrer": "$http_referer", '
# '"http_user_agent": "$http_user_agent", '
# '"http_version": "$server_protocol", '
# '"nginx_access": true }';
# access_log /dev/stdout nginxlog_json;
# See Move default writable paths to a dedicated directory (#119)
# https://github.com/openresty/docker-openresty/issues/119
client_body_temp_path /var/run/openresty/nginx-client-body;
proxy_temp_path /var/run/openresty/nginx-proxy;
fastcgi_temp_path /var/run/openresty/nginx-fastcgi;
uwsgi_temp_path /var/run/openresty/nginx-uwsgi;
scgi_temp_path /var/run/openresty/nginx-scgi;
sendfile on;
#tcp_nopush on;
#keepalive_timeout 0;
#keepalive_timeout 65;
client_max_body_size 0;
#gzip on;
include /usr/local/openresty/nginx/conf/conf.d/*.conf;
# Don't reveal OpenResty version to clients.
# server_tokens off;
server {
# http
autoindex off;
server_tokens off;
resolver local=on ipv6=off;
listen 80;
location /oauth2/ {
proxy_pass http://oauth2-proxy:4180;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Scheme $scheme;
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://oauth2-proxy:4180;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Scheme $scheme;
# 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 = /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_pass http://webapp/;
# or "root /path/to/site;" or "fastcgi_pass ..." etc
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/local/openresty/nginx/nginx/html;
}
# Load configuration files for the default server block.
include /usr/local/openresty/nginx/conf/default.d/*.conf;
}
}
确认访问
点击下方链接。
可以显示OAuth2 Proxy的身份验证界面。可以通过参数OAUTH2_PROXY_SKIP_PROVIDER_BUTTON来设置是否跳过此身份验证界面。
选择使用 OpenID Connect 进行登录。
在转到Keycloak的认证界面后,需要输入在Ldap中注册的用户名和密码(本次示例中为test001用户)。
如果打开OpenResty(Web应用程序)的欢迎页面,就表示一切正常。
总结
我尝试使用Keycloak+OAuth2 Proxy+OpenLDAP进行Ldap认证。
因为现在已经建立了一个可以在本地进行验证的环境,所以我在这里就结束了。