無印吉澤

Site Reliability Engineering(SRE)、ソフトウェア開発、クラウドコンピューティングなどについて、吉澤が調べたり試したことを書いていくブログです。

Building Secure and Reliable Systems 読書メモ - Chapter 21

f:id:muziyoshiz:20200430014145p:plain:w320

SRS 本の読書メモです。

Chapter 7 までは順番に読んでましたけど、途中を飛ばして、気になっていた最終章の Chapter 21 を先に読んでみました。

SRS 本について

SRS 本はこちらから無償でダウンロードできます。

前回までの読書メモは SRS Book タグ を参照のこと。

Chapter 21. Building a Culture of Security and Reliability

この章では、セキュリティと信頼性に関する健全な文化の側面について述べる。また、変化を起こすべきときに、良いプラクティスを選ぶことで、あなたがどのように組織文化に影響を与えることができるかについても触れる。そして、セキュリティと信頼性についての同意を得るために、組織を超えてリーダーとチームに対しての影響を与えるかについての洞察を提供する。

Google やその他の組織で有用だったいくつかのプラクティスを共有するが、同じ組織は2つと無いこと、これらのプラクティスをあなたの組織の文化に合わせて応用する必要があることに注意してほしい。また、Google もこれらを完璧に実践できているわけではなく、日々改善を進めていることを添えておく。

例えばこんなシナリオを想像しよう:あなたの組織には大きな取引(Big Deal)が近づいており、みな締切に追われている。しかし、今日あなたは攻撃者がシステムに進入した証拠を発見してしまう。あなたはすぐシステムをオフラインにすべきと気づいているが、もちろん顧客は怒り、大きな取引はリスクに晒されるだろう。おそらくあなたは、先月のセキュリティパッチを適用していなかったことを責められるだろう。こんな状況で、あなたの組織の文化は、どのような決定を促すだろう? 強いセキュリティ文化を持つ健全な組織なら、従業員がそのインシデントについてすぐ報告することを促すだろう。

この章では、セキュリティと信頼性の文化(a culture of security and reliability)を構築するためのパターンとアンチパターンの両方を紹介する。

Defining a Healthy Security and Reliability Culture

健全なシステム(healthy systems)のように、健全なチーム文化は明示的に設計され、実装され、そして維持されることができる。健全なシステムを構築するための設計の原則(design principles)があるように、健全な文化にもそのような設計の原則がある。

Culture of Security and Reliability by Default

Chapter 4 で議論したように、セキュリティと信頼性について考えることは、プロジェクトの最終盤に回されがちである。しかし、あとから技術的負債を支払おうとすると、長期的なベロシティは低下し、その仕組みには一貫性がなく、失敗を招くことになりかねない。

それはさながら、シートベルトやエアバッグが車に標準搭載されておらず、消費者があとから買わなければならない世界のようだ。これらは車の設計時から考慮されるべきだし、そのほうが最適な場所に設置されるだろう。このたとえ話は、システムにとって、最初から(デフォルトで)セキュアかつ信頼できること(secure and reliable by default)ことの必要性を示している。

最初からセキュリティと信頼性について考える健全な文化を持つ組織は、開発者に、それらをプロジェクトの初期段階で(例えば設計段階で)考えること、そして実装のイテレーションを繰り返すなかでも考え続けることを促す。そのように成熟した製品は、セキュリティと信頼性を維持しつづけることが自然にできるようになる。

Google の、最初からセキュリティと信頼性を考える文化(a culture of security and reliability by default)の発展についての知見を、本書の Chapter 12, 13, 19 で示した。

Culture of Review

強いレビュー文化が整っている環境では、誰もが変更を承認する際に、自分の役割について前もって考えることを促される。本書では、以下のようなピアレビューの方法を取り上げてきた。

  • 最小権限(least privilege)を保つための、アクセスや変更の認可に関する複数人でのレビュー(Multi-party authorization reviews)(Chapter 5)
  • コードの変更が適切かつ高品質(セキュリティと信頼性の考慮も含む)であることを保証するためのピアレビュー(Chapter 12)
  • 本番システムに適用される前の、設定変更のピアレビュー(Chapter 14)

このような文化を構築するためには、レビューの価値とそれをどのように実行するかについて、組織全体で幅広い理解が必要である。

レビューで期待されることを明らかにするために、変更レビューのプラクティスは文書化されるべきである。レビューで拒否されたときに、その理由を明確にできることは、感情的にならないためにも必要である。

レビューの文化は、そのレビュープロセスに全員が参加することを必要とする。その人がシニアロールであるとか、参加したくないとかいう理由で、レビューから逃れることを許すべきではない。

(コラム:Reducing Friction) 軽微な変更に対しては、より軽量なレビューを選択肢として提供できるようにすることを考えてみよう。Chapter 14 で述べたとおり、レビューはそれが必須である場合のみ有効に働く。ただし、どのような場合に軽量レビューを実施できるかを、ガイドラインで明確にする必要がある。

レビュアーが、意思決定するための十分なコンテキストを知らない場合は、レビューを断るか、他の人にレビューを回すという選択肢を保証すべきである。

自動チェックは、このようなコンテキストの構築を補助することができる。例として、Chapter 13 では、コードのサブミット前に、開発者とレビュアーに、セキュリティ問題を自動的に示すツール Tricorder を Google がどのように使っているかを示した。

Culture of Awareness

組織のメンバーが、自分たちのセキュリティと信頼性に対する責任に気づいていて、それを実践する方法を知っていたら、良い成果物を効率的に生み出すことができる。認知と教育の戦略は、強いセキュリティ文化を作るための鍵である。

Google では、従業員の教育のために、複数のアプローチを取っている。広範囲には、年1回の必須のトレーニングを全従業員に提供している。さらに、ここで伝えたメッセージを、役割ごとの特別なプログラムで強化している。Google での長年の実践により、有効とわかったいくつかの戦略を以下に示す。

  • Interactive talks
    • 参加型の講演。例えば、Google では重大なインシデントに関する主要な根本原因および緩和策について共有することで、私達がなぜこのようなトピックに集中しているかを従業員によく理解してもらうことができた
  • Games
    • 例えば、ゲームを通して XSS を見つける方法および悪用する方法を学べる XSS game
  • Reference documentation
    • Chapter 12 で、リファレンス文書の重要性を書いた。詳しい情報を口頭で正確に伝えることは難しい
  • Awareness campaigns
    • 開発者に、最近のセキュリティおよび信頼性に関する問題について知ってもらうことは難しい。この問題に対して、Google では1ページ形式の週刊のアドバイス "Testing on the Toilet" を発行している。これを全 Google オフィスのトイレに張り出している
  • Just-in-time notifications
    • なにか、良いプラクティスに反したことをした瞬間にタイミングよく知らせることで、従業員の理解を深めることができる。例えば、ソフトウェアを信頼できないリポジトリからアップグレードしようとしたときや、センシティブデータを未承認のクラウドストレージシステムにアップロードしようとしたときにポップアップを出す。Chapter 13 に、これに関連した話題を書いた

Culture of Yes

時とともに、組織にはリスクに対して保守的な文化が育ちやすい。特に、セキュリティ上の欠陥や信頼性の問題によって、収益を失ったり何らかの悪影響を被ったことがある場合はそうである。

そのような考え方は No の文化(a culture of no)、すなわちリスクのある変更や、ネガティブな結果になりうる変更を回避する文化に繋がりうる。健全な組織は、ある程度のリスクはあるがメリットのある選択を取る際に、"yes" と答えるための方法を持っている。

例えば、Chapter 8 の Google App Engine の例がある。Google App Engine は悪意のあるコードがその上で動く可能性があるサービスである。そのようなリスクはあるが、製品チームとセキュリティチームが連携することで、最終的にこのプロダクトをローンチできた。

リスクを許容するための他のアプローチには、エラーバジェットがある。

(コラム:Balancing Accountability and Risk Taking) コードレビューを行う人が、自分が最後の砦だ、と思うようになると、リスクのある変更は受け入れがたくなっていく。リスクを共用できるようにするためには、このプレッシャーを下げるための、例えば、最小権限や、回復性、テストといった要素のためのデザイン戦略が必要である。多層的なアプローチで、個人に対する重荷を軽くすることができる。例えばレビューでミスを犯しても他の自動的なチェックが働くなど。

Culture of Inevitably

完璧なシステムなどなく、どんなシステムもいつかは失敗する可能性がある。Googleでは、失敗はいつでも起こりうると仮定している。実世界のシステムは、100%セキュアかつ信頼できるとは決して言えないと私達は知っている。

Chapter 16 では、このような不可避の問題に対する準備の必要性について議論した。不可避の文化(culture of inevitability)を持つチームは、大規模な障害(disasters)に備えるために時間を割き、そのような場合に効率的に対応できる。

また、本書の Chapter 18、および SRE 本の Chapter 15 にある、批判のないポストモーテム(blameless postmortems)もその一例である。

Culture of Sustainability

システムの信頼性とセキュリティを長期的に維持するためには、その組織が、それらを改善するための努力を継続的に行う、つまり十分なリソース(スタッフと時間)を割き続けることを保証しなければならない。

この努力を維持するために、チームはリアクティブな仕事と、長期的に報われるプロアクティブな投資の間でバランスを取ることができなければならない。Chapter 17 の例では、効率的なチームは、ある1人に過大な責任を追わせないために、ハードワークを大勢の人の間で分かち合っていることを示した。

持続性の文化(a culture of sustainability)を持つ組織では、改善のための投資と同様に、運用業務(例えばインシデント対応)を扱うために必要な工数も計測する。ストレス、燃え尽き、士気(morale)を考慮する。

Google SRE の "follow the sun" シフトもその一例である(コラム:Sustainable Reliability and Security Culture at Google)。

持続性の文化を持つということは、例外的な状況では通常の業務量から一時的に逸脱することがありうるということを理解し、そのような逸脱を扱うための良いプロセスを持っているということも意味する。そのような例外的な状況が解決したあとで、引き続き持続性の文化を維持するために、以下のようなことを考慮するとよい。

  • 一時的な対応は、問題が解決したらもとに戻す。トイルが増えたまま戻さない、ということにならないようにする
  • 通常のプロセスを迂回する権限を持つ、専任のグループを持つ。ただし、ベストプラクティスを迂回したり覆した点を記録しておき、必ずあとで対処する
  • イベントが完了したら、ポストモーテムにて、今回の障害を導いた報酬システムを必ずレビューすること。開発を優先して信頼性やセキュリティを蔑ろにする文化では、このときに技術的負債を生む可能性がある

Changing Culture Through Good Practice

セキュリティと信頼性に関する新しい活動は、恐れを生じさせる。それは、開発速度を低下させるのではないか、新しいリスクを生じさせるのではないか、という恐れだ。しかし、著者らの意見としては、そのような摩擦を生じさせるというのは神話(myth)である。

もし、ある種の文化的な配慮を行ったうえで変更を実施すれば、それらの変化はすべての人の経験を向上させることができる、と私達は信じている。

この節では、変化を導入するための技術的な戦略について議論する。あなたは CEO やリーダーではないかもしれないが、すべての開発者、SRE、セキュリティ専門家はそれぞれの広さで影響を与えるための手段を持っている。

Align Project Goals and Participant Incentives

