Skip to content

Commit 8696304

Browse files
authored
Support Namespaced RBAC (#477)
* add role / rolebinding * templating updates * codegen test * add changelog
1 parent ef47cc2 commit 8696304

File tree

8 files changed

+248
-101
lines changed

8 files changed

+248
-101
lines changed
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
changelog:
2+
- type: NEW_FEATURE
3+
issueLink: https://github.com/solo-io/gloo-mesh-enterprise/issues/10521
4+
description: >
5+
Add the ability to specify namespace-scoped rbac policies.
6+
resolvesIssue: false

codegen/cmd_test.go

Lines changed: 112 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ var _ = Describe("Cmd", func() {
5757
DefaultPort: 9900,
5858
}},
5959
},
60-
Rbac: []rbacv1.PolicyRule{{
60+
ClusterRbac: []rbacv1.PolicyRule{{
6161
Verbs: []string{"*"},
6262
APIGroups: []string{"coordination.k8s.io"},
6363
Resources: []string{"leases"},
@@ -1230,7 +1230,7 @@ var _ = Describe("Cmd", func() {
12301230
{
12311231
Name: "painter",
12321232
CustomEnableCondition: "and $painter.enabled $.Values.test1.enabled $.Values.test2.enabled",
1233-
Rbac: []rbacv1.PolicyRule{
1233+
ClusterRbac: []rbacv1.PolicyRule{
12341234
{
12351235
Verbs: []string{"GET"},
12361236
},
@@ -1337,7 +1337,7 @@ var _ = Describe("Cmd", func() {
13371337
{
13381338
Name: "painter",
13391339
NamespaceFromValuePath: "$.Values.common.namespace",
1340-
Rbac: []rbacv1.PolicyRule{
1340+
ClusterRbac: []rbacv1.PolicyRule{
13411341
{
13421342
Verbs: []string{"GET"},
13431343
},
@@ -1816,6 +1816,115 @@ roleRef:
18161816
map[string]interface{}{"FOO": map[string]interface{}{"valueFrom": map[string]interface{}{"secretKeyRef": map[string]interface{}{"name": "bar", "key": "baz"}}}},
18171817
[]v1.EnvVar{{Name: "FOO", ValueFrom: &v1.EnvVarSource{SecretKeyRef: &v1.SecretKeySelector{LocalObjectReference: v1.LocalObjectReference{Name: "bar"}, Key: "baz"}}}}),
18181818
)
1819+
1820+
It("can configure cluster-scoped and namespace-scoped RBAC", func() {
1821+
cmd := &Command{
1822+
RenderProtos: false,
1823+
Chart: &Chart{
1824+
Operators: []Operator{
1825+
{
1826+
Name: "painter",
1827+
CustomEnableCondition: "$painter.enabled",
1828+
ClusterRbac: []rbacv1.PolicyRule{
1829+
{
1830+
Verbs: []string{"GET"},
1831+
},
1832+
},
1833+
NamespaceRbac: []rbacv1.PolicyRule{
1834+
{
1835+
Verbs: []string{"GET", "LIST", "WATCH"},
1836+
APIGroups: []string{""},
1837+
Resources: []string{"secrets"},
1838+
},
1839+
},
1840+
},
1841+
},
1842+
Values: nil,
1843+
Data: Data{
1844+
ApiVersion: "v1",
1845+
Description: "",
1846+
Name: "Painting Operator",
1847+
Version: "v0.0.1",
1848+
Home: "https://docs.solo.io/skv2/latest",
1849+
Sources: []string{
1850+
"https://github.com/solo-io/skv2",
1851+
},
1852+
},
1853+
},
1854+
1855+
ManifestRoot: "codegen/test/chart",
1856+
}
1857+
1858+
Expect(cmd.Execute()).NotTo(HaveOccurred(), "failed to execute command")
1859+
1860+
absPath, err := filepath.Abs("./codegen/test/chart/templates/rbac.yaml")
1861+
Expect(err).NotTo(HaveOccurred(), "failed to get abs path")
1862+
1863+
rbac, err := os.ReadFile(absPath)
1864+
Expect(err).NotTo(HaveOccurred(), "failed to read rbac.yaml")
1865+
roleTmpl := `
1866+
kind: Role
1867+
apiVersion: rbac.authorization.k8s.io/v1
1868+
metadata:
1869+
name: painter
1870+
namespace: {{ default .Release.Namespace $.Values.painter.namespace }}
1871+
labels:
1872+
app: painter
1873+
rules:
1874+
- apiGroups:
1875+
- ""
1876+
resources:
1877+
- secrets
1878+
verbs:
1879+
- GET
1880+
- LIST
1881+
- WATCH`
1882+
roleBindingTmpl := `
1883+
kind: RoleBinding
1884+
apiVersion: rbac.authorization.k8s.io/v1
1885+
metadata:
1886+
name: painter
1887+
namespace: {{ default .Release.Namespace $.Values.painter.namespace }}
1888+
labels:
1889+
app: painter
1890+
subjects:
1891+
- kind: ServiceAccount
1892+
name: painter
1893+
namespace: {{ default .Release.Namespace $.Values.painter.namespace }}
1894+
roleRef:
1895+
kind: Role
1896+
name: painter
1897+
apiGroup: rbac.authorization.k8s.io`
1898+
clusterRoleTmpl := `
1899+
kind: ClusterRole
1900+
apiVersion: rbac.authorization.k8s.io/v1
1901+
metadata:
1902+
name: painter-{{ default .Release.Namespace $.Values.painter.namespace }}
1903+
labels:
1904+
app: painter
1905+
rules:
1906+
- verbs:
1907+
- GET`
1908+
clusterRoleBindingTmpl := `
1909+
kind: ClusterRoleBinding
1910+
apiVersion: rbac.authorization.k8s.io/v1
1911+
metadata:
1912+
name: painter-{{ default .Release.Namespace $.Values.painter.namespace }}
1913+
labels:
1914+
app: painter
1915+
subjects:
1916+
- kind: ServiceAccount
1917+
name: painter
1918+
namespace: {{ default .Release.Namespace $.Values.painter.namespace }}
1919+
roleRef:
1920+
kind: ClusterRole
1921+
name: painter-{{ default .Release.Namespace $.Values.painter.namespace }}
1922+
apiGroup: rbac.authorization.k8s.io`
1923+
Expect(rbac).To(ContainSubstring(roleTmpl))
1924+
Expect(rbac).To(ContainSubstring(roleBindingTmpl))
1925+
Expect(rbac).To(ContainSubstring(clusterRoleTmpl))
1926+
Expect(rbac).To(ContainSubstring(clusterRoleBindingTmpl))
1927+
})
18191928
})
18201929

18211930
func helmTemplate(path string, values interface{}) []byte {

codegen/model/chart.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,10 @@ type Operator struct {
7676
Deployment Deployment
7777

7878
// these populate the generated ClusterRole for the operator
79-
Rbac []rbacv1.PolicyRule
79+
ClusterRbac []rbacv1.PolicyRule
80+
81+
// these populate the generated Role for the operator
82+
NamespaceRbac []rbacv1.PolicyRule
8083

8184
// if at least one port is defined, create a Service for it
8285
Service Service

codegen/templates/chart/operator-rbac.yamltmpl

Lines changed: 50 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ Expressions evaluating SKv2 Config use [[ "[[" ]] and [[ "]]" ]]
66

77
[[- range $operator := $.Operators -]]
88
[[- $operatorVar := (lower_camel $operator.Name) -]]
9-
[[- if $operator.Rbac ]]
9+
[[- if or $operator.ClusterRbac $operator.NamespaceRbac ]]
1010
# Rbac manifests for [[ $operator.Name ]]
1111

1212
{{- $[[ $operatorVar ]] := [[ (opVar $operator)]] }}
@@ -16,6 +16,7 @@ Expressions evaluating SKv2 Config use [[ "[[" ]] and [[ "]]" ]]
1616
[[- $operatorEnabledCondition = printf "\n{{- if %s }}\n" $operator.CustomEnableCondition -]]
1717
[[- end -]]
1818
[[- $operatorEnabledCondition -]]
19+
[[- if $operator.ClusterRbac ]]
1920
---
2021

2122
kind: ClusterRole
@@ -29,7 +30,7 @@ metadata:
2930
labels:
3031
app: [[ $operator.Name ]]
3132
rules:
32-
[[ toYaml $operator.Rbac ]]
33+
[[ toYaml $operator.ClusterRbac ]]
3334
[[- range $container := containerConfigs $operator -]]
3435
[[- if and ($container.Rbac) (gt (len $container.EnableStatement) 0) ]]
3536
[[ printf "{{- if %s }}" $container.EnableStatement ]]
@@ -70,6 +71,52 @@ roleRef:
7071
[[- end ]]
7172
apiGroup: rbac.authorization.k8s.io
7273

73-
{{- end }}
74+
[[- end ]][[/* if $operator.ClusterRbac */]]
75+
76+
[[- if $operator.NamespaceRbac ]]
77+
---
78+
79+
kind: Role
80+
apiVersion: rbac.authorization.k8s.io/v1
81+
metadata:
82+
name: [[ $operator.Name ]]
83+
[[- if $operator.NamespaceFromValuePath ]]
84+
namespace: [[ printf "{{ %s | default $.Release.Namespace }}" $operator.NamespaceFromValuePath ]]
85+
[[- else ]]
86+
namespace: {{ default .Release.Namespace [[ (opVar $operator) ]].namespace }}
87+
[[- end ]]
88+
labels:
89+
app: [[ $operator.Name ]]
90+
rules:
91+
[[ toYaml $operator.NamespaceRbac ]]
92+
93+
---
94+
95+
kind: RoleBinding
96+
apiVersion: rbac.authorization.k8s.io/v1
97+
metadata:
98+
name: [[ $operator.Name ]]
99+
[[- if $operator.NamespaceFromValuePath ]]
100+
namespace: [[ printf "{{ %s | default $.Release.Namespace }}" $operator.NamespaceFromValuePath ]]
101+
[[- else ]]
102+
namespace: {{ default .Release.Namespace [[ (opVar $operator) ]].namespace }}
74103
[[- end ]]
104+
labels:
105+
app: [[ $operator.Name ]]
106+
subjects:
107+
- kind: ServiceAccount
108+
name: [[ $operator.Name ]]
109+
[[- if $operator.NamespaceFromValuePath ]]
110+
namespace: [[ printf "{{ %s | default $.Release.Namespace }}" $operator.NamespaceFromValuePath ]]
111+
[[- else ]]
112+
namespace: {{ default .Release.Namespace [[ (opVar $operator) ]].namespace }}
75113
[[- end ]]
114+
roleRef:
115+
kind: Role
116+
name: [[ $operator.Name ]]
117+
apiGroup: rbac.authorization.k8s.io
118+
119+
[[- end ]][[/* if $operator.NamespaceRbac */]]
120+
{{- end }}[[/* $operatorEnabledCondition */]]
121+
[[- end ]][[/* if or $operator.ClusterRbac $operator.NamespaceRbac */]]
122+
[[- end ]][[/* range $operator := $.Operators */]]

codegen/test/chart/conditional-sidecar/templates/rbac.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
{{- $glooMgmtServer := $.Values.glooMgmtServer }}
77
{{- if $glooMgmtServer.enabled -}}
8+
89
---
910

1011
kind: ClusterRole
@@ -44,5 +45,4 @@ roleRef:
4445
kind: ClusterRole
4546
name: gloo-mgmt-server-{{ default .Release.Namespace $.Values.glooMgmtServer.namespace }}
4647
apiGroup: rbac.authorization.k8s.io
47-
4848
{{- end }}

codegen/test/chart/templates/deployment.yaml

Lines changed: 3 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -14,41 +14,31 @@ kind: Deployment
1414
metadata:
1515
labels:
1616
app: painter
17-
deployment: labels
1817
annotations:
1918
app.kubernetes.io/name: painter
20-
deployment: annotation
2119
name: painter
2220
namespace: {{ default .Release.Namespace $.Values.painter.namespace }}
2321
spec:
2422
selector:
2523
matchLabels:
2624
app: painter
27-
pod: labels
2825
template:
2926
metadata:
3027
labels:
3128
app: painter
32-
pod: labels
3329
annotations:
3430
app.kubernetes.io/name: painter
3531
prometheus.io/path: /metrics
3632
prometheus.io/port: "9091"
3733
prometheus.io/scrape: "true"
38-
pod: annotations
3934
spec:
4035
serviceAccountName: painter
41-
volumes:
42-
- emptyDir: {}
43-
name: paint
4436
containers:
4537
{{- $painter := $.Values.painter }}
4638
{{- $painterImage := $painter.image }}
4739
- name: painter
4840
image: {{ $painterImage.registry }}/{{ $painterImage.repository }}:{{ $painterImage.tag }}
4941
imagePullPolicy: {{ $painterImage.pullPolicy }}
50-
args:
51-
- foo
5242
{{- if $painter.env }}
5343
env:
5444
{{ toYaml $painter.env | indent 10 }}
@@ -89,77 +79,14 @@ spec:
8979
drop:
9080
- ALL
9181
{{- end }}
92-
readinessProbe:
93-
httpGet:
94-
path: /
95-
port: 8080
96-
initialDelaySeconds: 5
97-
periodSeconds: 10
98-
{{- $palette := $.Values.painter.sidecars.palette }}
99-
{{- $paletteImage := $palette.image }}
100-
- name: palette
101-
image: {{ $paletteImage.registry }}/{{ $paletteImage.repository }}:{{ $paletteImage.tag }}
102-
imagePullPolicy: {{ $paletteImage.pullPolicy }}
103-
args:
104-
- bar
105-
- baz
106-
{{- if $palette.env }}
107-
env:
108-
{{ toYaml $palette.env | indent 10 }}
109-
{{- else if $palette.extraEnvs }}
110-
env:
111-
{{- end }}
112-
{{- range $name, $item := $palette.extraEnvs }}
113-
- name: {{ $name }}
114-
{{- $item | toYaml | nindent 12 }}
115-
{{- end }}
116-
volumeMounts:
117-
- mountPath: /etc/paint
118-
name: paint
119-
resources:
120-
{{- if $palette.resources }}
121-
{{ toYaml $palette.resources | indent 10}}
122-
{{- else}}
123-
requests:
124-
cpu: 500m
125-
memory: 256Mi
126-
{{- end }}
127-
{{- /*
128-
Render securityContext configs if it is set.
129-
If securityContext is not set, render the default securityContext.
130-
If securityContext is set to 'false', render an empty map.
131-
*/}}
132-
securityContext:
133-
{{- if or ($palette.securityContext) (eq "map[]" (printf "%v" $palette.securityContext)) }}
134-
{{ toYaml $palette.securityContext | indent 10}}
135-
{{/* Because securityContext is nil by default we can only perform following conversion if it is a boolean. Skip conditional otherwise. */}}
136-
{{- else if eq (ternary $palette.securityContext true (eq "bool" (printf "%T" $palette.securityContext))) false }}
137-
{}
138-
{{- else}}
139-
runAsNonRoot: true
140-
{{- if not $painter.floatingUserId }}
141-
runAsUser: {{ printf "%.0f" (float64 $painter.runAsUser) }}
142-
{{- end }}
143-
readOnlyRootFilesystem: true
144-
allowPrivilegeEscalation: false
145-
capabilities:
146-
drop:
147-
- ALL
148-
{{- end }}
149-
livenessProbe:
150-
httpGet:
151-
path: /
152-
port: 8080
153-
initialDelaySeconds: 30
154-
periodSeconds: 60
15582
{{- if $painterImage.pullSecret }}
15683
imagePullSecrets:
15784
- name: {{ $painterImage.pullSecret }}
15885
{{- end}}
15986
{{- end }} {{/* define "painter.deploymentSpec" */}}
16087

16188
{{/* Render painter deployment template with overrides from values*/}}
162-
{{- if $painter.enabled -}}
89+
{{- if $painter.enabled }}
16390
{{- $painterDeploymentOverrides := dict }}
16491
{{- if $painter.deploymentOverrides }}
16592
{{- $painterDeploymentOverrides = $painter.deploymentOverrides }}
@@ -168,7 +95,7 @@ spec:
16895
{{ include "skv2.utils.merge" (list . $painterDeploymentOverrides "painter.deploymentSpec") }}
16996
{{- end }}
17097
---
171-
{{- if $painter.enabled -}}
98+
{{- if $painter.enabled }}
17299
apiVersion: v1
173100
kind: ServiceAccount
174101
metadata:
@@ -190,7 +117,7 @@ metadata:
190117
{{- define "painter.serviceSpec"}}
191118

192119
{{- end }} {{/* define "painter.serviceSpec" */}}
193-
{{- if $painter.enabled -}}
120+
{{- if $painter.enabled }}
194121
{{/* Render painter service template with overrides from values*/}}
195122
{{- $painterServiceOverrides := dict }}
196123
{{- if $painter.serviceOverrides }}

0 commit comments

Comments
 (0)