diff --git a/charts/postgres-operator-ui/templates/_helpers.tpl b/charts/postgres-operator-ui/templates/_helpers.tpl index a5e97081d..d83b9291a 100644 --- a/charts/postgres-operator-ui/templates/_helpers.tpl +++ b/charts/postgres-operator-ui/templates/_helpers.tpl @@ -24,6 +24,13 @@ If release name contains chart name it will be used as a full name. {{- end -}} {{- end -}} +{{/* +Create a service account name. +*/}} +{{- define "postgres-operator-ui.serviceAccountName" -}} +{{ default (include "postgres-operator-ui.fullname" .) .Values.serviceAccount.name }} +{{- end -}} + {{/* Create chart name and version as used by the chart label. */}} diff --git a/charts/postgres-operator-ui/templates/clusterrole.yaml b/charts/postgres-operator-ui/templates/clusterrole.yaml new file mode 100644 index 000000000..4f76400ec --- /dev/null +++ b/charts/postgres-operator-ui/templates/clusterrole.yaml @@ -0,0 +1,52 @@ +{{ if .Values.rbac.create }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: {{ include "postgres-operator-ui.serviceAccountName" . }} + labels: + app.kubernetes.io/name: {{ template "postgres-operator-ui.name" . }} + helm.sh/chart: {{ template "postgres-operator-ui.chart" . }} + app.kubernetes.io/managed-by: {{ .Release.Service }} + app.kubernetes.io/instance: {{ .Release.Name }} +rules: +- apiGroups: + - acid.zalan.do + resources: + - postgresqls + verbs: + - create + - delete + - get + - list + - patch + - update +- apiGroups: + - "" + resources: + - pods + verbs: + - get + - list + - watch +- apiGroups: + - "" + resources: + - services + verbs: + - get + - list +- apiGroups: + - apps + resources: + - statefulsets + verbs: + - get + - list +- apiGroups: + - "" + resources: + - namespaces + verbs: + - get + - list +{{ end }} diff --git a/charts/postgres-operator-ui/templates/clusterrolebinding.yaml b/charts/postgres-operator-ui/templates/clusterrolebinding.yaml new file mode 100644 index 000000000..7c874d3f7 --- /dev/null +++ b/charts/postgres-operator-ui/templates/clusterrolebinding.yaml @@ -0,0 +1,19 @@ +{{ if .Values.rbac.create }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: {{ include "postgres-operator-ui.serviceAccountName" . }} + labels: + app.kubernetes.io/name: {{ template "postgres-operator-ui.name" . }} + helm.sh/chart: {{ template "postgres-operator-ui.chart" . }} + app.kubernetes.io/managed-by: {{ .Release.Service }} + app.kubernetes.io/instance: {{ .Release.Name }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: {{ include "postgres-operator-ui.serviceAccountName" . }} +subjects: +- kind: ServiceAccount + name: {{ include "postgres-operator-ui.serviceAccountName" . }} + namespace: {{ .Release.Namespace }} +{{ end }} diff --git a/charts/postgres-operator-ui/templates/deployment.yaml b/charts/postgres-operator-ui/templates/deployment.yaml index 924bcf000..da0280e61 100644 --- a/charts/postgres-operator-ui/templates/deployment.yaml +++ b/charts/postgres-operator-ui/templates/deployment.yaml @@ -20,7 +20,7 @@ spec: app.kubernetes.io/instance: {{ .Release.Name }} team: "acid" # Parameterize? spec: - serviceAccountName: {{ template "postgres-operator-ui.name" . }} + serviceAccountName: {{ include "postgres-operator-ui.serviceAccountName" . }} containers: - name: "service" image: "{{ .Values.image.registry }}/{{ .Values.image.repository }}:{{ .Values.image.tag }}" @@ -45,8 +45,8 @@ spec: value: {{ .Values.envs.targetNamespace }} - name: "TEAMS" value: |- - [ - "acid" + [ + "acid" ] - name: "OPERATOR_UI_CONFIG" value: |- @@ -66,4 +66,4 @@ spec: "9.6", "9.5" ] - } \ No newline at end of file + } diff --git a/charts/postgres-operator-ui/templates/serviceaccount.yaml b/charts/postgres-operator-ui/templates/serviceaccount.yaml index 7bb715167..4c5a25543 100644 --- a/charts/postgres-operator-ui/templates/serviceaccount.yaml +++ b/charts/postgres-operator-ui/templates/serviceaccount.yaml @@ -1,81 +1,11 @@ +{{ if .Values.serviceAccount.create }} apiVersion: v1 kind: ServiceAccount metadata: - name: {{ template "postgres-operator-ui.name" . }} + name: {{ include "postgres-operator-ui.serviceAccountName" . }} labels: app.kubernetes.io/name: {{ template "postgres-operator-ui.name" . }} helm.sh/chart: {{ template "postgres-operator-ui.chart" . }} app.kubernetes.io/managed-by: {{ .Release.Service }} app.kubernetes.io/instance: {{ .Release.Name }} - ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: {{ template "postgres-operator-ui.name" . }} - labels: - app.kubernetes.io/name: {{ template "postgres-operator-ui.name" . }} - helm.sh/chart: {{ template "postgres-operator-ui.chart" . }} - app.kubernetes.io/managed-by: {{ .Release.Service }} - app.kubernetes.io/instance: {{ .Release.Name }} -rules: -- apiGroups: - - acid.zalan.do - resources: - - postgresqls - verbs: - - create - - delete - - get - - list - - patch - - update -- apiGroups: - - "" - resources: - - pods - verbs: - - get - - list - - watch -- apiGroups: - - "" - resources: - - services - verbs: - - get - - list -- apiGroups: - - apps - resources: - - statefulsets - verbs: - - get - - list -- apiGroups: - - "" - resources: - - namespaces - verbs: - - get - - list ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - name: {{ template "postgres-operator-ui.name" . }} - labels: - app.kubernetes.io/name: {{ template "postgres-operator-ui.name" . }} - helm.sh/chart: {{ template "postgres-operator-ui.chart" . }} - app.kubernetes.io/managed-by: {{ .Release.Service }} - app.kubernetes.io/instance: {{ .Release.Name }} -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: {{ template "postgres-operator-ui.name" . }} -subjects: -- kind: ServiceAccount -# note: the cluster role binding needs to be defined -# for every namespace the operator-ui service account lives in. - name: {{ template "postgres-operator-ui.name" . }} - namespace: {{ .Release.Namespace }} +{{ end }} diff --git a/charts/postgres-operator-ui/values.yaml b/charts/postgres-operator-ui/values.yaml index 9351d470e..dca093410 100644 --- a/charts/postgres-operator-ui/values.yaml +++ b/charts/postgres-operator-ui/values.yaml @@ -11,6 +11,17 @@ image: tag: v1.2.0 pullPolicy: "IfNotPresent" +rbac: + # Specifies whether RBAC resources should be created + create: true + +serviceAccount: + # Specifies whether a ServiceAccount should be created + create: true + # The name of the ServiceAccount to use. + # If not set and create is true, a name is generated using the fullname template + name: + # configure UI pod resources resources: limits: @@ -22,7 +33,7 @@ resources: # configure UI ENVs envs: - # IMPORTANT: While operator chart and UI chart are idendependent, this is the interface between + # IMPORTANT: While operator chart and UI chart are idendependent, this is the interface between # UI and operator API. Insert the service name of the operator API here! operatorApiUrl: "http://postgres-operator:8080" targetNamespace: "default" @@ -44,4 +55,4 @@ ingress: tls: [] # - secretName: ui-tls # hosts: - # - ui.exmaple.org \ No newline at end of file + # - ui.exmaple.org diff --git a/charts/postgres-operator/templates/clusterrole-postgres-pod.yaml b/charts/postgres-operator/templates/clusterrole-postgres-pod.yaml new file mode 100644 index 000000000..c327d9101 --- /dev/null +++ b/charts/postgres-operator/templates/clusterrole-postgres-pod.yaml @@ -0,0 +1,53 @@ +{{ if .Values.rbac.create }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: postgres-pod + labels: + app.kubernetes.io/name: {{ template "postgres-operator.name" . }} + helm.sh/chart: {{ template "postgres-operator.chart" . }} + app.kubernetes.io/managed-by: {{ .Release.Service }} + app.kubernetes.io/instance: {{ .Release.Name }} +rules: +# Patroni needs to watch and manage endpoints +- apiGroups: + - "" + resources: + - endpoints + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch +# Patroni needs to watch pods +- apiGroups: + - "" + resources: + - pods + verbs: + - get + - list + - patch + - update + - watch +# to let Patroni create a headless service +- apiGroups: + - "" + resources: + - services + verbs: + - create +# to run privileged pods +- apiGroups: + - extensions + resources: + - podsecuritypolicies + resourceNames: + - privileged + verbs: + - use +{{ end }} diff --git a/charts/postgres-operator/templates/clusterrole.yaml b/charts/postgres-operator/templates/clusterrole.yaml index 58feedf10..21bf3c3b4 100644 --- a/charts/postgres-operator/templates/clusterrole.yaml +++ b/charts/postgres-operator/templates/clusterrole.yaml @@ -122,6 +122,7 @@ rules: - delete - get - patch + - update # to CRUD the StatefulSet which controls the Postgres cluster instances - apiGroups: - apps @@ -178,7 +179,7 @@ rules: verbs: - get - create -# needed to grant to namespaced roles when pods run in privileged mode +# to grant privilege to run privileged pods - apiGroups: - extensions resources: diff --git a/docs/administrator.md b/docs/administrator.md index 5aee6f309..791d2e56f 100644 --- a/docs/administrator.md +++ b/docs/administrator.md @@ -99,8 +99,9 @@ access rights. The manifest [`operator-service-account-rbac.yaml`](../manifests/operator-service-account-rbac.yaml) defines the service account, cluster roles and bindings needed for the operator -to function under access control restrictions. To deploy the operator with this -RBAC policy use: +to function under access control restrictions. The file also includes a cluster +role `postgres-pod` with privileges for Patroni to watch and manage pods and +endpoints. To deploy the operator with this RBAC policies use: ```bash kubectl create -f manifests/configmap.yaml @@ -109,19 +110,15 @@ kubectl create -f manifests/postgres-operator.yaml kubectl create -f manifests/minimal-postgres-manifest.yaml ``` -### Namespaced service account and roles +### Namespaced service account, role and role binding -For each namespace the operator watches it creates (or reads) a service account -to be used by the Postgres Pods when a new cluster is deployed. This service -account is bound to a namespaced Role via RoleBinding, which are also created -(or read) by the operator. The name and definitions of these resources can be -[configured](reference/operator_parameters.md#kubernetes-resources). -Note, that the operator performs **no** further syncing of them. - -Until v1.3.1, RoleBindings pointed to the operator ClusterRole by default. This -can still be configured but is not recommended as the Postgres Pods should only -run with the least privileges required for Patroni to work. By default, the -namespaced RBAC resources are named `postgres-pod`. +For each namespace the operator watches it creates (or reads) a service account, +a role and a role binding to be used by the Postgres pods. The name and RBAC +definitions can be [configured](reference/operator_parameters.md#kubernetes-resources). +Note, that the operator performs **no** further syncing of these namespaced +resources. Therefore, to manage changes of the pod role it is currently easier +to make `postgres-pod` a [cluster role](../manifests/postgres-pod-rbac.yaml) and +overwrite the `pod_service_account_role_binding_definition` to reference it. ### Give K8s users access to create/list `postgresqls` diff --git a/docs/reference/operator_parameters.md b/docs/reference/operator_parameters.md index a0a45cb91..83ea05f4d 100644 --- a/docs/reference/operator_parameters.md +++ b/docs/reference/operator_parameters.md @@ -155,7 +155,7 @@ configuration they are grouped under the `kubernetes` key. The default is `postgres-pod`. * **pod_service_account_definition** - on Postgres cluster creation the operator tries to create the service account + On Postgres cluster creation the operator tries to create the service account for the Postgres pods if it does not exist in the namespace. The internal default service account definition (defines only the name) can be overwritten with this parameter. Make sure to provide a valid YAML or JSON string. The diff --git a/manifests/operator-service-account-rbac.yaml b/manifests/operator-service-account-rbac.yaml index 86eef4ffe..f6495162f 100644 --- a/manifests/operator-service-account-rbac.yaml +++ b/manifests/operator-service-account-rbac.yaml @@ -180,7 +180,7 @@ rules: verbs: - get - create -# needed to grant to namespaced roles when pods run in privileged mode +# to grant privilege to run privileged pods - apiGroups: - extensions resources: diff --git a/pkg/util/k8sutil/k8sutil_test.go b/pkg/util/k8sutil/k8sutil_test.go index 12288243e..9b4f2eac3 100644 --- a/pkg/util/k8sutil/k8sutil_test.go +++ b/pkg/util/k8sutil/k8sutil_test.go @@ -20,7 +20,7 @@ func newsService(ann map[string]string, svcT v1.ServiceType, lbSr []string) *v1. return svc } -func TestServiceAnnotations(t *testing.T) { +func TestSameService(t *testing.T) { tests := []struct { about string current *v1.Service @@ -267,8 +267,9 @@ func TestServiceAnnotations(t *testing.T) { }, v1.ServiceTypeLoadBalancer, []string{"128.141.0.0/16", "137.138.0.0/16"}), - match: false, - reason: `new service's annotations doesn't match the current one: Removed 'foo'. Added 'bar' with value 'foo'. 'zalan' changed from 'do' to 'do.com'`, + match: false, + // Test just the prefix to avoid flakiness and map sorting + reason: `new service's annotations doesn't match the current one: Removed 'foo'.`, }, { about: "service add annotations", @@ -301,7 +302,7 @@ func TestServiceAnnotations(t *testing.T) { } if !match && !tt.match { if !strings.HasPrefix(reason, tt.reason) { - t.Errorf("expected reason '%s', found '%s'", tt.reason, reason) + t.Errorf("expected reason prefix '%s', found '%s'", tt.reason, reason) return } } diff --git a/ui/manifests/ui-service-account-rbac.yaml b/ui/manifests/ui-service-account-rbac.yaml index f0a6e8bb7..2e09797a0 100644 --- a/ui/manifests/ui-service-account-rbac.yaml +++ b/ui/manifests/ui-service-account-rbac.yaml @@ -61,7 +61,5 @@ roleRef: name: postgres-operator-ui subjects: - kind: ServiceAccount -# note: the cluster role binding needs to be defined -# for every namespace the operator-ui service account lives in. name: postgres-operator-ui namespace: default