diff --git a/charts/postgres-operator/crds/postgresqls.yaml b/charts/postgres-operator/crds/postgresqls.yaml index 7604e8d5a..68a6e020c 100644 --- a/charts/postgres-operator/crds/postgresqls.yaml +++ b/charts/postgres-operator/crds/postgresqls.yaml @@ -394,6 +394,8 @@ spec: type: boolean defaultRoles: type: boolean + secretNamespace: + type: string replicaLoadBalancer: # deprecated type: boolean resources: diff --git a/docs/reference/cluster_manifest.md b/docs/reference/cluster_manifest.md index 1b2d71a66..c2425d488 100644 --- a/docs/reference/cluster_manifest.md +++ b/docs/reference/cluster_manifest.md @@ -109,7 +109,11 @@ These parameters are grouped directly under the `spec` key in the manifest. `SUPERUSER`, `REPLICATION`, `INHERIT`, `LOGIN`, `NOLOGIN`, `CREATEROLE`, `CREATEDB`, `BYPASSURL`. A login user is created by default unless NOLOGIN is specified, in which case the operator creates a role. One can specify empty - flags by providing a JSON empty array '*[]*'. Optional. + flags by providing a JSON empty array '*[]*'. If the config option + `enable_cross_namespace_secrets` is enabled you can specify the namespace in + the user name in the form `{namespace}.{username}` and the operator will + create the K8s secret in that namespace. The part after the first `.` is + considered to be the user name. Optional. * **databases** a map of database names to database owners for the databases that should be @@ -185,6 +189,35 @@ These parameters are grouped directly under the `spec` key in the manifest. If you set the `all` special item, it will be mounted in all containers (postgres + sidecars). Else you can set the list of target containers in which the additional volumes will be mounted (eg : postgres, telegraf) +## Prepared Databases + +The operator can create databases with default owner, reader and writer roles +without the need to specifiy them under `users` or `databases` sections. Those +parameters are grouped under the `preparedDatabases` top-level key. For more +information, see [user docs](../user.md#prepared-databases-with-roles-and-default-privileges). + +* **defaultUsers** + The operator will always create default `NOLOGIN` roles for defined prepared + databases, but if `defaultUsers` is set to `true` three additional `LOGIN` + roles with `_user` suffix will get created. Default is `false`. + +* **extensions** + map of extensions with target database schema that the operator will install + in the database. Optional. + +* **schemas** + map of schemas that the operator will create. Optional - if no schema is + listed, the operator will create a schema called `data`. Under each schema + key, it can be defined if `defaultRoles` (NOLOGIN) and `defaultUsers` (LOGIN) + roles shall be created that have schema-exclusive privileges. Both flags are + set to `false` by default. + +* **secretNamespace** + for each default LOGIN role the operator will create a secret. You can + specify the namespace in which these secrets will get created, if + `enable_cross_namespace_secrets` is set to `true` in the config. Otherwise, + the cluster namespace is used. + ## Postgres parameters Those parameters are grouped under the `postgresql` top-level key, which is @@ -258,7 +291,9 @@ explanation of `ttl` and `loop_wait` parameters. Those parameters define [CPU and memory requests and limits](https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/) for the Postgres container. They are grouped under the `resources` top-level -key with subgroups `requests` and `limits`. +key with subgroups `requests` and `limits`. The whole section is optional, +however if you specify a request or limit you have to define everything +(unless you are not modifying the default CRD schema validation). ### Requests @@ -266,11 +301,11 @@ CPU and memory requests for the Postgres container. * **cpu** CPU requests for the Postgres container. Optional, overrides the - `default_cpu_requests` operator configuration parameter. Optional. + `default_cpu_requests` operator configuration parameter. * **memory** memory requests for the Postgres container. Optional, overrides the - `default_memory_request` operator configuration parameter. Optional. + `default_memory_request` operator configuration parameter. ### Limits @@ -278,11 +313,11 @@ CPU and memory limits for the Postgres container. * **cpu** CPU limits for the Postgres container. Optional, overrides the - `default_cpu_limits` operator configuration parameter. Optional. + `default_cpu_limits` operator configuration parameter. * **memory** memory limits for the Postgres container. Optional, overrides the - `default_memory_limits` operator configuration parameter. Optional. + `default_memory_limits` operator configuration parameter. ## Parameters defining how to clone the cluster from another one diff --git a/docs/reference/operator_parameters.md b/docs/reference/operator_parameters.md index 2217d87bb..bd51d6ad8 100644 --- a/docs/reference/operator_parameters.md +++ b/docs/reference/operator_parameters.md @@ -267,9 +267,7 @@ configuration they are grouped under the `kubernetes` key. * **enable_cross_namespace_secrets** To allow secrets in a different namespace other than the Postgres cluster namespace. Once enabled, specify the namespace in the user name under the - `users` section in the form `{namespace}.{username}`. The operator will then - create the user secret in that namespace. The part after the first `.` is - considered to be the user name. The default is `false`. + `users` section in the form `{namespace}.{username}`. The default is `false`. * **enable_init_containers** global option to allow for creating init containers in the cluster manifest to diff --git a/docs/user.md b/docs/user.md index 47d10e7e0..be7b41cfe 100644 --- a/docs/user.md +++ b/docs/user.md @@ -139,9 +139,9 @@ secret, without ever sharing it outside of the cluster. At the moment it is not possible to define membership of the manifest role in other roles. -To define the secrets for the users in a different namespace than that of the cluster, -one can set `enable_cross_namespace_secret` and declare the namespace for the -secrets in the manifest in the following manner, +To define the secrets for the users in a different namespace than that of the +cluster, one can set `enable_cross_namespace_secret` and declare the namespace +for the secrets in the manifest in the following manner, ```yaml spec: @@ -150,7 +150,8 @@ spec: appspace.db_user: - createdb ``` -Here, anything before the first dot is taken as the namespace and the text after + +Here, anything before the first dot is considered the namespace and the text after the first dot is the username. Also, the postgres roles of these usernames would be in the form of `namespace.username`. @@ -520,7 +521,7 @@ Then, the schemas are owned by the database owner, too. The roles described in the previous paragraph can be granted to LOGIN roles from the `users` section in the manifest. Optionally, the Postgres Operator can also -create default LOGIN roles for the database an each schema individually. These +create default LOGIN roles for the database and each schema individually. These roles will get the `_user` suffix and they inherit all rights from their NOLOGIN counterparts. Therefore, you cannot have `defaultRoles` set to `false` and enable `defaultUsers` at the same time. @@ -550,6 +551,19 @@ Default access privileges are also defined for LOGIN roles on database and schema creation. This means they are currently not set when `defaultUsers` (or `defaultRoles` for schemas) are enabled at a later point in time. +For all LOGIN roles the operator will create K8s secrets in the namespace +specified in `secretNamespace`, if `enable_cross_namespace_secret` is set to +`true` in the config. Otherwise, they are created in the same namespace like +the Postgres cluster. + +```yaml +spec: + preparedDatabases: + foo: + defaultUsers: true + secretNamespace: appspace +``` + ### Schema `search_path` for default roles The schema [`search_path`](https://www.postgresql.org/docs/13/ddl-schemas.html#DDL-SCHEMAS-PATH) diff --git a/manifests/postgresql.crd.yaml b/manifests/postgresql.crd.yaml index 652a66fda..b80f25f6b 100644 --- a/manifests/postgresql.crd.yaml +++ b/manifests/postgresql.crd.yaml @@ -390,6 +390,8 @@ spec: type: boolean defaultRoles: type: boolean + secretNamespace: + type: string replicaLoadBalancer: # deprecated type: boolean resources: diff --git a/pkg/apis/acid.zalan.do/v1/crds.go b/pkg/apis/acid.zalan.do/v1/crds.go index a95eeab20..b017020dd 100644 --- a/pkg/apis/acid.zalan.do/v1/crds.go +++ b/pkg/apis/acid.zalan.do/v1/crds.go @@ -573,6 +573,9 @@ var PostgresCRDResourceValidation = apiextv1.CustomResourceValidation{ }, }, }, + "secretNamespace": { + Type: "string", + }, }, }, }, diff --git a/pkg/apis/acid.zalan.do/v1/postgresql_type.go b/pkg/apis/acid.zalan.do/v1/postgresql_type.go index 7346fb0e5..1178dccd8 100644 --- a/pkg/apis/acid.zalan.do/v1/postgresql_type.go +++ b/pkg/apis/acid.zalan.do/v1/postgresql_type.go @@ -95,6 +95,7 @@ type PreparedDatabase struct { PreparedSchemas map[string]PreparedSchema `json:"schemas,omitempty"` DefaultUsers bool `json:"defaultUsers,omitempty" defaults:"false"` Extensions map[string]string `json:"extensions,omitempty"` + SecretNamespace string `json:"secretNamespace,omitempty"` } // PreparedSchema describes elements to be bootstrapped per schema diff --git a/pkg/cluster/cluster.go b/pkg/cluster/cluster.go index c9abb10fd..cd3a751d1 100644 --- a/pkg/cluster/cluster.go +++ b/pkg/cluster/cluster.go @@ -1077,11 +1077,11 @@ func (c *Cluster) initPreparedDatabaseRoles() error { } // default roles per database - if err := c.initDefaultRoles(defaultRoles, "admin", preparedDbName, searchPath.String()); err != nil { + if err := c.initDefaultRoles(defaultRoles, "admin", preparedDbName, searchPath.String(), preparedDB.SecretNamespace); err != nil { return fmt.Errorf("could not initialize default roles for database %s: %v", preparedDbName, err) } if preparedDB.DefaultUsers { - if err := c.initDefaultRoles(defaultUsers, "admin", preparedDbName, searchPath.String()); err != nil { + if err := c.initDefaultRoles(defaultUsers, "admin", preparedDbName, searchPath.String(), preparedDB.SecretNamespace); err != nil { return fmt.Errorf("could not initialize default roles for database %s: %v", preparedDbName, err) } } @@ -1092,14 +1092,14 @@ func (c *Cluster) initPreparedDatabaseRoles() error { if err := c.initDefaultRoles(defaultRoles, preparedDbName+constants.OwnerRoleNameSuffix, preparedDbName+"_"+preparedSchemaName, - constants.DefaultSearchPath+", "+preparedSchemaName); err != nil { + constants.DefaultSearchPath+", "+preparedSchemaName, preparedDB.SecretNamespace); err != nil { return fmt.Errorf("could not initialize default roles for database schema %s: %v", preparedSchemaName, err) } if preparedSchema.DefaultUsers { if err := c.initDefaultRoles(defaultUsers, preparedDbName+constants.OwnerRoleNameSuffix, preparedDbName+"_"+preparedSchemaName, - constants.DefaultSearchPath+", "+preparedSchemaName); err != nil { + constants.DefaultSearchPath+", "+preparedSchemaName, preparedDB.SecretNamespace); err != nil { return fmt.Errorf("could not initialize default users for database schema %s: %v", preparedSchemaName, err) } } @@ -1109,10 +1109,19 @@ func (c *Cluster) initPreparedDatabaseRoles() error { return nil } -func (c *Cluster) initDefaultRoles(defaultRoles map[string]string, admin, prefix string, searchPath string) error { +func (c *Cluster) initDefaultRoles(defaultRoles map[string]string, admin, prefix, searchPath, secretNamespace string) error { for defaultRole, inherits := range defaultRoles { + namespace := c.Namespace + //if namespaced secrets are allowed + if secretNamespace != "" { + if c.Config.OpConfig.EnableCrossNamespaceSecret { + namespace = secretNamespace + } else { + c.logger.Warn("secretNamespace ignored because enable_cross_namespace_secret set to false. Creating secrets in cluster namespace.") + } + } roleName := prefix + defaultRole flags := []string{constants.RoleFlagNoLogin} @@ -1135,7 +1144,7 @@ func (c *Cluster) initDefaultRoles(defaultRoles map[string]string, admin, prefix newRole := spec.PgUser{ Origin: spec.RoleOriginBootstrap, Name: roleName, - Namespace: c.Namespace, + Namespace: namespace, Password: util.RandomPassword(constants.PasswordLength), Flags: flags, MemberOf: memberOf,