OSSTech株式会社

OpenLDAP 2.5のlloadd機能

2022-06-09 - 下窪 聖人

はじめに

OpenLDAP 2.5の新機能となるlloaddの機能について紹介します。

lloaddは、LDAP用のロードバランサーとして実装されたOpenLDAPのモジュールで、受け取ったLDAPリクエストを複数のLDAPサーバーに振り分けて、LDAPサーバーの負荷分散を行うことが可能です。

lloaddとマルチマスターのLDAP2台を組み合わせた場合の処理の流れは下図のようになります。

lloaddの設定

今回は1号機をロードバランサー、2号機と3号機でマルチマスター構成にします。

OpenLDAPの1号機はlloadd機能のみを利用するために、 1号機のslapd.confに下記のパラメーターを設定します。

本記事ではbackend-serverに2号機と3号機の2台のLDAPサーバーを指定していますので、 1号機のロードバランサーは2号機と3号機にリクエストを振り分けるようになります。

slapd.conf

moduleload lloadd.la

backend lload

# The Load Balancer manages its own sockets, so they have to be separate
# from the ones slapd manages (as specified with the -h "URLS" option at
# startup).
listen "ldap://:1389"

# Enable authorization tracking
feature proxyauthz

# Specify the number of threads to use for the connection manager.  The default is 1 and this is typically adequate for up to 16 CPU cores.
# The value should be set to a power of 2:
io-threads  2

# If TLS is configured above, use the same context for the Load Balancer
# If using cn=config, this can be set to false and different settings
# can be used for the Load Balancer
TLSShareSlapdCTX false

# Authentication and other options (timeouts) shared between backends.
#bindconf bindmethod=simple
#         binddn=cn=Manager,dc=example,dc=com credentials=secret
#         network-timeout=5
#         tls_cacert="/opt/osstech/etc/openldap/certs/ldap.example.com.crt"
#         tls_cert="/opt/osstech/etc/openldap/certs/ldap.example.com.crt"
#         tls_key="/opt/osstech/etc/openldap/private/ldap.example.com.key"


# List the backends we should relay operations to, they all have to be
# practically indistinguishable. Only TLS settings can be specified on
# a per-backend basis.

backend-server uri=ldaps://ldap2.example.com starttls=critical retry=5000
               max-pending-ops=50 conn-max-pending=10
               numconns=10 bindconns=5
backend-server uri=ldaps://ldap3.example.com starttls=critical retry=5000
               max-pending-ops=50 conn-max-pending=10
               numconns=10 bindconns=5

下記のコマンドで起動します。

# systemctl start slapd

bindconfパラメーター

lloaddはバックエンドのLDAPサーバーへの接続時に bindconfに指定された認証情報を使って接続します。 lloadd起動時にはbackendへのコネクションが接続されておらず、bindconfパラメーターを使って認証が行われます。

下記のパラメーターを使用します。

  • bindmethod

    simpleまたはsaslを指定します。

  • binddn

    bindで使用するdnを指定します。

  • saslmech

    bindmethodでsaslを指定した場合の認証方式の指定時に必要になります。

  • credentials

    binddnで指定したdnのパスワードを指定します。 bindmethodでsimpleを指定している場合は、cleartext(平文)でパスワードを指定する必要があります。

  • authcid

    認証アイデンティティーを指定する時に使用します。

  • authzid

    認証アイデンティティーを指定する時に使用します。

  • realm

    realmを指定します。

  • secprops

    セキュリティプロパティを指定します。

  • timeout

    サーバーからのレスポンスを受け取るまでの待ち時間(秒)を指定します。

  • network-timeout

    ネットワークコネクションが作成されるまでの待ち時間(秒)を指定します。

  • tcp-user-timeout

    0以外の値が指定されている場合、OSのTCP_USER_TIMEOUTが上書きされます。

TLS通信を利用する場合は、下記のパラメーターを使用します。

  • tls_cacert

    CA証明書を指定します。

  • tls_cert

    lloaddから振り分け先に提示するクライアント証明書を指定します。

  • tls_key

    秘密鍵を指定します。

  • tls_cacertdir

    CA証明書のディレクトリを指定します。

  • tls_reqcert

    TLSセッション確立時のサーバー認証方法を指定します。 never、allow、try、demandから1つを選択して指定します。

  • tls_reqsan

    Subject Alternative Nameの認証方法を指定します。 never、allow、try、demandから1つを選択して指定します。

  • tls_cipher_suite

    TLS通信時に使用する暗号スイートを指定します。

  • tls_protocol_min

    接続時に許容可能なTLSプロトコルの最低バージョンを指定します。

  • tls_ecname

    Diffie-Hellman鍵交換に使用する曲線を指定します。 OpenSSLでECDHEベースの暗号スイートを使用する時のみ使用します。

  • tls_crlcheck

    サーバーの証明書が失効していないかを確認するために、CRLを使用するかどうかを指定します。 none、peer、allから1つを選択して指定します。

backend-serverパラメーター

backend-serverは、lloaddの振り分け先のLDAP情報を指定するパラメーターです。

