Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions charts/postgres-operator/crds/operatorconfigurations.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,9 @@ spec:
pod_service_account_name:
type: string
default: "postgres-pod"
pod_service_account_role_definition:
type: string
default: ""
pod_service_account_role_binding_definition:
type: string
default: ""
Expand Down
3 changes: 2 additions & 1 deletion charts/postgres-operator/templates/clusterrole.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -220,11 +220,12 @@ rules:
verbs:
- get
- create
# to create role bindings to the postgres-pod service account
# to create roles and role bindings to the pod service account
- apiGroups:
- rbac.authorization.k8s.io
resources:
- rolebindings
- roles
verbs:
- get
- create
Expand Down
3 changes: 3 additions & 0 deletions charts/postgres-operator/values-crd.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,9 @@ configKubernetes:
# service account definition as JSON/YAML string to be used by postgres cluster pods
# pod_service_account_definition: ""

# role definition as JSON/YAML string to be used by postgres cluster pods
# pod_service_account_role_definition: ""

# role binding definition as JSON/YAML string to be used by pod service account
# pod_service_account_role_binding_definition: ""

Expand Down
3 changes: 3 additions & 0 deletions charts/postgres-operator/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,9 @@ configKubernetes:
# service account definition as JSON/YAML string to be used by postgres cluster pods
# pod_service_account_definition: ""

# role definition as JSON/YAML string to be used by postgres cluster pods
# pod_service_account_role_definition: ""

# role binding definition as JSON/YAML string to be used by pod service account
# pod_service_account_role_binding_definition: ""

Expand Down
17 changes: 9 additions & 8 deletions docs/administrator.md
Original file line number Diff line number Diff line change
Expand Up @@ -212,14 +212,15 @@ kubectl create -f manifests/postgres-operator.yaml
kubectl create -f manifests/minimal-postgres-manifest.yaml
```

### Namespaced service account and role binding

For each namespace the operator watches it creates (or reads) a service account
and role binding to be used by the Postgres Pods. The service account is bound
to the `postgres-pod` cluster role. 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.
### Namespaced service account, role and role binding

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`

Expand Down
19 changes: 13 additions & 6 deletions docs/reference/operator_parameters.md
Original file line number Diff line number Diff line change
Expand Up @@ -189,13 +189,20 @@ configuration they are grouped under the `kubernetes` key.
with this parameter. Make sure to provide a valid YAML or JSON string. The
default is empty.

* **pod_service_account_role_definition**
operator will try to create a role in the namespace to be used by pod service
account. The internal default definition contains permissions to manage pods
and endpoints necessary for Patroni to work. Therefore, when overwriting the
definition with this parameter make sure to provide sufficient access rights
in a valid YAML/JSON string. The default is empty.

* **pod_service_account_role_binding_definition**
This definition must bind the 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 'postgres-pod' [cluster role](../../manifests/operator-service-account-rbac.yaml#L198)
will be used. The default is empty.
the created service account and role are referenced with a role binding. When
overwriting its definition with this parameters using a valid YAML/JSON string
check that the specified service account and role either exist in the K8s
cluster or will be created by the operator. While it's possible to also
reference cluster roles, the binding itself can only be of kind `RoleBinding`,
not `ClusterRoleBinding`. The default is empty.

* **pod_terminate_grace_period**
Postgres pods are [terminated forcefully](https://kubernetes.io/docs/concepts/workloads/pods/pod/#termination-of-pods)
Expand Down
3 changes: 2 additions & 1 deletion manifests/configmap.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,8 @@ data:
pod_role_label: spilo-role
# pod_service_account_definition: ""
pod_service_account_name: "postgres-pod"
# pod_service_account_role_binding_definition: ""
# pod_service_account_role_definition: ""
# pod_service_account_role_binding_definition: '{"apiVersion":"rbac.authorization.k8s.io/v1","kind":"RoleBinding","metadata":{"name":"postgres-pod"},"roleRef":{"apiGroup":"rbac.authorization.k8s.io","kind": "ClusterRole","name": "postgres-pod"},"subjects":[{"kind": "ServiceAccount","name": "postgres-pod"}]}'
pod_terminate_grace_period: 5m
# postgres_superuser_teams: "postgres_superusers"
# protected_role_names: "admin"
Expand Down
51 changes: 2 additions & 49 deletions manifests/operator-service-account-rbac.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -195,11 +195,12 @@ rules:
verbs:
- get
- create
# to create role bindings to the postgres-pod service account
# to create roles and role bindings to the pod service account
- apiGroups:
- rbac.authorization.k8s.io
resources:
- rolebindings
- roles
verbs:
- get
- create
Expand All @@ -226,51 +227,3 @@ subjects:
- kind: ServiceAccount
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 grant privilege to run privileged pods (not needed by default)
#- apiGroups:
# - extensions
# resources:
# - podsecuritypolicies
# resourceNames:
# - privileged
# verbs:
# - use
3 changes: 3 additions & 0 deletions manifests/operatorconfiguration.crd.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,9 @@ spec:
pod_service_account_name:
type: string
default: "postgres-pod"
pod_service_account_role_definition:
type: string
default: ""
pod_service_account_role_binding_definition:
type: string
default: ""
Expand Down
53 changes: 53 additions & 0 deletions manifests/postgres-pod-rbac.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
apiVersion: v1
kind: ServiceAccount
metadata:
name: postgres-pod
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 grant privilege to run privileged pods (not needed by default)
#- apiGroups:
# - extensions
# resources:
# - podsecuritypolicies
# resourceNames:
# - privileged
# verbs:
# - use
1 change: 1 addition & 0 deletions manifests/postgresql-operator-default-configuration.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ configuration:
pod_role_label: spilo-role
# pod_service_account_definition: ""
pod_service_account_name: postgres-pod
# pod_service_account_role_definition: ""
# pod_service_account_role_binding_definition: ""
pod_terminate_grace_period: 5m
secret_name_template: "{username}.{cluster}.credentials.{tprkind}.{tprgroup}"
Expand Down
3 changes: 3 additions & 0 deletions pkg/apis/acid.zalan.do/v1/crds.go
Original file line number Diff line number Diff line change
Expand Up @@ -1119,6 +1119,9 @@ var OperatorConfigCRDResourceValidation = apiextv1.CustomResourceValidation{
"pod_service_account_name": {
Type: "string",
},
"pod_service_account_role_definition": {
Type: "string",
},
"pod_service_account_role_binding_definition": {
Type: "string",
},
Expand Down
1 change: 1 addition & 0 deletions pkg/apis/acid.zalan.do/v1/operator_configuration_type.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ type KubernetesMetaConfiguration struct {
PodServiceAccountName string `json:"pod_service_account_name,omitempty"`
// TODO: change it to the proper json
PodServiceAccountDefinition string `json:"pod_service_account_definition,omitempty"`
PodServiceAccountRoleDefinition string `json:"pod_service_account_role_definition,omitempty"`
PodServiceAccountRoleBindingDefinition string `json:"pod_service_account_role_binding_definition,omitempty"`
PodTerminateGracePeriod Duration `json:"pod_terminate_grace_period,omitempty"`
SpiloPrivileged bool `json:"spilo_privileged,omitempty"`
Expand Down
1 change: 1 addition & 0 deletions pkg/cluster/cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ type Config struct {
PgTeamMap pgteams.PostgresTeamMap
InfrastructureRoles map[string]spec.PgUser // inherited from the controller
PodServiceAccount *v1.ServiceAccount
PodServiceAccountRole *rbacv1.Role
PodServiceAccountRoleBinding *rbacv1.RoleBinding
}

Expand Down
87 changes: 86 additions & 1 deletion pkg/controller/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ type Controller struct {
workerLogs map[uint32]ringlog.RingLogger

PodServiceAccount *v1.ServiceAccount
PodServiceAccountRole *rbacv1.Role
PodServiceAccountRoleBinding *rbacv1.RoleBinding
}

Expand Down Expand Up @@ -248,6 +249,86 @@ func (c *Controller) initPodServiceAccount() {
// actual service accounts are deployed at the time of Postgres/Spilo cluster creation
}

func (c *Controller) initRole() {

// service account on its own lacks any rights starting with k8s v1.8
// operator binds it to the namespaced role with sufficient privileges
if c.opConfig.PodServiceAccountRoleDefinition == "" {
c.opConfig.PodServiceAccountRoleDefinition = fmt.Sprintf(`
{
Comment thread
FxKu marked this conversation as resolved.
"apiVersion": "rbac.authorization.k8s.io/v1",
"kind": "Role",
"metadata": {
"name": "%s"
},
"rules": [
{
"apiGroups": [
""
],
"resources": [
"endpoints"
],
"verbs": [
"create",
"delete",
"deletecollection",
"get",
"list",
"patch",
"update",
"watch"
]
},
{
"apiGroups": [
""
],
"resources": [
"pods"
],
"verbs": [
"get",
"list",
"patch",
"update",
"watch"
]
},
{
"apiGroups": [
""
],
"resources": [
"services"
],
"verbs": [
"create"
]
}
]
}`, c.PodServiceAccount.Name)
}
c.logger.Info("Parse roles")
// re-uses k8s internal parsing. See k8s client-go issue #193 for explanation
decode := scheme.Codecs.UniversalDeserializer().Decode
obj, groupVersionKind, err := decode([]byte(c.opConfig.PodServiceAccountRoleDefinition), nil, nil)

switch {
case err != nil:
panic(fmt.Errorf("unable to parse the role definition from the operator configuration: %v", err))
case groupVersionKind.Kind != "Role":
panic(fmt.Errorf("role definition in the operator configuration defines another type of resource: %v", groupVersionKind.Kind))
default:
c.PodServiceAccountRole = obj.(*rbacv1.Role)
c.PodServiceAccountRole.Namespace = ""
c.logger.Info("successfully parsed")

}

// actual roles bindings are deployed at the time of Postgres/Spilo cluster creation
}

func (c *Controller) initRoleBinding() {

// service account on its own lacks any rights starting with k8s v1.8
Expand All @@ -263,7 +344,7 @@ func (c *Controller) initRoleBinding() {
},
"roleRef": {
"apiGroup": "rbac.authorization.k8s.io",
"kind": "ClusterRole",
"kind": "Role",
"name": "%s"
},
"subjects": [
Expand Down Expand Up @@ -320,6 +401,10 @@ func (c *Controller) initController() {
}
c.initPodServiceAccount()
c.initRoleBinding()
// init role only if binding references a role
if c.PodServiceAccountRoleBinding != nil && c.PodServiceAccountRoleBinding.RoleRef.Kind == "Role" {
c.initRole()
}

c.modifyConfigFromEnvironment()

Expand Down
1 change: 1 addition & 0 deletions pkg/controller/operator_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ func (c *Controller) importConfigurationFromCRD(fromCRD *acidv1.OperatorConfigur
result.CustomPodAnnotations = fromCRD.Kubernetes.CustomPodAnnotations
result.PodServiceAccountName = util.Coalesce(fromCRD.Kubernetes.PodServiceAccountName, "postgres-pod")
result.PodServiceAccountDefinition = fromCRD.Kubernetes.PodServiceAccountDefinition
result.PodServiceAccountRoleDefinition = fromCRD.Kubernetes.PodServiceAccountRoleDefinition
result.PodServiceAccountRoleBindingDefinition = fromCRD.Kubernetes.PodServiceAccountRoleBindingDefinition
result.PodEnvironmentConfigMap = fromCRD.Kubernetes.PodEnvironmentConfigMap
result.PodEnvironmentSecret = fromCRD.Kubernetes.PodEnvironmentSecret
Expand Down
Loading