固有の権限を多数含むライブラリに対するセキュリティクロールの影響について

こんにちは。
マイクロソフト SharePoint サポートチームの荒川です。

以前にセキュリティクロールの動作について解説しましたが、今回は SharePoint へのアクセス権の付け方がセキュリティクロール時のネットワーク負荷にどのように影響するかについて解説します。

※追記: この問題は SharePoint 2013 の 2015 年 5 月の CU で修正されています。本修正により、GetContentEx で取得されるデータが限定され、この記事に書いているネットワーク負荷が極端に増大する問題は発生しなくなります。

 

テストシナリオ

今回は SharePoint Server 2013 で検証を行っていますが、SharePoint Server 2010 においても基本的には同様の動作になります。
また、今回クロールサーバーと Web フロントエンドサーバーは分離しているものとします。

テスト用のドキュメントライブラリ (Doclib01) には以下のように 5005 個のフォルダーが格納されています。

Site└─Doclib01 ├─Folder1 │ └─Folder1000 │ └─Folder1001 └─Folder1002 └─Folder1003 : └─Folder1999 //合計 1000 個のサブフォルダー ├─Folder2 │ └─Folder2000 … Folder2999 //以下、上記と同様に 1000 個のサブフォルダー ├─Folder3 │ └─Folder3000 … Folder3999 ├─Folder4 │ └─Folder4000 … Folder4999 └─Folder5 └─Folder5000 … Folder5999

各フォルダはすべて親からの権限の継承を解除して固有の権限を付与しています。
さらに、各フォルダの固有の権限の中にはそれぞれ Active Directory のユーザーアカウントを 50 個、直接的に権限を付与しています。

今回はこの状況で一旦フルクロールを行った後、ドキュメントライブラリ Doclib01 にユーザー権限を 1 つ追加して、増分クロールを行いました。

 

上記シナリオにおけるセキュリティクロール時のネットワーク負荷測定結果

上記のシナリオで増分クロールを実行し、セキュリティクロールが行われている最中のクロールサーバーにおけるネットワーク負荷を測定しました。

 

パフォーマンスログの値からは、セキュリティクロールが実行されている間 (約24分間) の平均受信バイト数が約 50 MB と、大きな値になっていることがわかります。

 

なぜこのような状況が発生するのか?

セキュリティクロール時には、セキュリティ変更が行われたオブジェクトだけでなく、そのオブジェクトに含まれるすべての子オブジェクトも対象としてセキュリティ情報の更新を行います。例えば、以下のようなライブラリのフォルダ構成において FolderA のアクセス権を変更した場合、FolderA だけでなく、FolderA1 や FolderA2、ItemA もセキュリティクロールの対象になります。このとき、対象となる子オブジェクトが親から権限を継承しているかどうかは考慮されません。

Doclib01└─RootFolder ├─FolderA <== セキュリティクロール対象 │ └─FolderA1 <== セキュリティクロール対象 │ └─FolderA2 <== セキュリティクロール対象 │ └─ItemA <== セキュリティクロール対象 ├─FolderB └─FolderC

今回のケースでは、ルートに位置づけられるドキュメントライブラリの権限を変更しているため、ライブラリに含まれる 5005 個のフォルダーすべてに対してセキュリティクロールが実行されます。
また、セキュリティクロールの際には、クローラーは更新されるセキュリティ情報を取得する必要があります。このため、クローラーは Web フロントエンドサーバーで提供されている GetContent API を使用して対象オブジェクトのセキュリティ情報を取得します。

クローラーによる GetContent API を介したセキュリティ情報の取得処理は、固有の権限を持つすべての子オブジェクトに対して実行されます。
先述のとおり、親から権限を継承したオブジェクトに対してもセキュリティクロールは実行されますが、これらのオブジェクトの権限情報は既に親から取得されたものを使いまわすため、GetContent API は使用されません。
今回は、5005 個のすべてのオブジェクトに固有の権限が付与されているため、GetContent API は 5005 個すべてのオブジェクトごとに実行されることになります。

GetContent API によりアイテムまたはフォルダーの情報を取得すると、その返り値には対象オブジェクトのセキュリティスコープ情報とともに、対象オブジェクトが所属するリストまたはライブラリが保有するすべてのセキュリティスコープ情報が含まれます。 これらの情報は Web フロントエンドサーバーから、ネットワークを介してクローラーに送信されます。
今回のシナリオのように多数のユーザーを含む固有権限が付与されたオブジェクトの数が多い環境では、GetContent API の応答に含まれるデータ量が非常に多くなるため、このデータ量がネットワーク帯域を消費する原因となります。

 

詳細な解説

上記の動作について詳しく解説していきます。

先のシナリオでは例示するデータ量が多くなるため、解説用として以下の単純な構成で作成したライブラリを例に解説を進めます。

doclib02 (固有の権限を付与)├─Folder1 (親から権限を継承)├─Folder2 (親から権限を継承)├─Folder3 (固有の権限を付与)├─Folder4 (固有の権限を付与)└─Folder5 (固有の権限を付与)

doclib02 および Folder3 ~ Folder5 の権限設定では、user001 から user005 の 5 つのユーザーに対して編集権限を付与します。
この状態で、各オブジェクトのセキュリティスコープ ID を参照すると以下のようになります。

<<実行コマンド>>

$web = Get-SPWeb("https://sps216sps1")$list = $web.Lists["doclib02"]$result = $list.Title + " : " + $list.RoleAssignments.Idforeach($folder in $list.Folders){ $result = $result + "`r`n" + " +--- " + $folder.Name + " : " + $folder.RoleAssignments.Id}Write-Host $result

<<出力結果>>

doclib02 : 7ae8f31e-8002-43fb-b948-2114cd9f4dc4 +--- Folder1 : 7ae8f31e-8002-43fb-b948-2114cd9f4dc4 +--- Folder2 : 7ae8f31e-8002-43fb-b948-2114cd9f4dc4 +--- Folder3 : bdfccff0-da71-4e96-9901-a90950e5552f +--- Folder4 : 20bc53c5-f5dd-4f41-ad2a-a30291175c28 +--- Folder5 : 739fd925-aa5d-4483-ad5f-0851de2090b2

上記で確認できるように、固有の権限を付与されたオブジェクトにはユニークなスコープ ID が付与されています。

セキュリティスコープの情報は、Xml フォーマットで格納されています。
試しに、Folder5 のセキュリティスコープの情報を参照してみましょう。

<<実行コマンド>>

$folder = $web.GetFolder("doclib02/Folder1")$folder.Item.RoleAssignments.Xml

<<出力結果>>

<permissions> <permission memberid="8" mask="1856436902639" /> <permission memberid="9" mask="1856436902639" /> <permission memberid="10" mask="1856436902639" /> <permission memberid="11" mask="1856436902639" /> <permission memberid="12" mask="1856436902639" /></permissions>

memberid が内部ユーザーの ID を示しており、mask がアクセス許可レベル (ロール) の情報を示しています。
実際には各ユニークスコープ ID ごとに、上記のような情報を保持しています。

次に、クロール時の動作について解説します。
上記の解説用ライブラリ doclib02 を一旦フルクロールをした後、ライブラリに対して新しいユーザー権限を追加して、増分クロールを行ってみます。
これにより親から権限を継承しているかどうかに関わらず、doclib02 ライブラリ配下のすべてのオブジェクトに対してセキュリティクロールが実行されることになります。

このとき、Web フロントエンドサーバーの診断ログには以下の例のように、クローラーから GetContent API が実行されたことを示すエントリーが出力されます。
※診断ログの出力レベルは詳細になっています。

w3wp.exe (0x0828) 0x13E8 SharePoint Foundation General ace47 VerboseEx SiteData.GetContentEx(2, <GetContent><ObjectType>6</ObjectType><ObjectId>{465757c8-ffc9-4b33-9c97-f27bb8ca8551}</ObjectId><FolderUrl /><ItemId>5</ItemId><RetrieveChildItems>False</RetrieveChildItems><SecurityOnly>False</SecurityOnly><LastItemIdOnPage /><AllowRichText>False</AllowRichText><RequestLoad>100</RequestLoad><RemoveInvalidXmlChars>True</RemoveInvalidXmlChars></GetContent>)

 

実際に GetContent リクエストに応答にどのようなデータが含まれるか確認してみましょう。

<<実行コマンド>>

$web = Get-SPWeb("https://sps216sps1")$folder = $web.GetFolder("doclib02/Folder5")$objectType = 6$objectId = $folder.ParentListIdif($folder.ParentFolder.Url -ne $folder.Item.ParentList.RootFolder.Url){ $folderUrl = $folder.ParentFolder.Url.Replace($folder.Item.ParentList.RootFolder.Url + "/","")}else{ $folderUrl = $null}$itemId = $folder.Item.RecurrenceID$retrieveChildItems = $false$securityOnly = $false$lastItemIdOnPage = $null$svc = New-WebServiceProxy -Uri 'https://sps216sps1/_vti_bin/sitedata.asmx?wsdl' -UseDefaultCredential$result = $svc.GetContent($objectType,$objectId,$folderUrl,$itemId,$retrieveChildItems,$securityOnly,[ref]$lastItemIdOnPage)$result > C:\result.txt

出力された result.txt をメモ帳で開いて確認すると、以下のようなデータが返されていることがわかります。
今回はセキュリティスコープに関する情報以外を省略していますが、実際にはセキュリティ情報のほかにコンテンツのメタデータ等の情報も含まれています。

<<出力結果>>

<Item> <Metadata> <scope id="{739fd925-aa5d-4483-ad5f-0851de2090b2}"> <permissions> <permission memberid="8" mask="1856436902639" /> <permission memberid="9" mask="1856436902639" /> <permission memberid="10" mask="1856436902639" /> <permission memberid="11" mask="1856436902639" /> <permission memberid="12" mask="1856436902639" /> </permissions> </scope> </Metadata>… <scopes> <scope id='{7ae8f31e-8002-43fb-b948-2114cd9f4dc4}' > <permission memberid='8' mask='1856436902639' /> <permission memberid='9' mask='1856436902639' /> <permission memberid='10' mask='1856436902639' /> <permission memberid='11' mask='1856436902639' /> <permission memberid='12' mask='1856436902639' /> <permission memberid='13' mask='1856436902639' /> </scope> <scope id='{bdfccff0-da71-4e96-9901-a90950e5552f}' > <permission memberid='8' mask='1856436902639' /> <permission memberid='9' mask='1856436902639' /> <permission memberid='10' mask='1856436902639' /> <permission memberid='11' mask='1856436902639' /> <permission memberid='12' mask='1856436902639' /> </scope> <scope id='{20bc53c5-f5dd-4f41-ad2a-a30291175c28}' > <permission memberid='8' mask='1856436902639' /> <permission memberid='9' mask='1856436902639' /> <permission memberid='10' mask='1856436902639' /> <permission memberid='11' mask='1856436902639' /> <permission memberid='12' mask='1856436902639' /> </scope> <scope id='{739fd925-aa5d-4483-ad5f-0851de2090b2}' > <permission memberid='8' mask='1856436902639' /> <permission memberid='9' mask='1856436902639' /> <permission memberid='10' mask='1856436902639' /> <permission memberid='11' mask='1856436902639' /> <permission memberid='12' mask='1856436902639' /> </scope> </scopes>…</Item>

上記のように、セキュリティクロールで使用される GetContent API の応答には、対象のオブジェクトだけでなく、そのオブジェクトが所属するリストまたはライブラリに含まれるすべてのユニークスコープ ID に関する情報も含まれていることがわかります。

アイテムまたはフォルダに対して GetContent を実行した際に、リストやライブラリのセキュリティスコープ情報が含まれる動作は、GetContent の想定された動作になります。
GetContent の詳細な仕様については以下で公開されています。

3.1.4.5 GetContent https://msdn.microsoft.com/en-us/library/dd929205.aspx --- 一部抜粋 ------------------------------- ・If the value of the securityOnly input parameter is false, the protocol server MUST return the following: ・Permission information about the list (1) that contains the list item. ・All the of metadata information about the list item. This information MUST include the following attributes: ows_Modified, ows_ServerRedirected, ows_UniqueId, ows_scopeID, ows_FileRef, ows_ID and ows_FSObjType. --------------------------------------------

また、GetContent メソッドの使い方については以下の資料が参考になります。

SiteData.GetContent method https://msdn.microsoft.com/en-us/library/office/websvcsitedata.sitedata.getcontent.aspx

ここまでの解説を踏まえ、今回のサンプルシナリオの例に戻って解説します。
今回のテスト用のドキュメントライブラリ (Doclib01) には以下のように 5005 個のフォルダーが格納されていました。

Site└─Doclib01 ├─Folder1 │ └─Folder1000 … Folder1999 ├─Folder2 │ └─Folder2000 … Folder2999 ├─Folder3 │ └─Folder3000 … Folder3999 ├─Folder4 │ └─Folder4000 … Folder4999 └─Folder5 └─Folder5000 … Folder5999

すべてのフォルダは権限の継承が切られているため、ユニークなスコープ ID の数はライブラリ全体で 5006 個 (ライブラリ自体のスコープ ID を含む) になります。
さらに、各スコープには、ユーザーアカウントが 50 個権限登録されているため、ユーザーアカウントのエントリー (permission memberid... から始まるエントリー) の数は、以下のように求めることができます。

<scope> 要素に含まれるエントリー数 50 <scopes> 要素に含まれるエントリー数 5006 × 50 = 250350

ユーザーアカウントのエントリー (permission memberid... から始まるエントリー) の数が 250350 になりますので、テキストデータに換算してもかなりの大きさであることが推測できます。

試しに、一つのフォルダ Folder5999 に対して直接クローラーが送信するものと同じように GetContent API を実行して、返されるデータのサイズを計測してみましょう。

<<実行コマンド>>

$web = Get-SPWeb("https://sps216sps1")$folder = $web.GetFolder("doclib01/Folder5/Folder5999")$objectType = 6$objectId = $folder.ParentListIdif($folder.ParentFolder.Url -ne $folder.Item.ParentList.RootFolder.Url){ $folderUrl = $folder.ParentFolder.Url.Replace($folder.Item.ParentList.RootFolder.Url + "/","")}else{ $folderUrl = $null}$itemId = $folder.Item.RecurrenceID$retrieveChildItems = $false$securityOnly = $false$lastItemIdOnPage = $null$svc = New-WebServiceProxy -Uri 'https://sps216sps1/_vti_bin/sitedata.asmx?wsdl' -UseDefaultCredential$result = $svc.GetContent($objectType,$objectId,$folderUrl,$itemId,$retrieveChildItems,$securityOnly,[ref]$lastItemIdOnPage)$result > C:\result.txt$size = [System.Text.Encoding]::Unicode.GetByteCount($result) / 1MBWrite-Host Size: $size MB

<<出力結果>>

Size: 24.4477310180664 MB

約 24.4 MB のデータが返されていることが確認できます。
さらに、メモ帳で出力されたテキストを開くと、セキュリティスコープに関する情報が大量に含まれていることがわかります。

このように、一つのリストまたはライブラリの中に権限の継承が切断されたオブジェクトが大量に存在した場合、セキュリティクロールの際に大量のデータを受信することになるため、ネットワーク帯域を消費する原因になります。

※なお、今回のシナリオでは増分クロールに伴うセキュリティクロールに注目していますが、継続的クロールにおいても増分クロールと同様にセキュリティクロールが発生します。継続的クロールを使用する場合は通常の増分クロールよりもさらに大きな負荷が予想されますので、後述の資料「SharePoint Server 2013 でクロールするためのベスト プラクティス」に記載があるように、クロール専用の Web フロントエンドサーバーを設けることをご検討ください。

権限が少ない時と比較してみる

最後に、先述のシナリオ (5005 個のサブフォルダーが格納されたシナリオ) と同じフォルダ構成で、Doclib01 にだけ固有の権限 (ユーザーがアカウント 50 個) が付与されており、子オブジェクトはすべて親からの権限を継承しているケースでのセキュリティクロール時のパフォーマンスログ (15 秒間隔で取得) を比較用に採取してみます。

先のケースでは 24 分かかっていた増分クロールがおよそ 3 分半で完了し、平均ネットワーク転送量は 500 KB (区間大受信量 約 1.7 MB) にまで下がりました。

今回は既定のチームサイトテンプレートに対して検証用データのみをアップロードした環境での比較になりますので、実際の運用時と単純に比較することはできませんが、固有の権限を減らすことにより大幅なパフォーマンス効果が得られることがわかります。

 

まとめ

今回の投稿では以下の内容について解説しました。

・一つのリストまたはライブラリの中に権限の継承が切断されたオブジェクトが大量に存在した場合、セキュリティクロール時に大量のデータを受信することになるため、ネットワーク帯域を消費する原因になります。

このような問題を避けるために、以下のガイドラインに従っていただくことをお勧めします。

・1 つのリストで管理される固有の権限を持つオブジェクトの数が可能な限り少なくなるように設計する
・セキュリティスコープに大量のユーザーが含まれないように、アクセス許可に対して個々のユーザーではなく Active Directory グループを使用する

また、運用上セキュリティスコープの分割が避けられない場合は、クロール専用の Web フロントエンドサーバーを用意してユーザーが使用するネットワークと分離するなどの工夫をすることでユーザーへの影響を少なくできます。

 

関連資料

以下の資料では、クロール専用の Web フロントエンドサーバーを使用する方法について解説しています。
SharePoint Server 2010 の資料となりますが、SharePoint Server 2013 においても同様にご参考いただけます。

クロールの負荷を管理する (SharePoint Server 2010) https://technet.microsoft.com/ja-jp/library/dd335962(v=office.14).aspx

以下の資料では、クロールに関するベストプラクティスについて解説されています。
今回触れた内容以外でも、役に立つ情報が集約されているため、ご一読いただくことをお勧めします。

SharePoint Server 2013 でクロールするためのベスト プラクティス https://technet.microsoft.com/ja-jp/library/dn535606.aspx --- 一部抜粋 ---継続的クロールは、クローラーおよびクロール ターゲットで負荷を高めます。このリソース消費量の増加に応じて計画およびスケール アウトしていることを確認してください。大きなコンテンツ ソースで継続的クロールを有効にする場合、それぞれに対して 1 つ以上のフロントエンド サーバーをクロールの専用ターゲットとして構成することをお勧めします。詳細については、「クロールの負荷を管理する (SharePoint Server 2010)」を参照してください。 アクセス許可に対して個々のユーザーではなく Active Directory グループを使用するサイト上でさまざまなアクティビティを実行するユーザーまたはグループの機能は、割り当てられるアクセス許可レベルによって決まります。サイトのアクセス許可に関してユーザーを個別に追加または削除する場合、または SharePoint グループを使用してサイトのアクセス許可を指定し、グループのメンバーシップを変更する場合、クローラーは、変更を反映するために、検索インデックスで影響を受けるすべてのアイテムを更新する "セキュリティのみのクロール" を実行する必要があります。同様に、異なるユーザーまたは SharePoint グループを使用した Web アプリケーション ポリシーの追加または更新は、そのポリシーの対象となるすべてのコンテンツのクロールをトリガーします。これはクロールの負荷を高め、検索結果の最新状態の程度が低下する可能性があります。 そのため、サイトのアクセス許可を指定するために、Active Directory ドメイン サービス (AD DS) グループを使用することをお勧めします。これはクローラーに検索インデックスの影響を受けるアイテムを更新することを要求しないためです。 ----------------