RedHat 8 で PostgreSQL に接続するタイミングで sssd_kcm が呼び出しされてWEBサービスが遅延した話

# 前置き

これは私が遭遇した RHEL 8 で `sssd_kcm` と PostgreSQL の組み合わせによってWEBサービスのパフォーマンス問題に遭遇したときの原因究明の記録です。

同じ事象に遭遇する方はかなり少ないと思いますが、レアケースであるが故に同じ不具合に遭遇した方のために情報を残す目的で書き残しています。

## `sssd_kcm` について

RHEL 8 （およびRHEL系OS）には標準で sssd （システムセキュリティーサービスデーモン）というシステムサービスが追加されています。
このサービスの中に sssd-kcm （プロセス名は `sssd_kcm`）というサービスが存在しており、Kerberos 認証のキャッシュを管理してくれる...のだが、CPU使用率が100％近くに張り付く不具合がありました。

該当の不具合は「sssd kcm high cpu」で検索をかけると次のバグ報告がヒットします。

https://bugzilla.redhat.com/show_bug.cgi?id=1645624

上記以外にもいくつか似たようなバグ報告がありますが、多くは上記のバグの複製としてクローズされています。

修正内容を見る限り、キャッシュの反復処理に問題があったため、CPU使用率が上がっていた様子。
RHEL 8 / CentOS 8 (Stream) の最新のバージョンでは不具合は修正済みのようです。

# 障害発生

問題が発生したのはあるWEBサービスでした。このサービスは単純なCRUDを行うもので、リクエスト内容をバリデーションしてからデータベースに `INSERT` もしくは `UPDATE` クエリを発行するというよくあるサービスでした。

構成自体もありがちなもので、 Nginx の下に php-fpm がぶら下がっており、別サーバーで稼働しているデータベースには PostgreSQL を採用しているという FastCGI でした。

普段はHTTPレスポンスまでに１秒かかるかどうかという速度で動作していたようですが、障害が起こった時は（普段と比較して）大量のHTTPリクエストが届いていたようです。

しかし、障害が発生した時はHTTPレスポンスタイムが30秒、40秒と伸びていき、ついには Nginx のHTTPレスポンスタイムアウト60秒を超えてしまっていました。

## 障害対処

ここから先はほとんど蛇足の情報になります。
結構前の出来事な上に疲れていた時の作業内容の記憶なので細部もあいまいです。
障害対応に興味が無い方は [`sssd_kcm` を止める](#sssd_kcm-を止める) 読み飛ばしてください。


障害発生時は私は別の業務で手が離せない状態でした。
別の担当者が状況把握のために奔走しており、後から聞いた障害内容は以下のような内容でした。

- WEBサービスは障害が発生してから15分程度は高負荷状態が継続していた。
- 負荷が下がるまでの間、レスポンスタイムが数十秒と長い状態になっていた。
- 障害発生直後は 1,500リクエスト/分 くらいのペースで数分間リクエストが到達していた。
- 最もリクエストが集中した瞬間は 50リクエスト/秒 ペースだった。

### 再発防止

障害発生後、原因の究明と対策が開始されました。

私はこのタイミングでアサインされたのですが、このWEBサービスの開発には直接携わっていませんでした。
リリース前に負荷試験を担当していた程度だったので、この時点では障害の原因は排他制御やコストが高いSQLの修正でどうにかなるようなものだろうと考えていました。

しかし、ソースコードを見ても、リクエスト数に比例して極端に処理時間が伸びそうな箇所を見つけることはできませんでした。
何よりWEBサービスが一切ログを出力していない（そもそもロギングに関するコードが存在していない）という状況だったので、障害発生時にどのような処理に時間がかかっていたのか知る事すらできない状態でした。

原因を究明できないまま時間が過ぎていき、障害発生から半日が経過しました。
根本的な原因の解決にはなりませんが、同じ状況が発生する可能性があるため
短期的な対策として php-fpm の `pm.max_children` パラメーターを調整して PHP プロセス数を増やし、
CPUを追加することでHTTPレスポンスの遅延を緩和する策を講じることになりました。

サーバーは仮想環境で稼働しているため、CPU追加はすぐに実施できます。
検証用の環境でリソース追加作業と php-fpm のパラメーター調整作業を実施して効果測定を行うことになりました。

検証環境でツールを用いて大量のHTTPリクエストを発生させる負荷試験を実施します、事象がいくらかは改善している...と思いきや、何も改善できていませんでした。

期待を裏切る結果にうろたえながらも、ツールを調整して同時に送信するリクエスト数を変えていく中で次の事実が分かりました。

- リクエスト数に比例してレスポンスタイムが大幅に伸びていく。
- 同時に100 リクエストがWEBサーバーで処理状態になったあたりからレスポンスタイムが30秒を超える。
- 同時 300 リクエストがWEBサーバーで処理状態になったあたりから、データベースコネクションタイムアウトが発生するようになる。

データベースコネクションのタイムアウトエラーまで発生するようになり、混乱に拍車がかかります。
このタイムアウトエラーですが、 PostgreSQL （サーバー側）のログには「クライアントがコネクションを切断した」と記録されており、
php-fpm （クライアント側）のエラーログには「サーバーから一定時間応答がなかった」というメッセージが記録されていました。
お互いが他方がレスポンスを返してこないと認識しているという状況はかなりイレギュラーです、ネットワークに問題があるのかという疑いまで出てきます。

**TIP:** アプリケーションで例外が `catch` されていなかったおかげで、php-fpm のエラーログにバックトレースが記録されていたおかがでデータベースエラーに関しては記録がありました。

その一方でWEBサーバーもデータベースサーバーもCPUやメモリには余裕がある状態でした。

八方ふさがりの状態になりましたが、一緒に障害対応をしていた方から疑問の声があがりました「試験中だけ `sssd_kcm` プロセスがCPUを1つ使い切っている」。

障害対応には多くの方が対応に参加しており、サーバー構築を担当者もその場にいらっしゃいました。
ここで「`sssd_kcm` はRHEL 8 で追加された認証サービスの一部だが、特定の条件でCPU使用率が上昇するバグがある」という情報がもたらされます。

しかし、この時点では `sssd_kcm` がこの障害の原因だとは誰も主張しませんでした。
サーバーには複数のCPUが搭載されていたので、1つのCPU使用率が100%になったくらいであればサーバーには余力があるはずです。

その後もあの手この手で思いつく限りの方法を試しますが、一向に状況が改善する気配がありません。
いよいよ思いつく手が無くなったときに `sssd_kcm` のバグを何とかしてみようという案が持ち上がりました。

`sssd_kcm` のバグリリースバージョンは `dnf` でインストールできる状態でしたが、本番環境は外部との通信が容易にできない構成になっており、依存ライブラリのバージョンアップの影響範囲もすぐには判断できない状況でした。

とりえあず、検証環境で `sssd_kcm` サービスを停止して効果があるか確認することになりました。

### `sssd_kcm` を止める

`sssd_kcm` は RHEL 8 では Minimal 構成であっても Systemd によって自動起動する様にインストールされているサービスです。

Systemd には `sssd_kcm` プロセスに関係する主なサービスに `sssd.service`, `sssd-kcm.service`, `sssd-kcm.socket` があります。
この中で `sssd-kcm.socket` を停止させることで `sssd_kcm` プロセスが停止するようになっています。

```shell
# 起動中の sssd_kcm を停止する。
$ sudo systemctl stop sssd-kcm.socket

# sssd_kcm を自動起動しないようにする。
$ sudo systemctl disable sssd-kcm.socket
```

`sssd_kcm` が停止しても、WEBサーバーにリクエストが集中した時にCPUが１つ占有されなくなるだけなので、あまり大きな効果は期待できない...と思いきや、劇的な効果が表れます。

100リクエストが集中した時にレスポンスタイムが30秒でしたが、`sssd_kcm` プロセスを停止してからは 1秒～3秒の範囲で返ってくるようになり、2,000リクエストがサーバーに集中していても障害が再発しなくなりました。

#### 原因不明

`sssd_kcm` を停止するだけで、障害が再発しなくなることが分かりました。
しかし、なぜ `sssd_kcm` を停止して Nginx + PHP + PostgreSQL 構成の FastCGI のレスポンスが早くなる（もしくは遅延が発生しなくなる）のかは分かりませんでした。

WEBサービス内で Kerberos 認証は使用されていません。
なぜ Kerberos 認証のキャッシュ管理をしている `sssd_kcm` の停止が劇的な速度改善につながったのか、因果関係は誰にも説明できませんでした。

RHEL のサポートにも確認がとられたようですが、正確な原因にたどり着けるような回答は得られませんでした。
※ Nginx と PHP 構成の FastCGI と sssd_kcm の関係を確認する質問をしていたようなので、
   ここに PostgreSQLの名前があれば回答があったかもしれません。

疑問は残ったままでしたが、障害への対策が行われたことで対応は一旦終了となりました。

# 原因は PostgreSQL の GSSAPI 呼び出しだった

答えは既に書かれているとおりです。
後述する原因究明のステップは興味がない方は [対策](#対策) まで読み飛ばしてください。


ある日、PostgreSQL のドキュメントを眺めていると、 `gssencmode` パラメーターの次の一文が目に留まります。

https://www.postgresql.jp/document/12/html/libpq-connect.html

> prefer (デフォルト)
>
> GSSAPI認証情報が(すなわち認証情報キャッシュに)存在すれば、まずGSSAPI暗号化接続を試行します。 その試行に失敗した場合、もしくは認証情報がない場合には非GSSAPI暗号化接続を試行します。 これがPostgreSQLをGSSAPIサポートを有効にしてコンパイルした場合のデフォルトです。

ここで、 PostgreSQL への接続の際にサーバーとクライアントの双方でタイムアウトエラーが発生していた事実が頭をよぎります。

もし、この GSSAPI 認証の実態が  Kerberos 認証であり、キャッシュサービスである `sssd_kcm` を呼び出していたのだとしたら
`sssd_kcm` のバグによってTCPソケット通信が大幅に遅延し、サーバーとクライアントの両方が「相手からの応答がなかった」という理由でタイムアウトエラーを報告する事象が説明できます。

## PostgreSQL は GSSAPI から `sssd_kcm` を呼ぶのか

### GSSAPI サポートが有効になっているか確認する

PostgreSQL と GSSAPI 認証の関係に気が付きましたが、障害が発生したWEBサービスで再現確認する事は出来なかったので
当時の作業ログなどから近い環境を CentOS 8 で再現して調査する事にしました。

まず、 PostgreSQL の GSSAPI 認証はオプショナルな機能であり、コンパイル時のフラグで有効化しなければいけません。

https://www.postgresql.jp/document/12/html/gssapi-auth.html

> GSSAPIサポートは、PostgreSQLを構築する時に有効にしなければなりません。詳細は、第16章を参照してください。

PostgreSQL をコンパイルした時の情報は `pg_config` で確認できます。
`CONFIGURE` に GSSAPI を有効化する `--with-gssapi` フラグが指定されていれば、この機能は有効化されています。
さらに `LIBS` に指定されているリンク対象ライブラリから GSSAPI の関数を提供しているライブラリも特定できそうです。

```shell
$ pg_config | grep -E "CONFIGURE|LIBS|LIBDIR"
LIBDIR = /usr/lib64
CONFIGURE = ... '--with-gssapi' ...
LIBS = ... -lgssapi_krb5 ...
```

出力内容が長いので、無関係なところは全て削除しています。

`CONFIGURE` に `--with-gssapi` が指定されていました。
さらに、`-lgssapi_krb5` で指定されている `libgssapi_krb5.so` が怪しいです。

### PostgreSQL が呼び出ししている GSSAPI 関数を確認する

次に、PostgreSQL のソースコードから GSSAPI を呼び出ししている場所を探し出します。
呼び出ししている関数が `libgssapi_krb5.so` から提供されていれば、PostgreSQL が使用している GSSAPI の提供ライブラリが確定します。

PostgreSQL のドライバーである `libpq` の接続に関係する処理は `fe-connect.c` に集中しています。

`gssencmode` パラメーターを使用しているので、この文字列を PostgreSQL 12 のソースコードから探して調査していきます。

https://github.com/postgres/postgres/blob/REL_12_10/src/interfaces/libpq/fe-connect.c#L1287

上記のコードで `conn->gssencmode` に `DefaultGSSMode` マクロをコピーしているので、 `gssencmode` 接続パラメーターに対応するのは `conn->gssencmode` のようです。
この変数を使用している箇所を追いかけていきます。

https://github.com/postgres/postgres/blob/REL_12_10/src/interfaces/libpq/fe-connect.c#L2385

`conn->try_gss = (conn->gssencmode[0] != 'd');	/* "disable" */` というコードが見つかりました。
`gssencmode` に disabled を指定していなければ GSSAPI を呼び出しするようです。

https://github.com/postgres/postgres/blob/REL_12_10/src/interfaces/libpq/fe-connect.c#L2782-L2811

`conn->try_gss` が真なら `pg_GSS_have_cred_cache()` を呼び出しするコードが見つかりました。
関数名も実にそれっぽいです。

https://github.com/postgres/postgres/blob/REL_12_10/src/interfaces/libpq/fe-gssapi-common.c#L68

`pg_GSS_have_cred_cache()` の実態は `fe-gssapi-common.c` にありました。
中身を確認すると `gss_acquire_cred()` を呼び出ししています。

`gss_acquire_cred()` は PostgreSQL のソースコード内に定義がなかったので、この関数が GSSAPI から提供されているようです。

続けて、`nm` コマンドでライブラリがエクスポートしている関数に `gss_acquire_cred` が存在するか確認します。

```shell
$ nm -D /usr/lib64/libgssapi_krb5.so | grep "gss_acquire_cred"
0000000000012a70 T gss_acquire_cred
0000000000012610 T gss_acquire_cred_from
0000000000013ca0 T gss_acquire_cred_impersonate_name
0000000000013230 T gss_acquire_cred_with_password
```

`libgssapi_krb5.so` からエクスポートされていました。
この時点で PostgreSQL は `libgssapi_krb5.so` がエクスポートされている関数を使っていると確定しました。

ちなみに `libgssapi_krb5.so` はシンボリックリンクで `libgssapi_krb5.so.2.2` が実態です。

```shell
$ ls -lha /usr/lib64/libgssapi_krb5.so*
/usr/lib64/libgssapi_krb5.so -> libgssapi_krb5.so.2.2
/usr/lib64/libgssapi_krb5.so.2 -> libgssapi_krb5.so.2.2
/usr/lib64/libgssapi_krb5.so.2.2
```

### `libgssapi_krb5.so` を調査する

`libgssapi_krb5.so` が GSSAPI を提供しており、名前から Kerberos 認証の実態と考えていいでしょう。
後は、 `libgssapi_krb5.so` が `sssd_kcm` サービスと繋がっていることを確認していきます。

ほとんどの場合にサービスを構成するライブラリと設定ファイルはまとめてインストールされるか、依存ライブラリを通じてインストールされるようになっています。
`libgssapi_krb5.so` をインストールした rpm パッケージを探します。

それっぽい rpm パッケージを探すと `krb5-libs` が見つかりました。

```shell
$ dnf list --installed | grep -E "gss|krb5"
krb5-devel.x86_64                    1.18.2-5.el8                            @baseos
krb5-libs.x86_64                     1.18.2-5.el8                            @BaseOS
```

`krb5-libs` でインストールされるファイルをリストします。

```shell
$ rpm -ql krb5-libs | grep "/usr/lib64/lib*"
/usr/lib64/libgssapi_krb5.so.2
/usr/lib64/libgssapi_krb5.so.2.2
/usr/lib64/libgssrpc.so.4
/usr/lib64/libgssrpc.so.4.2
/usr/lib64/libk5crypto.so.3
/usr/lib64/libk5crypto.so.3.1
/usr/lib64/libkdb5.so.10
/usr/lib64/libkdb5.so.10.0
/usr/lib64/libkrad.so.0
/usr/lib64/libkrad.so.0.0
/usr/lib64/libkrb5.so.3
/usr/lib64/libkrb5.so.3.3
/usr/lib64/libkrb5support.so.0
/usr/lib64/libkrb5support.so.0.1
```

### Kerberos 認証時に `sssd_kcm` が使用されている確証を得る

`krb5-libs` パッケージでインストールされたファイルの中に Kerberos 認証に関わる設定ファイルなどもインストールされていそうです。

<details>
<summary>`rpm -ql krb5-libs` の全体は長いので折りたたみました</summary>

```shell
$ rpm -ql krb5-libs
/etc/gss
/etc/gss/mech.d
/etc/krb5.conf
/etc/krb5.conf.d
/etc/krb5.conf.d/crypto-policies
/usr/lib/.build-id
/usr/lib/.build-id/2d
/usr/lib/.build-id/2d/23f710da5ceff9917492155ac50d05278390fc
/usr/lib/.build-id/4b
/usr/lib/.build-id/4b/41849e47029730e8d8042f5e47f80ba3545e44
/usr/lib/.build-id/60
/usr/lib/.build-id/60/b105c9f466b2cac797d8de1a815091899134eb
/usr/lib/.build-id/6b
/usr/lib/.build-id/6b/922606f809f4c9bf90bfaa5832c3c5acf80339
/usr/lib/.build-id/6e
/usr/lib/.build-id/6e/6eda5aeb939068afe61aa5bb25b3077352f4d0
/usr/lib/.build-id/75
/usr/lib/.build-id/75/bf1be5128ebc3d3ae9efc9cb60b99885a3d29e
/usr/lib/.build-id/77
/usr/lib/.build-id/77/e25a93fc6f234116fcd716472d87d7c0210309
/usr/lib/.build-id/b6
/usr/lib/.build-id/b6/2f615763854f0675bd283f34ca358500ec954a
/usr/lib/.build-id/be
/usr/lib/.build-id/be/dd1cefd7f87e04326537f6ad2b7063459bb64d
/usr/lib64/krb5
/usr/lib64/krb5/plugins
/usr/lib64/krb5/plugins/authdata
/usr/lib64/krb5/plugins/kdb
/usr/lib64/krb5/plugins/libkrb5
/usr/lib64/krb5/plugins/preauth
/usr/lib64/krb5/plugins/preauth/spake.so
/usr/lib64/krb5/plugins/tls
/usr/lib64/krb5/plugins/tls/k5tls.so
/usr/lib64/libgssapi_krb5.so.2
/usr/lib64/libgssapi_krb5.so.2.2
/usr/lib64/libgssrpc.so.4
/usr/lib64/libgssrpc.so.4.2
/usr/lib64/libk5crypto.so.3
/usr/lib64/libk5crypto.so.3.1
/usr/lib64/libkdb5.so.10
/usr/lib64/libkdb5.so.10.0
/usr/lib64/libkrad.so.0
/usr/lib64/libkrad.so.0.0
/usr/lib64/libkrb5.so.3
/usr/lib64/libkrb5.so.3.3
/usr/lib64/libkrb5support.so.0
/usr/lib64/libkrb5support.so.0.1
/usr/share/doc/krb5-libs
/usr/share/doc/krb5-libs/NOTICE
/usr/share/doc/krb5-libs/README
/usr/share/licenses/krb5-libs
/usr/share/licenses/krb5-libs/LICENSE
/usr/share/locale/de/LC_MESSAGES/mit-krb5.mo
/usr/share/locale/en_US/LC_MESSAGES/mit-krb5.mo
/usr/share/man/man5/.k5identity.5.gz
/usr/share/man/man5/.k5login.5.gz
/usr/share/man/man5/k5identity.5.gz
/usr/share/man/man5/k5login.5.gz
/usr/share/man/man5/krb5.conf.5.gz
/usr/share/man/man7/kerberos.7.gz
/var/kerberos
/var/kerberos/krb5
/var/kerberos/krb5/user
```
</details>

`rpm -ql krb5-libs` でインストールされたファイルの中に `/etc/krb5.conf` と `/etc/krb5.conf.d` が見つかりました。
設定ファイルのようなので確認しようとすると `/etc/krb5.conf.d/kcm_default_ccache` という怪しいファイルが見つかります。

```conf:/etc/krb5.conf.d/kcm_default_ccache
# This file should normally be installed by your distribution into a
# directory that is included from the Kerberos configuration file (/etc/krb5.conf)
# On Fedora/RHEL/CentOS, this is /etc/krb5.conf.d/
#
# To enable the KCM credential cache enable the KCM socket and the service:
#   systemctl enable sssd-secrets.socket sssd-kcm.socket
#   systemctl start sssd-kcm.socket
#
# To disable the KCM credential cache, comment out the following lines.

[libdefaults]
    default_ccache_name = KCM:
```

先頭のコメントを読むと、もう `sssd_kcm` との関わっているのは確定的と考えていいでしょう。
`sssd-kcm.socket` にも言及されています。

あとは、上記設定ファイルから Kerberos 認証の時に `sssd_kcm` がキャッシュ機構として使用されている仕組みを調べておきます。

`man sssd-kcm` から、次の記述が見つかります。

> In order to use KCM credential cache, it must be selected as the default credential type in krb5.conf(5), The credentials cache name must be only “KCM:” without any template expansions. For example:
>
> [libdefaults]
>     default_ccache_name = KCM:
> Next, make sure the Kerberos client libraries and the KCM server must agree on the UNIX socket path. By default, both use the same path /var/run/.heim_org.h5l.kcm-socket. To configure the Kerberos library, change its “kcm_socket” option which is described in the krb5.conf(5) manual page.

DeepL翻訳に翻訳してもらいます。

DeepL翻訳:

```
KCMクレデンシャルキャッシュを使用するためには、
krb5.conf(5)でデフォルトのクレデンシャルタイプとして選択されている必要があります。例えば

[libdefaults]
    default_ccache_name = KCM:

次に、Kerberosクライアント・ライブラリとKCMサーバーがUNIXソケット・パスについて合意している必要があることを確認する。
デフォルトでは、両方とも同じパス /var/run/.heim_org.h5l.kcm-socket を使用します。
Kerberosライブラリを設定するには、krb5.conf(5)のマニュアルページで説明されている「kcm_socket」オプションを変更します。
```

KCMクレデンシャルキャッシュを使うためには `default_ccache_name = KCM:` を指定すると書かれています。
この内容は `/etc/krb5.conf.d/kcm_default_ccache` に書かれていました。
KCMクレデンシャルキャッシュは `sssd_kcm` と思われますが、ここでは明言されていません。

とりあえず、KCMクレデンシャルキャッシュとの通信はUNIXソケットで行われる仕様となっており、デフォルトのソケットパスは `/var/run/.heim_org.h5l.kcm-socket` というのは確定しました。

Kerberos ライブラリの設定については `man krb5.conf` を読むように記載されていますが、krb5 のWEBサイト上で同じ内容が確認できたので、こちらにはクレデンシャルキャッシュについて記載されたページのリンクを張ります。

こちらも長い内容になるのでDeepL翻訳に翻訳してもらいます。

https://web.mit.edu/KERBEROS/krb5-1.18/doc/basic/ccache_def.html

> 4. KCM caches work by contacting a daemon process called kcm to perform cache operations. If the cache name is just KCM:, the default cache as determined by the KCM daemon will be used. Newly created caches must generally be named KCM:uid:name, where uid is the effective user ID of the running process.
>
> KCM client support is new in release 1.13. A KCM daemon has not yet been implemented in MIT krb5, but the client will interoperate with the KCM daemon implemented by Heimdal. macOS 10.7 and higher provides a KCM daemon as part of the operating system, and the KCM cache type is used as the default cache on that platform in a default build.

DeepL翻訳:

```
4. KCMキャッシュは、kcmというデーモンプロセスに連絡してキャッシュ処理を行うことで動作します。
キャッシュ名が単に KCM: である場合、KCM デーモンによって決定されるデフォルトのキャッシュが使用されます。
新規に作成されるキャッシュは、一般に KCM:uid:name という名前でなければならず、
uid は実行プロセスの有効ユーザー ID である。

KCM クライアントのサポートは、リリース 1.13 で新たに追加されました。
KCM デーモンは MIT krb5 にはまだ実装されていませんが、
このクライアントは Heimdal が実装した KCM デーモンと相互運用できます。
macOS 10.7 以降では KCM デーモンがオペレーティングシステムの一部として提供され、
KCM キャッシュタイプはそのプラットフォームのデフォルトビルドでキャッシュとして使用されます。
```

https://web.mit.edu/kerberos/krb5-1.18/doc/admin/conf_files/krb5_conf.html

> kcm_socket
>
> Determines the path to the Unix domain socket used to access the KCM daemon for the KCM credential cache type. If the value is -, Unix domain sockets will not be used to contact the KCM daemon. The default value is /var/run/.heim_org.h5l.kcm-socket.

DeepL翻訳:

```
KCMクレデンシャルキャッシュタイプのKCMデーモンへのアクセスに使用されるUnixドメインソケットへのパスを決定します。
値が-である場合、UnixドメインソケットはKCMデーモンへの連絡に使用されません。
デフォルト値は、/var/run/.heim_org.h5l.kcm-socketです。
```

上記マニュアルには Mac OS についても記載がありますね、もしかしたら Mac OS でも似たような事象が起こるかもしれませんが、私は確認していないのでここでは割愛します。

krb5 のマニュアルでも `default_ccache_name = KCM` が指定されるとkcmというデーモンプロセスと通信をすると書かれています。
`sssd_kcm` と明言はされていないのでUNIXソケットの方から kcmデーモン = `sssd_kcm` を確認します。

サーバー上の krb5 設定ファイルの中には `kcm_socket` によるソケットのパス指定が見つからなかったので、デフォルトの  `/var/run/.heim_org.h5l.kcm-socket` が使用されているようです。

`sssd-kcm.socket` が `/var/run/.heim_org.h5l.kcm-socket` を生成しているのか確認します。

```shell
$ systemctl status sssd-kcm.socket
● sssd-kcm.socket - SSSD Kerberos Cache Manager responder socket
   Loaded: loaded (/usr/lib/systemd/system/sssd-kcm.socket; disabled; vendor preset: enabled)
   Active: inactive (dead)
     Docs: man:sssd-kcm(8)
   Listen: /var/run/.heim_org.h5l.kcm-socket (Stream)
```

```systemd:/usr/lib/systemd/system/sssd-kcm.socket
[Unit]
Description=SSSD Kerberos Cache Manager responder socket
Documentation=man:sssd-kcm(8)


[Socket]
ListenStream=/var/run/.heim_org.h5l.kcm-socket

[Install]
WantedBy=sockets.target
```

Systemd のユニットファイルから、 `sssd_kcm.socket` が Kerberos との通信で使用する `/var/run/.heim_org.h5l.kcm-socket` を生成しているところまで確認できました。

ここまで確認が取れれば PostgreSQL クライアントがサーバーとのTCPソケットで接続するときに GSSAPI (Kerberos) を通じて `sssd_kcm` が呼び出しされていたと判断していいでしょう。

# 考察

恐らく、`sssd_kcm` は同時に1つのキャッシュ操作しか受け付けないような作りになっているため、単一障害点になっていたのでしょう。

`sssd_kcm` の処理が完了しないと、各プロセスは処理を継続できないので1つのCPUだけ使用率が上昇していたのでしょう。
データベース接続でTCPタイムアウトが発生したのも、ソケット接続の確立後にデータベースのコネクション確立フローに時間がかかり過ぎたのが原因と考えることが出来ます。

# 対策

`sssd_kcm` が不具合のあるバージョンのまま運用されていたことに加え、PostgreSQL から意図せずに呼び出しされていたことが原因で障害が発生していました。

`sssd_kcm` は RHEL 8 系のディストリビューションであれば Minimal 構成でもインストールされるようなので、新規にサーバーを構築する場合は必ずアップデートをするべきでしょう。

既に運用しているサーバーなどの場合は `sssd_kcm` を無効化する事で不具合を回避できると思います。

Kerberos 認証を使用していたりして `sssd_kcm` を無効化する事も難しいという環境の場合は、
`sssd_kcm` を不用意に呼び出ししないようにするしかありません。

PostgreSQL であれば接続パラメーターに `gssencmode=disable` を指定すれば GSSAPI が呼び出しされなくなります。
ただし、PostgreSQL以外にも GSSAPI などを経由して Kerberos 認証ライブラリを呼び出ししてしまっているかもしれないので調査範囲はかなり広くなってしまうと思います。