Skip to content

Commit 08ce44f

Browse files
authored
Merge pull request #231 from projectsyn/feat/custom-roles
Implement support for deploying custom ArgoCD cluster roles
2 parents 5063756 + 46b1226 commit 08ce44f

File tree

9 files changed

+535
-2
lines changed

9 files changed

+535
-2
lines changed

class/defaults.yml

+2
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,8 @@ parameters:
121121
manifests_version: ${argocd:images:argocd_operator:tag}
122122
cluster_scope_namespaces:
123123
- "${argocd:namespace}"
124+
controller_cluster_role_selectors: {}
125+
server_cluster_role_selectors: {}
124126
env: {}
125127
kustomization_url: https://github.com/argoproj-labs/argocd-operator//config/default/
126128
kustomize_input:

component/common.libsonnet

+6
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,15 @@ local render_image(imagename, include_tag=false) =
2323
else
2424
image;
2525

26+
local custom_cr_name(component) =
27+
assert
28+
std.member([ 'server', 'application-controller' ], component) :
29+
'Custom clusterrole only supported for server and application-controller';
30+
'syn:argocd-%s:custom' % component;
2631

2732
{
2833
render_image: render_image,
2934
evaluate_log_level: evaluate_log_level,
3035
evaluate_log_format: evaluate_log_format,
36+
custom_cluster_role_name: custom_cr_name,
3137
}

component/operator.jsonnet

+31-2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
local common = import 'common.libsonnet';
12
local com = import 'lib/commodore.libjsonnet';
23
local kap = import 'lib/kapitan.libjsonnet';
34

@@ -7,8 +8,36 @@ local params = inv.parameters.argocd.operator;
78
local image = params.images.argocd_operator;
89
local rbac = params.images.kube_rbac_proxy;
910

11+
local managed_custom_controller_cr =
12+
std.length(std.prune(std.objectValues(params.controller_cluster_role_selectors))) > 0;
13+
local managed_custom_server_cr =
14+
std.length(std.prune(std.objectValues(params.server_cluster_role_selectors))) > 0;
15+
1016
local skip(var) =
11-
var == 'ARGOCD_CLUSTER_CONFIG_NAMESPACES';
17+
(var == 'ARGOCD_CLUSTER_CONFIG_NAMESPACES') ||
18+
(managed_custom_controller_cr && var == 'CONTROLLER_CLUSTER_ROLE') ||
19+
(managed_custom_server_cr && var == 'SERVER_CLUSTER_ROLE');
20+
21+
local managed_clusterrole_patches =
22+
local controller_patch =
23+
if managed_custom_controller_cr then {
24+
op: 'add',
25+
path: '/spec/template/spec/containers/1/env/-',
26+
value: {
27+
name: 'CONTROLLER_CLUSTER_ROLE',
28+
value: common.custom_cluster_role_name('application-controller'),
29+
},
30+
};
31+
local server_patch =
32+
if managed_custom_server_cr then {
33+
op: 'add',
34+
path: '/spec/template/spec/containers/1/env/-',
35+
value: {
36+
name: 'SERVER_CLUSTER_ROLE',
37+
value: common.custom_cluster_role_name('server'),
38+
},
39+
};
40+
std.prune([ controller_patch, server_patch ]);
1241

1342
local kustomize_patch_scopens = if std.length(params.cluster_scope_namespaces) > 0 then {
1443
patches+: [
@@ -33,7 +62,7 @@ local kustomize_patch_scopens = if std.length(params.cluster_scope_namespaces) >
3362
}
3463
for envvar in std.objectFields(params.env)
3564
if !skip(envvar)
36-
]),
65+
] + managed_clusterrole_patches),
3766
target: {
3867
kind: 'Deployment',
3968
name: 'argocd-operator-controller-manager',

component/rbac.jsonnet

+225
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
local common = import 'common.libsonnet';
12
local kap = import 'lib/kapitan.libjsonnet';
23
local kube = import 'lib/kube.libjsonnet';
34
local inv = kap.inventory();
@@ -54,7 +55,231 @@ local aggregated_rbac = [
5455
},
5556
];
5657

58+
// Create custom cluster roles that are suitable to be used for
59+
// argocd-operator's `CONTROLLER_CLUSTER_ROLE` and `SERVER_CLUSTER_ROLE`, cf.
60+
// https://argocd-operator.readthedocs.io/en/latest/usage/custom_roles/
61+
// NOTE(sg): we only deploy these cluster roles if the respective
62+
// `cluster_role_match_labels` parameter in `operator` isn't empty.
63+
local internalControllerAggregationLabel = {
64+
'rbac.argocd.syn.tools/aggregate-to-controller': 'true',
65+
};
66+
67+
local custom_controller_cr =
68+
kube.ClusterRole(common.custom_cluster_role_name('application-controller')) {
69+
metadata+: {
70+
annotations+: {
71+
'syn.tools/description': |||
72+
Custom ClusterRole which is used to give the ArgoCD application
73+
controller access to managed namespaces. Intended to be configured
74+
as `CONTROLLER_CLUSTER_ROLE` on the operator.
75+
76+
This ClusterRole aggregates the rules of all clusterroles which
77+
match one of the selectors in `aggregationRule.clusterRoleSelectors`.
78+
|||,
79+
},
80+
},
81+
rules:: [],
82+
aggregationRule: {
83+
clusterRoleSelectors: [
84+
{ matchLabels: internalControllerAggregationLabel },
85+
] + [
86+
sel
87+
for sel in
88+
std.objectValues(params.operator.controller_cluster_role_selectors)
89+
if sel != null
90+
],
91+
},
92+
};
93+
94+
local controller_required_cr =
95+
kube.ClusterRole('syn:argocd-application-controller:required') {
96+
metadata+: {
97+
labels+: internalControllerAggregationLabel,
98+
annotations+: {
99+
'syn.tools/description': |||
100+
Custom ClusterRole which is used to aggregate a basic set of
101+
permissions to the custom ArgoCD application controller clusterrole
102+
`%s`.
103+
104+
This rule is required to allow the application controller to run
105+
normally (emitting events, updating application status, etc)
106+
||| % common.custom_cluster_role_name('application-controller'),
107+
},
108+
},
109+
rules: [
110+
// NOTE(sg): I wasn't able to fully determine why this is required, but
111+
// the application controller logs errors when it doesn't have
112+
// permissions to read secrets in target namespaces.
113+
{
114+
apiGroups: [ '' ],
115+
resources: [ 'secrets' ],
116+
verbs: [
117+
'get',
118+
'list',
119+
'watch',
120+
],
121+
},
122+
// Required to access and update argocd apps which the controller is
123+
// managing.
124+
{
125+
apiGroups: [ 'argoproj.io' ],
126+
resources: [
127+
'applications',
128+
'applicationsets',
129+
'appprojects',
130+
],
131+
verbs: [
132+
'create',
133+
'get',
134+
'list',
135+
'watch',
136+
'update',
137+
'delete',
138+
'patch',
139+
],
140+
},
141+
// Required to emit events for app state changes and similar
142+
{
143+
apiGroups: [ '' ],
144+
resources: [ 'events' ],
145+
verbs: [ 'create', 'list', 'patch' ],
146+
},
147+
// Required for running hook jobs
148+
{
149+
apiGroups: [ 'batch' ],
150+
resources: [
151+
'jobs',
152+
],
153+
verbs: [
154+
'create',
155+
'get',
156+
'list',
157+
'watch',
158+
'update',
159+
'delete',
160+
'patch',
161+
],
162+
},
163+
],
164+
};
165+
166+
local internalServerAggregationLabel = {
167+
'rbac.argocd.syn.tools/aggregate-to-server': 'true',
168+
};
169+
170+
local custom_server_cr =
171+
kube.ClusterRole(common.custom_cluster_role_name('server')) {
172+
metadata+: {
173+
annotations+: {
174+
'syn.tools/description': |||
175+
Custom ClusterRole which is used to give the ArgoCD server access to
176+
managed namespaces. Intended to be configured as
177+
`SERVER_CLUSTER_ROLE` on the operator.
178+
179+
This ClusterRole aggregates the rules of all clusterroles which
180+
match one of the selectors in `aggregationRule.clusterRoleSelectors`.
181+
|||,
182+
},
183+
},
184+
rules:: [],
185+
aggregationRule: {
186+
clusterRoleSelectors: [
187+
{ matchLabels: internalServerAggregationLabel },
188+
] + [
189+
sel
190+
for sel in
191+
std.objectValues(params.operator.server_cluster_role_selectors)
192+
if sel != null
193+
],
194+
},
195+
};
196+
197+
local server_required_cr =
198+
kube.ClusterRole('syn:argocd-server:required') {
199+
metadata+: {
200+
labels+: internalServerAggregationLabel,
201+
annotations+: {
202+
'syn.tools/description': |||
203+
Custom ClusterRole which is used to aggregate some required rules to
204+
the ArgoCD server custom clusterrole `%s`.
205+
206+
The rules in this role are necessary to allow the ArgoCD server to
207+
fetch the information it needs to display its web interface and to
208+
perform operations on its managed ArgoCD apps.
209+
||| % common.custom_cluster_role_name('server'),
210+
},
211+
},
212+
rules: [
213+
// required to determine status of resources to display in the web
214+
// interface. In theory, this could be restricted, but the main really
215+
// critical namespaced resource is `secrets` which we need to give full
216+
// permissions on anyway.
217+
{
218+
apiGroups: [ '*' ],
219+
resources: [ '*' ],
220+
verbs: [ 'get' ],
221+
},
222+
// required so users can configure ArgoCD from the server web interface.
223+
// The server doesn't work correctly without this even if no
224+
// configuration via web interface is required.
225+
{
226+
apiGroups: [ '' ],
227+
resources: [ 'secrets', 'configmaps' ],
228+
verbs: [
229+
'create',
230+
'get',
231+
'list',
232+
'watch',
233+
'update',
234+
'patch',
235+
'delete',
236+
],
237+
},
238+
// required so users can create and modify argocd apps and projects from
239+
// the web interface.
240+
{
241+
apiGroups: [ 'argoproj.io' ],
242+
resources: [
243+
'applications',
244+
'applicationsets',
245+
'appprojects',
246+
],
247+
verbs: [
248+
'create',
249+
'get',
250+
'list',
251+
'watch',
252+
'update',
253+
'delete',
254+
'patch',
255+
],
256+
},
257+
// required so the server can emit events
258+
{
259+
apiGroups: [ '' ],
260+
resources: [ 'events' ],
261+
verbs: [ 'create', 'list', 'patch' ],
262+
},
263+
// NOTE(sg): I assume this is also required for hook jobs, but haven't
264+
// found solid evidence for or against that assumption.
265+
{
266+
apiGroups: [ 'batch' ],
267+
resources: [
268+
'jobs',
269+
'cronjobs',
270+
'cronjobs/finalizers',
271+
],
272+
verbs: [ 'create', 'update' ],
273+
},
274+
],
275+
};
276+
57277
{
58278
['%s-%s' % [ obj.metadata.name, std.asciiLower(obj.kind) ]]: obj
59279
for obj in aggregated_rbac
280+
} + {
281+
[if std.length(custom_controller_cr.aggregationRule.clusterRoleSelectors) > 1
282+
then 'custom_controller_clusterrole']: [ custom_controller_cr, controller_required_cr ],
283+
[if std.length(custom_server_cr.aggregationRule.clusterRoleSelectors) > 1
284+
then 'custom_server_clusterroles']: [ custom_server_cr, server_required_cr ],
60285
}

0 commit comments

Comments
 (0)