今回はArgoCDでマルチテナント向けに利用できる Project
という機能について整理しようと思います。
ここ最近のArgoCDのアップデートにもProjectに関するものが含まれていたので、そちらも試しています。
Projectとは
ArgoCDでは、Applicationというカスタムリソースの中にGitリポジトリやデプロイ先のクラスター・Namespaceなどの情報を設定します。Projectは、このApplicationを論理的にグループ分けするカスタムリソースです。
ArgoCDでは、複数のチームやプロジェクトで同じArgoCDクラスターを利用する、いわゆるマルチテナントと呼ばれる利用方法を、デフォルトでサポートしています。ArgoCDアカウントとSSOとの統合やRBACの利用などがそれにあたりますが、Projectもその一つに含まれます。
ArgoCDを複数チームが利用する場合、それぞれのチームが利用するApplicationなどのリソースは異なることが多いでしょう。あるチームが別チームのリソースにアクセスできてしまうと、誤ったアプリケーションをデプロイしたり、別のクラスターやNamespaceにデプロイしてしまうような事故にもつながるため、チームごとに利用できるリソースを制限したり条件付けをしたくなります。ArgoCDではProjectを利用することで、それを実現します。
Projectでは、以下のような設定を行い、利用するリソースの制限をすることができます。
なお、ArgoCDはクラスター作成時点でデフォルトのProject ( default
) が作成されます。Application作成時にProjectを指定しないと、デフォルトのProjectと紐づくため、通常Applicationを作成する場合は全てこの default
Projectに分類されます。 default
はGitリポジトリ、クラスター・Namespace、リソースの種類に制限がなく、全ての操作が可能です。
Projectの基本的な使い方
ここからProjectの利用方法をなぞってみます。Projectを利用する場合、ユーザーとProjectとをRBACのポリシーで紐づけます。Projectが利用できるリソースの条件やポリシーを設定し、 argocd-rbac-cm
というConfigMapでユーザーとProjectとを紐づけることで、ユーザーがそのProjectを操作できるようになります。
※Project-Account-RBACの概要図
今回はローカルユーザーを作成し、そのユーザーが一部操作できるようなProjectを作成し、権限を付与して操作できるまでを試します。
ArgoCDはインストール済み、 admin
ユーザーでArgoCDにログインされている前提で進めます。
ローカルユーザーの作成
まずはローカルユーザーを作成します。ローカルユーザーは argocd-cm
ConfigMapに追加することで作成できます。ここでは dev1
というアカウントを作成します。
$ kubectl edit cm argocd-cm -n argocd ~~~ # 以下のようにアカウントを追加 data: accounts.dev1: login ~~~ configmap/argocd-cm edited # 作成後のアカウントの状態 $ argocd account list NAME ENABLED CAPABILITIES admin true login dev1 true login $ kubectl describe cm argocd-cm -n argocd Name: argocd-cm Namespace: argocd Labels: app.kubernetes.io/name=argocd-cm app.kubernetes.io/part-of=argocd Annotations: <none> Data ==== accounts.dev1: ---- login Events: <none>
アカウント作成後はパスワードを設定します。
$ argocd account update-password \ > --account dev1 \ > --current-password <adminユーザーのパスワード> \ > --new-password <dev1に設定するパスワード> Password updated # 設定するパスワードが条件を満たしてないと以下のようなエラーが出力される $ argocd account update-password \ > --account dev1 \ > --current-password <adminユーザーのパスワード> \ > --new-password <dev1に設定するパスワード> FATA[0001] rpc error: code = Unknown desc = New password does not match the following expression: ^.{8,32}$.
上記までで新規アカウントが作成され、ArgoCDにログインできるようになります。
# adminアカウントからログアウトする $ argocd logout a9ed881bfa2bd42248dca9000f88f583-776281281.ap-northeast-1.elb.amazonaws.com:443 Logged out from 'a9ed881bfa2bd42248dca9000f88f583-776281281.ap-northeast-1.elb.amazonaws.com:443' # dev1アカウントでログインする $ argocd login a9ed881bfa2bd42248dca9000f88f583-776281281.ap-northeast-1.elb.amazonaws.com:443 WARNING: server certificate had error: x509: certificate is valid for localhost, argocd-server, argocd-server.argocd, argocd-server.argocd.svc, argocd-server.argocd.svc.cluster.local, not a9ed881bfa2bd42248dca9000f88f583-776281281.ap-northeast-1.elb.amazonaws.com. Proceed insecurely (y/n)? y Username: dev1 Password: <dev1パスワードを入力> 'dev1:login' logged in successfully Context 'a9ed881bfa2bd42248dca9000f88f583-776281281.ap-northeast-1.elb.amazonaws.com:443' updated
試しにArgoCD UIにアクセスしてみると、アカウントも登録されていることが確認できます。
※参考:
Projectの作成
次にProjectを作成します。Projectは AppProject
というカスタムリソースで定義するか、 argocd proj create
コマンドで作成できます。今回利用したyamlファイルはこちらです。
apiVersion: argoproj.io/v1alpha1 kind: AppProject metadata: name: dev1 namespace: argocd spec: description: dev1 project sourceRepos: - '*' destinations: - namespace: '*' server: '*' clusterResourceWhitelist: - group: '*' kind: '*' roles: - name: dev1 description: dev1 role for dev1 project policies: - p, proj:dev1:dev1, applications, get, dev1/*, allow
AppProject
の spec
配下でどんなパラメータが利用できるか記載します。
description
: Projectに関する記述sourceRepos
: 利用を許可するGitリポジトリを指定destinations
: 利用できるデプロイ先を指定namespace
: Namespaceの指定server
: クラスターURLの指定
clusterResourceBlacklist
: 作成を禁止するcluster-scope (StorageClass
CustomResourceDefinition
ClusterRole
など) のリソースを指定group
: Groupの指定kind
: Kindの指定
clusterResourceWhitelist
: 作成を許可するcluster-scopeのリソースを指定group
kind
namespaceResourceBlacklist
: 作成を禁止するnamescope-scope (Pod
Service
ResourceQuota
など)のリソースを指定group
kind
namespaceResourceWhitelist
: 作成を許可するnamescope-scopeのリソースを指定group
kind
orphanedResources
: ArgoCD applicationに属さないリソースのモニタリングを有効化するとき、warningメッセージを出すか否かwarn
:true
/false
を指定
roles
: Projectと紐づけるRBACロールを指定name
: Role名description
: Roleの説明policies
: ポリシーの記載groups
: Roleと紐づけるOIDCグループjwtTokens
: Roleと紐づけるJWTトークン
signatureKeys
: Gitコミットに対してSyncを許可するためのPGP keyのリストkeyID
: 16進数で示したキーID
syncWindows
: Project内でSyncを実行するタイミングを制御するapplications
: アプライするApplicationのリストclusters
: アプライするクラスターのリストduration
: Syncを許可する有効期間を指定kind
: Syncを許可するか禁止するかを指定。allow
/deny
を選択manualSync
: 手動Syncを許可するか否か。true
false
を選択namespaces
: アプライするnamespaceのリストschedule
: Cron形式でSyncを開始するスケジュールを指定timeZone
: TimeZoneの指定
このうち spec.roles.policies
は Casbin-format形式でポリシーを定義します。ポリシーの記載は以下のようなルールで行います。
p, <role/user/group>, <resource>, <action>, <object>, allow
resource
action
で利用できるのは、以下の通りです。
resource
:clusters
projects
applications
repositories
certificates
accounts
gpgkeys
action
:get
create
update
delete
sync
override
action
例えば proj-name
というProjectにある role-name
というroleに対し、同じProject内の Application
へのアクセスを許可する場合は、以下のように設定します。
spec: roles: - name: test-role policies: - p, proj:proj-name:role-name, applications, get, proj-name/*, allow
なお、後ほど出てきますが、AppProject
でポリシーを指定する場合、resource
で利用できるのは applications
だけのようです。Projectで利用するポリシーは argocd-rbac-cm
ConfigMapでも指定できるのですが、そちらでは repositories
など別の resource
も指定できます。
ここで上記 AppProject
の定義ファイルをデプロイします。なお、デプロイ前には default
というProjectが既にあることも確認できます。
# adminアカウントでArgoCDにログインし直す $ argocd login a9ed881bfa2bd42248dca9000f88f583-776281281.ap-northeast-1.elb.amazonaws.com:443 # default Projectの確認 $ argocd proj list NAME DESCRIPTION DESTINATIONS SOURCES CLUSTER-RESOURCE-WHITELIST NAMESPACE-RESOURCE-BLACKLIST SIGNATURE-KEYS ORPHANED-RESOURCES default *,* * */* <none> <none> disabled # dev1 Projectの作成 $ kubectl apply -f proj-dev1.yaml appproject.argoproj.io/dev1 created # 作成後の確認 $ argocd proj list NAME DESCRIPTION DESTINATIONS SOURCES CLUSTER-RESOURCE-WHITELIST NAMESPACE-RESOURCE-BLACKLIST SIGNATURE-KEYS ORPHANED-RESOURCES default *,* * */* <none> <none> disabled dev1 dev1 project *,* * */* <none> <none> disabled $ argocd proj get dev1 Name: dev1 Description: dev1 project Destinations: *,* Repositories: * Allowed Cluster Resources: */* Denied Namespaced Resources: <none> Signature keys: <none> Orphaned Resources: disabled # dev1 ProjectのRoleを確認 $ argocd proj role list dev1 ROLE-NAME DESCRIPTION dev1 dev1 role for dev1 project
RBACの設定
次にRBACの設定を行うため、 argocd-rbac-cm
ConfigMapを修正します。ここでアカウントとRoleを紐づけることで、アカウントからProjectを操作することが可能になります。
argocd-rbac-cm
では policy.csv
というdataを指定し、そこに必要なポリシーを定義します。ここでは先ほどProjectで設定したRBACポリシーとアカウントとを紐づける設定を行います。アカウントとポリシーとを紐づける場合は、以下のようなルールで定義します。
g, <user/group>, <role>
なお、ArgoCDはデフォルトで readonly
admin
というポリシーと admin
アカウントが用意されています。ポリシーの内容は builtin-policy.csvで定義されており、 admin
アカウントは全てのリソースに対する権限を持っています。
$ kubectl edit cm argocd-rbac-cm -n argocd
~~~
(中略)
data:
policy.csv: |
g, dev1, proj:dev1:dev1
~~~
configmap/argocd-rbac-cm edited
アクセスのテスト
ここまでで dev1
アカウントから dev1
Project中のApplicationにアクセスできる設定が完了しました。ここで dev1
Projectにテスト用のApplicationを作成してみます。
admin
アカウントでArgoCD UIにログインし、テスト用Applicationを作成します。
次に dev1
アカウントでUIにログインすると、上記Applicationは画面上に確認できます。一方で、上記ApplicationをSync / deleteなどしようとすると、権限が足りないというメッセージが表示されます。
dev1
Projectに新しいApplicationを作成しようとしても、同じくエラーメッセージが出力されます。
またProjectの画面を見ると、 dev1
Projectのみが表示されており、 default
Projectは確認ができません。
一応CLIからも試すと、同様にエラーメッセージが出力されます。
# dev1アカウントでログイン $ argocd login a9ed881bfa2bd42248dca9000f88f583-776281281.ap-northeast-1.elb.amazonaws.com:443 # Applicationは確認できる $ argocd app list NAME CLUSTER NAMESPACE PROJECT STATUS HEALTH SYNCPOLICY CONDITIONS REPO PATH TARGET guestbook-admin https://kubernetes.default.svc default dev1 Synced Healthy <none> <none> https://github.com/argoproj/argocd-example-apps.git guestbook HEAD # sync / deleteはできない $ argocd app sync guestbook-admin FATA[0000] rpc error: code = PermissionDenied desc = permission denied: applications, sync, dev1/guestbook-admin, sub: dev1, iat: 2021-12-29T04:08:03Z $ argocd app delete guestbook-admin Are you sure you want to delete 'guestbook-admin' and all its resources? [y/n] y FATA[0002] rpc error: code = PermissionDenied desc = permission denied: applications, delete, dev1/guestbook-admin, sub: dev1, iat: 2021-12-29T04:08:03Z # dev1 Projectしか確認できない $ argocd proj list NAME DESCRIPTION DESTINATIONS SOURCES CLUSTER-RESOURCE-WHITELIST NAMESPACE-RESOURCE-BLACKLIST SIGNATURE-KEYS ORPHANED-RESOURCES dev1 dev1 project *,* * */* <none> <none> disabled
簡単ですが、ここまででProjectの基本的な利用方法を紹介しました。
※参考:
- GitHub - argoproj/argo-cd: project.yaml
- GitHub - argoproj/argo-cd: appproject-crd.yaml
- ArgoCD Doc - Sync Windows
- ArgoCD: users, access, and RBAC
Projectに関連する機能
ここからProjectと関連する他の機能を紹介します。
Orphaned Resource Monitoring
Orphaned resource monitoringは、ArgoCD Applicationで管理されないリソースを検知し、ArgoCD UIからそのリソースを確認・削除する機能を提供します。Orphaned resource monitoringを有効化するかは AppProject
の定義で制御することができます。
ここでは先ほどの dev1
Projectの定義に、以下のようにOrphaned resourceの設定を追加します。
apiVersion: argoproj.io/v1alpha1 kind: AppProject metadata: name: dev1 namespace: argocd spec: (中略) orphanedResources: warn: true
設定後に再度デプロイすると、UIからApplicationでの管理外リソースとして kubernetes
EndpointSliceを検知します。
$ kubectl apply -f proj-dev1.yaml appproject.argoproj.io/dev1 configured $ argocd proj get dev1 Name: dev1 Description: dev1 project Destinations: *,* Repositories: * Allowed Cluster Resources: */* Denied Namespaced Resources: <none> Signature keys: <none> Orphaned Resources: enabled (warn=true) $ kubectl get endpoints NAME ENDPOINTS AGE guestbook-ui 192.168.2.161:80 64m kubernetes 192.168.0.202:443,192.168.2.65:443 3h2m
※参考:
Global Project
Global Projectは ver 1.8で追加された機能で、他のProjectに同じ設定を引き継ぐことができます。
ここではまず以下のようにGlobal Projectのソースとなる global
Projectを作成します。 global
には sourceRepos
destinations
の設定をしています。
apiVersion: argoproj.io/v1alpha1 kind: AppProject metadata: name: global namespace: argocd spec: description: global project sourceRepos: - '*' destinations: - namespace: 'argocd' server: '*'
# global Projectの作成 $ kubectl apply -f proj-global.yaml appproject.argoproj.io/global created $ argocd proj list NAME DESCRIPTION DESTINATIONS SOURCES CLUSTER-RESOURCE-WHITELIST NAMESPACE-RESOURCE-BLACKLIST SIGNATURE-KEYS ORPHANED-RESOURCES default *,* * */* <none> <none> disabled dev1 dev1 project *,* * */* <none> <none> enabled (warn=true) global global project *,argocd * <none> <none> <none> disabled
次にGlobal Projectの設定を argocd-cm
ConfigMapに追加します。 argocd-cm
には、Global Projectの指定と、どのProjectに設定を引き継ぐかの条件を追加します。現在は Project
にラベルを付与し、その設定から条件に合致するものを検索する形でのみ利用できるようです。
argocd-cm
には以下のように data.globalProjects
という設定を追加し、 matchExpressions
にてラベルの条件を記載します。
$ kubectl edit cm argocd-cm -n argocd ~~~ (中略) data: accounts.dev1: login globalProjects: |- - labelSelector: matchExpressions: - key: env operator: In values: - dev projectName: global kind: ConfigMap ~~~ configmap/argocd-cm edited # 追加後の設定内容 $ kubectl describe cm argocd-cm -n argocd Name: argocd-cm Namespace: argocd Labels: app.kubernetes.io/name=argocd-cm app.kubernetes.io/part-of=argocd Annotations: <none> Data ==== accounts.dev1: ---- login globalProjects: ---- - labelSelector: matchExpressions: - key: env operator: In values: - dev projectName: global Events: <none>
次に、条件に合致するような test
Projectを作成します。こちらには clusterResourceWhitelist
のみを設定しています。 global
Projectから設定を引き継ぐことができれば、ここに sourceRepos
destinations
が追加されるはずです。
apiVersion: argoproj.io/v1alpha1 kind: AppProject metadata: name: test namespace: argocd labels: env: dev spec: description: test project clusterResourceWhitelist: - group: '*' kind: '*'
ここで上記Projectを作成してみますが、CLIからは特に設定が引き継がれていないように見えます。
# test Projectの作成 $ kubectl apply -f proj-test.yaml appproject.argoproj.io/test created # 作成後の設定内容 $ argocd proj list NAME DESCRIPTION DESTINATIONS SOURCES CLUSTER-RESOURCE-WHITELIST NAMESPACE-RESOURCE-BLACKLIST SIGNATURE-KEYS ORPHANED-RESOURCES default *,* * */* <none> <none> disabled dev1 dev1 project *,* * */* <none> <none> enabled (warn=true) global global project *,argocd * <none> <none> <none> disabled test test project <none> <none> */* <none> <none> disabled $ kubectl describe appproject test -n argocd Name: test Namespace: argocd Labels: env=dev Annotations: <none> API Version: argoproj.io/v1alpha1 Kind: AppProject Metadata: Creation Timestamp: 2021-12-29T05:20:47Z Generation: 1 Managed Fields: API Version: argoproj.io/v1alpha1 Fields Type: FieldsV1 fieldsV1: f:metadata: f:annotations: .: f:kubectl.kubernetes.io/last-applied-configuration: f:labels: .: f:env: f:spec: .: f:clusterResourceWhitelist: f:description: Manager: kubectl-client-side-apply Operation: Update Time: 2021-12-29T05:20:47Z Resource Version: 28205 UID: 5064245e-8823-4dd4-85c2-9cbbbb301c71 Spec: Cluster Resource Whitelist: Group: * Kind: * Description: test project Events: <none>
一方でUIを確認すると、こちらでは以下のようにGlobal Projectの設定を引き継ぎ、 sourceRepos
destinations
の設定が追加されています。
UIでは Inherited from Global Projects
というパートもあり、こちらにはGlobal Projectの設定内容が確認できます。
このあと作成した global-test
ProjectでApplicationを作成しましたが、argocd
namespaceを指定することで作成できることを確認できました。また argocd
でなく default
namespaceを指定するとエラーが出力されるのも確認できました。
$ argocd app list NAME CLUSTER NAMESPACE PROJECT STATUS HEALTH SYNCPOLICY CONDITIONS REPO PATH TARGET guestbook-admin https://kubernetes.default.svc default dev1 Synced Healthy <none> OrphanedResourceWarning https://github.com/argoproj/argocd-example-apps.git guestbook HEAD guestbook-test https://kubernetes.default.svc argocd test Synced Healthy <none> <none> https://github.com/argoproj/argocd-example-apps.git guestbook HEAD
※参考:
Project scoped Repositories & Clusters
Project scoped Repositories / Clustersは ver 2.2で追加された機能です。概要は以前触れましたが、Project単位で別れたチームに対し、リポジトリ・クラスターを追加する権限を付与するための機能になります。
ここでは argocd-rbac-cm
ConfigMapにポリシーを追加することで、 dev1
アカウントがリポジトリを追加できるようにします。当初は先ほどと同様Projectの中にポリシーを追加しようとしたのですが、ProjectからはApplicationリソースに対するポリシーしか設定することができないようです。
こちらのIssueに対応策が書かれていたのですが、将来的にはProjectからも repositories
など別のリソースに対するポリシーを設定できるようになるかもしれません。
まずは検証用に project-scoped
Projectを作成します。
kind: AppProject metadata: name: project-scoped namespace: argocd spec: description: project-scoped project sourceRepos: - '*' destinations: - namespace: '*' server: '*' clusterResourceWhitelist: - group: '*' kind: '*' roles: - name: dev1-scope description: role for project-scoped repo policies: - p, proj:project-scoped:dev1-scope, applications, *, project-scoped/*, allow
# dev1 アカウントでログイン $ argocd login a9ed881bfa2bd42248dca9000f88f583-776281281.ap-northeast-1.elb.amazonaws.com:443 # project-scoped Projectの作成 $ kubectl apply -f proj-scoped.yaml appproject.argoproj.io/project-scoped configured # 作成後の確認 $ argocd proj list NAME DESCRIPTION DESTINATIONS SOURCES CLUSTER-RESOURCE-WHITELIST NAMESPACE-RESOURCE-BLACKLIST SIGNATURE-KEYS ORPHANED-RESOURCES dev1 dev1 project *,* * */* <none> <none> enabled (warn=true) project-scoped project-scoped project *,* * */* <none> <none> disabled
次にここでは以下のように argocd-rbac-cm
ConfigMapに dev1-scope
というポリシーを追加し、 dev1-scope
ポリシーと dev1
アカウントを紐づけます。
$ kubectl edit cm argocd-rbac-cm -n argocd
~~~
(中略)
data:
policy.csv: |
g, dev1, proj:dev1:dev1
p, role:dev1-scope, repositories, *, project-scoped/*, allow
g, dev1, role:dev1-scope
~~~
configmap/argocd-rbac-cm edited
最後に project-scoped
Projectを指定しながらリポジトリを追加します。なお argocd repo add
コマンドで --project
オプションが有効なのは ver 2.2以降なので、 argocd
CLIのバージョンが古い場合はアップデートが必要です。
# リポジトリを追加 $ argocd repo add --name project-scoped https://github.com/argoproj/argocd-example-apps.git --project project-scoped Repository 'https://github.com/argoproj/argocd-example-apps.git' added $ argocd repo list TYPE NAME REPO INSECURE OCI LFS CREDS STATUS MESSAGE PROJECT git project-scoped https://github.com/argoproj/argocd-example-apps.git false false false false Successful project-scoped
※参考:
その他
Projectをどのように分割するかについては、具体的な事例も少なく、まだBest practiceのようなものは共有されていないようです。ArgoCDでApp-of-appsパターンを利用する場合のProjectについては、こちらの記事が参考になりそうです。
※参考: