allow secrets of default users in a different namespace
This commit is contained in:
		
							parent
							
								
									47dc0a9aee
								
							
						
					
					
						commit
						d65b5452e2
					
				|  | @ -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: | ||||||
|  |  | ||||||
|  | @ -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 | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -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 | ||||||
|  |  | ||||||
							
								
								
									
										24
									
								
								docs/user.md
								
								
								
								
							
							
						
						
									
										24
									
								
								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 | 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) | ||||||
|  |  | ||||||
|  | @ -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: | ||||||
|  |  | ||||||
|  | @ -573,6 +573,9 @@ var PostgresCRDResourceValidation = apiextv1.CustomResourceValidation{ | ||||||
| 											}, | 											}, | ||||||
| 										}, | 										}, | ||||||
| 									}, | 									}, | ||||||
|  | 									"secretNamespace": { | ||||||
|  | 										Type: "string", | ||||||
|  | 									}, | ||||||
| 								}, | 								}, | ||||||
| 							}, | 							}, | ||||||
| 						}, | 						}, | ||||||
|  |  | ||||||
|  | @ -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
 | ||||||
|  |  | ||||||
|  | @ -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, | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue