[SQL Connectivity] Case Study : 認証 DC ダウン時の SSPI エラーについて

横井 羽衣子(よこい ういこ)
SQL Developer Support Engineer

皆様ごきげんよう。今日は、接続できない系問題の一つをご紹介いたします。

【今日のお題】 複数台ドメイン コントローラ (DC) がある環境でサーバーメンテナンスをした。その時一台の DC だけが再起動処理に入ったタイミングで、ドメイン内の SQL Server に Windows 認証で接続を試みたら、なぜか "SSPI コンテキストを生成できません" エラーが返されて接続できなかった。 せっかく DC が複数台あるんだから、一台が使用不可の場合においても、自動的にその他の稼働中の DC に接続し、処理が行われるものじゃないの? なんで DC が複数台あってこんな現象が発生するの??

これは「SQL Server が最初にアクセスする認証 DC がダウンしているタイミングで認証処理をしにいくと、生存中の他の DC へ問い合わせを切り替える動作に時間がかかるのでタイムアウトしちゃう」という話となります。
DC がダウンしている状況下において SQL Server に対して接続を実施したり、ADSI などの DC で認証を行うアプリケーションなどで一般的に発生し得る現象です。例えば、SharePoint など、SQL Server をバックエンドに持つ環境でも影響を受けます。

はじめに : SSPIエラーとは

SQL Server に Windows 認証による接続を試行した際に発生する SSPI エラーは、一般的には、Active Directory の問題などにより正常に認証が行えないことが要因で SQL Server への接続が失敗したことを表します。

例えば SharePoint Server を使用する場合には、SharePoint 内の処理で SQL Server に接続するために Windows 認証を使用しますが、この場合既定で Kerberos 認証を使用します。その過程において、 DC への問い合わせし、認証処理が発生します。つまり、認証先 DC が、再起動などにより応答しない場合には、切り替わりに発生する時間帯には、SQL Server への接続時も認証が出来ない状況になるということです。 

SQL Server の認証先 DC は?

認証は最初に接続した DC と継続してやり取りを行います。認証先の DC がダウンしている場合は一定期間認証先の DC に対し問い合わせを行い、応答がない場合に別の DC を探しに行くという動作になります。ところが、SQL Server の接続処理は無限に待機するわけではなく、タイムアウト値(私これだけ待つことができます値)が存在しています。タイムアウト値を明示的に指定していない場合も、実は 15 秒という値が存在します。この 15 秒の間に必要な処理が終わらない場合、接続に失敗します。

上記お題のシナリオの場合、DC がダウンしている最中に接続失敗のエラーとして、 SSPI エラーが発生していたとのことですので、このダウンしていた DC が SQL Server からの認証先 DC であり、別の DC への切り替えまでに時間を要した結果、タイムアウト値の期間内に認証処理を正しく行えなかった結果、問題が発生していると判断することができます。

処理の流れ (1) : 正常時の接続処理

通常、SQL Server から DC への接続要求において OS レベルでのセッションが確立され、認証処理に入った場合以下のような処理の流れになります。

接続要求の処理の流れ image

   SQL Server [secur32.dll(1) -> LPC(2)] --> NetLogon(3) —> DC

SQL Server サービスと、NetLogon サービスは別のプロセスです。 つまり、直接 SQL Server サービスから DC に認証を要求するのではありません。 (1) SQL Server サービスは secur32.dll 内にある AcceptSecurityContext API を呼び出します。 (2) さらに Local Procedure Call (LPC) 経由で NetLogon サービス (lsass.exe) に渡されます。 (3) 次に NetLogon サービスは、Remote Procedure Call (RPC) 経由で DC に認証を求めます。

処理の流れ (2) : DC ダウン時の接続処理

一方、今回のように接続先の DC がダウンしている場合は以下のような処理の流れになります。

<キャッシュされた DC が応答しない場合の処理の流れ> (1) SQL Server から RPC でキャッシュされた DC に対して認証要求を実行します。 image (2) DC から応答がない場合、RPC のタイムアウト待ち状態になり、DC からの応答待ちになります。 (3) レスポンスがない場合再度リクエストを出します。  image (4) 応答待ち期間に応答がない場合、RPC_S_SERVER_UNAVAILABLE エラーが返り、DC との通信が終わります。 image (5) DsGetDcName 関数を DS_FORCE_REDISCOVERY フラグで呼び出し、他の DC を探索します。 image (6) 一番最初に応答した DC と接続確立します。 image