下記の情報を指定することができます。

  • uri

    LDAP URIを指定します。

  • retry

    リトライ時間(ms)を指定します。デフォルトは5秒です。

  • keepalive

    ソケットが接続中であるかどうかの確認で使用します。idle:probes:intervalの形式で指定します。 idleは接続がアイドル状態であるのに必要な時間(秒)です。 probesはコネクションを切断する前に送信するキープアライブ・プローブの最大数です。 intervalはキープアライブ・プローブ間の間隔時間(秒)です。

  • starttls

    TLSセッションを確立するためのStartTLSの操作を使用するかどうかをyesまたはcriticalで指定します。 criticalを指定する場合、StartTLSリクエストが失敗するとそのセッションは廃棄されます。これは、TLSなしにsyncreplセッションが続いてしまうのを防ぐことができます。

  • tls_cert

    lloaddから振り分け先に提示するクライアント証明書を指定します。

  • tls_key

    秘密鍵を指定します。

  • tls_cacert

    CA証明書を指定します。

  • tls_cacertdir

    CA証明書のディレクトリを指定します。

  • tls_reqcert

    TLSセッション確立時のサーバー認証方法を指定します。 never、allow、try、demandから1つを選択して指定します。

  • tls_cipher_suite

    TLS通信時に使用する暗号スイートを指定します。

  • tls_crlcheck

    サーバーの証明書が失効していないかを確認するために、CRLを使用するかどうかを指定します。 none、peer、allから1つを選択して指定します。

  • tls_protocol_min

    接続時に許容可能なTLSプロトコルの最低バージョンを指定します。

  • numconns

    アクティブコネクション数を指定します。

  • bindconns

    LDAPクライアントからのbindリクエストを処理するためのアクティブコネクション数を指定します。

  • max-pending-ops

    全てのバックエンドコネクションにおける未完了の操作数を制限します。デフォルトは0です。0はこのバックエンドに制限がないことを示します。

  • conn-max-pending

    0に指定しない限り、アップストリームコネクション1つあたりの未完了の操作数を制限します。デフォルトは0です。

動作確認

動作環境

AlmaLinux 9.0

OpenLDAP 2.5 3台

OpenLDAPの起動

起動時の各号機の挙動を確認します。

設定が正しければ、1号機/2号機/3号機を起動した際に下記のログメッセージが記録されます。

1号機の起動時のログ

slapd starting

2号機/3号機の起動時のログ

ACCEPT from IP=1号機のIPアドレス:34958 (IP=0.0.0.0:636)
TLS established tls_ssf=256 ssf=256 tls_proto=TLSv1.3 tls_cipher=TLS_AES_256_GCM_SHA384

上記の状態で、試しに下記の操作を行い、各号機のログを確認します。

# /opt/osstech/bin/ldapmodify -x -W -D "cn=Manager,dc=example,dc=com" -f modifycn.ldif -H ldap://:1389

1号機のログ

operation_init: received a new operation, bind request with msgid=1 for client connid=31
handle_bind_response: received response for bind request msgid=1 by client connid=31, result=0
forward_final_response: connid=3 msgid=1 finishing up with a request for client connid=31
operation_init: received a new operation, modify request with msgid=2 for client connid=31
forward_final_response: connid=0 msgid=2 finishing up with a request for client connid=31
operation_init: received a new operation, unbind request with msgid=3 for client connid=31
handle_one_request: received unbind, closing client connid=31

2号機のログ(今回は2号機に振り分けられたため、3号機のログは変化なし。)

MOD dn="uid=testuser,ou=Users,dc=example,dc=com"
MOD attr=cn
RESULT tag=103 err=0 qtime=0.000032 etime=0.083721 text=

1号機のログにはlloaddが受け取ったLDAPリクエストがログに記録されます。 振り分けられたLDAP(今回は2号機)のログを確認することで、lloaddから送られたリクエストが振り分けられてLDAPサーバーに届いていることが確認できます。

2号機と3号機はマルチマスター構成にしているので、2号機の更新内容は3号機に同期されます。

OpenLDAPの停止

停止時の各号機の挙動を確認します。

  • 1号機が停止する場合

    lloaddも停止するため、振り分けられません。

    1号機のログ

    lloadd shutdown: Main event loop finished: rc=0
    lload_listener_thread: event loop finished: rc=0
    lloadd_io_task: Daemon 0, event loop finished: rc=1
    lloadd_io_task: Daemon 1, event loop finished: rc=1
    slapd stopped.
    
  • 2号機が停止する場合

    3号機に振り分けられます。

  • 3号機が停止する場合

    2号機に振り分けられます。

  • 2号機と3号機が停止する(振り分け先が停止している)場合

    LDAPクライアント側に下記のエラーが返され、リクエストは振り分けられません。

    ldap_bind: Server is unavailable (52)
          additional info: no connections available
    

負荷分散方式

slapd.confのbackend-serverを選択する処理のソースコードによると、OpenLDAP 2.5の負荷分散方式はラウンドロビンでした。 大量のリクエストを投げることでも確認できました。

ソースコードのコメント

* Round-robin step:
* Rotate the queue to put this connection at the end, same for
* the backend.

この分散方式のデメリットとして、データの一貫性が得られないことが挙げられます。 具体的には、2号機に更新のリクエスト、3号機に参照のリクエストが送られる場合、2号機の更新が3号機に反映される前に参照の処理が行われてしまうことが考えられます。 この問題を回避するために、更新を伴うリクエストはlloaddを経由しないようにするなどの工夫が必要になります。

OpenLDAP 2.6では、slapd.confにtierのオプションを追記することでroundrobin/weighted/bestofを指定できます。1 また、write_coherenceやrestrict_controlの設定が可能であるため、本記事で説明したデメリットを回避することができます。

  • write_coherence

    write_coherence 5のように設定すると、書き込みが終了してから5秒経過するまで、lloaddは同じbackendに振り分けるようになります。

  • restrict_control

    restrict_control <OID> <action>のように設定します。actionは下記が用意されています。

    • reject

      操作は拒否されます。

    • connection

      今後の全ての操作は同じコネクションに振り分けられるようになります。

    • backend

      タイムアウトしないことを除けば、write(action)と同じです。

    • write

      write_coherenceと同じように扱えます。

    • ignore

      制限に影響を与えません。


  1. https://www.openldap.org/software/man.cgi?query=lloadd.conf ↩︎