TT Lab
はじめる
学ぶ 学習パス コース

GitOpsとArgo CD

Argo CD の権限はクラスタの権限ではない

TT Labで続きを見る

一言でいうと

Argo CDの中で誰が何をできるかは、Kubernetes RBACではなくargocd-rbac-cmのpolicy.csv1枚が決めます。その判定は、サーバーなしでargocd admin settings rbacを使って、その場で確認できます。

なぜこの区別が必要なのか

GitOpsを有効にした瞬間から、クラスターを実際に変える主体は、人ではなくArgo CDです。Argo CDのコントローラーは、自分が管理するネームスペースに対して非常に広い権限を持っています。そうでなければ、マニフェストに書かれたものを何でも作れないからです。ここに落とし穴があります。クラスターで何の権限もない人がArgo CDのアカウントだけをもらうと、その人が押した同期はArgo CDの権限で実行されます。Kubernetes RBACは、この人を一度も見ていません。

そのため、Argo CDは自分だけの権限の表を別に持っています。argocd-rbac-cm ConfigMapのpolicy.csvキーがそれで、形式はcasbinのポリシーCSVです。行は2種類だけです。

p, <주체>, <자원>, <동작>, <객체>, allow|deny
g, <사용자 또는 그룹>, <역할>

コードブロックの韓国語のプレースホルダーは、順に、主体、リソース、アクション、オブジェクト、ユーザーまたはグループ、ロールです。pは権限の1行、gは所属の1行です。リソースはapplications、applicationsets、projects、clusters、repositories、certificates、gpgkeys、logsのような名前で、アクションはリソースごとに違います。アプリケーションなら、get、create、update、delete、sync、override、そしてリソースのアクションを指すaction/<그룹>/<종류>/<이름>の形まで受け付けます。オブジェクトは、アプリケーションの場合<프로젝트>/<앱이름>の2つの欄です(プレースホルダーは、順にグループ、種類、名前、プロジェクト、アプリ名です)。dev/*はdevプロジェクトのすべてのアプリで、*/*はすべてです。ここで最もよく起きるミスは、オブジェクトにアプリ名だけを書くことです。webは、どのアプリとも一致しません。

どう動くのか

判定には2つの性質があります。1つ目は、denyは行の順序と関係なく勝つことです。上にallowを書いて下にdenyを書いても、逆に書いても、結果は同じです。最初の一致のルールを使うファイアウォールの文法とは違うので、その感覚で読むと間違えます。2つ目は、どの行にも当てはまらなかったリクエストは、policy.defaultが定めたロールで改めて判定されることです。ここにrole:readonlyを書けば、アカウントだけを持つ人は読み取りだけができ、空の値なら何もできません。

グローバルなポリシーとは別に、プロジェクトの中でだけ通用するロールもあります。AppProjectのspec.rolesに、名前とポリシーの行、そして結び付けるグループを書きます。主体の名前はproj:<프로젝트>:<역할>で、文法はグローバルなポリシーとまったく同じです(プレースホルダーは、順にプロジェクトとロールです)。違いは置き場所です。グローバルなポリシーはプラットフォームチームが持つConfigMapで、プロジェクトのロールは、そのプロジェクトを持つチームが自分のマニフェストで直せます。チームが増えるほど、この境界が実際に仕事を減らしてくれます。

最後に、このラボを可能にするツールです。argocd admin settings rbac validateとargocd admin settings rbac canは、Argo CDサーバーに接続しません。--policy-fileで渡したファイルだけを読んで判定します。そのため、ポリシーをコードとして扱い、変更のたびに期待値表を実行する回帰チェックが可能になります。canはデフォルトが厳格モードなので、存在しないリソース名やアクション名を与えると、判定の前に止まって知らせてくれます。綴りを間違えたポリシーをデプロイしたあとで「なぜだめなのか」と悩む時間が、まるごとなくなります。

現場での姿

事故は、たいていこうやって起きます。オンコール担当者が夜間に決済アプリを同期し、その同期が誤ったコミットを反映しました。事後に見ると、ポリシーにはp, role:oncall, applications, sync, prod/*, allowの1行だけがありました。決済は別のチームのアプリなのに、prod/*という1つの欄が、それまで覆っていたのです。直す方法は簡単です。prod/paymentsに対するdenyの1行を足せば、順序と関係なく止まります。難しいのは、直したあとで、ほかのものが壊れていないかを知ることで、それは期待値表を実行して確認するしかありません。

反対方向の事故もよくあります。権限を絞ったら、デプロイの自動化が静かに止まります。CIアカウントが使っていたアクションがsyncではなくaction/apps/Deployment/restartだったり、オブジェクトの表記を*1つの欄だけで書いておいたため、*/*に直す必要があったりした、という具合です。絞る変更ほど、表が必要です。表がなければ誰も絞らず、権限は広がるばかりです。

このラボ環境の限界

ラボのPodには、Argo CDコントローラーもAPIサーバーもありません。そのため、「この人でログインしてボタンを押してみる」はできません。その代わり、ポリシーを実際に解釈するそのコードがCLIの中にそのまま入っているので、判定結果はサーバーが出すのと同じ経路で出ます。本物のコントローラーを起動して、同期が動くのを見るラボは、認定資格コースのほうに別にあります。

次のラボですること

ポリシー1枚を書き、文法を検査し、rbac canで8つのことを尋ねます。わざと欄を抜かしてエラーを見て、denyを上に書いてみて、policy.defaultで見知らぬ人に与える分を決めます。AppProjectにプロジェクトのロールを入れてkwokクラスターに上げ、最後に、期待値表と検査スクリプトを作って、すべてを一度に実行します。