🇦🇺

認証・認可サーバの OSS Authelia を試す

2024/04/15に公開

ユーザー認証や管理を行うアプリケーションを探していたところ Authelia という良さそうな OSS を見つけました。ただ現時点では日本語の情報が殆どなかったので、実際に環境を構築して機能をいろいろ試してみます。

Authelia について

ドキュメント
https://www.authelia.com/

Github
https://github.com/authelia/authelia

ドキュメントより引用

Authelia is an open-source authentication and authorization server and portal fulfilling the identity and access management (IAM) role of information security in providing multi-factor authentication and single sign-on (SSO) for your applications via a web portal. It acts as a companion for common reverse proxies.

上記の通り authelia はユーザー認証・認可やアクセス管理、multi-factor 認証を行うための OSS アプリケーションとなっています。主な特徴・機能としては以下。

  • OIDC によるアプリケーションへのシングルサインオン
  • ワンタイムパスワード、モバイルプッシュ等の multi-factor 認証
  • LDAP との連携
  • ユーザのアクセスコントロール (RBAC など)
  • リバースプロキシとの統合
  • Go 言語で実装されており軽量

上記の機能より、独自のアプリケーションにおいて認証に関する処理の開発や既存アプリケーションにおいて multi-factor 認証を追加したい際などに、認証を行うコンポーネントを authelia に置き換えることで開発や管理の手間を省くといったユースケースが考えられます。
Github のスター数は現時点で 19 k であり、世界中に多くの利用者がいることが伺えます(の割には日本語情報はほとんどないですが)。

カスタム可能な構成の例

authelia は構築時にさまざまな構成がカスタム可能となっており、なるべく余計な手間をかけず簡潔に構築する、外部のコンポーネントと合わせて機能や利便性、データ保護を強化するといったことができます。
基本的に設定項目はすべて configuration ファイルに記載し、authelia 起動時にファイルを読み込んで適用する形式になってます。構成可能な項目の一覧は以下を参照。

https://www.authelia.com/configuration/

今回の検証に関わりそうな設定項目については以下で簡単に触れておきます。

ユーザ管理

authelia では認証のためのユーザ情報をローカルファイルで定義する、または外部にある LDAP サーバから取得する方法に対応しています。これは configuration ファイルの authentication_backend で指定します。
ローカルファイルに記載する場合は yaml 形式でユーザ名やパスワードを事前に定義します。詳細は password を参照。

Example
users:
  john:
    disabled: false
    displayname: 'John Doe'
    password: '$argon2id$v=19$m=65536,t=3,p=2$BpLnfgDsc2WD8F2q$o/vzA4myCqZZ36bUGsDY//8mKUYNZZaR0t4MFFSs+iM'
    email: 'john.doe@authelia.com'
    groups:
      - 'admins'
      - 'dev'
  harry:
    disabled: false
    displayname: 'Harry Potter'
    password: '$argon2id$v=19$m=65536,t=3,p=2$BpLnfgDsc2WD8F2q$o/vzA4myCqZZ36bUGsDY//8mKUYNZZaR0t4MFFSs+iM'
    email: 'harry.potter@authelia.com'
    groups: []
...

LDAP を使う場合は外部 LDAP サーバの接続先や認証の設定を configuration ファイルに記載します。詳細は LDAP を参照。

データベース

ユーザーの情報など authelia に関する種々のデータはローカルの sqlite か外部の mysql または postgres に保存することができます。これは configuration ファイルの storage で指定します。

セッション情報

ユーザーの cookie などのセッション情報はインメモリ、または外部の redis サーバに保存します。これは configuration ファイルの session で指定します。

メール通知

authelia ではユーザーのメールアドレス等に向けて notification を送る機能があります(例えばユーザー初期認証時に確認コードを送信するなど)。これは configuration ファイルの notifications で指定でき、filesystem と外部の SMTP サーバが利用できます。
filesystem ではユーザーのメールアドレスに送信する代わりに authelia 環境内の指定したファイル内に通知が書き込まれるようになります。外部 SMTP サーバの設定が面倒な場合はこちらを利用できます。
SMTP サーバを指定する場合は authelia → 外部の SMTP サーバという経路でユーザーのメールアドレスに通知が送信されます。

Proxy

authelia は基本的に認証やユーザのアクセスコントロールを管理する役割のみを持っているため、トラフィックの制御などは外部の Proxy と統合して管理する構成が推奨されています。proxies に対応している proxy の一覧があり, nginx, envoy, traefik などの主要な proxy との統合がデフォルトでサポートされています。

OIDC

authelia は identity provider として OpenID Connect 1.0 をサポートしているため、authelia に登録されているユーザー情報を使って OIDC 対応のアプリケーションにシングルサインオンすることが可能となっています。

セットアップ

今回の検証では docker compose を使って環境構築を行いますが、その前にいくつか準備や設定が必要となります。

構成

authelia はコンテナイメージが用意されているため、docker compose による構成や helm による k8s 上へのインストールに対応しています。
今回は手軽さ重視で docker-compose で構築します。また、構成項目はなるべく簡単な以下の構成にします。

機能 設定
ユーザー管理 ローカルファイル
データベース local (sqlite)
セッション情報 インメモリ
メール通知 SMTP (google)
Proxy Traefik

上記項目のうち SMTP サーバの連携については事前に設定が必要になります。

SMTP サーバの準備

google の SMTP サーバを利用するには以下が必要となります。

設定が完了したら authelia configuration ファイルに以下の設定を追加。

configuration.yml
notifier:
  smtp:
    address: 'smtp://smtp.gmail.com:587' # google smtp サーバのアドレス
    timeout: '5s'
    username: 'xxx' # google アカウント名。gmail アドレスのうち @gmail.com より前の部分
    password: 'yyy' # アプリケーションパスワードの値
    sender: "Authelia <admin@example.com>"
    identifier: 'localhost'
    subject: "[Authelia] {title}"

ユーザの作成

今回の検証ではユーザ管理にローカルファイルを指定するため、authelia に登録するユーザを users_database.yml で定義します。必要なプロパティは以下。

  • displayname: authelia で表示されるユーザ名
  • password: password のハッシュ値
  • email: メールアドレス。
  • groups: ユーザが所属するグループを list で指定。

ユーザ作成時の制限等は特になさそうなので、ここでは admin グループに属する authelia ユーザと dev グループに属する testuser を定義します。

users_database.yml
users:
  authelia:
    disabled: false
    displayname: "authelia"
    password: "$argon2id$v=19$m=65536,t=3,p=4$bsLnKlPffIRQ7k/qrtcFiA$JJ2ZhZ/vGNqGA3BQ8cgPvuaSAd1+xwTBy7hQHUKvadY"
    email: admin@example.com
    groups:
      - admins
  testuser:
    displayname: "testuser"
    password: "$argon2id$v=19$m=65536,t=3,p=4$nA46jBWRAbbrIR2ajUrAAg$ZBG8BslLdfsUMv73LEgEmhtRhcgBtr9RYEasRPzqGeo"
    email: testuser@example.com
    groups:
      - dev

注意が必要なのは password で、password に記載の通り hash 化された digest を指定する必要があります (デフォルトでは Argon2 形式)。digest は authelia/authelia イメージにデフォルトで入っている authelia CLI を使って生成できます。例えばパスワード test に対応する digest を生成するコマンドは以下。

$ docker run --rm authelia/authelia:latest authelia crypto hash generate argon2 --password test

Digest: $argon2id$v=19$m=65536,t=3,p=4$xpoHwJU215/3hEq/mh8hfw$xjwGD4t2jsrXQl1T+HS42lQDg81b4gCVw08SScBj59M

password には上記 digest の値を設定すれば ok。
記載したユーザを authelia に作成するには configuration の authentication_backend.file.path にファイルのパスを記載し、ファイルをコンテナ内にマウントすれば ok です。これで authelia 起動時にユーザが作成されます。

configuration.yml
authentication_backend:
  file:
    path: '/config/users_database.yml'

configuration

上記の構成とするため設定ファイル configuration.yml を以下のように指定します。

configuration.yml
---
###############################################################
#                   Authelia configuration                    #
###############################################################
theme: "dark"
server:
  address: 'tcp://:9091'
log:
  level: 'debug'
identity_validation:
  reset_password:
    jwt_secret: 'a_very_important_secret'
authentication_backend:
  file:
    path: '/config/users_database.yml'
access_control:
  default_policy: 'deny'
  rules:
    - domain: 'traefix.example.com'
      policy: 'bypass'
    - domain: 'auth.example.com'
      policy: 'one_factor'
session:
  secret: 'insecure_session_secret'
  cookies:
    - name: 'authelia_session'
      domain: 'example.com'  # Should match whatever your root protected domain is
      authelia_url: 'https://auth.example.com'
      expiration: '1 hour'  # 1 hour
      inactivity: '5 minutes'  # 5 minutes
      default_redirection_url: 'https://traefik.example.com'
regulation:
  max_retries: 3
  find_time: '2 minutes'
  ban_time: '5 minutes'
storage:
  encryption_key: 'you_must_generate_a_random_string_of_more_than_twenty_chars_and_configure_this'
  local:
    path: '/config/db.sqlite3'
notifier:
  smtp:
    address: 'smtp://smtp.gmail.com:587'
    timeout: '5s'
    username: 'google_account_name'
    password: 'application_password'
    sender: "Authelia <admin@example.com>"
    identifier: 'localhost'
    subject: "[Authelia] {title}"

configuration については Github にサンプルがあるのでこちらも参照。

docker-compose

docker での構築手段はドキュメントの Docker に記載されています。また、Github の以下にサンプルの docker-compose.yml もあります。

https://github.com/authelia/authelia/blob/v4.38.7/examples/compose/lite/docker-compose.yml

Github の docker-compose.yml では以下 4 つのコンテナが起動します。

  • authelia (authelia 本体)
  • traefik (リバースプロキシ)
  • public
  • secure

ただ public, secure コンテナは authelia の bypass (認証なしのアクセス) と two-factor 認証の動作を確認するためだけに設定されているので、実質的には authelia 本体と proxy の traefik の構成となっています。
今回の検証では上記ファイルを参考に以下のように設定します。

docker-compose.yml
---
networks:
  net:
    driver: bridge

services:
  authelia:
    image: authelia/authelia
    container_name: authelia
    volumes:
      - ./authelia:/config
    networks:
      - net
    labels:
      - 'traefik.enable=true'
      - 'traefik.http.routers.authelia.rule=Host(`c.example.com`)'
      - 'traefik.http.routers.authelia.entrypoints=https'
      - 'traefik.http.routers.authelia.tls=true'
      - 'traefik.http.routers.authelia.tls.options=default'
      - 'traefik.http.middlewares.authelia.forwardauth.address=http://authelia:9091/api/authz/forward-auth'  # yamllint disable-line rule:line-length
      - 'traefik.http.middlewares.authelia.forwardauth.trustForwardHeader=true'
      - 'traefik.http.middlewares.authelia.forwardauth.authResponseHeaders=Remote-User,Remote-Groups,Remote-Name,Remote-Email'  # yamllint disable-line rule:line-length
    expose:
      - 9091
    restart: always
    healthcheck:
      ## In production the healthcheck section should be commented.
      disable: true
    environment:
      - TZ=Asia/Tokyo
  traefik:
    image: traefik:2.11.0
    container_name: traefik
    volumes:
      - ./traefik:/etc/traefik
      - /var/run/docker.sock:/var/run/docker.sock
    networks:
      - net
    labels:
      - 'traefik.enable=true'
      - 'traefik.http.routers.api.rule=Host(`traefik.example.com`)'
      - 'traefik.http.routers.api.entrypoints=https'
      - 'traefik.http.routers.api.service=api@internal'
      - 'traefik.http.routers.api.tls=true'
      - 'traefik.http.routers.api.tls.options=default'
      - 'traefik.http.routers.api.middlewares=authelia@docker'
    ports:
      - '80:80'
      - '443:443'
    command:
      - '--api'
      - '--providers.docker=true'
      - '--providers.docker.exposedByDefault=false'
      - '--providers.file.filename=/etc/traefik/certificates.yml'
      - '--entrypoints.http=true'
      - '--entrypoints.http.address=:80'
      - '--entrypoints.http.http.redirections.entrypoint.to=https'
      - '--entrypoints.http.http.redirections.entrypoint.scheme=https'
      - '--entrypoints.https=true'
      - '--entrypoints.https.address=:443'
      - '--log=true'
      - '--log.level=DEBUG'

