尝试使用Keycloak+OAuth2代理+OpenLDAP进行Ldap认证

这次实施的组合

構成図.jpg

环境

    • 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_001.jpg

选择管理控制台。

请转到登录界面,并输入在keycloak.yaml文件中设置的以下登录信息。

    • admin

 

    passwd
keycloak_002.jpg

创建领域

keycloak_003.jpg

创建客户

选择”创建客户”选项。

在输入oauth2-proxy作为客户端ID后,选择下一步。

keycloak_004.jpg

将客户身份认证设置为开启,并选择”下一步”。

keycloak_005.jpg

在”Valid redirect URIs”和”Web origins”栏目中输入*,然后选择保存。

keycloak_006.jpg

复制凭证

保留下来后面会在OAuth2代理中使用。

keycloak_007.jpg

在中国本土化的情况下,创建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按钮保存。

kecloak_ldap_001.jpg
kecloak_ldap_002.jpg

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 进行登录。

oauth2-proxy_001.jpg

在转到Keycloak的认证界面后,需要输入在Ldap中注册的用户名和密码(本次示例中为test001用户)。

oauth2-proxy_002.jpg

如果打开OpenResty(Web应用程序)的欢迎页面,就表示一切正常。

welcome_to_openresty.jpg

总结

我尝试使用Keycloak+OAuth2 Proxy+OpenLDAP进行Ldap认证。

因为现在已经建立了一个可以在本地进行验证的环境,所以我在这里就结束了。

广告
将在 10 秒后关闭
bannerAds