KeyCloak行车记录仪

开发 May 10, 2023

今天,我们在一个已有的swarm集群上跑一个keycloak,如果没有swarm集群的朋友,可以参考这里去搞一个。

KeyCloak是一个认证服务,提供标准的OAuth啥啥的,好多地方都在用,关于它的解释,ChatGPT是这么说的(我自己懒得编):

Keycloak 是一个开源身份验证和授权管理解决方案。它提供了身份验证、授权、单点登录等功能,可以轻松地将这些功能集成到 Web 应用程序中。Keycloak 提供了一个安全的用户管理系统,可以用来注册、登录、重置密码、管理用户、身份验证、授权等。它还提供了一系列身份验证方法,包括 OAuth 2.0、OpenID Connect、SAML 等,可帮助用户确保应用程序的安全性和合规性。

醒醒,上路了。


首先,先搞个数据库,起个PG:

  • runtime.yaml
version: "3.9"
services:
  # Postgres
  postgres:
    image: postgres:latest
    networks:
      - herewe-net
    ports:
      - 5432:5432
    environment:
      POSTGRES_PASSWORD: hello.password  # 写个密码,pg会用这个初始化。
    volumes:
      - /data/db/postgres:/var/lib/postgresql/data
    deploy:
      mode: global
      placement:
        constraints:
          - node.role == manager
      update_config:
        parallelism: 1
        delay: 2s
      restart_policy:
        condition: on-failure

启动runtime:

docker stack deploy --compose-file runtime.yaml runtime

创建一个数据库<keycloak>,这里主要用于演示,所以就直接用postgres这个管理账户直接干到底了。

然后,来编写keycloak的service:

  • keycloak.yaml
version: "3.9"
services:
  # Keycloak
  main:
    image: quay.io/keycloak/keycloak:latest  # 这是keycloak的官方镜像,如果不喜欢它,或者有特别的要求,可以自己再打一个
    command: ["start"]
    networks:
      - herewe-net
    environment:
      KEYCLOAK_ADMIN: herewe
      KEYCLOAK_ADMIN_PASSWORD: hello.password  # keycloak的管理密码
      KC_DB: postgres
      KC_DB_URL_HOST: runtime_postgres  # PG服务器的地址,前面启动了stack <runtime>,那么pg就在runtime_postgres上,直接写服务名就可以解析
      KC_DB_URL_DATABASE: keycloak
      KC_DB_USERNAME: postgres
      KC_DB_PASSWORD: hello.password  # 前面配置好的postgres用户的密码
      KC_HOSTNAME: keycloak.d.herewe.tech  # 这个要写正确
      KC_HOSTNAME_STRICT: "false"
      KC_HTTP_ENABLED: "true"  # 由于咱们的服务是跑在traefik后面的,所以不需要keycloak自己去暴露流量,申请SSL证书,直接启动一个HTTP服务就可以了
      KC_PROXY: edge  # 告诉它,你是跑在网关后面的
    deploy:
      update_config:
        parallelism: 1
        delay: 2s
      restart_policy:
        condition: on-failure
      labels:
        - "traefik.enable=true"
        - "traefik.http.services.keycloak.loadbalancer.server.port=8080"  # Keycloak的默认端口是8080
        - "traefik.http.routers.keycloak.rule=Host(`keycloak.d.herewe.tech`)"  # 我们要的域名,交给traefik去自动处理ssl证书问题
        - "traefik.http.routers.keycloak.entrypoints=websecure"
        - "traefik.http.routers.keycloak.tls.certresolver=le"
        # Redirect
        - "traefik.http.routers.keycloak-http.rule=Host(`keycloak.d.herewe.tech`)"
        - "traefik.http.routers.keycloak-http.entrypoints=web"
        - "traefik.http.routers.keycloak-http.middlewares=keycloak-redirectscheme"
        - "traefik.http.middlewares.keycloak-redirectscheme.redirectscheme.scheme=https"  # 强制traefik以https方式处理它。认证服务如果不是标准的https,可能会有问题
        - "traefik.http.middlewares.keycloak-redirectscheme.redirectscheme.permanent=true"

可以了,咱们来启动它:

docker stack deploy --compose-file keycloak.yaml keycloak

服务会翻滚一堆log,然后keycloak会发现数据库是空的,表示这是一个全新安装,它会自动初始化数据表。会在刚刚创建的库<keycloak>创建一大堆表,当前版本是92张:

接下来,访问刚刚配置好的域名,如果traefik工作正常且自动生效了ssl证书,就会看见入口页:

打开Administration Console,用yaml文件中设置的帐号密码登录:

Keycloak在安装时会创建一个默认的realm <master>,console会显示一堆系统信息什么的。这个页面是支持中文的,但是默认的语言包的汉化并不完整,如果真的不是24个字母认不全的程度,不建议切换语言,否则在搜索文档时会很不方便。

下面,我们来用一个三方系统来接入keycloak,完成认证配置。其实keycloak官方的仓库里提供了一大堆的quickstart实例,但是我觉得,用一个功能完整又有实用价值的三方系统来做,可能更有意义。这里我打算起一个wiki,所以使用了wiki.js,这东西默认支持keycloak。如果你想搞一些内容发布什么的,也可以用drupal,它也支持。


来准备wiki.yaml:

  • wiki.yaml
