define postgres-pod clusterrole and align rbac in chart

This commit is contained in:
Felix Kunde 2020-02-17 12:52:22 +01:00
parent 702a194c41
commit 69dad12b2e
15 changed files with 200 additions and 80 deletions

View File

@ -9,6 +9,7 @@ metadata:
app.kubernetes.io/managed-by: {{ .Release.Service }}
app.kubernetes.io/instance: {{ .Release.Name }}
rules:
# all verbs allowed for custom operator resources
- apiGroups:
- acid.zalan.do
resources:
@ -16,7 +17,15 @@ rules:
- postgresqls/status
- operatorconfigurations
verbs:
- "*"
- create
- delete
- deletecollection
- get
- list
- patch
- update
- watch
# to create or get/update CRDs when starting up
- apiGroups:
- apiextensions.k8s.io
resources:
@ -26,12 +35,14 @@ rules:
- get
- patch
- update
# to read configuration from ConfigMaps
- apiGroups:
- ""
resources:
- configmaps
verbs:
- get
# to manage endpoints which are also used by Patroni
- apiGroups:
- ""
resources:
@ -43,7 +54,9 @@ rules:
- get
- list
- patch
- watch # needed if zalando-postgres-operator account is used for pods as well
- update # needed if postgres-operator account is used for pods as well
- watch # needed if postgres-operator account is used for pods as well
# to CRUD secrets for database access
- apiGroups:
- ""
resources:
@ -53,6 +66,7 @@ rules:
- update
- delete
- get
# to check nodes for node readiness label
- apiGroups:
- ""
resources:
@ -61,6 +75,7 @@ rules:
- get
- list
- watch
# to read or delete existing PVCs. Creation via StatefulSet
- apiGroups:
- ""
resources:
@ -69,6 +84,7 @@ rules:
- delete
- get
- list
# to read existing PVs. Creation should be done via dynamic provisioning
- apiGroups:
- ""
resources:
@ -77,6 +93,7 @@ rules:
- get
- list
- update # only for resizing AWS volumes
# to watch Spilo pods and do rolling updates. Creation via StatefulSet
- apiGroups:
- ""
resources:
@ -86,13 +103,16 @@ rules:
- get
- list
- watch
- update
- patch
# to resize the filesystem in Spilo pods when increasing volume size
- apiGroups:
- ""
resources:
- pods/exec
verbs:
- create
# to CRUD services to point to Postgres cluster instances
- apiGroups:
- ""
resources:
@ -102,6 +122,8 @@ rules:
- delete
- get
- patch
- update
# to CRUD the StatefulSet which controls the Postgres cluster instances
- apiGroups:
- apps
resources:
@ -112,12 +134,14 @@ rules:
- get
- list
- patch
# to get namespaces operator resources can run in
- apiGroups:
- ""
resources:
- namespaces
verbs:
- get
# to define PDBs. Update happens via delete/create
- apiGroups:
- policy
resources:
@ -126,6 +150,7 @@ rules:
- create
- delete
- get
# to create ServiceAccounts in each namespace the operator watches
- apiGroups:
- ""
resources:
@ -133,25 +158,19 @@ rules:
verbs:
- get
- create
# to create role bindings to the postgres-pod service account
- apiGroups:
- "rbac.authorization.k8s.io"
- rbac.authorization.k8s.io
resources:
- rolebindings
verbs:
- get
- create
- apiGroups:
- "rbac.authorization.k8s.io"
resources:
- clusterroles
verbs:
- bind
resourceNames:
- {{ include "postgres-operator.serviceAccountName" . }}
# to CRUD cron jobs for logical backups
- apiGroups:
- batch
resources:
- cronjobs # enables logical backups
- cronjobs
verbs:
- create
- delete
@ -159,4 +178,57 @@ rules:
- list
- patch
- update
---
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 }}

View File

