将Nginx作为OpenID Connect的Relying Party来实现
這篇文章是驗證認可聖誕日曆的第24天的文章。
首先
本文介绍了将Nginx作为OpenID Connect的依赖方实现的方法。
通过将Nginx作为OpenID Connect的依赖方实现,可以期待引入基于OpenID Connect的认证,而不依赖于现有实施方法。
本次实现时,将使用lua-resty-openidc作为Nginx的OpenIDConnect的Relying Party。
在Nginx+中,可以使用nginx-openid-connect。可能值得先尝试这个。
本文提供了示例实现。最后还附有验证方法的说明,请一并确认,谢谢。
https://github.com/kg0r0/lua-resty-openidc-example
关于Nginx模块
这次我们将使用OpenResty开发在Nginx上运行的模块。
通过使用OpenResty,您可以在Nginx中使用LuaJIT和方便的库。
此外,Lua还有一个名为Luarocks的包管理系统,我们也将一并使用。
关于OpenResty和Luarocks的详细使用方法,请查阅它们在OpenResty官方网站上的说明。
-
- OpenRestyのインストール: https://openresty.org/en/installation.html
Luarocksのインストール: https://openresty.org/en/using-luarocks.html
引入OpenID Connect身份验证
首先,我们将使用 lua-resty-openidc 在 Nginx 中引入基于 OpenID Connect 的身份验证。要做到这一点,首先需要通过 Luarocks 安装 lua-resty-openidc。
$ /usr/local/openresty/luajit/bin/luarocks install lua-resty-openidc
通过调用lua-resty-openidc的authenticate()方法来进行一系列的认证处理。
因此,首先需要准备lua-resty-openidc和authenticate()所需的参数。
具体而言,需要事先在OpenID提供者(OP)中注册或获取Redirect URI、OpenID Provider Configuration Request的URL、Client ID、Client Secret等值。
准备好的值将如下所示传递给authenticate()方法以供使用。
local opts = {
ssl_verify = "no",
redirect_uri = "https://<hostname>/secure/redirect_uri",
discovery = "<issuer>/.well-known/openid-configuration",
client_id = "<client_id>",
client_secret = "<client_secret>"
}
local res, err = require("resty.openidc").authenticate(opts)
if err then
ngx.log(ngx.ERR, err)
ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR)
end
ngx.req.set_header("X-USER", res.id_token.sub)
ngx.var.pass="http://webapp:3000/
その他設定値についての詳細やGoogle Signinで利用する設定のサンプルなどはlua-resty-openidcのREADMEやWikiに記載されているので合わせて参照してください。
authenticate()を呼び出し、エラーがなかった場合、戻り値として下記の通り、ID Token、Access Tokenやユーザーに関する情報が返ってきます。
— at this point res is a Lua table with 3 keys:
— id_token : a Lua table with the claims from the id_token (required)
— access_token: the access token (optional)
— user : a Lua table with the claims returned from the user info endpoint (optional)
(引用元: https://github.com/zmartzone/lua-resty-openidc/blob/master/README.md#sample-configuration-for-google-signin)
次に、上記の認証処理を行うスクリプトを呼び出す設定をnginx.confに記述します。
スクリプトの配置について、OpenRestyを利用する場合、OpenRestyに含まれるパッケージはデフォルトで/usr/local/openresty/以下に展開されるため、今回は認証処理を記述したLuaスクリプトも/usr/local/openresty/以下に配置します。
スクリプトの配置場所が決まったら、前述したとおりnginx.confのaccess_by_lua_fileディレクティブでスクリプトのパスを指定します。
ここまでのlua-resty-openidcを利用する設定を記述した場合、nginx.confは下記のようになります。
#user nobody;
worker_processes 1;
#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;
#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;
sendfile on;
#tcp_nopush on;
#keepalive_timeout 0;
keepalive_timeout 65;
server {
listen 80;
server_name localhost;
resolver local=on ipv6=off;
location / {
set $pass '';
default_type 'text/html';
access_by_lua_file /usr/local/openresty/main.lua;
proxy_pass $pass;
}
}
#gzip on;
include /etc/nginx/conf.d/*.conf;
}
以上でlua-resty-openidcの導入は終了です。問題なく設定ができていれば、ここまでで動作確認を行うことができるはずです。
使用Redis实现会话管理的引入。
在考虑实际应用时,需要将会话信息存储在Redis等地方而不是Nginx的内存中。根据lua-resty-openidc,它内部使用lua-resty-session,并且可以指定会话信息的存储位置。
通过利用 lua-resty-session,它为经过身份验证的用户保持会话,从而在客户端浏览器 cookie 中存储会话状态或使用服务器端存储机制之一(共享内存|内存缓存|Redis)之间提供可配置的选择。
しかし、本記事の執筆時点(2019/12/21)ではlua-resty-openidcには設定方法が記載されていなかったため、lua-resty-sessionのドキュメントを参照して設定を行います。
lua-resty-sessionのREADMEのPluggable Storage Adaptersの章に設定方法は記載されています。
セッション情報の保管先は、nginx.confで下記のように記述することで指定できるようです。
set $session_storage redis;
下面指定了除了Redis之外,还可以用作会话信息存储的地方。
-
- cookie aka Client Side Cookie (this is the default adapter)
-
- shm aka Lua Shared Dictionary
-
- memcache aka Memcached Storage Backend
- redis aka Redis Backend
本次我们将指定Redis作为会话信息的存储位置,因此可以使用以下参数来指定连接目标等信息。
当指定非Redis存储位置时,请适当进行修改。
set $session_redis_prefix sessions;
set $session_redis_socket unix:///var/run/redis/redis.sock;
set $session_redis_host 127.0.0.1;
set $session_redis_port 6379;
set $session_redis_auth password;
set $session_redis_uselocking on;
set $session_redis_spinlockwait 10000;
set $session_redis_maxlockwait 30;
set $session_redis_pool_timeout 45;
set $session_redis_pool_size 10;
以下是指定連接目標的nginx.conf範例。
#user nobody;
worker_processes 1;
#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;
#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;
sendfile on;
#tcp_nopush on;
#keepalive_timeout 0;
keepalive_timeout 65;
server {
listen 80;
server_name localhost;
resolver local=on ipv6=off;
location / {
set $pass '';
set $session_storage redis;
set $session_redis_prefix sessions;
set $session_redis_host redis;
set $session_redis_port 6379;
default_type 'text/html';
access_by_lua_file /usr/local/openresty/main.lua;
proxy_pass $pass;
}
}
#gzip on;
include /etc/nginx/conf.d/*.conf;
}
以上でセッション管理の設定も終了になります。
确认动作
如前所述,我已經準備了一個實現範例供您參考,現在我將介紹其操作驗證方法。
请查看以下链接以获取示例代码:
https://github.com/kg0r0/lua-resty-openidc-example
在确认操作时,由于浏览器中的信息可能导致不符合意图的操作。
请尽量使用隐私模式浏览器进行操作确认。
如果遇到意图不符的情况,请使用Issue提出问题,谢谢。
关于示例实现
本样例采用了OpenResty官方的Docker镜像进行实现。
在样例实现中,选择了名为alpine-fat的Docker镜像版本。
这是因为alpine-fat、centos和xenial这三个版本中含有Lua的软件包管理工具LuaRocks。
LuaRocks被包含在alpine-fat、centos和xenial变体中。它在alpine中被排除在外,因为它通常需要一个构建系统,而我们希望保持这个变体的精简。
此外,在示例实现中,我们将使用Keycloak作为OpenID提供者(OP)。
我们将使用预先注册所需的操作确认信息启动Keycloak。
操作步骤
首先,执行以下命令来启动各个组件。
$ git clone https://github.com/kg0r0/lua-resty-openidc-example.git
$ cd lua-resty-openidc-example
$ docker-compose build
$ docker-compose up -d
在这里,由于设置的重定向URL,当通过http://keycloak访问时,将通过编辑/etc/hosts文件并添加以下描述来访问http://127.0.0.1。
127.0.0.1 localhost keycloak
完成了这一步骤后,我们将实际访问并进行操作确认。
首先,访问 http://localhost。然后,登录页面将会显示。
一旦登录页面显示出来,请在“用户名或电子邮件”栏中输入“user”,在“密码”栏中输入“password”,然后点击“登录”按钮。
请确认最后会话信息是否已经在Redis中注册。
首先,确认Redis的容器ID。
$ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
521f1756ccfb redis:alpine "docker-entrypoint.s…" 2 weeks ago Up 2 weeks 6379/tcp lua-resty-openidc-example_redis_1
如果能够确认 Redis 的容器 ID ,就可以使用 redis-cli 来确认已注册的键。
$ docker exec -it 521f1756ccfb redis-cli
127.0.0.1:6379> keys *
1) "sessions:lfgDATCFRlSKF1ML5T7usw.."
Cookie中的值已经被成功注册。
Redis中的会话信息也正常运行。
最后的结果
本文介绍了使用lua-resty-openidc将Nginx作为OpenID Connect的依赖方实现的方法。同时,还介绍了如何使用lua-resty-openidc自定义会话管理的方法。希望对您引入OpenID Connect身份验证时有所帮助。
这是一个参考。
-
- https://github.com/zmartzone/lua-resty-openidc
-
- https://github.com/openresty/lua-resty-redis
-
- https://github.com/openresty/lua-nginx-module
-
- https://github.com/bungle/lua-resty-session
- https://openid.net/specs/openid-connect-discovery-1_0.html