version: "3.9"
services:
  main:
    image: "requarks/wiki:latest"
    user: root
    volumes:
      - /data/herewe/wiki/data:/var/lib/wiki/storage
    networks:
      - herewe-net
    environment:
      DB_TYPE: postgres
      DB_HOST: runtime_postgres
      DB_PORT: 5432
      DB_USER: postgres
      DB_PASS: hello.password  # 管理员梭哈到底
      DB_NAME: wiki
    deploy:
      update_config:
        parallelism: 1
        delay: 2s
      restart_policy:
        condition: on-failure
      labels:
        - "traefik.enable=true"
        - "traefik.http.services.wiki.loadbalancer.server.port=3000"
        - "traefik.http.routers.wiki.rule=Host(`wiki.d.herewe.tech`)"
        - "traefik.http.routers.wiki.entrypoints=websecure"
        - "traefik.http.routers.wiki.tls.certresolver=le"
        # Redirect
        - "traefik.http.routers.wiki-http.rule=Host(`wiki.d.herewe.tech`)"
        - "traefik.http.routers.wiki-http.entrypoints=web"
        - "traefik.http.routers.wiki-http.middlewares=wiki-https"
        - "traefik.http.middlewares.wiki-https.redirectscheme.scheme=https"
        - "traefik.http.middlewares.wiki-https.redirectscheme.permanent=true"

然后,创建数据库 <wiki>:

启动stack:

docker stack deploy --compose-file wiki.yaml wiki

wiki.js的log也会提示,数据库是空的,要进入安装模式,根据它的提示往下走,访问wiki地址:

把网站的信息都填好,最后的telemetry看心情就好,然后点INSTALL进行安装,这个东西也会自动创建好数据表:

Wiki会要你登录,然后会提示你写第一页wiki,或者进入管理。写不写大家随便就好了,这里进入ADMINISTRATION继续:

后台看起来很不错。下面我们转到keycloak去配置一下。

Realm <master>有一堆配置:

首先来创建一个client:

把信息都填写好就可以了。Wiki.js是使用OIDC方式接入的,所以这里的type就是OpenID Connect。下一步中记得要把Client Authentication打开,才可以生成Client Secret:

填上Root和Home URL:

下面,我们回到wiki.js的后台。


在Authentication菜单中,ADD STRATEGY,选择keycloak:

填写相关信息,Client Secret在keycloak后台的credentials中:

这里有一点需要注意的是,如果我们用了17+版的keycloak(现在其实就是),http的basepath就不是/auth,是/了。这时,login url就应该写成:

/realms/master/protocol/openid-connect/auth

wiki.js的后台中要打开allow self-registration,这会将用户信息写入到wiki.js自身的数据库。

注意,页面下面有一些配置,可能需要更新到keycloak中:

保存,然后我们打开另一个独立的浏览器进程,打开wiki的登录页面:

这时就出现了两个登录认证的provider,我们选择keycloak,页面会跳转到我们刚刚设置的login page,如果我们设置对了,它就会打开keycloak的登录页面:

如果错误,就会提示页面不存在:

我们再来看看,keycloak中的配置,我们需要添加用户。在client的scopes中,可以看到这个client对应的用户数据的基础结构,它其实是继承自realm的。整个realm也有上一层的设置:

这些基础字段,可以修改或增加新的。然后我们创建第一个用户,打开左侧的Users,然后Create一个,require user actions是将用户置为可用的前置条件,比如发邮件验证,更新密码什么的。这里我们可以无视它,填写必要的信息:

创建了用户writer后,我们给它设置一个密码,测试方便,密码不是临时的:

然后,我们尝试在wiki上登录,提示没权限操作:

这个时候,其实我们已经认证成功了,看一下wiki的后台:

王写手同志,已经加入了这个三方应用。但是它是没权限创建页面的,因为它不在任何一个用户组里。

我们在wiki中创建一个写手组 <Writer>,给它一些权限:

然后把王写手同志拉到写手组里去:

重新登录王写手的帐号:

这个时候,我们点击CREATE HOME PAGE,发现还是提示无权限,说明写手组不能创建首页。我们用管理员初始化一个页面:

这时,王写手就可以开始工作了:

我们可以看到,王写手写了一篇文章。

这个时候,王写手是在线的,在keycloak的后台中,我们可以看到它的session:

咱们在wiki中退出登录一下,这时候发现keycloak中的session还是在线的。我们可以在wiki的后台中设置:Logout from Keycloak on Logout:

再来操作一遍,大哥它报错了:

一顿研究之后,发现keycloak在18版本之后,取消了logout时对redirect_uri这个参数的响应,取而代之用post_logout_redirect_uri代替,观察log,会发现这样一条:

[org.keycloak.protocol.oidc.endpoints.LogoutEndpoint] (executor-thread-67) Parameter 'redirect_uri' no longer supported. Please use 'post_logout_redirect_uri' with 'id_token_hint' for this endpoint. Alternatively you can enable backwards compatibility option 'legacy-logout-redirect-uri' of oidc login protocol in the server configuration.

然而,wiki.js并没有升级到最新的js库,所以就需要在keycloak服务上动手。我们需要在启动keycloak时候传递给它一个配置标记,让它来兼容老的redirect_uri参数,所以需要把compose文件修改成这样:

    image: quay.io/keycloak/keycloak:latest
    command:
      [
        "--spi-login-protocol-openid-connect-legacy-logout-redirect-uri=true",
        "start",
      ]
    networks:

这条“--spi-login-protocol-openid-connect-legacy-logout-redirect-uri=true”就是让它兼容老的logout方式的。这时更新stack,重启,尝试再退出:

点一下logout,看看session里,writer不见啦:

完美。


先FIN一下,FIN一下。

Tags