@ -15,7 +15,7 @@ roleRef:
subjects:
- kind: ServiceAccount
# note: the cluster role binding needs to be defined
# for every namespace the operator service account lives in.
# for every namespace the postgres-pod service account lives in.
name: {{ include "postgres-operator.serviceAccountName" . }}
namespace: {{ .Release.Namespace }}
{{ end }}

View File

@ -9,7 +9,6 @@ metadata:
app.kubernetes.io/managed-by: {{ .Release.Service }}
app.kubernetes.io/instance: {{ .Release.Name }}
data:
pod_service_account_name: {{ include "postgres-operator.serviceAccountName" . }}
{{ toYaml .Values.configGeneral | indent 2 }}
{{ toYaml .Values.configUsers | indent 2 }}
{{ toYaml .Values.configKubernetes | indent 2 }}

View File

@ -14,7 +14,6 @@ configuration:
{{ toYaml .Values.configUsers | indent 4 }}
kubernetes:
oauth_token_secret_name: {{ template "postgres-operator.fullname" . }}
pod_service_account_name: {{ include "postgres-operator.serviceAccountName" . }}
{{ toYaml .Values.configKubernetes | indent 4 }}
postgres_pod_resources:
{{ toYaml .Values.configPostgresPodResources | indent 4 }}

View File

@ -100,6 +100,8 @@ configKubernetes:
pod_management_policy: "ordered_ready"
# label assigned to the Postgres pods (and services/endpoints)
pod_role_label: spilo-role
# name of service account to be used by postgres cluster pods
pod_service_account_name: "postgres-pod"
# Postgres pods are terminated forcefully after this timeout
pod_terminate_grace_period: 5m
# template for database user secrets generated by the operator

View File

@ -93,6 +93,8 @@ configKubernetes:
pod_management_policy: "ordered_ready"
# label assigned to the Postgres pods (and services/endpoints)
pod_role_label: spilo-role
# name of service account to be used by postgres cluster pods
pod_service_account_name: "postgres-pod"
# Postgres pods are terminated forcefully after this timeout
pod_terminate_grace_period: 5m
# template for database user secrets generated by the operator

View File

@ -47,6 +47,12 @@ patching the CRD manifest:
zk8 patch crd postgresqls.acid.zalan.do -p '{"spec":{"validation": null}}'
```
## Non-default cluster domain
If your cluster uses a DNS domain other than the default `cluster.local`, this
needs to be set in the operator configuration (`cluster_domain` variable). This
is used by the operator to connect to the clusters after creation.
## Namespaces
### Select the namespace to deploy to
@ -89,30 +95,6 @@ lacks access rights to any of them (except K8s system namespaces like
'list pods' execute at the cluster scope and fail at the first violation of
access rights.
The watched namespace also needs to have a (possibly different) service account
in the case database pods need to talk to the K8s API (e.g. when using
K8s-native configuration of Patroni). The operator checks that the
`pod_service_account_name` exists in the target namespace, and, if not, deploys
there the `pod_service_account_definition` from the operator
[`Config`](../pkg/util/config/config.go) with the default value of:
```yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: operator
```
In this definition, the operator overwrites the account's name to match
`pod_service_account_name` and the `default` namespace to match the target
namespace. The operator performs **no** further syncing of this account.
## Non-default cluster domain
If your cluster uses a DNS domain other than the default `cluster.local`, this
needs to be set in the operator configuration (`cluster_domain` variable). This
is used by the operator to connect to the clusters after creation.
## Role-based access control for the operator
The manifest [`operator-service-account-rbac.yaml`](../manifests/operator-service-account-rbac.yaml)
@ -127,14 +109,15 @@ kubectl create -f manifests/postgres-operator.yaml
kubectl create -f manifests/minimal-postgres-manifest.yaml
```
### Service account and cluster roles
### Namespaced service account and roles
Note that the service account is named `zalando-postgres-operator`. You may have
to change the `service_account_name` in the operator ConfigMap and
`serviceAccountName` in the `postgres-operator` deployment appropriately. This
is done intentionally to avoid breaking those setups that already work with the
default `operator` account. In the future the operator should ideally be run
under the `zalando-postgres-operator` service account.
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 ClusterRole 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 namespaced service
accounts and role bindings.
### Give K8s users access to create/list `postgresqls`

View File

@ -152,21 +152,22 @@ configuration they are grouped under the `kubernetes` key.
service account used by Patroni running on individual Pods to communicate
with the operator. Required even if native Kubernetes support in Patroni is
not used, because Patroni keeps pod labels in sync with the instance role.
The default is `operator`.
The default is `postgres-pod`.
* **pod_service_account_definition**
The operator tries to create the pod Service Account in the namespace that
doesn't define such an account using the YAML definition provided by this
option. If not defined, a simple definition that contains only the name will
be used. The default is empty.
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
default is empty.
* **pod_service_account_role_binding_definition**
This definition must bind pod service account to a role with permission
sufficient for the pods to start and for Patroni to access K8s endpoints;
service account on its own lacks any such rights starting with K8s v1.8. If
not explicitly defined by the user, a simple definition that binds the
account to the operator's own 'zalando-postgres-operator' cluster role will
be used. The default is empty.
account to the operator's own 'postgres-operator' cluster role will be used.
The default is empty.
* **pod_terminate_grace_period**
Postgres pods are [terminated forcefully](https://kubernetes.io/docs/concepts/workloads/pods/pod/#termination-of-pods)

View File

@ -63,7 +63,7 @@ data:
pod_label_wait_timeout: 10m
pod_management_policy: "ordered_ready"
pod_role_label: spilo-role
pod_service_account_name: "zalando-postgres-operator"
pod_service_account_name: "postgres-pod"
pod_terminate_grace_period: 5m
# postgres_superuser_teams: "postgres_superusers"
# protected_role_names: "admin"

View File

@ -1,14 +1,14 @@
apiVersion: v1
kind: ServiceAccount
metadata:
name: zalando-postgres-operator
name: postgres-operator
namespace: default
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: zalando-postgres-operator
name: postgres-operator
rules:
# all verbs allowed for custom operator resources
- apiGroups:
@ -18,7 +18,14 @@ rules:
- postgresqls/status
- operatorconfigurations
verbs:
- "*"
- create
- delete
- deletecollection
- get
- list
- patch
- update
- watch
# to create or get/update CRDs when starting up
- apiGroups:
- apiextensions.k8s.io
@ -48,7 +55,8 @@ rules:
- get
- list
- patch
- watch # needed if zalando-postgres-operator account is used for pods as well
- update # needed if postgres-operator account is used for pods as welle
- watch # needed if postgres-operator account is used for pods as well
# to CRUD secrets for database access
- apiGroups:
- ""
@ -96,6 +104,7 @@ rules:
- get
- list
- watch
- update
- patch
# to resize the filesystem in Spilo pods when increasing volume size
- apiGroups:
@ -150,9 +159,9 @@ rules:
verbs:
- get
- create
# to create role bindings to the operator service account
# to create role bindings to the postgres-pod service account
- apiGroups:
- "rbac.authorization.k8s.io"
- rbac.authorization.k8s.io
resources:
- rolebindings
verbs:
@ -175,14 +184,60 @@ rules:
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: zalando-postgres-operator
name: postgres-operator
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: zalando-postgres-operator
name: postgres-operator
subjects:
- kind: ServiceAccount
# note: the cluster role binding needs to be defined
# for every namespace the operator service account lives in.
name: zalando-postgres-operator
name: postgres-operator
namespace: default
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: postgres-pod
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

View File

@ -12,7 +12,7 @@ spec:
labels:
name: postgres-operator
spec:
serviceAccountName: zalando-postgres-operator
serviceAccountName: postgres-operator
containers:
- name: postgres-operator
image: registry.opensource.zalan.do/acid/postgres-operator:v1.3.1

View File

@ -45,7 +45,7 @@ configuration:
# pod_priority_class_name: ""
pod_role_label: spilo-role
# pod_service_account_definition: ""
pod_service_account_name: zalando-postgres-operator
pod_service_account_name: postgres-pod
# pod_service_account_role_binding_definition: ""
pod_terminate_grace_period: 5m
secret_name_template: "{username}.{cluster}.credentials.{tprkind}.{tprgroup}"

View File

@ -11,7 +11,14 @@ rules:
- postgresqls
- postgresqls/status
verbs:
- "*"
- create
- delete
- deletecollection
- get
- list
- patch
- update
- watch
---
apiVersion: rbac.authorization.k8s.io/v1
@ -48,4 +55,3 @@ rules:
- get
- list
- watch

View File

@ -161,11 +161,12 @@ func (c *Controller) initPodServiceAccount() {
if c.opConfig.PodServiceAccountDefinition == "" {
c.opConfig.PodServiceAccountDefinition = `
{ "apiVersion": "v1",
"kind": "ServiceAccount",
"metadata": {
"name": "operator"
}
{
"apiVersion": "v1",
"kind": "ServiceAccount",
"metadata": {
"name": "postgres-pod"
}
}`
}
@ -175,13 +176,13 @@ func (c *Controller) initPodServiceAccount() {
switch {
case err != nil:
panic(fmt.Errorf("Unable to parse pod service account definition from the operator config map: %v", err))
panic(fmt.Errorf("Unable to parse pod service account definition from the operator configuration: %v", err))
case groupVersionKind.Kind != "ServiceAccount":
panic(fmt.Errorf("pod service account definition in the operator config map defines another type of resource: %v", groupVersionKind.Kind))
panic(fmt.Errorf("pod service account definition in the operator configuration defines another type of resource: %v", groupVersionKind.Kind))
default:
c.PodServiceAccount = obj.(*v1.ServiceAccount)
if c.PodServiceAccount.Name != c.opConfig.PodServiceAccountName {
c.logger.Warnf("in the operator config map, the pod service account name %v does not match the name %v given in the account definition; using the former for consistency", c.opConfig.PodServiceAccountName, c.PodServiceAccount.Name)
c.logger.Warnf("in the operator configuration, the pod service account name %v does not match the name %v given in the account definition; using the former for consistency", c.opConfig.PodServiceAccountName, c.PodServiceAccount.Name)
c.PodServiceAccount.Name = c.opConfig.PodServiceAccountName
}
c.PodServiceAccount.Namespace = ""
@ -223,9 +224,9 @@ func (c *Controller) initRoleBinding() {
switch {
case err != nil:
panic(fmt.Errorf("Unable to parse the definition of the role binding for the pod service account definition from the operator config map: %v", err))
panic(fmt.Errorf("unable to parse the definition of the role binding for the pod service account definition from the operator configuration: %v", err))
case groupVersionKind.Kind != "RoleBinding":
panic(fmt.Errorf("role binding definition in the operator config map defines another type of resource: %v", groupVersionKind.Kind))
panic(fmt.Errorf("role binding definition in the operator configuration defines another type of resource: %v", groupVersionKind.Kind))
default:
c.PodServiceAccountRoleBinding = obj.(*rbacv1.RoleBinding)
c.PodServiceAccountRoleBinding.Namespace = ""

View File

@ -96,7 +96,7 @@ type Config struct {
DockerImage string `name:"docker_image" default:"registry.opensource.zalan.do/acid/spilo-cdp-12:1.6-p16"`
Sidecars map[string]string `name:"sidecar_docker_images"`
// default name `operator` enables backward compatibility with the older ServiceAccountName field
PodServiceAccountName string `name:"pod_service_account_name" default:"operator"`
PodServiceAccountName string `name:"pod_service_account_name" default:"postgres-pod"`
// value of this string must be valid JSON or YAML; see initPodServiceAccount
PodServiceAccountDefinition string `name:"pod_service_account_definition" default:""`
PodServiceAccountRoleBindingDefinition string `name:"pod_service_account_role_binding_definition" default:""`