NetLogon サービスでは RPC プロトコルを利用してキャッシュ情報にある DC に接続しますが、最終的に 45 秒以内 に応答が得られない場合、SQL Server の動作して いるコンピュータ上の NetLogon サービスのプロセスである lsass.exe が DsGetDcName() API を使って DC を再検索します。
この "45 秒" という値は、残念ながらシステムに固定値として保持されており、変更することはできません。

参考情報
Windows でのドメイン コントローラの検索方法
https://support.microsoft.com/kb/247811/ja

DsGetDcName 関数
https://msdn.microsoft.com/en-us/library/ms675983(VS.85).aspx

対処方法

1. SQL Server に接続する元がプログラムの場合

以下のいずれかを実施することで対処可能です。

a. Connection Timeout キーワードを入れて、明示的に試行時間を延ばす
b. Retry 処理をする (接続処理からやりなおす)

なお、待機時間延長を実施してもすぐにレスポンスがあれば全体の処理遅延は起こりえません。あくまでも、レスポンスが来ない場合に効果を発揮するキーワードだからです。

image

なぜタイムアウト値の延長が有効なのか

前述 “処理の流れ (2) : DC ダウン時の接続処理” でご案内したように、つながっていた DC が応答してくれない場合、あきらめて別の素敵なDCを探しに行く動作まで 45 秒かかります。しかし、Connection timeout キーワードを明示的に指定しない場合は、タイムアウト値は既定で 15 秒となります。そのために問題が起こってしまうわけです。よって、タイムアウト値を 45 秒以上長くすれば、タイムアウトになるリスクを回避できるということになるのです。

例えば、タイムアウト値を 60 にしたとします。すると、接続先の DC の切り替え時間より長い間待つので、タイムアウトにならない可能性が高くなります。60 だとちょっとギリギリかもしれないので、90 くらいでもいいかもしれません。

image

さらに、接続に失敗して例外がスローされたタイミングですでに切り替えも終わったくらいの時間でもう一度接続しなおせば(リトライ)、対応可能な DC を探すところから対応しますので、接続できるようになります。

image

2. 接続元が SharePoint の場合

SharePoint としては、以下のコマンドを実行することで接続タイムアウトを変更することが可能です。

Database-connection-timeout : Stsadm プロパティ (Windows SharePoint Services)
https://technet.microsoft.com/ja-jp/library/cc287950(office.12).aspx

3. 接続元の接続文字列設定を操作できない場合

残念ながら、この上記の一連の動作における時間をを制御するのに有効なレジストリ等設定はありませんが、当該 DC がシャットダウンされることがあらかじめ分かっている場合は、当該 DC のシャットダウンの前のタイミングで、SQL Server の構成されたマシン上の DC に関するキャッシュ情報をクリアにし、他の稼動している DC とセキュア チャネルを明示的に張ることを対処策として実施することができます。これには SQL Server の構成されたマシン上で nltest.exe を使用し、以下のコマンドを実行することで対処可能です。
しかし、再起動のタイミングが予期できない場合は、対処が難しい状況になります。

nltest /SC_RESET:<ドメイン名>\<DC 名>

nltest はクライアント側のコマンドであり、ドメイン コントローラ側で実施する必要はありません。
また、AD に対し認証を実施する動作を行うプログラム(サービス含む)が動作してない環境であれば、nltest を実施頂く必要はありません。

参考情報
ドメイン セキュア チャネル ユーティリティ -- Nltest.exe
https://support.microsoft.com/kb/158148/ja

・・・・・・・・・

みなさんいかがでしたでしょうか。
SQL などプログラム自身が認証をしておらず、認証をする別のプロセスに任せているというような動きになっているというところは意外に思われた方もいらっしゃるのではないでしょうか。
OS では、共通化できる機能は共通化していますので、こんな感じで各処理分担して一つのプロセスが成り立っています。
これはすなわち、トラブルが起きたときにどこに目を付けるかというところに関係してくる重要な視点だと思います。
ご参考になれば幸いです。

それでは皆さんごきげんよう。