Help us understand the problem. What is going on with this article?

Nextcloud + KeycloakでSAML認証する in ラズパイ4

はじめに

株式会社ピー・アール・オーのアドベントカレンダー22日目は、
表題の通り、NextcloudとKeycloakを組み合わせてファイルサーバを構築したのでその手順を書き残す。

背景

ツーリング中にドラレコやGoProで撮影した動画を友人内で共有するにあたり、
SFTPを使用してファイル共有をしていた。
(1ファイル4GBくらいあるので、GoogleDriveはあっという間に容量超過した)
さすがにパスワード認証は論外なので公開鍵認証を使用していたところ、以下の運用上の問題が発生した。

  • 秘密鍵を失くすケース(まあわかる)
  • 接続手順を忘れるケース(彼は本当にエンジニアなのだろうか)

失くされる度に公開鍵を登録し直すのは億劫かつ、こんなことのために手順書を用意するのも馬鹿らしいので、
簡単に使えるファイルサーバを立ち上げることにした。

目的

SAML認証の目的

普段よく使うサービス、それこそGoogleアカウントなら忘れないだろうし、
忘れても自分でパスワード再発行してよしなにやってもらえるため。
本当はopenid connectでやりたかったが、外部idp(google)を挟むと認証後Nextcloudが例外吐くので断念。Keycloak単体だと普通に動くんだけど。。。

Nextcloudを使用する目的

オープンソースのオンラインストレージを検索すると引っかかったのはownCloudとNextcloudの2つ。どちらもDropboxライク。
残念なことにownCloudは有償版のみSAML対応であったため、Nextcloudを使用することにした。
詳しくは公式サイトでも見て下さい。

Keycloakを使用する目的

環境

名称 バージョンなど 備考
OS Raspbian GNU/Linux 10 (buster) ラズパイ4
Keycloak 8.0.1 ID管理/認証
Nextcloud 17.0.1 ファイルサーバ
Postgresql 12.1 Nextcloud & Keycloak用DB
nginx 1.14.2 リバプロ

流れ

image.png

構築

各種ミドルウェアの準備

docker-composeはpipで入れた。

$ sudo apt-get install -y nginx
$ sudo curl -fsSL https://get.docker.com/ | sh
$ sudo apt-get -y install python3-dev python3-pip
$ sudo pip3 install docker-compose

Nextcloud、Keycloak、PostgresqlはDockerで入れた。
Keycloakはarmで動くいい感じのイメージが見つからなかったので、Dockerfile書いてビルドした。
ついでに設定ファイルやらJDBCドライバやらを放り込んでいる。

$ docker build -t me/keycloak .
dockerfile
FROM balenalib/raspberry-pi-openjdk:8-stretch
MAINTAINER me

ENV KEYCLOAK_VERSION 8.0.1
ENV JDBC_POSTGRES_VERSION 42.2.5

USER root

RUN apt-get -y update
RUN apt-get -y upgrade
RUN apt-get -y install wget
RUN mkdir /data
RUN wget -nv -P /data https://downloads.jboss.org/keycloak/8.0.1/keycloak-8.0.1.tar.gz

WORKDIR /data

RUN tar xfp keycloak-${KEYCLOAK_VERSION}.tar.gz --no-same-owner
RUN mv /data/keycloak-${KEYCLOAK_VERSION} /data/keycloak
RUN rm /data/keycloak-${KEYCLOAK_VERSION}.tar.gz
RUN chmod +x /data/keycloak/bin/standalone.sh

RUN mkdir -p /data/keycloak/modules/system/layers/keycloak/org/postgresql/main/
COPY postgresql-${JDBC_POSTGRES_VERSION}.jar /data/keycloak/modules/system/layers/keycloak/org/postgresql/main/
COPY module.xml /data/keycloak/modules/system/layers/keycloak/org/postgresql/main/
COPY standalone.xml /data/keycloak/standalone/configuration/
COPY standalone.conf /data/keycloak/bin/
COPY keycloak-add-user.json /data/keycloak/standalone/configuration/keycloak-add-user.json

EXPOSE 8080

CMD [ "/bin/bash", "/data/keycloak/bin/standalone.sh", "-b", "0.0.0.0" ]
  • 設定ファイルについて
設定ファイル名 設定内容
module.xml JDBCドライバをWildFlyにインストール
standalone.xml DB接続設定など
standalone.conf JAVA_OPTSの設定(デフォルトのヒープサイズがやたら小さい)
keycloak-add-user.json adminユーザの作成
  • module.xml
module.xml
<?xml version="1.0" ?>
<module xmlns="urn:jboss:module:1.3" name="org.postgresql">

    <resources>
        <resource-root path="postgresql-42.2.5.jar"/>
    </resources>

    <dependencies>
        <module name="javax.api"/>
        <module name="javax.transaction.api"/>
    </dependencies>
</module>
  • standalone.xml(主な差分のみ)
standalone.xml
            <!-- H2をPostgresqlに書き換え -->
            <datasources>
                <datasource jndi-name="java:jboss/datasources/KeycloakDS" pool-name="KeycloakDS" enabled="true" use-java-context="true" statistics-enabled="${wildfly.datasources.statistics-enabled:${wildfly.statistics-enabled:false}}">
                    <connection-url>jdbc:postgresql://postgres:5432/keycloak</connection-url>
                    <driver>postgresql</driver>
                    <security>
                        <user-name>keycloak</user-name>
                        <password>password</password>
                    </security>
                </datasource>
                <drivers>
                    <driver name="postgresql" module="org.postgresql">
                        <xa-datasource-class>org.postgresql.xa.PGXADataSource</xa-datasource-class>
                    </driver>
                </drivers>
            </datasources>

            ...

        <!-- DataSourceを上記のKeycloakDSに書き換え -->
        <subsystem xmlns="urn:jboss:domain:ee:4.0">
            <spec-descriptor-property-replacement>false</spec-descriptor-property-replacement>
            <concurrent>
                <context-services>
                    <context-service name="default" jndi-name="java:jboss/ee/concurrency/context/default" use-transaction-setup-provider="true"/>
                </context-services>
                <managed-thread-factories>
                    <managed-thread-factory name="default" jndi-name="java:jboss/ee/concurrency/factory/default" context-service="default"/>
                </managed-thread-factories>
                <managed-executor-services>
                    <managed-executor-service name="default" jndi-name="java:jboss/ee/concurrency/executor/default" context-service="default" hung-task-threshold="60000" keepalive-time="5000"/>
                </managed-executor-services>
                <managed-scheduled-executor-services>
                    <managed-scheduled-executor-service name="default" jndi-name="java:jboss/ee/concurrency/scheduler/default" context-service="default" hung-task-threshold="60000" keepalive-time="3000"/>
                </managed-scheduled-executor-services>
            </concurrent>
            <default-bindings context-service="java:jboss/ee/concurrency/context/default" datasource="java:jboss/datasources/KeycloakDS" managed-executor-service="java:jboss/ee/concurrency/executor/default" managed-scheduled-executor-service="java:jboss/ee/concurrency/scheduler/default" managed-thread-factory="java:jboss/ee/concurrency/factory/default"/>
        </subsystem>

            ...

        <!-- 外から管理コンソールに入ってこられると困るのでローカルのみ許容 -->
        <!-- プロキシさせるので`http-listener`に`proxy-address-forwarding="true"`を追加 -->
        <subsystem xmlns="urn:jboss:domain:undertow:10.0" default-server="default-server" default-virtual-host="default-host" default-servlet-container="default" default-security-domain="other" statistics-enable
d="${wildfly.undertow.statistics-enabled:${wildfly.statistics-enabled:false}}">
            <buffer-cache name="default"/>
            <server name="default-server">
                <http-listener name="default" socket-binding="http" proxy-address-forwarding="true" redirect-socket="https" enable-http2="true"/>
                <https-listener name="https" socket-binding="https" security-realm="ApplicationRealm" enable-http2="true"/>
                <host name="default-host" alias="localhost">
                    <location name="/" handler="welcome-content"/>
                    <http-invoker security-realm="ApplicationRealm"/>
                    <filter-ref name="ipAccess"/>
                </host>
            </server>
            <filters>
                <expression-filter name="ipAccess" expression="path-prefix('/auth/admin') -> ip-access-control(acl={'192.168.0.0/24 allow'})"/>
            </filters>
            <servlet-container name="default">
                <jsp-config/>
                <websockets/>
            </servlet-container>
            <handlers>
                <file name="welcome-content" path="${jboss.home.dir}/welcome-content"/>
            </handlers>
        </subsystem>
  • standalone.conf(差分のみ)
JAVA_OPTS="-Xms512m -Xmx1024m -XX:MetaspaceSize=96M -XX:MaxMetaspaceSize=256m -Djava.net.preferIPv4Stack=true"
  • keycloak-add-user.json
keycloak-add-user.json
[ {
  "realm" : "master",
  "users" : [ {
    "username" : "admin",
    "enabled" : true,
    "credentials" : [ {
      "type" : "password",
      "secretData" : "{\"value\":\"iglMTMsGp4OfqSZr0x6sDAzVaatE1i5B8jKMwNYXfM6pVUiEXBfyGFA5yTdPYGR2WUQryzKtxar277hBbCcgXA==\",\"salt\":\"eLxa1xVzxfsGu7qlYsZosg==\"}",
      "credentialData" : "{\"hashIterations\":100000,\"algorithm\":\"pbkdf2-sha256\"}"
    } ],
    "realmRoles" : [ "admin" ]
  } ]
} ]

起動はdocker-composeを使用する。
全部同じネットワークに乗っけて、ローカルからのみアクセスOKとしている。
また、コンテナ立ち上げ時にNextcloud、KeycloakのDBをそれぞれ作成するため、initdbディレクトリにSQLを放り込んでおく。
見よう見まねで書いているので、質についてはなんともいえない。

docker-compose.yml
version: '3'

services:
  postgres:
      image: postgres
      container_name: 'postgres'
      volumes:
        - ./postgres_data:/var/lib/postgresql/data
        - ./initdb:/docker-entrypoint-initdb.d
      ports:
      - "127.0.0.1:5432:5432"
      environment:
        POSTGRES_USER: postgres
        POSTGRES_PASSWORD: password
      restart: always
      network_mode: bridge
  nextcloud:
      image: nextcloud
      container_name: 'nextcloud'
      volumes:
        - ./nextcloud:/var/www/html
      ports:
      - "127.0.0.1:10443:443"
      - "127.0.0.1:10080:80"
      restart: always
      environment:
      - POSTGRES_HOST=postgres
      - POSTGRES_DB=nextcloud
      - POSTGRES_USER=nextcloud
      - POSTGRES_PASSWORD=password
      - NEXTCLOUD_ADMIN_USER=admin
      - NEXTCLOUD_ADMIN_PASSWORD=password
      links:
      - postgres
      network_mode: bridge
  keycloak:
      image: me/keycloak
      container_name: 'keycloak'
      volumes:
          - ./deployments:/data/keycloak/standalone/deployments/
      restart: always
      links:
      - postgres
      ports:
      - 127.0.0.1:8080:8080
      network_mode: bridge
  • initdbに格納するSQL
init.sql
CREATE USER keycloak WITH PASSWORD 'password';
CREATE DATABASE keycloak OWNER keycloak;
CREATE USER nextcloud WITH PASSWORD 'password';
CREATE DATABASE nextcloud OWNER nextcloud;

リバースプロキシの設定

Dockerにプロキシする設定。
以下のようなconfファイルを作成し、/etc/nginx/conf.dに放り込む。
http ⇒ httpsのリダイレクトは省略。

proxy.conf
### nextcloud ###
server {
    listen 443;
    client_max_body_size 10G;
    server_name cloud.example.com;
    ssl on;
    ssl_certificate /etc/nginx/cert/privatekey.pem;
    ssl_certificate_key /etc/nginx/cert/server.crt;

    location / {
        proxy_pass http://localhost:10080;
        proxy_redirect http:// https://;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-Proto https;
        proxy_set_header X-Forwarded-Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}
### keycloak ###
server {
    listen 443;
    server_name sso.example.com;
    ssl on;
    ssl_certificate /etc/nginx/cert/privatekey.pem;
    ssl_certificate_key /etc/nginx/cert/server.crt;

    location / {
        proxy_pass http://localhost:8080;
        proxy_redirect http:// https://;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-Proto https;
        proxy_set_header X-Forwarded-Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}

起動

この後の設定は各システムのGUIで行うので立ち上げる。

$ docker-compose up -d

Keycloakの設定

https://sso.example.com/authにアクセスすると、ウェルカムページが表示される。
Administration Consoleを選択し、管理コンソールにログインする。

image.png

image.png

レルムの作成

レルムの追加から、nextcloudレルムを作成する。

image.png

クライアントの作成

SAML用クライアントを新規作成する。
クライアントIDにはNextcloudのsamlエンドポイントを設定する。

  • 設定項目
設定項目 設定内容
クライアントID https://Nextcloudのホストネーム/apps/user_saml/saml/metadata
クライアントプロトコル saml
  • 設定例 image.png

クライアントの設定

左メニューのクライアントを選択し、さっき作成したクライアントを選択して設定画面に入る。
image.png

差分のみ記載する。

  • 設定
設定項目 設定内容
クライアント署名が必須 オフ
ルートURL https://Nextcloudのホストネーム
有効なリダイレクトURL https://Nextcloudのホストネーム/*
アサーションコンシューマサービスの POST Binding URL https://Nextcloudのホストネーム/apps/user_saml/saml/sls
ログアウトサービスの Redirect Binding URL https://Nextcloudのホストネーム/apps/user_saml/saml/sls
  • 設定例その1
    image.png

  • 設定例その2
    image.png

また、Nextcloudユーザーはusernameemailが必須属性なのでマッパーも設定する。

  • マッパー
名前 マッパータイプ プロパティ Friendly Name SAML Attribute Name SAML Attribute NameFormat
username User Property username username username Basic
email User Property email email email Basic
  • 設定例 image.png

クライアントスコープの設定

左メニューのクライアントスコープからrole_listを選択 ⇒ マッパーからrole_listを選択 ⇒ Single Role Attributeオンにしておく。
この設定を入れないと、SAML認証時、Nextcloudが謎の例外を吐いてしまい認証に失敗する。

  • 設定例
    image.png

  • 散り様
    image.png

外部idp設定

Googleの設定

Googleアカウントによる認証を行うには、まず、GoogleのAPIコンソールから認証情報を作成する。

  • 新しいプロジェクトを作成
    image.png

  • OAuth同意画面を作成(G Suiteは私用しないのでUserTypeは外部とする)

    image.png

  • 承認済みドメインには最上位のドメイン名を登録する。
    image.png

  • 保存後
    image.png

  • 認証情報を作成する。(OAuth クライアントIDを選択)

    image.png

  • 認証情報の設定内容
    image.png

  • 登録後(クライアントIDとシークレットが発行されるので控えておく)
    image.png

Keycloakの設定

左メニューからアイデンティティプロバイダーを選択し、Googleを選択し、設定画面に入る。

  • 設定内容(差分のみ)
設定項目 設定内容
クライアントID さっき控えたクライアントID
クライアントシークレット さっき控えたクライアントシークレット
デフォルトスコープ openid profile email
  • 設定例 image.png

IdPの情報取得

後ほどNextcloud側の設定を実施するにあたり、IdPの情報が必要となる。
https\://Keycloakのホスト名/auth/realms/レルム名/protocol/saml/descriptorにアクセスし、dsig:X509Certificate要素から証明書を控えておく。
image.png

Nextcloudの設定

AdminユーザでNextcloudにログインし、右上のアイコンからアプリ連携 から SSO & SAML authenticationをインストールする。

image.png

SSOとSAML認証の設定

右上のアイコンから設定 ⇒ 左メニューのSSOとSAML認証から設定画面に入り、Identity providerを追加するを押して設定を作成する。

設定内容は以下。

  • 一般
設定項目 設定内容
UIDをマップする属性 username
IPプロバイダオプションの表示名 お好みで
  • Identity Providerデータ
設定項目 設定内容
IdPエンティティの識別子 https://Keycloakのホスト名/auth/realms/レルム名
SPが認証要求メッセージをを送信するIdPのURIターゲット https://Keycloakのホスト名/auth/realms/レルム名/protocol/saml
  • オプションのIdentity Provider設定
設定項目 設定内容
SPが認証要求メッセージをを送信するIdPのURIターゲット https://Keycloakのホスト名/auth/realms/レルム名/protocol/saml
IdPの公開X.509証明書 -----BEGIN CERTIFICATE-----さっき控えた証明書-----END CERTIFICATE-----
  • 属性マッピング
設定項目 設定内容
表示名をにマップする属性(原文ママ) username
電子メールアドレスをマップする属性 email
  • 設定例 image.png

拡張モジュール作成/設定

実はここまでの設定でもうNextcloudにGoogleアカウントでログインすることができる。
ただ、このままではGoogleアカウント持ってれば誰でもウェルカム状態なので、
Keycloakに拡張認証モジュールを投入し、Keycloak上にアカウントが存在しない人をたたき出すようにする。
今回拡張したいのはFirst Broker Loginなので、AbstractIdpAuthenticatorを継承してモジュールを作成した。

認証モジュールの流れ

  1. Google経由のログインが初めてなら拡張モジュールに入る
  2. Googleから送られてきたユーザー情報のメールアドレスで、Keycloakにアカウントが作られているかチェック
  3. 作られていなければ認証NGにしてたたき出す
  4. 作られていたらGoogleとアカウントリンクして認証OKにする

ファイル構成

認証モジュールの作成には、少なくとも認証モジュール本体、ファクトリー、クラス登録用定義ファイルの3種が必要。

  • ファイル構成例 image.png

コード

  • 認証モジュール本体
IpdAccountExistsAuthenticator.java
package local.example;

import org.jboss.logging.Logger;
import org.keycloak.authentication.AuthenticationFlowContext;
import org.keycloak.authentication.AuthenticationFlowError;
import org.keycloak.authentication.authenticators.broker.AbstractIdpAuthenticator;
import org.keycloak.authentication.authenticators.broker.util.SerializedBrokeredIdentityContext;
import org.keycloak.broker.provider.BrokeredIdentityContext;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;

import java.util.Objects;

public class IpdAccountExistsAuthenticator extends AbstractIdpAuthenticator {

    private static Logger logger = Logger.getLogger(IpdAccountExistsAuthenticator.class);


    /**
     * 認証処理
     *
     * @param context
     * @param serializedCtx
     * @param brokerContext
     */
    @Override
    protected void authenticateImpl(AuthenticationFlowContext context, SerializedBrokeredIdentityContext serializedCtx, BrokeredIdentityContext brokerContext) {

        logger.info("Start IpdAccountExistsAuthenticator");

        // ユーザがKeycloakに登録済みであればOK
        UserModel user = context.getSession().users().getUserByUsername(brokerContext.getEmail(), context.getRealm());

        if (Objects.isNull(user)) {
            // ユーザが取れなければ認証失敗にする
            logger.info("そんなユーザいません : " + brokerContext.getEmail());
            context.failure(AuthenticationFlowError.INVALID_USER);
            return;
        }

        // Googleにリンクさせたいので1回消す
        context.getSession().users().removeUser(context.getRealm(), user);

        // ユーザ名はメールアドレス
        UserModel linkedUser = context.getSession().users().addUser(context.getRealm(), brokerContext.getEmail());
        linkedUser.setEnabled(true);
        linkedUser.setEmail(brokerContext.getEmail());
        linkedUser.setFirstName(brokerContext.getFirstName());
        linkedUser.setLastName(brokerContext.getLastName());

        logger.info("認証OK : " + linkedUser.getUsername());

        // 認証成功
        context.setUser(linkedUser);
        context.getAuthenticationSession().setAuthNote(BROKER_REGISTERED_NEW_USER, "true");
        context.getAuthenticationSession().setAuthenticatedUser(linkedUser);
        context.success();
    }

    @Override
    protected void actionImpl(AuthenticationFlowContext context, SerializedBrokeredIdentityContext
            serializedCtx, BrokeredIdentityContext brokerContext) {

    }

    @Override
    public boolean requiresUser() {
        return false;
    }

    @Override
    public boolean configuredFor(KeycloakSession session, RealmModel realm, UserModel user) {
        return false;
    }
}
  • ファクトリー
IpdAccountExistsAuthenticatorFactory.java
package local.example;

import org.keycloak.authentication.Authenticator;
import org.keycloak.authentication.AuthenticatorFactory;
import org.keycloak.models.AuthenticationExecutionModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.provider.ProviderConfigProperty;

import java.util.List;

public class IpdAccountExistsAuthenticatorFactory implements AuthenticatorFactory {

    public static final String ID = "exists-account-from-keycloak";
    public static final AuthenticationExecutionModel.Requirement[] REQUIREMENT_CHOICES = {
            AuthenticationExecutionModel.Requirement.ALTERNATIVE,
            AuthenticationExecutionModel.Requirement.REQUIRED,
            AuthenticationExecutionModel.Requirement.DISABLED};

    @Override
    public Authenticator create(KeycloakSession keycloakSession) {
        return new IpdAccountExistsAuthenticator();
    }

    @Override
    public void init(org.keycloak.Config.Scope scope) {

    }

    @Override
    public void postInit(KeycloakSessionFactory keycloakSessionFactory) {

    }

    @Override
    public void close() {

    }

    @Override
    public String getId() {
        return ID;
    }

    @Override
    public String getReferenceCategory() {
        return "exists-account";
    }

    @Override
    public boolean isConfigurable() {
        return true;
    }

    @Override
    public AuthenticationExecutionModel.Requirement[] getRequirementChoices() {
        return REQUIREMENT_CHOICES;
    }

    @Override
    public String getDisplayType() {
        return ID;
    }

    @Override
    public String getHelpText() {
        return "ipd exists accoount";
    }

    public boolean isUserSetupAllowed() {
        return true;
    }

    public List<ProviderConfigProperty> getConfigProperties() {
        return null;
    }
}
  • クラス登録用定義ファイル
org.keycloak.authentication.AuthenticatorFactory
local.example.IpdAccountExistsAuthenticatorFactory

ビルド

上記ファイルをビルドしてjarを生成する。
pomはこんな感じにしてmvn installした。

pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xmlns="http://maven.apache.org/POM/4.0.0"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>local.example</groupId>
    <version>1.0-SNAPSHOT</version>
    <name>IDP Exists</name>
    <description/>
    <artifactId>idp_account_exists</artifactId>
    <packaging>jar</packaging>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <java.version>1.8</java.version>
        <maven.compiler.source>${java.version}</maven.compiler.source>
        <maven.compiler.target>${java.version}</maven.compiler.target>
        <keycloak.version>8.0.1</keycloak.version>
        <version.wildfly.maven.plugin>1.1.0.Final</version.wildfly.maven.plugin>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.keycloak</groupId>
            <artifactId>keycloak-core</artifactId>
            <version>${keycloak.version}</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.keycloak</groupId>
            <artifactId>keycloak-server-spi</artifactId>
            <version>${keycloak.version}</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.keycloak</groupId>
            <artifactId>keycloak-server-spi-private</artifactId>
            <version>${keycloak.version}</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.keycloak</groupId>
            <artifactId>keycloak-services</artifactId>
            <version>${keycloak.version}</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.jboss.logging</groupId>
            <artifactId>jboss-logging</artifactId>
            <version>3.4.1.Final</version>
        </dependency>
    </dependencies>

    <build>
        <finalName>idp-account-exists</finalName>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-jar-plugin</artifactId>
                <configuration>
                    <archive>
                        <manifestEntries>
                            <Dependencies>org.keycloak.keycloak-services</Dependencies>
                        </manifestEntries>
                    </archive>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

デプロイ

Keycloakのホームディレクトリ/standalone/deploymentsに産み出されたjarを放り込むと、ホットデプロイしてくれる。

認証フローの作成

左メニューの認証から、以下のような認証フローを作成する。

設定が複雑なので、デフォルトで存在するフローをコピー ⇒ アクションからExecution全削除 ⇒ Executionを追加から拡張モジュールを追加とすると楽。

  • 設定例 image.png

作成したフローの設定

左メニューのアイデンティティプロバイダーからさっき作ったgoogleを選択し、
初回ログインフローにフローを設定する。

  • 設定例 image.png

動作確認

ログイン成功(登録済みユーザ)

  • Keycloakにユーザを登録
    image.png

  • SSO & SAMLログインを選択
    image.png

  • Keycloakのログイン画面に飛ぶのでGoogleを選択
    image.png

  • テストユーザを選択してログイン
    image.png

  • ログイン成功
    image.png

ログイン失敗(未登録ユーザ)

  • Keycloak上からテストユーザを抹殺して再度ログイン
    image.png

  • 弾いた
    image.png

  • ログ
    image.png

メモ

NextcloudのSSOとSAML認証の設定の保存場所

設定はNextcloudDBのoc_appconfigテーブルに存在する。
appidカラムをuser_samlで絞ると見つけるのが楽。
ガチャガチャいじってたらデータが壊れ、設定が保存されなくなってしまったが、レコード修正したら治った。

Keycloakのリモートデバッグ

standalone.confにこんな設定があるので、コメント解除してKeycloakを再起動してやればOK。
ポート開けてやればIDEから接続可能。

standalone.conf
# Sample JPDA settings for remote socket debugging
#JAVA_OPTS="$JAVA_OPTS -agentlib:jdwp=transport=dt_socket,address=8787,server=y,suspend=n"
docker-compose.yml
      ports:
      - 127.0.0.1:8080:8080
      - 8787:8787

終わりに

ラズパイ4でもなかなか機敏に動く。感動した。
もともとこのシステムはラズパイ2で無理矢理動かしていたが、サムネ生成の速度とか雲泥の差。
記事書くついでに移植したが、やってよかった。。。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした