Brahms: 動的ネットワークでのランダムなノードサンプリング (2008)
Brahms: Byzantine Resilient Random Membership Sampling
Brahms [1] はノードが自由に参加と離脱を行うことのできる動的なネットワークで任意のノードをランダムにサンプリング (抽出) することのできる分散アルゴリズムです。
全体のスケッチとしては、Gossipping モジュールがネットワークに流通しているノード ID を収集し、そのノード ID で継続的に Sampler ベクトルを更新していると、Sampler ベクトルの状態がだんだん「ネットワークからランダムに選んだノード ID の集合」と同じものになってゆく、というものです。もしノードの参加や離脱のない安定したネットワークでれば、すべてのノードの ID がネットワークに浸透し切ったときに、すべてのノードの Sampler ベクトルはそれぞれネットワークからランダムサンプリングされたノード ID の集合と同じものになります (ノードごとにサンプル集合は異なります)。
P2P や大規模ネットワークのように他のすべてのノードを認識したりすべてのノードと接続するのが困難な環境では、代わりにネットワークの一部となるノードを選ぶ必要があります。このようなときに Brahm で偏りなくノードをサンプリングして接続先にしたり、処理を依頼したり、(Tor のような) トンネルにしたり、統計情報を収集することができます。さらに Brahms はネットワークにビザンチン故障ノード (悪意的に動作するノード) が含まれていても機能するという特徴を持っています。
システムモデル
Brahms は次のようなシステムを想定しています。
- すべてのノードは任意のタイミングでネットワークに参加/離脱する。
- ネットワーク上のすべてのノードはユニークな ID を持っている。
- 受信したメッセージの送信者のノード ID が正確に分かる。
- ただし認証 (身元証明のための事前の公開鍵の交換や証明書の配付) は必要ない。
- 中間者はピア間で交換されるメッセージを改ざんできない。
サンプリングアルゴリズム
ノード ID は Brams の Gossip プロトコルによってネットワーク拡散されます。ノードは観測したノード ID で Sampler ベクトルを継続的に更新してノード ID のサンプルを維持します (ネットワークの任意のスナップショットに対するサンプリングではない点に注意)。ネットワークはノードの参加と離脱によって動的に変化するため Brahms が維持しているサンプルも時間と共に変化します。
min-wise independent permutations
Brahms のサンプリングは「ノード ID に基づいて一様にランダムな値を算出し、いままでに観測した中で最も小さな値を持つ ID を現在のサンプル値
このサンプリングを Python のコードで表現すると Sampler の処理は次のようになります (後述で擬似コードも示しています)。ネットワークのすべてのノード ID に対して next()
を呼び出した後、q
はその中からランダムに選んだ 1 つを保持していることが分かると思います。
import hashlib
import secrets
class Sampler:
def __init__(self):
self.q = None
self.salt = secrets.token_bytes(16)
def h(self, id):
return hashlib.sha256(id + self.salt).digest()
def next(self, id):
if self.q is None or self.h(id) < self.h(self.q):
self.q = id
def sample(self):
return self.q
※ID の乱数化関数は「入力に対する出力(ハッシュ値)の分布が一様となることが保証されているハッシュ関数」のことです。経験的に SHA-256 や BLEAK2 のようなハッシュ関数がそのような動作になることを期待できますが、公式にそれがどの程度の品質なのかは分かりません。
乱数化関数
乱数化関数
salt
のようなハイパーパラメータを持たせたハッシュ関数と見なすことができます。
これは、ある数学的特徴を持ったハッシュ関数の集合からランダムに一つ選んだハッシュ関数を使うことと同じで、他の Sampler との衝突を最小限にすることができます (このような手法は Universal Hashing と呼ばれます)。論文ではこのような衝突は無視できる程度として議論の対象外としていますが、サンプルが少ないと衝突も十分起きるでしょう。
死活監視
各 Sampler が最小値として保持している
ゴシッピングアルゴリズム
一般に Gossipping による情報伝達には push と pull が併用されます。push は自身の持つ情報を隣接するノードに拡散する動作です。ただし push だけでは中継するノードの故障によってネットワークの末端まで情報が到達しない可能性があるため、各ノードは定期的に任意のノードに pull 要求を出して自身の状態が最新かを確認します。
Brahms は Sampler ベクトル
Brahms の Gossipping 処理の流れは次のようになります。
-
に含まれているノード ID からランダムに\mathcal{V} 個を選択し\alpha m push_request
を送信する。 -
に含まれているノード ID からランダムに\mathcal{V} 個を選択し\beta m pull_request
を送信する。 - 応答待ち。
- 受信した
push_request
の送信者ノード ID の集合を とする。\mathcal{V}_{\rm push} - 受信した
pull_request
の送信者ノード ID に対して を設定した\mathcal{V} pull_reply
を送信する。 - 受信した
pull_reply
に含まれているノード ID 集合の和集合を とする (\mathcal{V}_{\rm pull} pull_request
を送っていない相手からのpull_reply
は無視する)。 -
に含まれるノード ID が\mathcal{V}_{\rm push} 個以下であり、\alpha m も\mathcal{V}_{\rm push} も空でなければ、次の集合の和集合を新しいビュー\mathcal{V}_{\rm pull} とする:\mathcal{V} -
からランダムに選んだ\mathcal{V}_{\rm push} 個のノード ID の集合。\alpha m -
からランダムに選んだ\mathcal{V}_{\rm pull} 個のノード ID の集合。\beta m -
からランダムに選んだ\mathcal{S} 個の Sampler が持つノード ID の集合。\gamma m
-
- 新たに観測した
と\mathcal{V}_{\rm push} で Sampler ベクトル\mathcal{V}_{\rm pull} を更新する。\mathcal{S}
Brahms のノードが持つ状態とデータの流れ、それを実行する Sampling, Gossipping の擬似コードをまとめると次の図のようになります。
攻撃耐性
ビザンチン故障ノードが存在せず、クラッシュ障害のような良性障害のみを想定したネットワークでは Brahms は正しく機能します。十分に時間が経過して各 Sampler ベクトル
問題は悪意的な攻撃意思を持ったビザンチン故障ノードの存在するケースです。Brahms に対する攻撃者の目標はノードの Sampler ベクトル
Push Flooding 攻撃
一般的な Gossipping での push flooding 攻撃は、大量の不正な情報を push で伝達してシステム全体をポイズニングします。こうなるとシステム上の正しい情報は pull でしか伝達されなくなり、システムで正しい情報の占める割合は指数関数的に減衰します。
一方、Brams の push_request
は自身のノード ID のみを伝達できる制限された送信です。大量の故障ノード ID を push で拡散することができないため、push flooding のような手法での攻撃は難しくなります。
Balanced 攻撃
Balanced 攻撃は、悪意的なノードがネットワークの広域に push_request
を送信して、すべてのノードのビューに悪意的なノードが含まれるように仕向けます。このような状況は、時間の経過と共にシステム全体に流通する故障 ID の比率がある固定値に収束します。そしてパラメータを調整すればこの固定値を 1 より小さくすることができます。したがって、このような Balanced 攻撃でネットワーク全体の効率を低下させることはできますが、ネットワークを分断したり特定のノードを孤立させることは… 調整次第です。
標的型攻撃
標的型攻撃はある 1 つの正常なノードに大量の攻撃ノードの push_request
を集中させそのビュー push_request
でビュー push_request
の数がその期待値
Brahms の push は自身のノード ID しか伝達できないため、単一の攻撃者がこのブロック動作を利用してすべての正しいノードのビュー更新を阻害することは困難です。
しかし、ネットワークに攻撃者が多数 (例えば push_request
を送信し続けることで、このブロック機能でビューの更新を停止させることができそうです。この場合、pull によるノード ID の収集で Sampler ベクトル
攻撃がない場合はほとんどのラウンドでこのブロック動作は機能せず正しくビュー
履歴のフィードバック
Brahms は標的型攻撃を緩和することはできても防ぐことはできません。標的型攻撃でビューに故障 ID を含むことに成功すると、その ID への pull によってさらに多くの故障 ID を増やすことになります。ビューがポイズニングされると正常なノードへの push が減少してシステム全体の更新が停滞します。
Brahms では、過去に観測され死活監視されている Sampler ベクトルに由来するノード ID を比率
個人的な所感
プライベートネットワークへの適用: ノード数が概ね
Sybil 攻撃耐性: 参加に制限のないオープンなネットワークでは 1 台の物理コンピュータを 1000 ノードに見せかけることは難しくない。Brahms はこのような Sybil 攻撃ですべての (あるいは一部の) 正常なノードのビュー更新を阻害させることができるように見える。追加の制約的な設計 (例えば承認参加制や push のためには proof-of-work が必要など) や "運用でカバー" できる何かがなければ P2P のような匿名のオープン環境への適用は難しいかなと想うところ。もちろん、良性の任意障害のみを想定するネットワークはその限りではない。
他の確率的データ構造と併用: 例えば Gossipping で収集したノード ID を Sampler ベクトルだけではなく HyperLogLog にも適用すると、ネットワークに接続しているノード数の概算値を得ることができます。HLL のような確率的データ構造は大規模データ処理でしばしば使われています。Gossipping をストリームデータ処理のデータソースとみなすと min-wise independent permutation や HyperLogLog のような確率的データ構造とは相性が良さそうな気がします。
-
BORTNIKOV, Edward, et al. Brahms: Byzantine resilient random membership sampling. In: Proceedings of the twenty-seventh ACM symposium on Principles of distributed computing. 2008. p. 145-154. ↩︎
-
BRODER, Andrei Z., et al. Min-wise independent permutations. In: Proceedings of the thirtieth annual ACM symposium on Theory of computing. 1998. p. 327-336. ↩︎
Discussion
素敵な記事をありがとうございます、このレイヤのアルゴリズムは普段目にすることがないので興味深く読みました。
1点、脚注のリンクがうまくいってないようです。
>
はなくてよさそうです[^1]:
のように、コロンを付けていただくとうまくリンクされると思います。修正を検討いただければと思います!
↓
Brahms [1]
BORTNIKOV, Edward, et al. Brahms: Byzantine resilient random membership sampling. In: Proceedings of the twenty-seventh ACM symposium on Principles of distributed computing. 2008. p. 145-154. ↩︎
ありがとうございます! 助かりました!