信頼を築くのは難しく、失うのは易しい。システムを設計、実装、維持する人々が、異なる役目を超えてコラボレーションするためには、みんなが共通の報酬システム(common reward system)を共有する必要がある。

技術的なレベルでは、プロジェクトの信頼性や安全性は、SLO のような測定可能なメトリクスや、脅威モデル(例:Chapter 2, 14)を通して評価できる。プロセスや人のレベルでは、信頼性や安全性への貢献がキャリアアップに繋がることを保証すべきである。理想的には、そのような評価基準が明文化されているべきである。

プロジェクトのゴールを組織の目的に揃える一方で、個人のインセンティブに揃えない場合、それは非協力的な組織文化に繋がりかねない。

Reduce Fear with Risk-Reduction Mechanisms

大きな変更は、組織の中で反対されることがある。そのような場合は、よいデプロイ上の選択を提示することで、信頼を得ることができる。Chapter 7 で触れた話題だが、組織文化への影響についてここで改めて触れる価値があるだろう。

  • Canaries and staged rollouts
    • 安全なリリースを繰り返すことで、次第に、組織はそのような気配りと勤勉さをすべての変更に期待するようになる。そして変更に対する信頼を構築し、恐れを減らすことができる
    • このようなリリース方法については SRE workbook の Chapter 16 で触れた。また本書の Chapter 19 では Chrome のリリースサイクルについて議論した
  • Dogfood
    • 自分たちが実施しようとしている変更を自分たち自身に適用し、それをユーザーに示すことで、その変更が安定性や生産性に与える影響をユーザーに保証することができる
    • 例えば、多要素認証のような新しいメカニズムを導入する際に、まず自分たちの業務に導入する
  • Trusted Testers
    • 社内でテスターを募集し、フィードバックをオープンに求める。そして、フィードバックを活用して、自分たちがフィードバックを聞いていることを知ってもらう
  • Opt in before mandatory
    • いきなり必須にするのではなく、移行期間を設ける。いつ有効化するかを選択できるようにする
  • Progressive stringency (漸進的な説得力、妥当性、厳密さ)
    • いきなり厳密なポリシーを適用するのではなく、徐々に適用できないかを考える
    • 例えば、最小権限を例に挙げると、最初は対象を開発者に制限したり、権限がないアクセスを拒否するのではなくエラーを出すだけに留めるなど

Make Safety Nets the Norm(セーフティネットを規範にする?)

信頼性およびセキュリティに対する改善が、あなたが今まで頼ってきたリソースを使えなくすることはよくある。例えば、いままで root 権限を使えていたものが使えなくなったら。緊急時の作業の妨げにならないか?といった恐れを抱くことは自然なことである。

breakglass procedures のようなセーフティネット(Chapter 5)を提供することで、変更に対する恐れを減らすことができる。セーフティネットは最後の手段であって、便利な代替手段とみなされないようにすべきである。また、システムを安全に保つための段階的なロールアウト手段が、場合によっては緊急リリースの邪魔になるかもしれない。変更を即座にプッシュするための迂回手段があれば、そのような懸念は避けられる。この種の状況については Chapter 14 で議論した。

Increase Productivity and Usability

セキュリティ及び信頼性の観点での組織的な変更が、生産性や使い勝手を落とすのではないか、という恐れが生じると、その導入を難しくする。そのため、導入戦略について注意深く考えることは重要である。私達は、以下のテクニックが摩擦を減らす助けになることを見てきた。

  • Build transparent functionality
    • Chapter 6, 12 にあるように、デフォルトの選択が良い状態なら、罰を与えるようなことをしなくても、開発者は正しいことをする
    • このアプローチではれば、開発者自身がセキュアかつ信頼できるシステムの恩恵を受けるだけでなく、これらの活動をシンプルかつ簡単にしようとしているあなたの意図を理解できる
  • Focus on usability
    • 今までの仕組みより、新しい仕組みのほうが使いやすければ、導入は簡単に進む
    • chapter 7 の二要素認証の例では、認証はセキュリティキーをタッチするだけで済むようになり、パスワードの定期変更も求められなくなった
  • Self-registration and self-resolution
    • Googleではインストールして良いソフトウェアの allow list と deny list を持っている。中央集中で管理していると、そこが許可のボトルネックになってしまう
    • Googleでは、これらのリストにないソフトをインストールしたいときには、Upvote というポータルを通して、必要な許可を迅速に得られるようにしている。また、大勢の希望があるときは自動的に許可する仕組みもある

Overcommunicate and Be Transparent

システム変更を主張する際に、コミュニケーション手段は結果に影響しうる。Chapter 7 および 19 で議論したように、よいコミュニケーションは変更への同意と信頼を得るための鍵である。

どのように変更が起こるかについての情報とわかりやすい知見を与えることで、人々の恐れを減らし、信頼を構築できる。私達は以下に示す戦略が成功するのを見てきた。

  • Document decisions
    • 変更を行うときは、なぜそれが起こっているのか、成功はどのように見えるのか、操作が状況を悪化させたらどのように変更をロールバックするのか、懸念がある場合は誰に話せばいいのかを明確に文書化する
    • それが従業員に直接影響する場合は特に、なぜあなたがその変更を行っているのかを明確に伝える
    • 注釈に、SRE が行う Production Excellence reviews についての記載あり
  • Create feedback channels
    • 懸念を持つ可能性がある人々が使えるフィードバックチャンネルを作り、双方向のコミュニケーションを作る
    • フィードバックフォームや、バグトラッキングシステムへのリンク、あるいは単なるメールアドレス
    • 関係者を巻き込むことで、恐れを減らすことができる
  • Use dashboards
    • あなたが人々に期待していることを明確に示し、その進捗に対するフィードバックを提供するためにダッシュボードを使う
  • Write frequent updates
    • 変更に長い時間がかかる(Google の場合は数年に渡ることも)場合は、進捗を頻繁に(例えば月1回)ステークホルダーに共有するための担当者をアサインする。これは、特にリーダーへの、信頼を得る役に立つ

Build Empathy

チームを超えた共感(Cross-team empathy)は、それがシステムの信頼性とセキュリティに関わるものであれば特に重要である。なぜなら、Chapter 20 で論じたように、信頼性とセキュリティに対する責任は組織全体で共有されるべきものだからである。

Chapter 19 では、チームを超えた共感を作るためのいくつかのテクニック、特に実装、デバッグ、コード修正に対する責任を共有する方法について述べた。

共感を作るための他のアプローチとしては Job shadowing(他の人の仕事を後ろで観察する)や job swapping(仕事を交換する)がある。これらはトップダウンでの組織的な変更を必要とせずに実施できる。これは数時間から、(マネジメント層の許可は必要だろうが)数ヶ月までの幅がありうる。他の人にあなたのチームの仕事を経験してもらうことで、あなたは組織のサイロを壊して、共通の理解を作ろうとしているというシグナルを送ることができる。

Google の SRE Security Exchange Program は、他の SRE やセキュリティエンジニアを1週間経験できる(shadow, 後ろで観察する)プログラムである。この交換プログラムの最後に、SRE は自分たちのチーム自身と、ホストチームの両方に対する改善提案をレポートに書く。このプログラムは非常に低い投資で、組織をまたいだ知識共有の面で大きな利益を生んでいる。他には、SRE 組織に6ヶ月参加してもらう Mission Control program もある。

最後に、感謝を伝える仕組みの構築(building in mechanisms to say thank you)ーー簡単なメールから、より詳細な形式のものまでーーは、人々がお互いに対して与えるポジティブな影響を強め、正しいインセンティブを与える。Google には、長年に渡るピアボーナスの文化がある。これの、現金のやり取りがないバージョンは Kudos と呼ばれるもので、Googler は全員に見えるフォーム上で感謝を伝えることができる。いくつかのオフィスでは、感謝のポストカードも試してきた。

Convincing Leadership(リーダーを説得する)

もしあなたが大きな組織で働いているなら、あなたが行いたいセキュリティや信頼性に関する変更に対して、支持を得る(原文では get buy-in = 買付を得る?)のは難しいことだろう。多くの組織は、自分たちの数少ないリソースを、利益を生むために使うように動機づけされているため、彼らの目に見えない部分を改善するための支持を得ることは難しい。

この節では、そのような変更に対して、リーダーからの支持を得るための戦略について見ていく。これらは私達が Google で用いたものに加え、他の場所で用いられているのを見たものも含む。

Understand the Decision-Making Process

ここでは、リーダー(leadership)という単語は、方針決定やリソース配分、利害関係の調整にまつわる決定を行うことができる人々を緩やかに指すものとする。

あなたがそれらの人々に影響を与えたいので、それが具体的に誰なのかを明らかにする必要がある。あなたが大企業で働いていたら、それは VP であったり、上位の管理職かもしれない。スタートアップや非営利団体のようなもっと小さな組織なら、それは CEO かもしれない。オープンソースプロジェクトなら、プロジェクトの創設者かトップコントリビューターだろう。

「誰がその変更に対する決定権を持っているのか?」という質問への答えは、解くのが難しい場合がある。それは組織上のリーダーではなく、テックリードのような場合もある。また、複数の人の場合もある。Chapter 7 で述べた Chrome の HTTPS 対応の例のように、コミュニティの場合もある。しかし、この質問の答えを避けて通ることはできない。

意思決定者を見つけたら、その人が直面している困難や要求について理解しようとすべきである。それらを理解することは、あなたの提案する変更がうまくはまる場所を理解するために重要である。

Build a Case for Change

すでに述べたように、変更への抵抗は、恐れや摩擦の認識に起因する。しかし、多くのケースでは、変更の理由を理解していないことにも起因する。

成功する case-building process に必要なステップを以下に示す。

※ ここで言う case はどういう意味? リーダーに問題として認識してもらう、といった意味?

  • Gather data
    • 変更が必要と思うに至った理由があるはず。それをデータで説明できること
  • Educate others
    • 例えば、Google の Red Team postmortems(Chapter 20)。SREが直面するリスクの種類について、リーダーを教育するためのもの。もともとは教育が目的に作られたものではないが、会社の全階層における認識を高めることができる
  • Align incentives
    • 他の面でもメリットが有ることを説明し、お互いにインセンティブがあるという状態にする
    • 例えば、DDoS 攻撃に強いフレームワークを導入することで、セキュリティ上の利点に加えて、より安定したウェブサイトは売上を高めるという効果もある。これは企業のリーダー層にとって強い根拠になる
  • Find allies(支持者を探す)
    • 支持者の存在は説得力を高めるし、あなたの論拠をピアレビューしてより強くしてくれる
  • Observe industry trends
    • あなたが適用しようとしている変更が、他の組織がすでに適用している変更であれば、その他組織の経験はあなたの組織のリーダー層を説得する際の助けになるかもしれない。その特定のトピックおよび業界トレンドに詳しいエキスパートを呼んで、あなたの組織のリーダー層に説明してもらうことさえ可能かもしれない。
  • Change the zeitgeist(時代精神を変える)
    • もしあなたが、あなたの問題について日々考えている人々の考え方を変えられるなら、そのあとで意思決定者を説得するのはより容易だろう。これは、あなたがその変更に対する広いコンセンサスを必要としているときに、特に有効である。Chapter 7 の HTTPS のケーススタディはこれにあたる

Pick Your Battles

