allow secrets of default users in a different namespace

This commit is contained in:
Felix Kunde 2021-08-12 17:37:26 +02:00
parent 47dc0a9aee
commit d65b5452e2
8 changed files with 80 additions and 20 deletions

View File

@ -394,6 +394,8 @@ spec:
type: boolean type: boolean
defaultRoles: defaultRoles:
type: boolean type: boolean
secretNamespace:
type: string
replicaLoadBalancer: # deprecated replicaLoadBalancer: # deprecated
type: boolean type: boolean
resources: resources:

View File

@ -109,7 +109,11 @@ These parameters are grouped directly under the `spec` key in the manifest.
`SUPERUSER`, `REPLICATION`, `INHERIT`, `LOGIN`, `NOLOGIN`, `CREATEROLE`, `SUPERUSER`, `REPLICATION`, `INHERIT`, `LOGIN`, `NOLOGIN`, `CREATEROLE`,
`CREATEDB`, `BYPASSURL`. A login user is created by default unless NOLOGIN is `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 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** * **databases**
a map of database names to database owners for the databases that should be 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). 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) 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 ## Postgres parameters
Those parameters are grouped under the `postgresql` top-level key, which is 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/) 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 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 ### Requests
@ -266,11 +301,11 @@ CPU and memory requests for the Postgres container.
* **cpu** * **cpu**
CPU requests for the Postgres container. Optional, overrides the CPU requests for the Postgres container. Optional, overrides the
`default_cpu_requests` operator configuration parameter. Optional. `default_cpu_requests` operator configuration parameter.
* **memory** * **memory**
memory requests for the Postgres container. Optional, overrides the memory requests for the Postgres container. Optional, overrides the
`default_memory_request` operator configuration parameter. Optional. `default_memory_request` operator configuration parameter.
### Limits ### Limits
@ -278,11 +313,11 @@ CPU and memory limits for the Postgres container.
* **cpu** * **cpu**
CPU limits for the Postgres container. Optional, overrides the CPU limits for the Postgres container. Optional, overrides the
`default_cpu_limits` operator configuration parameter. Optional. `default_cpu_limits` operator configuration parameter.
* **memory** * **memory**
memory limits for the Postgres container. Optional, overrides the 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 ## Parameters defining how to clone the cluster from another one

View File

@ -267,9 +267,7 @@ configuration they are grouped under the `kubernetes` key.
* **enable_cross_namespace_secrets** * **enable_cross_namespace_secrets**
To allow secrets in a different namespace other than the Postgres cluster To allow secrets in a different namespace other than the Postgres cluster
namespace. Once enabled, specify the namespace in the user name under the namespace. Once enabled, specify the namespace in the user name under the
`users` section in the form `{namespace}.{username}`. The operator will then `users` section in the form `{namespace}.{username}`. The default is `false`.
create the user secret in that namespace. The part after the first `.` is
considered to be the user name. The default is `false`.
* **enable_init_containers** * **enable_init_containers**
global option to allow for creating init containers in the cluster manifest to global option to allow for creating init containers in the cluster manifest to

View File

@ -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 At the moment it is not possible to define membership of the manifest role in
other roles. other roles.
To define the secrets for the users in a different namespace than that of the cluster, To define the secrets for the users in a different namespace than that of the
one can set `enable_cross_namespace_secret` and declare the namespace for the cluster, one can set `enable_cross_namespace_secret` and declare the namespace
secrets in the manifest in the following manner, for the secrets in the manifest in the following manner,
```yaml ```yaml
spec: spec:
@ -150,7 +150,8 @@ spec:
appspace.db_user: appspace.db_user:
- createdb - 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 the first dot is the username. Also, the postgres roles of these usernames would
be in the form of `namespace.username`. 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 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 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 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 counterparts. Therefore, you cannot have `defaultRoles` set to `false` and enable
`defaultUsers` at the same time. `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` schema creation. This means they are currently not set when `defaultUsers`
(or `defaultRoles` for schemas) are enabled at a later point in time. (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 ### Schema `search_path` for default roles
The schema [`search_path`](https://www.postgresql.org/docs/13/ddl-schemas.html#DDL-SCHEMAS-PATH) The schema [`search_path`](https://www.postgresql.org/docs/13/ddl-schemas.html#DDL-SCHEMAS-PATH)

View File

@ -390,6 +390,8 @@ spec:
type: boolean type: boolean
defaultRoles: defaultRoles:
type: boolean type: boolean
secretNamespace:
type: string
replicaLoadBalancer: # deprecated replicaLoadBalancer: # deprecated
type: boolean type: boolean
resources: resources:

View File

@ -573,6 +573,9 @@ var PostgresCRDResourceValidation = apiextv1.CustomResourceValidation{
}, },
}, },
}, },
"secretNamespace": {
Type: "string",
},
}, },
}, },
}, },

View File

@ -95,6 +95,7 @@ type PreparedDatabase struct {
PreparedSchemas map[string]PreparedSchema `json:"schemas,omitempty"` PreparedSchemas map[string]PreparedSchema `json:"schemas,omitempty"`
DefaultUsers bool `json:"defaultUsers,omitempty" defaults:"false"` DefaultUsers bool `json:"defaultUsers,omitempty" defaults:"false"`
Extensions map[string]string `json:"extensions,omitempty"` Extensions map[string]string `json:"extensions,omitempty"`
SecretNamespace string `json:"secretNamespace,omitempty"`
} }
// PreparedSchema describes elements to be bootstrapped per schema // PreparedSchema describes elements to be bootstrapped per schema

View File

@ -1077,11 +1077,11 @@ func (c *Cluster) initPreparedDatabaseRoles() error {
} }
// default roles per database // 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) return fmt.Errorf("could not initialize default roles for database %s: %v", preparedDbName, err)
} }
if preparedDB.DefaultUsers { 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) 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, if err := c.initDefaultRoles(defaultRoles,
preparedDbName+constants.OwnerRoleNameSuffix, preparedDbName+constants.OwnerRoleNameSuffix,
preparedDbName+"_"+preparedSchemaName, 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) return fmt.Errorf("could not initialize default roles for database schema %s: %v", preparedSchemaName, err)
} }
if preparedSchema.DefaultUsers { if preparedSchema.DefaultUsers {
if err := c.initDefaultRoles(defaultUsers, if err := c.initDefaultRoles(defaultUsers,
preparedDbName+constants.OwnerRoleNameSuffix, preparedDbName+constants.OwnerRoleNameSuffix,
preparedDbName+"_"+preparedSchemaName, 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) return fmt.Errorf("could not initialize default users for database schema %s: %v", preparedSchemaName, err)
} }
} }
@ -1109,10 +1109,15 @@ func (c *Cluster) initPreparedDatabaseRoles() error {
return nil 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 { for defaultRole, inherits := range defaultRoles {
namespace := c.Namespace
//if namespaced secrets are allowed
if c.Config.OpConfig.EnableCrossNamespaceSecret && secretNamespace != "" {
namespace = secretNamespace
}
roleName := prefix + defaultRole roleName := prefix + defaultRole
flags := []string{constants.RoleFlagNoLogin} flags := []string{constants.RoleFlagNoLogin}
@ -1135,7 +1140,7 @@ func (c *Cluster) initDefaultRoles(defaultRoles map[string]string, admin, prefix
newRole := spec.PgUser{ newRole := spec.PgUser{
Origin: spec.RoleOriginBootstrap, Origin: spec.RoleOriginBootstrap,
Name: roleName, Name: roleName,
Namespace: c.Namespace, Namespace: namespace,
Password: util.RandomPassword(constants.PasswordLength), Password: util.RandomPassword(constants.PasswordLength),
Flags: flags, Flags: flags,
MemberOf: memberOf, MemberOf: memberOf,