TECHSTEP

ITインフラ関連の記事を公開してます。

ArgoCD Projectの使い方を整理する

今回は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の概要図

f:id:FY0323:20211230212556p:plain

今回はローカルユーザーを作成し、そのユーザーが一部操作できるような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にアクセスしてみると、アカウントも登録されていることが確認できます。

f:id:FY0323:20211230212828p:plain

※参考:

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

AppProjectspec 配下でどんなパラメータが利用できるか記載します。


  • 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を作成します。

f:id:FY0323:20211230214343p:plain

次に dev1 アカウントでUIにログインすると、上記Applicationは画面上に確認できます。一方で、上記ApplicationをSync / deleteなどしようとすると、権限が足りないというメッセージが表示されます。

f:id:FY0323:20211230214510p:plain

f:id:FY0323:20211230214607p:plain

dev1 Projectに新しいApplicationを作成しようとしても、同じくエラーメッセージが出力されます。

f:id:FY0323:20211230214727p:plain

またProjectの画面を見ると、 dev1 Projectのみが表示されており、 default Projectは確認ができません。

f:id:FY0323:20211230214838p:plain

一応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の基本的な利用方法を紹介しました。

※参考:

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

f:id:FY0323:20211230215459p:plain

※参考:

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 の設定が追加されています。

f:id:FY0323:20211230220139p:plain

UIでは Inherited from Global Projects というパートもあり、こちらにはGlobal Projectの設定内容が確認できます。

f:id:FY0323:20211230220307p:plain

このあと作成した 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については、こちらの記事が参考になりそうです

※参考: