TT Lab
Get started
Learn Learning paths Courses

GitOps and Argo CD

Nothing Happened When They Hit Sync: Judging Policy From a File

Continue in TT Lab

Goal

You write argocd-rbac-cm's policy.csv yourself and check the syntax and the verdicts without a server with argocd admin settings rbac. When you are done, you can keep in the repository, together with the policy, a table of "this person must be able to do this", and run it as a regression check.

Why it matters

Argo CD's permissions are a different table from Kubernetes RBAC. Even a person with no permissions in the cluster can, with just an Argo CD account, synchronize anything with the permissions Argo CD holds — so the real boundary is drawn by policy.csv. But it used to be hard to know whether this file was right until you deployed it and had a person press it. argocd admin settings rbac makes this verdict from local files alone. That makes it possible to treat the policy as code and run the expectation table every time you change it. Once permissions are widened, nobody narrows them — all the more so if the grounds for narrowing do not remain as a table.

Steps

  1. Create /root/ga-rbac/policy.csv. It is two lines that let role:dev, on applications of the dev project, do get and sync (two p lines), and one line that binds alice to that role (a g line). argocd admin settings rbac validate --policy-file /root/ga-rbac/policy.csv must pass.
  2. Write four lines in /root/ga-rbac/can-basic.txt. Each line separates four fields with spaces: <주체> <동작> <객체> <답> (subject, action, object, answer). The four things to ask are alice get dev/web, alice sync dev/web, alice sync prod/web, and bob sync dev/web, and for the answer, write as it is the output of argocd admin settings rbac can <주체> <동작> applications <객체> --policy-file, Yes or No.
  3. In /root/ga-rbac/broken.csv, write a deliberately wrong policy — just, in a p line, leave out the last allow/deny field. Save the output of argocd admin settings rbac validate --policy-file /root/ga-rbac/broken.csv, including standard error, to /root/ga-rbac/broken.txt. That file must contain a sentence saying the policy is not valid.
  4. Create /root/ga-rbac/deny.csv. role:oncall can, on prod/*, sync, but only prod/payments is deny. Write the deny line before the allow line, and bind carol to that role. Then, in /root/ga-rbac/deny.txt, write two lines — prod/web <답> and prod/payments <답> (the placeholder stands for the answer).
  5. Write /root/ga-rbac/argocd-rbac-cm.yaml in the shape of a ConfigMap. The name is argocd-rbac-cm, the namespace is argocd, data.policy.default is role:readonly, and data.policy.csv holds the three lines from step 1 as they are. This file can also be passed as it is with --policy-file — ask, with zoe, who is in no role, get and sync to check the difference.
  6. Ask rbac can with a resource name that does not exist (workloads) and an action that does not exist (deploy), and append both outputs, including standard error, to /root/ga-rbac/strict.txt. Then save the result of the same questions with --strict=false added to /root/ga-rbac/loose.txt. For the policy file, use the policy.csv from step 1.
  7. In the argocd namespace of the kwok cluster, create an AppProject ga-team-a (save it as /root/ga-rbac/appproject.yaml and then apply it). In spec.roles, put a role named deployer, and put in the one policy line p, proj:ga-team-a:deployer, applications, sync, ga-team-a/*, allow and the group ga-team-a-oncall. Also write the same policy line and g, dana, proj:ga-team-a:deployer in /root/ga-rbac/project-role.csv and check with rbac can that the verdict is the same.
  8. In /root/ga-rbac/matrix.tsv, write six or more lines separated by tabs — <주체>\t<동작>\t<자원>\t<객체>\t<기대> (subject, action, resource, object, expectation), where the expectation is Yes or No. There must be at least one line each of Yes and No. /root/ga-rbac/check-rbac.sh reads this table line by line, and for /root/ga-rbac/argocd-rbac-cm.yaml, runs rbac can, prints OK … if it matches and MISMATCH … if not to standard output only, and must end with a non-zero code if even one line is wrong. Save that output to /root/ga-rbac/matrix-result.txt.

Notes

Write one policy and check its syntax

Create /root/ga-rbac/policy.csv. It is two lines that let role:dev, on applications of the dev project, do get and sync (two p lines), and one line that binds alice to that role (a g line). argocd admin settings rbac validate --policy-file /root/ga-rbac/policy.csv must pass.

A p line has six fields: p, <주체>, <자원>, <동작>, <객체>, allow|deny (subject, resource, action, object, allow or deny). The resource is applications, and the object is in the <프로젝트>/<앱이름> notation (project/app name), so the whole dev project is dev/*. A g line has three fields: g, <사용자>, <역할> (user, role).

Ask the policy directly

Write four lines in /root/ga-rbac/can-basic.txt. Each line separates four fields with spaces: <주체> <동작> <객체> <답> (subject, action, object, answer). The four things to ask are alice get dev/web, alice sync dev/web, alice sync prod/web, and bob sync dev/web, and for the answer, write as it is the output of argocd admin settings rbac can <주체> <동작> applications <객체> --policy-file, Yes or No.

The last line of the command is Yes or No. It also tells you with the exit code — 0 for Yes, 1 for No. Think about the fact that bob is bound to no role.

How a line with too few fields shows up

In /root/ga-rbac/broken.csv, write a deliberately wrong policy — just, in a p line, leave out the last allow/deny field. Save the output of argocd admin settings rbac validate --policy-file /root/ga-rbac/broken.csv, including standard error, to /root/ga-rbac/broken.txt. That file must contain a sentence saying the policy is not valid.

To capture both output and errors, use > 파일 2>&1 (the placeholder stands for the file). This command gives a non-zero exit code when the policy is wrong, so be careful that your answer sheet does not stop with a failure either.

deny wins regardless of line order

Create /root/ga-rbac/deny.csv. role:oncall can, on prod/*, sync, but only prod/payments is deny. Write the deny line before the allow line, and bind carol to that role. Then, in /root/ga-rbac/deny.txt, write two lines — prod/web <답> and prod/payments <답> (the placeholder stands for the answer).

casbin does not use a first-match rule; it rejects if even one deny matches. So the result is the same even if you swap the order — check it for yourself.

What to give someone who is in no role

Write /root/ga-rbac/argocd-rbac-cm.yaml in the shape of a ConfigMap. The name is argocd-rbac-cm, the namespace is argocd, data.policy.default is role:readonly, and data.policy.csv holds the three lines from step 1 as they are. This file can also be passed as it is with --policy-file — ask, with zoe, who is in no role, get and sync to check the difference.

policy.default is the role to apply to requests that match no policy. role:readonly is a built-in role that Argo CD holds by default, so it exists even if you do not write it in policy.csv. Write a multi-line value of a ConfigMap as a | block.

A resource name or action that does not exist is caught at the asking stage

Ask rbac can with a resource name that does not exist (workloads) and an action that does not exist (deploy), and append both outputs, including standard error, to /root/ga-rbac/strict.txt. Then save the result of the same questions with --strict=false added to /root/ga-rbac/loose.txt. For the policy file, use the policy.csv from step 1.

rbac can is strict by default — it checks the resource and action names against the lists Argo CD knows and stops right away on an unknown name. It is better than finding out after deploying a typo'd policy. If you give --strict=false, it skips the check and just makes the verdict.

Create a role that works only inside a project

In the argocd namespace of the kwok cluster, create an AppProject ga-team-a (save it as /root/ga-rbac/appproject.yaml and then apply it). In spec.roles, put a role named deployer, and put in the one policy line p, proj:ga-team-a:deployer, applications, sync, ga-team-a/*, allow and the group ga-team-a-oncall. Also write the same policy line and g, dana, proj:ga-team-a:deployer in /root/ga-rbac/project-role.csv and check with rbac can that the verdict is the same.

The subject name of a project role has the format proj:<프로젝트>:<역할> (project, role). It has the same syntax as the global policy.csv, so you can check it with the same tool — the difference is that this line lives inside the AppProject and so the project's administrators can edit it themselves.

Make a table and run it every time the policy changes

In /root/ga-rbac/matrix.tsv, write six or more lines separated by tabs — <주체>\t<동작>\t<자원>\t<객체>\t<기대> (subject, action, resource, object, expectation), where the expectation is Yes or No. There must be at least one line each of Yes and No. /root/ga-rbac/check-rbac.sh reads this table line by line, and for /root/ga-rbac/argocd-rbac-cm.yaml, runs rbac can, prints OK … if it matches and MISMATCH … if not to standard output only, and must end with a non-zero code if even one line is wrong. Save that output to /root/ga-rbac/matrix-result.txt.

The expected values in the table are based on the ConfigMap of step 5 — alice can synchronize dev, and someone who is in no role can only read thanks to policy.default. If the script writes files itself, it overwrites the student's outputs when the grader runs it again. Send to standard output only, and have a person do the redirection.