組織が信頼性とセキュリティの課題に数多く直面している場合、継続的なアドバイスは、アドバイスされることへの疲れや、追加の変更への抵抗感を生んでしまう。あなたの戦う場所を慎重に選ぶ(pick your battles carefully)、つまり成功する見込みのある取り組みを優先し、勝ち目のない戦い(lost causes)の止めどきを知ることが重要である。

勝ち目のない戦い、つまり棚上げにしなければならない提案にも価値がある。変更をうまく主張できないときでさえ、自分のアイデアを裏付けるデータや、自分の計画を支持してくれる仲間がいること、そして問題について人々を教育することには、価値がある。将来、あなたの組織がその課題に取り組む準備ができたときに、それまでにあなたが準備したものがあれば、チームはより素早く行動することができる。

Escalations and Problem Resolution

最善の努力を尽くしても、セキュリティや信頼性に関わる変更についての意思決定の必要が、爆発的に表面化することがある。重大な障害やセキュリティ脆弱性により、あなたは急遽追加のリソースやスタッフを必要とするかもしれない。あるいは、2つのチームが問題解決の方法について異なる意見を持ち、意思決定の自然な流れが働かないかもしれない。

このような状況では、あなたはマネジメントチェーンから解決手段を探す必要があるかもしれない。意思決定のエスカレーションを行う場合、私達は以下のガイドラインを推奨する。

  • その状況に関する意見を、両方の側から提供するために、同僚、メンター、テックリード、またはマネージャーのグループを作る
  • そのグループに、現在の状況と、提案された選択肢を、マネジメント層のために要約させる
  • あり得る解決手段についてさらに調整するために、あなた自身のチームのリーダーにその要約を共有する
  • 影響を受けるマネジメントチェーン全員にその状況を説明し、それぞれのチェーンでの適切な意思決定者を指名するための会議を設定する

具体例をあげると、Googleでは、セキュリティ問題に対するアクションについて、プロダクトチームとセキュリティレビュアーの間で意見が一致しなかった場合に、エスカレーションが必要になる。このケースでは、セキュリティチームからエスカレーションが開始される。Googleではこのようなエスカレーションを通常の企業文化の一部としているため、エスカレーションは対立的なものとはみなされない。

Conclusion

あなたがシステムを設計して管理するのと同様に、あなたはセキュリティと信頼性の目標を達成するために、時間をかけて組織の文化を設計、実装そして維持することができる。

セキュリティと信頼性の向上は、摩擦の増大に対する恐れや懸念を抱かせることがある。そのような恐れに対処し、変更の影響を受ける人々からの支持を得るのを助けるための戦略がある。あなたのゴールと、リーダーを含むステークホルダーのゴールを揃えることが鍵である。ユーザビリティに焦点を当て、ユーザーへの共感を行動で示すことで、ユーザーが変化を受け入れることを促すことができる。他の人が変化をどのように認識しているかを理解するために小さな投資をすることで、あなたの変更が妥当なものだとユーザーに納得させることができるかもしれない。

この章の冒頭で述べたように、同じ文化は2つとして無いので、本書で紹介した戦略をあなた自身の組織に適合させる必要がある。あなたの組織で最も解決すべき問題領域を選び、それを時間をかけて解決していくことが必要である。

ここまでの感想

最終章だけあって、本書のエッセンスが詰まったような章でした。自分の SRE としての経験からも頷ける内容が多い一方で、本章にある「恐れを抱く側」になっていることも時々あるな、と感じました。

あとは、普段自分でもなんとなく考えていたことがうまく明文化されていたので、他の人と議論するときに本書の概念をうまく使っていきたいと思いました。特に「持続性の文化(a culture of sustainability)」は、そういうふうに表現するのかと感心しました。

さて次はどの章を読もうか……。あとはざっと読むだけで、特に面白かった箇所だけ読書メモを残すようにしようか、とも考えてます。

Building Secure and Reliable Systems 読書メモ - Chapter 7

f:id:muziyoshiz:20200430014145p:plain:w320

SRS 本の読書メモです。

Chapter 7 は、Google での事例を中心に、変更にはその内容によって異なるタイムライン(短期、中期、長期)があることと、タイムラインの種類ごとに注意すべき点を解説しています。

事例情報がメインなので、要約してしまうと面白さが減ってしまいそうな章でした。そのため、今回のメモは短めです。興味を持たれた方は原文をご参照ください。

SRS 本について

SRS 本はこちらから無償でダウンロードできます。

前回までの読書メモは SRS Book タグ を参照のこと。

Chapter 7. Design for a Changing Landscape

この章では、Googleが長年、どのような変更を行ってきたか、そしてそれをどのように適用してきたかという事例を複数紹介する。そして、その道程で私達が学んできた教訓を示す。

ほとんどの設計上の判断は、特にそれがアーキテクチャに関するものは、システムの設計フェーズで実装するのが最も容易かつ安価である。しかし、この章で示すベストプラクティスのほとんどは、システムライフサイクルの後半のステージで実装されたものである。

私達が日々扱うライブラリやアプリケーションの数は増え続けており、それらには常に新たな脆弱性の影響を受ける可能性がある。また、ユーザーや各種規制からの、セキュリティやプライバシーに対する期待も常に上がり続けている。

このような状況に対応するためには、自分たちのインフラを頻繁かつ迅速に変更できる必要があるが、その一方で高信頼なシステムを維持する必要もある。このバランスをとるためには、変更をいつ、どのくらいのスピードでロールアウトする(展開する)かを決めることが重要になる。

Types of Security Changes

自分たちのセキュリティ体制(security posture)を改善したり、セキュリティインフラの回復力を高めるためには、さまざまな変更が考えられる:

  • Changes in response to security incidents (see Chapter 18)
  • Changes in response to newly discovered vulnerabilities
  • Product or feature changes
  • Internally motivated changes to improve your security posture
  • Externally motivated changes, such as new regulatory requirements

Designing Your Change

セキュリティ上の変更(security changes)は、他のソフトウェア変更と同様、基本的な信頼性要件とリリースエンジニアリング原則の対象となる(本書の Chapter 4 や SRE 本の Chaprter 8 を参照)。セキュリティ上の変更をロールアウトするためのタイムラインは異なるかもしれないが、その全体的なプロセスは同じベストプラクティスに従うべきである。

すべての変更がもつべき特性(characteristics)は以下の通り:

  • Incremental
    • 個々の変更を、小さく、独立したもののする。リファクタリングのような、無関係な変更を混ぜたくなる誘惑に逆らう
  • Documented
    • その変更内容およびロールアウトの相対的な緊急性を理解できるように、変更についての "how" と "why" を記載する
    • 文書に含まれる内容:
      • Requirements
      • Systems and teams in scope
      • Lessons learned from a proof of concept
      • Rationale for decisions (in case plans need to be reevaluated)
      • Points of contact for all teams involved
  • Tested
    • ユニットテスト、および可能ならインテグレーションテストを行う
  • Isolated
    • Feature flag を用いて、機能のオンオフと、バイナリの更新を別に扱う(詳しい情報は SRE workbook の Chapter 16 を参照)
  • Qualified
    • 複数のステージからなる、正規のリリースプロセスに従ってロールアウトする
  • Staged
    • カナリアリリースのような方法で、段階的にリリースする。変更前後でのシステムの振る舞いの違いを確認できるようにすべき

これらのプラクティスは "slow and steady" approach を取ることを勧めているが、スピードと安全さの間にはトレードオフがある。

Architecture Decisions to Make Changes Easier

システムを変更しやすい状態にするための戦略について。

Keep Dependencies Up to Date and Rebuild Frequently

自分がシステムが依存しているソフトウェア(OpenSSL や Linux カーネルなど)について、最新のバージョンを利用するように保ち続ける。最新版を利用していればパッチを当てるのも用意になる。

Release Frequently Using Automated Testing

SRE の基本原則では、巨大なリリースを行う代わりに、そのリリースをより小さなものに分割し、定期的にロールアウトすることを推奨している。リリースに含まれる変更が小さいほど、ロールバックの可能性も減る。SRE Workbook の Figure 16-1 にある "virtuous cycle"(好循環)を参照。

同様に、コンテナやマイクロサービスを用いることで、パッチを当てるべき境界面を減らすことができる。

Use Containers

コンテナは、あなたのアプリケーションに必要なバイナリとライブラリを、その下にあるホスト OS と切り離し、依存関係を減らす。コンテナレジストリを更新したら、その更新後のコンテナが自動的にロールアウトされていく。

脆弱性が発生した場合は、コンテナそのものではなく、コンテナイメージをスキャンすることで影響範囲を特定できる。パッチがあたっていない古いイメージがデプロイされないように、本番環境には最新のイメージのみデプロイされるように強制すべきである。

コンテナに関連する話題は、SRE 本の Chapter 7 の "Case Study 4: Running Hundreds of Microservices on a Shared Platform"、および SRE workbook の Chapter 16(blue/green deployment によるパッチ当ての話)にある。

Use Microservices

マイクロサービス化することで、それぞれのマイクロサービスをスケールアウト、負荷分散、ロールアウトできる。つまりインフラの変更を柔軟に行えるようになる。

マイクロサービス化すると、あらゆるトラフィックがそこを流れるようになるので、limited or zero trusted network が必要になる。マイクロサービス化により、チーム間で共通のライブラリやインフラを使うようになる。そのライブラリやインフラを少数の責任者が更新・管理する。望ましいセキュリティ特性を維持するために、できるだけサービスをシンプルにすることを保証するための制約が必要になる(※本書の Chapter 6 で取り上げられていた話題)。

Example: Google’s frontend design

Google のフロントエンドデザインは、回復性と多層防御(defense in depth)を実現するためにマイクロサービスを用いている。

Google Front End (GFE) は、Google サービスに対するフロントエンド層を提供する。ほとんどの Google サービスはバックエンド層のマイクロサービスとして実装されていて、インターネットに直接公開されていない。

GFE は外部からの通信を終端し、DDoS対策や、ルーティングや、ロードバランシングを提供する。また、個々の開発チームが苦労することなく、新しいセキュリティ機能を導入できる。例えば、Google セキュリティチームが Application Layer Transport Security (ALTS) ライブラリを、自社の RPC ライブラリに組み込むことで、ALTS サポートを迅速に広範囲へ適用できた。

また、バックエンド層とフロントエンド層は、その内部にまた複数のレイヤーを持つことができる。そのそれぞれのレイヤーで負荷分散できる。このようなアーキテクチャにより、それぞれのレイヤーへの変更が適用しやすくなっている。

Different Changes: Different Speeds, Different Timelines

すべての変更が同じタイムライン、または同じ速度で発生するわけではない。以下に示す複数の要素が、変更をどれくらいの速さで更新する必要があるかに影響する。

  • Severity (重大性)
  • Dependent systems and teams
    • 他の人の作業を待つ必要があるケース(ベンダーのパッチ提供、顧客側のクライアントへのパッチ適用など)
  • Sensitivity (※何と訳すべき? 感度?)
    • ※Severity との違いがよくわからなかった。セール期間のような重要なイベント中は変更を後回しにしたいと思うかもしれない、という話が書かれているので、客観的な重大性ではなくて、自分たちの事情と比較した上での緊急性という意味?
  • Deadline
    • 法規制などによる、明確な期限がある場合
    • 脆弱性情報の報道規制(embargo)の期間が終わるまでにパッチを当てなければいけない場合

以下の節では、タイムラインが異なる事例を3つ紹介する。

  • 短期の変更:新しいセキュリティ脆弱性への対応
  • 中期の変更:新しいプロダクトの段階的な適用
  • 長期の変更:規制上の理由による変更(その変更を実装するために Google は新しいシステムを作る必要があったもの)

Short-Term Change: Zero-Day Vulnerability

Shellshock(bash の脆弱性)の事例。

Shellshock に対して、Googleのインシデントレスポンスチームはこの例外的な脆弱性を解決するために Black Swan protocol (?) を開始し、以下を行うための大規模な対応を調整した。

  • Identify the systems in scope and the relative risk level for each
  • Communicate internally to all teams that were potentially affected
  • Patch all vulnerable systems as quickly as possible
  • Communicate our actions, including remediation plans, externally to partners and customers

この対応と並行して、インシデントレスポンスチームは、Google のネットワーク境界内にある脆弱なシステムを検知するためのソフトウェアを開発した。そして、このソフトウェアを、残りのパッチ作業を完了させるために用いるだけでなく、Google の標準セキュリティ監視の機能に追加した。

この対応中に実施したこと(およびしなかったこと)から、他のチームや組織にも適用できるいくつかの教訓が得られた:

  • Standardize software distribution to the greatest extent possible (ソフトウェアの配布を可能な限り標準化する)
  • Use public distribution standards (?)
    • ※読んでも、何を指しているのかわからなかった
  • Ensure that you can accelerate your mechanism to push changes for emergency changes (緊急時の変更のために、自分たちの変更適用メカニズムを高速化できるかどうか確認する)
    • 例えば、validation をスキップするなどして高速化できるか?
  • Make sure that you have monitoring to track the progress of your rollout, that you identify unpatched systems, and that you identify where you’re still vulnerable (ロールアウトの進捗を監視し、パッチが当てられていない=まだ脆弱性のあるシステムを特定できるようにする)
  • Prepare external communications as early in your response efforts as possible (脆弱性対応のできるだけ早い段階で、外部とのコミュニケーションを準備する)
  • Draft a reusable incident or vulnerability response plan (再利用可能なインシデントや脆弱性対応計画の作成)
  • Know which systems are nonstandard or need special attention (どのシステムが標準に従っていないか、または特別な対応を必要とするかを知る)

Medium-Term Change: Improvement to Security Posture

FIDO セキュリティキーを用いた二要素認証の事例。

変更のロールアウトに関しては、2つの対立する哲学がある。

  • Start with the easiest use case, where you’ll get the most traction and prove value.
  • Start with the hardest use case, where you’ll find the most bugs and edge cases.

組織内での賛同をまだ十分に得られていないなら、最も簡単なユースケースから始めることに意味がある。組織の上層部からすでに十分な支持が得られているなら、最も難しいユースケースから始めて、バグや課題を早期に発見したほうがいい。

Phishing 対策として、Google 社内では二要素認証の調査およびテストを2011年に開始した。さまざまな選択肢を考慮したのち、Google は FIDO セキュリティキーを共同設計した。そして、OTP の代わりに FIDO セキュリティキーを導入する活動を2013年から開始した。

社内への展開はセルフサービス的に行った。最初は、志願してこの機能をオプトインしたユーザーから導入した。このデバイスは、各オフィスにある TechStop location(ヘルプデスク)から入手しやすくした。

それと同時に、セキュリティチームは FIDO 対応が必要なアプリケーションの長いリストに対応した。2015年にロールアウトの完了と、OTP サービスの廃止に集中し始め、2015年内に対応を完了した。

この経験から得られた教訓は、セキュリティ及び信頼性に関する文化を作ることに関係するものだった。

  • Make sure the solution you choose works for all users. (そのソリューションを、すべてのユーザーが利用できるものにする)
  • Make the change easy to learn and as effortless as possible. (その変更内容を、学びやすく、できるだけ苦労せずに済むものにする)
  • Make the change self-service in order to minimize the burden on a central IT team. (中央集中的な IT チームの負荷を最小化するために、その変更を、セルフサービス可能なものにする)
  • Give the user tangible proof that the solution works and is in their best interest. (そのソリューションが動作し、自分たちの利益になることを、実際に触れて確認できる証拠を提供する)
  • Make the feedback loop on policy noncompliance as fast as possible. (ポリシーに準拠していないことを、できるだけ早く気付けるようなフィードバックループを作る)
  • Track progress and determine how to address the long tail. (進捗を計測し、ロングテールをどのように解決するかを決定する)

Long-Term Change: External Demand

Google Chrome チーム、Let's Encrypt およびその他の組織による努力により、HTTPS の利用率が増加した事例。

長期の変更では、きちんとしたドキュメンテーションと、進捗の計測&可視化の自動化が必要。長期に渡るプロジェクトでは、担当者が途中で会社を去る可能性がある。

作業にはロングテール(変更が行き届かない部分)がある。全体の80〜90%に適用できたら、それはセキュリティリスクの削減に対する計測可能なインパクトを持つため、成功したとみなされるべきだろう。

ここから得られた教訓:

  • Understand the ecosystem before committing to a strategy.
  • Overcommunicate to maximize reach.
  • Tie security changes to business incentives.
  • Build an industry consensus.

Complications: When Plans Change

変更計画を変更して、変更を加速させたり、遅らせなければいけなくなる潜在的な理由:

  • You might need to delay a change based on external factors.
  • You might need to speed up a change based on a public announcement.
  • You might not be severely impacted.
  • You might be dependent on external parties.

Heartbleed(OpenSSL の脆弱性)は、キャッチーなロゴと名前で、予想外にメディアの衆目を集めた脆弱性だった。そのため、当初の計画より、パッチの適用を加速させる必要が生じた。

Heartbleed の事例からは、いくつかの重要な教訓が得られた:

  • Plan for the worst-case scenario of the embargo breaking or being lifted early.
  • Prepare for rapid deployment at scale.
  • Regularly rotate your encryption keys and other secrets.
    • SRE workbook の Chapter 9 も参照
  • Make sure you have a communication channel to your end users—both internal and external.

ここまでの感想

次にセキュリティ変更を行うタイミングがあったら、読み返して、計画立案の参考にしたいと思える内容でした。

セキュリティ変更を行う際には事前に計画を行い、それが短期〜長期のいずれに該当するかによって適切なアプローチが変わる、ということを事例とともに説明していて説得力がありました。また、変更がロールアウトされた範囲を自動的に検出可能にする(検出のために人手を挟まないようにする)ことで、各開発チームが自律的にセキュリティ変更を進められるようにするというのは、良いプラクティスだと思いました。今後うまく適用できそうな機会があれば、是非試して見たいです。

Building Secure and Reliable Systems 読書メモ - Chapter 6

f:id:muziyoshiz:20200430014145p:plain:w320

月一連載になっている SRS 本の読書メモです。

Chapter 6 の "Design for Understandability" は、最初にタイトルを見たときから気になっている章でした。実際読んでみて、勉強になる考え方が多かったです。

じっくり英訳しようとすると全然先に進めなくなってしまうので、今回から若干翻訳の手を抜いて、ほとんど意訳で書いてるのと、一部は英語のまま引用してます。また、細部はかなり省略してしまってます。興味が湧いた部分については原文をあたってみてください。

本章で頻出する "Understandability" という単語は、以下のメモでは「わかりやすさ」と訳しました。「理解可能性」のように訳したほうがいいかとも思ったのですが、文章が固くなりすぎるように感じたので、このメモでは「わかりやすさ」にしました。

また、本章には、"reason about" という表現も頻出します。これは「すべてのコードを深く読んで理解しなくても、システムの不変条件やメンタルモデルからそのことを説明できる」というような意味で使われています。適切な訳という自信はありませんが、以下では「論証する」と訳しました。

SRS 本について

SRS 本はこちらから無償でダウンロードできます。

前回までの読書メモは SRS Book タグ を参照のこと。

Chapter 6. Design for Understandability

本章では、システムのわかりやすさ(understandability)について議論する。

最初に、不変条件(invariants)とメンタルモデル(mental models)に従って、あなたのシステムを分析し、理解する方法についての議論から始める。そして、アイデンティティ、認可、アクセス制御のための標準フレームワークに基づく階層化されたシステムアーキテクチャが、わかりやすいシステムの設計を助けることを示す。

そして、セキュリティ境界(security boundaries)に関する話題を題材として、どのようにソフトウェア設計(特に、アプリケーションフレームワークとAPIの利用)が、セキュリティおよび信頼性に関する特性(security and reliability properties)に対する論証(reason about)を助けるかを示す。

本書では、システムのわかりやすさ(understandability)とは、関連分野の専門知識を持つ人が、以下の両方を、正確かつ自信を持って論証できる度合いと定義する。

  • The operational behavior of the system(システムの運用上の振る舞い)
  • The system’s invariants, including security and availability(セキュリティや可用性を含む、システムの不変条件)

Why Is Understandability Important?

わかりやすいシステム(understandable system)を設計し、そのわかりやすさを維持するには努力を要する。わかりやすいシステムは、プロジェクトのベロシティを維持するという(4章で示した)利点もあるが、それ以上に、以下のような明確な利点がある。

  • Decreases the likelihood of security vulnerabilities or resilience failures
    • わかりにくいシステムでは、修正時にバグや脆弱性を生みやすい。またそれらに気づきにくい
  • Facilitates effective incident response
    • わかりにくいシステムは、インシデント発生時の対応〜根本原因の解決を妨げる
  • Increases confidence in assertions about a system’s security posture
    • システムのセキュリティに対するアサーション(表明)は、一般に、不変条件(invariants)と呼ばれる。不変条件とは、そのシステムの、すべてのありうる振る舞いに対して成立しなければならない特性(property)のことである
    • 例えば、外部からの入力(悪意あるものを含む)に対して必ず特定の検査が行われる、というのは不変条件である。そのような抽象化がなければ、システムが外部からの入力に対して安全かどうかの論証が難しい

System Invariants

システムの不変条件(system invariant)とは、そのシステムを取り巻く環境が正しく振る舞うか間違って振る舞うかに関係なく、常に真である特性のこと。ここで言う環境は、あなたが直接コントロールできないものもすべて含む。

以下は、セキュリティと信頼性に関する望ましい特性の例:

  • Only authenticated and properly authorized users can access a system’s persistent data store.
  • All operations on sensitive data in a system’s persistent data store are recorded in an audit log in accordance with the system’s auditing policy.
  • All values received from outside a system’s trust boundary are appropriately validated or encoded before being passed to APIs that are prone to injection vulnerabilities (e.g., SQL query APIs or APIs for constructing HTML markup).
  • The number of queries received by a system’s backend scales relative to the number of queries received by the system’s frontend.
  • If a system’s backend fails to respond to a query after a predetermined amount of time, the system’s frontend gracefully degrades—for example, by responding with an approximate answer.
  • When the load on any component is greater than that component can handle, in order to reduce the risk of cascading failure, that component will serve overload errors rather than crashing.
  • A system can only receive RPCs from a set of designated systems and can only send RPCs to a set of designated systems.

あなたのシステムがこれらの望ましいセキュリティ特性に反している、つまり不変条件が実際には不変条件ではない、という場合には、そのシステムはセキュリティ上の弱点や脆弱性を持つということになる。

上記リストの4番目にあるような、信頼性に関する特性についても同様である。フロントエンドが外部から受け取った以上のリクエストを、バックエンドに対して生成するようなシステムは、可用性に関する潜在的な弱点を持つといえる。

Analyzing Invariants

意図した特性が実際に不変条件として成立しているかは、解析しないとわからない。不変条件が成立していないことによって起こりうる悪影響と、不変条件が成立するかを検証するために割く労力は、トレードオフの関係にある。

両極端な例を上げると、例えば少しのテストとバグチェックのためのソースコードレビューをするくらいでは、不変条件の違反を見逃す恐れがある。一方で、マイクロカーネルの実装で行われているように、セキュリティ特性を形式論理で証明できれば完璧だが、そのようなことを大規模なアプリケーション開発に適用するのは難しい。

本章では、その両極端な例の間にある、現実的な妥協案を示す。わかりやすさについての明確な目標を持ってシステムを設計することで、システムが特定の不変条件を満たすことを(形式的にとまではいかなくても)原理的に示すことができ、それらのアサーションをかなり高い度合いで信頼できる。Google では、このアプローチが、大規模ソフトウェア開発に対して実用的で、かつよくある種類の脆弱性の発生を効果的に減らすことができることを発見した。テストと検証についての詳細は Chapter 13 で示す。

Mental Models

複雑なシステムをそのまま全体的に理解することは難しいため、エンジニアや特定分野のエキスパートは、不必要な詳細を省いたメンタルモデルを構築する。複雑なシステムでは、メンタルモデルが複数になることもある。

メンタルモデルは有用だが、限界もある。もし、典型的な運用シナリオに基づいてメンタルモデルを作ったら、それは通常でない場合のシナリオでのシステムの振る舞いの予測には使えない。

例えば、通常はリクエスト数の増加に応じて段階的に負荷が増えるシステムが、ある閾値を超えると、全く違う振る舞いを見せることがある。そのようなシステムでは、単純すぎるメンタルモデルは、トラブルシューティングを阻害する。

極端な状況、または通常でない状況でも、システムが予測可能な振る舞いをして、メンタルモデルが保たれるようにシステムを設計すべきである。そのようなシステムであれば、障害発生時にも、そのメンタルモデルは有用なままになる。

※本文ではメモリのスラッシングを例に挙げて、システムの振る舞いを予測可能にする方法を例示している。

Designing Understandable Systems

この章の残りでは、システムをよりわかりやすくし、そのシステムが日々進化するなかでもわかりやすさを維持し続けるために使える、いくつかの具体的な指標について議論する。

Complexity Versus Understandability

わかりやすさに対する最初の敵は、管理されていない複雑さ(unmanaged complexity)である

いくつかの複雑さは、現在のソフトウェアシステム(特に分散システム)の規模と、それらのシステムが解決する問題による生来のもので、回避できない。Google では数万のエンジニアが、10億行を超えるコードを含むソースコードリポジトリを編集しているが、そこまでの規模でない組織でも、1つのプロダクトで数百を超える機能を提供することはざらにあるだろう。

例えば、Gmail は多くの機能を提供している(p.94 のリスト)。このように多数の機能を提供するシステムは、機能がより少ないシステムよりも本質的に複雑である。しかしこれらの機能は価値を生み出しているので、複雑さを下げるためにそれらの機能を削除しろとは言えない。私達がそれらの複雑さを管理できれば、システムを十分にセキュアかつ高信頼にすることができる。

私達の目標は、この本質的な複雑さをより小さい部品・コンパートメントに分割して含むように、システムの設計を構成することである。そして、それは具体的かつ重要なシステム特性や振る舞い(specific, relevant system properties and behaviors)を、高い確度で人間が論証できるような方法で行わなければならない。言い換えると、私達はわかりやすさの観点に立って、複雑さという特性を管理しなければならない。(※なんとなく言いたいことはわかるがうまく訳せない)

本書ではセキュリティと信頼性の観点で、わかりやすさについて議論している。しかし、私達が議論するパターンの話は、この2つの観点に限ったものではなく、一般的なソフトウェア設計テクニック全般に渡る。システム及びソフトウェア設計に関する全般的な読み物としては、John Ousterhout の A Philosophy of Software Design を参照のこと。

Breaking Down Complexity

複雑なシステムの振る舞いのすべての側面を理解するためには、巨大な1個のメンタルモデルを内面化して維持する必要がある。しかし、そのようなことは人間には難しい。

システムを複数の小さなコンポーネントにわけて、それを組み合わせることで、システムをわかりやすくすることができる。そして、個々のコンポーネントの特性を論証可能にして、そこからシステム全体の特性を引き出せるようにする。

このアプローチは、現実には単純明快ではない。そのようなことが可能かどうかは、そのシステム全体がどのように複数のコンポーネントへと分解されているか、そしてそれらのコンポーネント間のインターフェイスと信頼関係の性質に依存する。この点については p.97 からの "System Architecture" にて見ていく。

Centralized Responsibility for Security and Reliability Requirements

セキュリティおよび信頼性に関する要求(例えば何らかのチェック処理)を個々のコンポーネントで実装していたら、そのシステム全体がそれらの要求を満たしているか判断するのは難しい。

Chapter 4 で議論したように、そのような共通タスクを実装する責任は、その組織共通のコンポーネント(ライブラリやフレームワーク)に集約させることができる。

例えば、サービスで共通して必要なセキュリティ機能(認証、認可、ロギング)は RPC サービスフレームワークが実装する。また、信頼性に関する機能(リクエストのタイムアウトなど)も RPC サービスフレームワークに任せる。

セキュリティと信頼性に関する要求に関する責任を1箇所に集中させることで、以下の利点が得られる。

  • Improved understandability of the system
    • レビュアーは、システムの1箇所を見るだけで、セキュリティおよび信頼性に関する要求が満たされているかどうかを判断できる
  • Increased likelihood that the resulting system is actually correct
    • アプリケーション上での要求のアドホックな実装が間違っていたり、実装し忘れている可能性について考えずに済むようになる

System Architecture

システムを複数のレイヤーとコンポーネントに分割することは、複雑さを管理するためのキーツールである。

しかし、この分割をどのように行うかは、慎重に考える必要がある。もしコンポーネント同士が密結合していたら、モノリシックシステムと同様に、理解が難しいシステムになってしまう。システムをわかりやすくするためには、境界とインターフェイスに注意を払う必要がある。

経験豊富なソフトウェアエンジニアは、外部の環境から来た入力は信頼すべきでなく、それらの入力についてシステムはなんの仮定も置いてはいけない、ということを知っている。その一方で、システム内部からの呼び出しについては信頼し、想定通りの呼び出し方をされると期待しがちである。

しかし、システムのセキュリティ特性が実現されるかどうかは、実際にはシステム内部からの入力が正しいかどうかに依存する。そのため、セキュリティ特性を論証可能にするためには、システム内部からの呼び出しについてもなんの仮定も置けない。

コンポーネントが呼び出し元に対して置く仮定を減らせば減らすほど、そのコンポーネントの特性の論証が容易になる。理想的には、そのような仮定は全く無くしたい。

もし、呼び出し元について仮定を置くことを強制される場合は、その仮定を、インターフェイスの設計に明示的に加える事が重要である。例えば、呼び出し元のプリンシパルを制限する。

Understandable Interface Specifications

構造化されたインターフェイス(structured interfaces)、一貫したオブジェクトモデル(consistent object models)、そして冪等なオペレーション(idempotent operations)は、システムのわかりやすさに貢献する。

  • Prefer narrow interfaces that offer less room for interpretation
    • フレームワークは、RPC メソッドの入出力に対する型定義の機能を(gRPC, Thrift, OpenAPI のように)持っていたほうがいい
    • OpenAPI や Protocol buffer が持つようなバージョニング機能の有無は、将来のアップグレードのしやすさに影響する(アップデートに関する Protocol buffer のガイドライン
    • 任意の文字列(JSON の string 型)を受け付けるような API は、入出力が制約されないため、わかりやすさを損ないやすい。また、例えばクライアントとサーバが別のタイミングでアップデートされるときに、片方の処理を壊すことがありうる
    • 明確な API 仕様がないと、例えば Istio Authorization Policy のような認可フレームワーク上のポリシーと、サービスが公開する実際の境界面(actual surface area)を関連付けるための、自動的なセキュリティ監査システムの構築は難しくなる
  • Prefer interfaces that enforce a common object model
    • 複数のタイプのリソースを管理するシステムは、Kubernetes Objects のような共通オブジェクトモデル(common object model)から恩恵を得られる
    • そのオブジェクトで各リソースを取り扱えるようになるだけでなく、エンジニアが巨大なシステムを理解するのに役立つ単一のメンタルモデルを提供する(p.99 にその利点についての箇条書きあり)
    • Google は resource-oriented APIs を設計するための一般的なガイドライン を公開している
  • Pay attention to idempotent operations
    • API 呼び出しは失敗したり、成功したあとでその応答が届かないことがある。API が冪等(idempotent)であれば、呼び出し元は再実行すればいい。もし、冪等でなければ、呼び出し結果を確認するためのポーリングが必要になる
    • 冪等性は、エンジニアのメンタルモデルにも影響する。例えば、エンジニアがなにかデータを作成する API を冪等だと思って呼び出しているのに、実際にはそうでなかった場合、そのデータが意図せず重複して作られる、ということが起こりうる
    • 本質的に冪等でない処理であっても、サーバ側で冪等にすることができる。上記の例では、リクエストに一意な識別子(例:UUID)を含めることで、2回目の API 呼び出しをサーバ側で検出して、重複したデータの作成を防げる

Understandable Identities, Authentication, and Access Control

どんなシステムでも、機密性の高い(highly sensitive)リソースに、誰がアクセスしたかは特定できるようにすべきである。例えば課金システムでは、PII(personally identifiable information)データに、社内の誰がアクセスしたかを、監査できる必要がある。監査フレームワークは、アクセスのログを取り、そのログを自動的に解析し、定期的なチェックや、インシデント調査に活用できるべきである。

Identities

アイデンティティ(identity)とは、属性の集合、またはあるエンティティに関連する識別子のことである。クレデンシャル(credentials)は、特定のエンティティのアイデンティティを主張する。クレデンシャルは、認証プロトコル(authentication protocol)を使って送信される。

システムがどのように人間のエンティティ(顧客および管理者)識別するかを論証することは相対的に簡単な一方で、巨大なシステムは人間以外のエンティティも識別できる必要がある。マイクロサービスの集合から構成された巨大システムは、人が介在する場合もしない場合も含めて、相互に呼び出し合う。エンティティには、人、ソフトウェアコンポーネント、ハードウェアコンポーネントが含まれる。

一般に、アイデンティティと認証のサブシステム(identity and authentication subsystems)は、アイデンティティのモデル化と、それらのアイデンティティを表すためのクレデンシャルの提供に責任を持つ。アイデンティティが意味があるもの("meaningful")であるためには、以下の条件を満たす必要がある。

  • Have understandable identifiers
    • 人間にとってわかりやすい文字列であること(例:widget-store-frontend-prod のような文字列)
    • わかりやすい文字列には、設定ミスを防げたり、異常なアクセスにすぐ気づけたりするメリットがある
    • これが 24245223 のような無意味な数字だと、間違った設定をしていても気づけない
  • Be robust against spoofing
    • ID/password を clear-text channel でやり取りするようなものは簡単に spoofing できてしまう
  • Have nonreusable identifiers
    • 例えば、社員のメールアドレスを管理ツールのアイデンティティに使っていて、その管理者が退職したあとで同じメールアドレスを新入社員に割り当てたら、いろいろな権限が渡ってしまう可能性がある

意味のあるアイデンティティを、システム上のすべてのアクティブなエンティティに割り当てることが、わかりやすさへの基本的な第一歩である。組織内で統一されたアイデンティティシステム(organization-wide identity system)は、エンティティを参照するための共通の方法を提供し、全従業員が共通のメンタルモデルを持つことを助ける。

Example: Identity model for the Google production system.

例として、Google のシステムでは、異なる種類のアクティブなエンティティを以下のように分類している。

  • Administrators
    • システムの状態を変更する(例えば新しいリリースを行ったり、設定を変更する)権限を持つエンジニア
    • Administrator は、システムの状態を直接変更せず、そのような一連のワークロードを起動する
    • そのアイデンティティは global directory service で管理されて、シングルサインオンに使われる
  • Machines
    • Google データセンタ内の物理マシン
    • global inventory service/machine database で管理されている
    • Google プロダクション環境では、マシンは DNS 名で解決可能
    • そのマシン上で動作するソフトウェアの修正権限を表すために、administrators と machines の間の関連を管理する
  • Workloads
    • Borg オーケストレーションによってスケジューリングされたワークロード
    • ワークロードと、それが動作するマシンのアイデンティティは、大抵の場合異なる
    • オーケストレーションシステムは、ワークロードと、それが動作可能なマシンの関係(制約)を強制する
  • Customers
    • Google が提供するサービスにアクセスする顧客
    • Customers のアイデンティティは、専用のアイデンティティサブシステムで管理される
    • Google は OpenID Connect workflows を提供しており、顧客は Google identity を、Google 外のエンドポイントで使えるようにしている
Authentication and transport security

認証およびトランスポートセキュリティの分野は難しいので、すべてのエンジニアが完全に理解することは期待できない。その代わりに、それらの抽象化とAPIを理解すべき。

Google ではエンジニアに Application Layer Transport Security (ALTS) を提供している。ALTS は、以下のようなシンプルなメンタルモデルを提供する。

  • An application is run as a meaningful identity:
    • A tool on an administrator’s workstation to access production typically runs as that administrator’s identity.
    • A privileged process on a machine typically runs as that machine’s identity.
    • An application deployed as a workload using an orchestration framework typ‐ ically runs as a workload identity specific to the environment and service pro‐ vided (such as myservice-frontend-prod).
  • ALTS provides zero-config transport security on the wire.
  • An API for common access control frameworks retrieves authenticated peer information.

ALTS に類似のシステムとして、Istio のセキュリティモデル がある。

このようなシステムがない場合、セキュリティ対策が正しく行われるか確認するためには、すべてのアプリケーションコードに目を通す必要が出てきてしまう。

Access control

アクセス制御の部分をフレームワークで提供することは、システム全体のわかりやすさを大きく向上させる。フレームワークはアクセス制御に関する共通の知識、共通の方法をエンジニアに提供する。

フレームワークは、複数のコンポーネント(Ingress → Frontend → Backend)を渡るワークロードに対するアクセス権限、といった難しい問題の扱いを助けることができる。このようなワークロードチェインでは、リクエストを送った Customer の権限と、ワークロードを実行する Machine の権限があり、両方を考慮する必要がある。

Security Boundaries

あるシステムの信頼されたコンピューティングベース(trusted computing base, TCB)とは、「セキュリティポリシーが適用されることを確実にするのに十分な正確な機能を持つ(ハードウェア、ソフトウェア、人間、...)の集合であり、より端的に言えば、その失敗がセキュリティポリシーの違反を引き起こす可能性があるもの」である(Anderson, Ross J. 2008. Security Engineering: A Guide to Building Dependable Distributed Systems. Hoboken, NJ: Wiley. からの引用)。

TCB と「それ以外のすべて」の間のインターフェイスを、セキュリティ境界(security boundary)と呼ぶ。TCB は、そのセキュリティ境界を超えてくるすべての入力を、信頼できないものとして扱わなければならない。

Chapter 4 の web application(ウィジェット販売サイト)を例に挙げる。このサイトは発送先住所を扱う。1個のサーバがすべての機能を提供するモノリシックシステムでは、発送先住所の TCB(TCB_AddressData)はそのシステム全体になる。例えば、発送先住所とは関係ない機能(例えばカタログ検索機能)に SQL インジェクション脆弱性があると、それは即座に発送先住所の漏洩に繋がってしまう。

※ 以下の TCB の具体例については、図を見たほうがわかりやすいと思う。p.107 以降を参照のこと。

アプリケーションをマイクロサービスに分割することで、TCB を小さくし、セキュリティを改善することができる。上記の例では、カタログ検索機能のバックエンドおよびデータベースを、(発送先住所を含む)販売機能のバックエンドおよびデータベースと分けることで、発送先住所の TCB を小さくできる。

ただ、このようにマイクロサービスに分割しても、フロントエンドに全ユーザーの発送先住所を表示できる機能の実装を許すと、TCB の範囲は web frontend まで広がる。Web frontend の脆弱性が、全ユーザーの発送先住所の漏洩に繋がるためである。

フロントエンドに強すぎる権限を与える代わりに、フロントエンドとバックエンドの間で、OAuth2 のトークンのような エンドユーザーコンテキストチケット(EUC) をやり取りするという方法がある。この方法では、フロントエンドは任意のユーザーの EUC を取得することができず、フロントエンドの脆弱性があっても、攻撃者がアクセスできるデータは限定される。

また、カタログ検索機能と販売機能のフロントエンドが同一のドメインで動いていたら、片方の XSS 脆弱性が、もう一方にも影響してしまう。つまり TCB は再びシステム全体にまで広がってしまう。両者のフロントエンドが別ドメインで動いていれば、TCB は狭まる。

TCB にはセキュリティ上の利点だけでなく、システムをわかりやすくするというメリットもある。TCB として成り立つことを示すためには、コンポーネントはそれ以外の部分から独立していなければならない。そのためには、コンポーネントは明確に定義された、きれいなインターフェイスを持たなければならない。もし、コンポーネントの正しさが、そのコンポーネント外の環境に対する仮定に依存するなら、それは定義上 TCB とは言えない。

TCB はしばしばそれ自身の failure domain を持ち、バグの発生や DoS 攻撃、またはその他の運用上の影響に対して、どのように振る舞えばいいかについての理解を与える。Chapter 8 で、システムを区分することによる利点について、さらに詳しく議論する。

Software Design

巨大なシステムを一度、セキュリティ境界によって区分された複数のコンポーネントによって構造化したあとも、あなたはそのすべてのコードとサブコンポーネントを引き続き論証し続ける必要がある。そして大抵の場合、分割後のコードも十分に巨大かつ複雑なソフトウェアになる。

この節では、不変条件を、より小さいソフトウェアコンポーネント(モジュールやライブラリ、API)のレベルでさらに論証できるように、ソフトウェアを構成するための技術について議論する。

Using Application Frameworks for Service-Wide Requirements

これまでに議論したように、フレームワークは再利用可能な機能の部品を提供する。あるシステムは、認証フレームワーク、認可フレームワーク、RPC フレームワーク、オーケストレーションフレームワーク、監視フレームワーク、ソフトウェアリリースフレームワーク、などなど……から成るということがありうる。これらのフレームワークは柔軟性を持つが、柔軟性を持ちすぎる。あり得るフレームワークの組み合わせや、その設定方法は無数にあり、エンジニアを圧倒するほどである。

Googleでは、この複雑さを管理するために、より高レベルのフレームワークを作ることが有用だと気づいた。これをGoogleでは "application frameworks" と呼ぶ。あるいは、"full-stack" または "batteries-included frameworks" とも呼ばれる。

Application frameworks は、サブフレームワークの標準的なセットを、妥当なデフォルト値と、それらのサブフレームワークが相互動作することの保証とともに提供する。

例えば、開発者が自分たちのお気に入りのフレームワークを使った開発したとする。そして、認証のための設定はしたが、認可やアクセス制御のための設定をし忘れるということがありうる。Application framework は、すべてのアプリケーションが有効な(valid)認可ポリシーを持つことを保証し、明示的な許可を持たないすべてのアクセスを拒否するというデフォルト設定を持つ。したがって、設定し忘れのような問題を回避できる。

全般的に、application frameworks は、アプリケーション開発者やサービスオーナーが必要とするすべての機能(下記)を有効化および設定するための、こうあるべきという方法(an opinionated way)を提供できなければならない。

  • Request dispatching, request forwarding, and deadline propagation
  • User input sanitization and locale detection
  • Authentication, authorization, and data access auditing
  • Logging and error reporting
  • Health management, monitoring, and diagnostics
  • Quota enforcement
  • Load balancing and traffic management
  • Binary and configuration deployments
  • Integration, prerelease, and load testing
  • Dashboards and alerting
  • Capacity planning and provisioning
  • Handling of planned infrastructure outages

※ "deadline" は SRE 本では「タイムアウト」と訳されている。deadline propagationは「タイムアウトの伝播」。例えば、全体のタイムアウトが30秒で、あるリクエストで10秒かかったら、後続処理のタイムアウト時間は20秒にする、というように伝播させる。

Understanding Complex Data Flows

多くのセキュリティ特性は、システムの中を流れる値(values)についてのアサーションに依存している。

例えば、多くの Web サービスは様々な目的に URL を使っている。URL パラメータは string 型。string 型の値は、その URL が well-formed かどうかに関わらず、その値に関する明示的なアサーションを渡すことができない。したがって、セキュリティ特性を論証するためには、システムのなかで、その文字列が正しく渡されていくかを、上流のコード(upstream code)すべてを読んで理解する必要がある。

型を用いれば、検証すべきコードを大幅に減らすことができて、システムをわかりやすくする助けになる。URL を型として表現し、例えば Url.parse() のようなコンストラクタにデータ検証処理を実装すれば、あなたはその Url.parse() のコードを理解するだけでセキュリティ特性を論証できるようになる。

このような型の実装は、TCB のように振る舞う(すべての URL が well-formed である、という特性に責任を持つ)。しかし、これはその型(のモジュール)を呼び出す側のコードに悪意がなく、周りの環境に欠陥がある(compromised)状態ではない、という仮定があったうえでの話である。そのような仮定が置けない場合は、セキュリティチームは結果として発生する最悪のシナリオに対処する必要がある(Part IV を参照)。

より複雑なセキュリティ特性に対して、型を用いることもできる。例えば、インジェクション脆弱性(XSS または SQL インジェクション)は、外部から渡された信用できない入力に対する検証やエンコーディングに依存する。このような脆弱性を防ぐための効果的な方法の一つが、SQL クエリや HTML マークアップのような injection sink context(※なんて訳せばいい??)で安全に使えるとすでにわかっている値とそうでない値を区別するために、型を使うという方法である。

  • 内部的に、SafeSql または SafeHtml のような型を用意する。安全だと検証される前の String と区別する
  • SQL や HTML を受け取る側が、String ではなく、SafeSql または SafeHtml 型を受け取るようにする
  • 引き続き String 型を受け取るかもしれないが、その値の安全性に対してなんのアサーションも持ってはならない

このようにすることで、アプリケーション全体が XSS 脆弱性に対して安全かどうかを調べるためには、type と、type-safe sink API の実装のみを理解すればよくなる。Chapter 12 では、呼び出し元についての仮定をなにも持たずに、型の contract を保証するためのアプローチについて議論する。

Considering API Usability

API の適用および利用が、あなたの組織の開発者とその生産性に与える影響について考えるのは良いアイディアだ。もし API が使いづらければ、開発者はその採用に気乗りせず、動きが遅くなってしまう。

Secure-by-construction API(※なんて訳せばいい?? 構造による保証?)は、コードをわかりやすくし、エンジニアがそのアプリケーションのロジックに集中できるようにする、という二重のメリットを持つ。加えて、セキュアなアプローチをあなたの組織文化へと自動的に組み込む。

開発者にとって利益のあるライブラリやフレームワーク(secure-by-construction APIs のようなもの)を設計することは、可能である。また、これはセキュリティと信頼性の文化を促進する。

例として、エスケープを自動的に行う HTML テンプレートシステムを考える。これは、XSS 脆弱性を起こさないことを保証するセキュリティ不変条件であると同時に、開発者の観点では通常の HTML テンプレートと変わらず利用できる。

暗号化技術を正しく使うのはとても難しく、微妙なミスが脆弱性を生むことが多い。そのような経験から、Google では Tink という名前のライブラリを開発した。Tink は、Google が開発するアプリケーション内で、暗号化技術を安全に使う ことを簡単にする(間違って使うことを難しくする)ような API を提供する。

Tink の設計および開発を導いた原則:

  • Secure by default
  • Usability
  • Readability and auditability
  • Extensibility
  • Agility
  • Interoperability
    • Tink is available in many languages and on many platforms.

通常の暗号化ライブラリは、ローカルディスク上に秘密鍵を持つことを簡単にしているが、そのようなライブラリを使うと、鍵管理に関するインシデントを完全に防ぐことは難しい。対象的に、Tink の API は生の鍵情報(raw key material)を受け付けない。その代わりに、Tink はCloud Key Management Service (KMS)、AWS Key Management Service、Android Keystore のような鍵管理サービスと統合された鍵管理機能を提供することで、鍵管理サービスの利用を促進する。

注意点として、データの重要さに合わせて、適切な暗号化方法を選ぶのは Tink を利用するエンジニアの責任である。そのような設計レベルのミスを Tink で防ぐことはできない。ソフトウェア開発者とレビュアーは、セキュリティおよび信頼性に関する特性のうち、ライブラリやフレームワークが保証するものとしないものをしっかり理解しなければならない。

Tink に限界があるのと同様に、 secure-by-construction web framework も、XSS 脆弱性を防ぐことはできるが、アプリケーションのビジネスロジックに含まれるセキュリティバグは防げない。

Conclusion

システムのわかりやすさと、信頼性およびセキュリティは深く結びついている。

わかりやすいシステムを構築するための私たちからの第一のガイダンスは、システムを、明確かつ限られた目的を持つコンポーネントを用いて構築することである。それらのコンポーネントのなかにはそれぞれの TCB を構成するものもあり、セキュリティリスクへの対応に集中するものもある。

また、私達は望ましい特性(例えばセキュリティ不変条件や、構造的な回復力、データ耐久性)を、それらのコンポーネント内やコンポーネント間で強制するための戦略についても議論した。これらの戦略は以下を含む。

  • Narrow, consistent, typed interfaces: 狭く、一貫性があり、型を持つインターフェイス
  • Consistent and carefully implemented authentication, authorization, and accounting strategies: 一貫性があり、注意深く実装された認証、認可、課金の戦略
  • Clear assignment of identities to active entities, whether they are software com‐ ponents or human administrators: アクティブなエンティティ(ソフトウェアコンポーネントや人間の管理者)に対するアイデンティティの明快な割り当て
  • Application framework libraries and data types that encapsulate security invari‐ ants to ensure that components follow best practices consistently: コンポーネントがベストプラクティスに一貫して従うことを保証するように、セキュリティ不変条件を埋め込んだ、アプリケーションフレームワークライブラリやデータ型

あなたの最も重要なシステムが誤動作したときに、システムのわかりやすさが、それをささいなインシデントにするか、あるいは大災害にするかの明暗を分ける。SRE は自らの職務のために、そのシステムのセキュリティ不変条件を知っておかなければならない。極端なケースでは、SRE はセキュリティインシデントの発生時に、セキュリティのために可用性を犠牲にして、サービスをダウンさせなければならないかもしれない。

ここまでの感想

6章も、5章に続いて読み応えのある内容でした。タイトルにある Understandability(わかりやすさ)という言葉からは予想できなかった、説得力のある議論が次々と続いて、読んでいて飽きませんでした。

システムのわかりやすさ(understandability)について、明確な定義がなされていて、正直言って一度読んだだけでは理解しきれてない気がしますが、うまく身につけたら今後役立ちそうです。

文中で触れられていた A Philosophy of Software Design は、日本語訳がないようですが、ちょっと読んでみたいですね。検索したところ、日本語の解説記事がいくつかあったので、まずはそのあたりから読んでみます。

詳しい説明のなかでは、通常時と障害発生時でメンタルモデルが変わってしまうとシステムがわかりにくくなる、という指摘は、なるほどと思いました。

また、コンポーネントの責務を明確にするというのは、つまり特定の不変条件に対して責任を持つコンポーネントを明確にすることで、その結果、ある不変条件が成立していることを論証するために読むべきコード量が減る、というのはうまい説明方法だと思いました。現実には、なかなかそんなふうにきれいな構造のコードになってないことってありますよね……。この説明方法は、今後うまく活用していきたいです。

Building Secure and Reliable Systems 読書メモ - Chapter 5

f:id:muziyoshiz:20200430014145p:plain:w320

SRS 本の読書メモの続きです。Chapter 5 はなかなか重たい内容だったので、この章のみでメモを上げます。

SRS 本はこちらから無償でダウンロードできます。

前回までの読書メモは SRS Book タグ を参照のこと。

Chapter 5. Design for Least Privilege

最小権限の原則(The principle of least privilege)とは、アクセス主体が人間かシステムかに関わらず、タスクを達成するのに必要な最低限度のアクセス権限のみを与えるべきという原則である。新機能の設計段階から考慮することで、この原則が最も有効に働く。

社員の操作に完璧は期待できない。そこに悪意があるにせよないにせよ。思考実験として、もしあなたがなにか悪いことをしたいと思ったら、自分の組織にどのようなダメージを与えられるか考えてみよう。どんなことを、どのように実行できる? あなたの悪事は気づかれる? あなたの操作履歴を隠すことはできる? あるいは、もし良い意図だとしても、あなたができる最も破壊的なミスはどんなもの? 普段どれくらいのコマンドを手で実行していて、そのなかにどれくらいのタイミングミスやコピペミスがある?

そのような悪いアクションによる影響を最小化できる、または取り除けるようにシステムを設計することを推奨する。ここでも SRE の格言を引用すると、hope is not a strategy. である。

Concepts and Terminology

ベストプラクティスの詳細に入る前に、業界および Google での用語の定義について。

  • Least privilege(最小権限)
    • セキュリティ業界で確立された、幅広いコンセプト
    • 最小権限の目的は、そのシステムのすべての認証・認可のレイヤに渡って拡張されるべき
  • Zero Trust Networking
    • ユーザのネットワーク的な位置(それが社内ネットワークかどうか)によって、何の権限も付与しないという原則
    • Google では BeyondCorp program によって大規模な zero trust networking の実装に成功した
  • Zero Touch
    • SRE が直接システムやネットワークに触らず、拡張可能な自動化や、安全な API や、回復性のある多要素承認システム(resilient multi-party approval system)を通してのみ変更を行うこと
    • Google ではこれを実現するために Zero Touch Production (ZTP) や Zero Touch Networking (ZTN) などのインターフェイスを開発してきた

Classifying Access Based on Risk

すべてのアクセスを同じ方法で一律に制限するのではなく、セキュリティリスクや影響度に応じてアクセスを分類する必要がある。例えば、アクセスされるデータの種類による分類や、その API が書き込み可能かどうかによる分類がありがちだろう。

あなたのシステムの分類が明確に定義され、一貫性を持って適用され、広く理解されれば、開発者はそれを共通言語としてシステムやサービスを設計できる。この分類フレームワーク(classification framework)は、データストア、API、サービスやその他のエンティティに幅広く適用されうる。

p.64 の Table 5-1. Example access classifications based on risk に、アクセスを Public, Sensitive, Highly sensitive の3種類に分類した例を示す。

Best Practices

著者らが推奨する、最小権限モデルを実装する際のベストプラクティスを紹介する。

  • Small Functional APIs
    • さながら UNIX 哲学にあるように、1つのことだけをうまくやる API を作る
    • POSIX API のような巨大な API に対しては、制限や監査ログの記録(audit)は難しい
    • ユーザー向けの API より、管理用の API(Administrative API)のほうが権限が強い分、より一層の注意が必要
  • Breakglass
    • ガラスを割って非常ボタンを押すように、非常時に認証システムをバイパスする仕組みを breakglass mechanism と呼ぶ
    • 予期せぬ状況から復旧するために、このような仕組みは有用
    • For more context, see “Graceful Failure and Breakglass Mechanisms” on page 74 and “Diagnosing Access Denials” on page 73.
  • Auditing
    • 監査ログの記録から、意味のある検知ができるかどうかは、そのシステムの「アクセス制限がどれくらいの粒度か」と「リクエストに関するメタデータがどれくらい明確に記録されるか」に依存している
  • Testing and Least Privilege
    • 最小権限に関連するテストには、2つの重要な次元がある
    • Testing of least privilege
      • 必要なリソースのみに対するアクセス権限を正しく与えられていることを保証する
      • 本番環境への悪影響を避けるため、アクセス権限を実際に変更する前に、このテストは自動実行されるべき。テストカバレッジが不完全な場合は、監視やアラートの仕組みで一部補うこともできる
    • Testing with least privilege
      • テストのためのインフラが、そのテストに必要なアクセスのみを与えられていることを保証する
      • 例えば、新しい機能を開発したい人(データアナリスト)向けに、限られたデータセットへのアクセスのみを許可したいケースがある
      • テスト環境を作る代わりに、本番環境にテストアカウントを作るという方法もあるが、これは監査やACLにノイズを増やすことにもなる。テスト環境は高価だが、それを用意しなかった場合のリスクもある。あなたの状況ごとに、費用対効果をよく考えることが重要
  • Dignosting Access Denials
    • ユーザーの持っている権限によって、アクセス拒否時に渡す情報を変える。情報を与えすぎるとシステムを攻撃するための情報を与えることになりかねないが、与えなさすぎるとセキュリティポリシーチームのサポート負荷が限界を超えてしまう
    • Zero trust model の実装の初期段階では、アクセス拒否時にトークンを提示し、サポートチャンネルに問い合わせさせることを推奨する
  • Graceful Failure and Breakglass Mechanisms
    • breakglass mechanism を導入する際に考慮すべきガイドライン:
      • breakglass mechanism の利用は強く制限されるべき。一般的に、そのシステムの SLA に責任を持つ SRE チームに制限するべき
      • Zero trust networking のための breakglass mechanism は、特定の場所(例えば、物理的なアクセス制限のある panic rooms)からのみ利用できるようにすべき。ネットワーク上の位置を信頼しないという ZTN のコンセプトに反しているように見えるかもしれないが、この場合は、物理的なアクセス制限を前提として信頼する
      • breakglass mechanism の全ての利用は入念に監視されるべき
      • breakglass mechanism は、本番サービスに責任を持つチームによって定期的にテストされるべき

Auditing のベストプラクティスについては、この節のなかで更に深堀りされている。

  • よい監査ログを集める
    • よい監査ログを集めるうえで、Small Functional APIs には強いアドバンテージがある。Small Functional APIs がないと、一時的に強くて広い権限の API へのアクセスを与えるか、事前に監査を受けた上でシステムの直接操作を許可することになる。いずれも監査ログの追跡を難しくする
    • 最終的には、管理用 API と自動化を構築するチームは、監査を容易にする方法でそれらを設計する必要がある。そのためには、監査を重視する文化が必要。文化なしでは、監査は常に承認され、breakglass は日常利用され、意味をなさなくなってしまう
  • 監査者(auditor)を選ぶ
    • 監査の目的次第で、どのような監査者を選ぶべきかは変わる。Google では監査を以下の大きな2カテゴリに分類している
      • ベストプラクティスに沿っていることを確実にするために監査する(Audits to ensure best practices are being followed)
      • セキュリティ違反を特定するために監査する(Audits to identify security breaches)
    • 前者は、例えば breakglass を多用しすぎていないかを毎週の SRE チームミーティングでレビューすること。Google では breakglass review をチームレベルで分散して、社会規範化(social norming)を促進している。これは、現行の administrative APIs の欠点を明らかにし、改善を促すことにもなる
    • 後者は、Google では central auditing team に集約している。そのような攻撃は、複数の組織の監査ログを俯瞰的に見ないとわからないことがある
    • 著者らは、Google にて、structured justification(構造化された正当化)というものを使って、audit log events を構造化されたデータと関連付けている。これは、バグ番号や、チケット番号、カスタマーケース番号などに対する構造化された参照のことである。このような構造化されたデータがあれば、監査ログを自動的に検証できる

Worked Example: Configuration Distribution

具体例として、Web サーバ群に設定ファイルを配布する事例を考える。設定ファイルの管理に関するベストプラクティスは次のようになる。

  1. バージョン管理システム上に設定ファイルを保管する
  2. そのファイルに対する変更をコードレビューする
  3. その変更をまず canary set に配布し、ヘルスチェックを通ったら、すべての Web サーバに対して段階的に配布する。このステップは、この自動的なアクセスに対して、設定ファイルの更新権限を与えることを要求する

この更新機能を実現するために公開する Small API のアプローチはいくつかある。この節では以下の4通りを考える(p.75 の Table 5-2 に詳細)。

  • POSIX API via OpenSSH
  • Software update API
  • Custom OpenSSH ForceCommand
  • Custom HTTP receiver

POSIX API via OpenSSH はシンプルで一般的だが、何でもできてしまうため様々なリスクがある。Software update API は apt-get などのパッケージを使った更新のことだが、バイナリの更新と設定ファイルの更新を同時に行うため、やりたいことに対して操作が高価かつ破壊的すぎる(詳しくは Chapter 9 で触れる)。

Custom OpenSSH ForceCommand は OpenSSH 経由の操作を安全にできるが、いろいろな操作に広げていくことが難しい。OpenSSH の上でいろいろなコマンドを送れるようにしていくと、それは結局独自の RPC メカニズムを作ることになる。それなら gRPC や Thrift の上に作ったほうが良い。

Custom HTTP Receiver は Sidecar として実装する方法と、In-Process に実装する方法がある。In-Process の方法が最も柔軟性があり、Google での設定ファイルの管理方法にとても近い。

A Policy Framework for Authentication and Authorization Decisions

API に対するアクセス制限として、認証(Authentication)と認可(Authorization)の2つのステップをどのように行うかを決めなければいけない。この節では、Google で有用とわかったさまざまなテクニックについて議論する。

  • Using Advanced Authorization Controls
    • 認証、認可を正しく実装するのは大変なので、AWS や GCP が提供する IAM 機能を使ったほうがいい
  • Investing in a Widely Used Authorization Framework
    • 認証、認可のフレームワークを共通化することで、すべてのサービスエンドポイントに対する機能の強化(後述する MFA や MPA のサポート追加など)や設定変更が可能になる
  • Avoiding Potential Pitfalls
    • ポリシー言語の設計は難しい。ポリシー言語をシンプルにしすぎると目的を達成できず、汎用的にしすぎるとその内容を推測するのが難しくなる。いずれかの極端によらないよう、反復的なアプローチで設計するのがよい

Advanced Controls

多くの認可に関する判断は yes か no かの二択になるが、厳密な二択にする代わりに、逃がし弁としての "maybe"(追加のチェック) を用意することで、システムにおける緊張を劇的に緩和できる。

以下で示す方法は、単独でも使えるし、組み合わせて使うこともできる。どのような使い方が適切かは、データの重要性、操作の危険性や、既存のビジネスプロセス次第である。

  • Multi-Party Authorization (MPA)
    • 他の人に認可を与えてもらうこと。ミスを防げる、悪事への抑止効果がある、などのいくつかのメリットがある
    • MPA は、広範なアクセス権限を与えるために使われがちであるが、可能なら Small Functional API と組み合わせたほうがよい
    • MPA を導入するだけでなく、組織的、あるいは仕組み的に No といえる環境を用意する必要がある。悩んだ場合はセキュリティチームにエスカレーションできるような仕組みがあれば、組織的な圧力を弱めることができる
  • Three-Factor Authorization (3FA)
    • 1個のプラットフォーム(例えば全員が共通で使っているワークステーション1台)が攻撃されただけでは権限を奪われないようにしたい。MPA だとこの種の攻撃は防げない
    • 少なくとも2個のプラットフォームを用意したい。安価な方法としては、リスクが高い操作については、セキュリティ的に強化されたモバイルプラットフォーム(hardened mobile platform)からの認可を求める、という方法がある
    • 3FA と Two-factor authentication(2FA、二要素認証)や Multi-factor authentication(MFA、多要素認証)は別のものである。3FA は強い認可を提供するものであり、強い認証を提供するものではない。ただし、3FA の 3 が誤解を招く用語であることは確か。3FA は第2のプラットフォームからの認可を求めるものであり、第3ではない
    • MPAと3FAによって防がれる脅威は別のものである、ということを理解することが重要。そうすることで、一貫したポリシーに基づいて、これらの技術を適用できるようになる
  • Business Justifications
    • 認可を求める際に、ビジネス的な正当性を、バグID、インシデントID、チケットID、ケースID、関連アカウントなどと紐付けることを強制する
    • また、それらの ID に関連して、アクセスを許可するデータも特定のものに限定すべきである。適切に構成されたシステムでは、入力されたチケット ID に関連する、特定の顧客に関連するデータへのアクセスしか許可しないことも可能になる
  • Temporary Access
    • 一時的なアクセス権限を与えることで、認可に関する決定によるリスクを限定することができる。これは、特に、すべての操作に対して細かなアクセス権限を設定できない場合に有用である。また、root 権限のような ambient authority(環境全体に関する認可)を減らすことにも繋がる
  • Proxies
    • バックエンドサービスに対する細かなアクセス権限を設定できない場合、次善の策として、強く監視され、利用が制限されたプロクシ(あるいは踏み台)を用いることができる
    • 例えば、緊急時に、他の API による操作では復旧できない場合に、プロクシ経由の操作を許可する

Tradeoffs and Tensions

最小権限アクセスモデルを導入することで、あなたの組織のセキュリティに対する姿勢は間違いなく改善される。しかし、これまでに説明した利点と、モデルを導入することによるコストを割り引いて考えなければいけない。この節ではそれらのコストについて考える。

  • セキュリティの複雑性が増す。このユーザーはこのデータにアクセスできるのか? このデータにアクセスできるのは誰なのか?
  • すべてのデータへのアクセスを同様に禁じると、情報の透明性がなくなり、組織文化に影響する。例えば、ソースコードも機密情報の一種である。しかし、ある程度のリスクを許容して、開発者にソースコードへの広範なアクセス権限を与えた場合、開発者はソースコードから学んだり、コントリビュートすることができる。この問題については Chapter 21 で詳しく触れる
  • 最小権限を与えるためのデータ(コンテキスト)の品質が悪いと、セキュリティに影響する。セキュリティに影響するデータの品質をできるだけ高くするために、それらのデータを作るシステムをレビューすべきである
  • ワークフローを行うユーザーにとっての利便性が下がる。ユーザーの苦痛をなるべく小さくするためのナビゲーションや、自己診断〜サポートへの連絡を助ける方法が必要である
  • 最小権限のためのモデルが導入されたら、開発者はそれに従う必要がある。開発者にこのモデルを理解してもらうために、トレーニング教材や API ドキュメントを整備すべきである。また、開発者がセキュリティエンジニアによるレビューを気軽かつ迅速に受けられるようにすべきである

Conclusion

複雑なシステムを設計するにあたって、最小権限モデルは、クライアントにその目的を達成するために必要な能力を与え、かつそれ以上のものを与えないための最も安全な方法である。

Google はこのモデルを実装するために莫大な時間および労力を投じてきた。最後に、最小権限モデルを実装するための主要なコンポーネントを挙げる。

  • あなたのシステムの機能に関する包括的な知識。この知識があることで、あなたはそれぞれの機能が持つセキュリティリスクのレベルに従って、それらの機能を分類できる
  • この分類に基づく、できるだけ細かいレベルでの、あなたのシステムおよびあなたのデータへのアクセスの分割(partitioning)
  • あなたのシステムへのアクセスを試みるユーザーのクレデンシャルを検証するための認証システム
  • 適切に分割されたシステムに対してアタッチされた、うまく定義されたセキュリティポリシーを適用するための認可システム
  • 細かな認可を与えるための、高度な制御機能。例えば、一時的な認可、MPA、3FA など
  • これらのキーコンセプトを支えるための、あなたのシステムに対する運用面での要件。少なくとも、以下が必要である
    • すべてのアクセスを監査し、あなたが脅威を見つけてフォレンジック調査を行うきっかけとなるシグナルを生成する能力
    • セキュリティポリシーを論理的に考えて、定義し、テストし、デバッグするための手段、およびそのポリシーに関するエンドユーザーサポートを提供する手段
    • あなたのシステムが想定通りに動かなかった場合に breakglass mechanism を提供する手段

ここまでの感想

5章だけで30ページ近くあり、具体例も豊富で勉強になりました。例えば、Auditor には2種類ある、というのは僕にとっては新しい観点でした。MPA と 3FA で目的が違う(MFA もまた違う)、というのも本書を読んでよく理解できました。

扱われている話題の範囲も広く、Google が実現しているレベルに達するのはかなり大変そうだ……というのが率直な感想です。

その一方で、いままで自分たちもやっていたことだけど、Google ではそう呼んでいるのか、と気づいた部分もありました。システムに対する作業を、今の仕事では Backlog の課題キー(チケット ID に相当するもの)と関連付けて記録しているのですが、これはまさに本章で "Business Justification" と呼ばれているものでした。

Google の事例を見て落ち込みすぎず、本書の内容を手本として、ベストプラクティスをできるところから少しずつ取り入れていきたいですね。