ディレクトリ構造は以下。

.
├── README.md
├── authelia
│   ├── configuration.yml
│   └── users_database.yml
├── docker-compose.yml
├── setup.sh
└── traefik
    ├── certificates.yml
    ├── certs
    │   ├── private.pem
    │   ├── public.crt
    │   ├── traefik.example.com.crt
    │   └── traefik.example.com.key
    ├── traefik.example.com.crt
    └── traefik.example.com.key

準備が完了したら docker-compose up -d で各コンテナを起動します。authelia コンテナが起動しない場合、たいてい docker log authelia にエラーメッセージ (configuration ファイルで過不足がある等)が表示されるので適宜修正します。
また、デフォルトではログレベル debug で設定されているのでログ出力が多いですが configuration.yml の log.level で変更できます。

auth.example.com, traefik.example.com のドメイン名を docker が起動しているホストの IP address に解決できるよう /etc/hosts 等に追加する対応もしておきます。

/etc/hosts
[host_ip_address] auth.example.com traefik.example.com

ユーザ認証

まずは非常にシンプルな用途として username /password で authelia にログインする動作を確認します。
ブラウザで https://auth.example.com にアクセスすると、ホストの port 443 では traefik が待ち受けているため最初は traefik に入りますが、traefik の proxy 機能により authelia のログイン画面にルーティングされます。ここで users_database.yml で定義した testuser/test でログインします。

ユーザ認証に成功すると configuration ファイルの session.cookies.default_redirection_url で設定した url にリダイレクトされます(今回の設定ではtraefik のダッシュボード)。

実際の運用では別のアプリケーションにアクセスする際に上記のようにユーザ認証を要求する、シングルサインオンを利用するなど他のコンポーネントと統合して使うことになるかと思います。その時の動作についても以下で確認していきます。

OIDC によるシングルサインオン

authelia は OIDC provider の機能があるため、keycloak OpenID Connect を使って Gitea にシングルサインオンする の記事でやったように gitea などの OIDC に対応するアプリケーションに対してシングルサインオンを実現できます。前回と同様に Gitea に対して SSO を試してみます。
なお、OIDC 対応の主要なアプリケーションに関しては以下に設定例が記載されています。
https://www.authelia.com/integration/openid-connect/
Gitea の設定例もあります。
https://www.authelia.com/integration/openid-connect/gitea/

Authelia 側の設定

authelia 側では configuration ファイルに identity_providers.oidc を追加することで oidc の設定を行います。
https://www.authelia.com/configuration/identity-providers/openid-connect/provider/

client

接続先のアプリケーションの情報を client に設定します。基本的は keycloak でやったときと同じ。

  • client_id: client 名
  • client_name: authelia UI 上の表示名。client_id に一致させる。
  • client_secret: client secret の値
  • authorization_policy: ユーザ/パスワードでシングルサインオンする場合は one_factor, ワンタイムパスワードの入力を要求する場合は two_factor を指定。
  • redirect_uris: gitea 側の redirect url を指定。
  • scopes: client が使用可能な scope

この中で client_secret のみは平文ではなく hash 化された値を指定する必要があります(デフォルト設定では pbkdf2, sha512 形式)。ランダムな値の場合は よくある質問 のコマンドで作成できますが、--password オプションをつけると任意の値を作成できます。例えば client secret の値を test にしたい場合、以下のコマンドで対応するハッシュ値を取得します。

$ docker run --rm authelia/authelia:latest authelia crypto hash generate pbkdf2 --variant sha512  --password test

Digest: $pbkdf2-sha512$310000$A9NYmWUTPh7zbuWG4bFRWg$M.8Gm2eyW3CV8OxC7KvjKAv7yo1QCB5QrIFFyDRic2USc9g7VU2Le7lfz8ti8TMn2/satWlmz9JxKNlTVGBHvw

設定ファイルにはこのハッシュ値を指定すれば ok 。

clients:
  - client_id: 'gitea'
    client_name: 'gitea'
    client_secret: '$pbkdf2-sha512$310000$A9NYmWUTPh7zbuWG4bFRWg$M.8Gm2eyW3CV8OxC7KvjKAv7yo1QCB5QrIFFyDRic2USc9g7VU2Le7lfz8ti8TMn2/satWlmz9JxKNlTVGBHvw'

その他、clients に設定可能な項目の一覧は以下を参照。
https://www.authelia.com/configuration/identity-providers/openid-connect/clients/

jwks

OIDC の設定を追加する際は JSON Web Keys が必須項目となるのでこちらも設定します。

https://www.authelia.com/configuration/identity-providers/openid-connect/provider/

この内の key に関しては openssl を使って作成できます。

$ openssl genrsa -out oidc_key.pem 2048
$ openssl rsa -in oidc_key.pem  -pubout -out oidc_pub.pem -outform der

作成される秘密鍵 oidc_pub.pem の内容を key に指定すれば ok

identity_providers:
  oidc:
    jwks:
      - key_id: "authelia"
        algorithm: "RS256"
        use: "sig"
        key: |
          -----BEGIN PRIVATE KEY-----
          ...
          -----END PRIVATE KEY-----

まとめ

上記をまとめると、configuration.yml に追加する項目は以下のようになります。

configuration.yml
identity_providers:
  oidc:
    jwks:
      - key_id: "authelia"
        algorithm: "RS256"
        use: "sig"
        key: |
          -----BEGIN PRIVATE KEY-----
          ...
          -----END PRIVATE KEY-----
    ## The other portions of the mandatory OpenID Connect 1.0 configuration go here.
    ## See: https://www.authelia.com/c/oidc
    clients:
      - client_id: 'gitea'
        client_name: 'gitea'
        client_secret: '$pbkdf2-sha512$310000$A9NYmWUTPh7zbuWG4bFRWg$M.8Gm2eyW3CV8OxC7KvjKAv7yo1QCB5QrIFFyDRic2USc9g7VU2Le7lfz8ti8TMn2/satWlmz9JxKNlTVGBHvw'
        public: false
        authorization_policy: 'one_factor'
        redirect_uris:
          - 'https://gitea.centre.com:3000/user/oauth2/authelia/callback'
        scopes:
          - 'openid'
          - 'email'
          - 'profile'
          - 'groups'
        response_types:
          - "code"
        grant_types:
          - 'authorization_code'
        userinfo_signed_response_alg: 'none'

Gitea 側の設定

Gitea 側の設定項目は keycloak と同じなので そちらを参照。
また、せっかくなのでサインイン時のアイコンも設定します。

アイコンの指定方法

Serving custom public files に記載の通り、gitea では $GITEA_CUSTOM/public/assets/ に image を配置することで http://[ドメイン名]/assets/image.png でアクセスできます。前回の設定では APP_DATA_PATH = /data/gitea に指定しているので、コンテナ内の /data/gitea/public/assets/ が対応します。よって以下のページから logo をダウンロードしてコンテナ内のディレクトリに配置することで画像が参照できます。

https://www.authelia.com/reference/guides/branding/

oidc のアイコンに設定するには Icon URL にパスを指定すれば ok です。

設定後に gitea のサインイン画面にアクセスすると authelia の OIDC の項目が追加されています。

動作確認

Gitea サインイン画面から authelia でサインイン を選択すると authelia 側にリダイレクトされ、ユーザの認証が求められます。

authelia 側で作成したユーザ情報を入力すると同意リクエストが求められるので 同意する を選択。

gitea 側に戻ってログインが完了します(gitea 側でユーザ登録が完了してない場合は新規登録が要求される)。
よって keycloak のときと同様に authelia のユーザー情報を利用した SSO ログインができます。

ワンタイムパスワード認証

authelia では Time-based One Time Password (TOTP) の機能があり、ユーザの認証が成功した後に外部デバイスによるワンタイムパスワードの入力を要求してセキュリティを強化することができます。
totp を使うにはワンタイムパスワードを発行するための外部アプリケーションが必要になります。以下ページに authelia がサポートしているアプリケーションの一覧が記載されています。

https://www.authelia.com/reference/integrations/time-based-one-time-password-apps/

ここでは Google Authenticator を使って検証を行うため、事前にスマホ等にダウンロードしておきます。

設定

totp の設定は configuration ファイルの totp セクションで指定します。ここでは totp の有効時間や桁数などを指定できます。利用するアプリケーションによってサポートされている algorithm や digit が異なるため、上記の一覧を確認して設定します。

configuration.yml
totp:
  disable: false
  issuer: 'auth.centre.com'
  algorithm: "sha1"
  digits: 6
  period: 30
  secret_size: 32
  disable_reuse_security_policy: false

totp は configuration 内の policy を two_factor にすることで有効化されます。configuration ファイルの様々な箇所で設定できるため、特定のドメインや url にアクセスした際は user/password 認証のみで通過し、特定グループがアクセスする際は追加で totp を要求するといった設定も可能です。
例えば、auth.example.com ドメインにアクセスしたときのみ totp を要求するには、 access_control.rules に以下のようにドメイン名と two_factor を設定します。

configuration.yml
access_control:
  default_policy: 'deny'
  rules:
    - domain: 'auth.example.com'
      policy: 'two_factor'

動作確認に使用するユーザも追加しておきます。

  • username: totp
  • password: test
  • email: authelia からのメールが受信可能なメールアドレスを指定。ここでは gmail を使用。
user_database.yml
users:
 totp:
    displayname: "totp"
    password: "$argon2id$v=19$m=65536,t=3,p=4$nA46jBWRAbbrIR2ajUrAAg$ZBG8BslLdfsUMv73LEgEmhtRhcgBtr9RYEasRPzqGeo"
    email: xxx@gmail.com
    groups:
      - dev

動作確認

まず auth.example.com を開いて先程作った totp ユーザでログインします。

ワンタイムパスワードのセットアップを要求されるので、デバイスを登録する をクリック。

設定画面に移動するので上のワンタイムパスワード > 追加を選択。

ここで本人確認が必要となり、ユーザのメールアドレス (xxx@gmail.com) に [Authelia] Confirm your identity という件名のメールが送信されます。メール内にワンタイムコードが記載されているのでこれを入力します。

認証に成功すると totp の登録に移動します。画面に QR コードが表示されるので google authenticator で読み取ります。

読み取りに成功すると google authenticator に totp が追加されます。またブラウザでも登録した totp の設定が確認できます。

以上で totp の設定が完了しました。
authelia からサインアウトした後再度同じユーザでログインすると、以下のように totp の入力が要求されるようになります。

その他に OIDC を使って SSO する際に totp を要求することもできます。これは configuration の authorization_policy を two_factor に変更することで適用されます。

configuration.yml
identity_providers:
  oidc:
    clients:
      - client_id: 'gitea'
        client_name: 'gitea'
-        authorization_policy: 'one_factor'
+        authorization_policy: 'two_factor'

この場合、gitea でのサインイン > ユーザの username/password を指定した後に totp の入力が要求されるようになります。

authelia では上記のように簡単にワンタイムパスワードを設定してリソースに対するセキュリティを強化できます。

アクセスコントロール

authelia のアクセスコントロールではユーザやグループ、アクセス先の url に基づいてアクセス許可、拒否などを設定することが可能です。

https://www.authelia.com/overview/authorization/access-control/
https://www.authelia.com/configuration/security/access-control/

ルールの設定

アクセスコントロールの設定は configuration の access_control セクションで設定します。ここでは試しに traefik.example.com へのアクセスを制御してみます。
traefik ではブラウザで https://traefik.example.com/dashboard/#/http/routers にアクセスすると登録済みの http route が確認できますが、このとき内部的には traefik の /api/http/routers に対する GET リクエストが実行されます。そこで dev group に属するユーザはこの url へのアクセスを拒否するように設定します。

  • policy: deny
  • resources: 対象の url を正規表現で指定
  • subject: group:dev を指定

上記以外のリソースに関しては user/password の認証が通ればアクセスできるように policy: 'one_factor' のルールも設定します。

configuration.yml
access_control:
  default_policy: 'deny'
  rules:
    - domain: 'traefik.example.com'
      policy: 'deny'
      resources:
        - '^/api/http/routers.*$'
      subject: "group:dev"
    - domain: 'traefik.example.com'
      policy: 'one_factor'
    - domain: 'auth.example.com'
      policy: 'one_factor'

ルールの検証

configuration ファイルに記載したルールは authelia access-control check-policy コマンドで検証できます。ここでは dev group に属するユーザが https://traefik.example.com//dashboard/#/http/routers に対して GET method でリクエストを送信した際に拒否されるかどうかを見るため、各オプションに指定してコマンドを実行します。

$ docker exec -it authelia sh

/app # authelia access-control check-policy --config /config/configuration.yml --groups dev --url https://traefik.example.com/api/http/routers
Performing policy check for request to 'https://traefik.example.com/api/http/routers' method 'GET' groups 'dev'.

  #     Domain  Resource        Method  Network Subject
* 1     hit     hit             hit     hit     hit

The policy 'deny' from rule #1 will be applied to this request.

実行結果では指定したリクエストと rules 内の条件が一致するときに hit, 一致しないときは miss と表示されます。Domain ~ Subject の 5 つの条件が hit となる場合は rule に指定した policy が適用されます。上記の例では policy: deny が適用されるため、想定通り dev group に属するユーザは上記 url へのアクセスが拒否されます。

ちなみに admin グループに属するユーザに対するルールの評価も見てみます。

/app # authelia access-control check-policy --config /config/configuration.yml --groups admin --url https://traefik.example.com/api/http/routers
Performing policy check for request to 'https://traefik.example.com/api/http/routers' method 'GET' groups 'admin'.

  #     Domain  Resource        Method  Network Subject
  1     hit     hit             hit     hit     miss
* 2     hit     hit             hit     hit     hit

The policy 'one_factor' from rule #2 will be applied to this request.

この場合、1 つめのルールは subject: "group:dev" の条件がマッチしないため miss になっており、ルールの適用がスキップされます。2 つめのルールはマッチするため one_factor のポリシーが適用されます。よって admin グループのユーザはユーザ認証 (user/password によるログイン)が成功する場合に上記 url にアクセスできるというようになります。

また、access_control では複数のドメインやルールを柔軟に設定できますが、上記で見たようにルールは rules で指定した順に上から評価されます。なので同じドメインやリソースを対象とする複数の rule を定義する場合は順序も意識する必要があります。このあたりはドキュメントの以下ページを参照。

https://www.authelia.com/configuration/security/access-control/#rule-matching
https://www.authelia.com/reference/guides/rule-operators/

動作確認

ルールの検証では問題なさそうだったので実際にブラウザからアクセスしてみます。
はじめに admin グループに属する authelia ユーザでログインし、traefik ダッシュボードの https://traefik.example.com/dashboard/#/http/routers にアクセスすると route の一覧が確認できます。

いったんログアウトし、dev グループの testuser でログインして同じ url にアクセスすると route 一覧が表示されないことが確認できます。

authelia のログでは testuser による /api/http/routers への method が拒否されています。

$ docker logs authelia

level=info msg="Access to 'https://traefik.example.com/api/http/routers?search=&status=&per_page=10&page=1' is forbidden to user 'testuser'" method=GET path=/api/authz/forward-auth remote_ip=192.168.3.18

よってアクセスコントロールが設定通りに動作していることが確認できました。
アクセスコントロールを使うことでリクエストを行うグループに応じて許可・拒否の設定を行ったり、アプリケーション全体では one_factor の認証を要求し、特定のパスのみ two_factor を要求してセキュリティを強化するといった制御を実装することができます。

終わりに

認証・認可サーバの役割を持つ OSS の Authelia の機能をいくつか試しました。前回記事にした keycloak と同様に OIDC による SSO やユーザ認証、アクセスコントロールの機能を持ちますが、個人的には keycloak よりも内部で使われる概念や設定方法が簡単で扱いやすいと感じました。ドキュメントもプロパティの定義や設定例が多い点も良さそうです(実際動かさないとわかりにくい部分もありますが)。
アクセスコントロールやワンタイムパスワードの設定も容易なため、既存アプリケーションで認証の部分を authelia に置き換えてマイクロサービスを実現するといったようなユースケースで有用そうです。

Discussion