From cfc1222bfbeab2c0659fe5e4da318309219b524e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Skrz=C4=99tnicki?= Date: Thu, 4 Apr 2024 12:31:01 +0200 Subject: [PATCH] [docs] Database Access Controls (#39109) * Documentation: Database object permissions * Address review feedback * Add note about admin user permissions * Review feedback * Address review feedback * Suggest clarifications in the introduction (#39725) Add more context around what a database object is. * fix lint * Update feature name * clarify import process description * rephrase * minor changes * typo/cspell fixes * silence misguided warning * lint disable --------- Co-authored-by: Paul Gottschling --- docs/config.json | 4 + docs/cspell.json | 3 +- .../auto-user-provisioning.mdx | 13 +- .../database-access-controls.mdx | 213 ++++++++++++++++++ .../auto-user-provisioning/postgres.mdx | 97 +++++++- .../database-object-import-rule-spec.mdx | 56 +++++ docs/pages/includes/role-spec.mdx | 18 +- docs/pages/reference/resources.mdx | 9 + 8 files changed, 397 insertions(+), 16 deletions(-) create mode 100644 docs/pages/database-access/auto-user-provisioning/database-access-controls.mdx create mode 100644 docs/pages/includes/database-access/auto-user-provisioning/database-object-import-rule-spec.mdx diff --git a/docs/config.json b/docs/config.json index 6b9c6042c02fd..c39100556a689 100644 --- a/docs/config.json +++ b/docs/config.json @@ -1401,6 +1401,10 @@ { "title": "PostgreSQL", "slug": "/database-access/auto-user-provisioning/postgres/" + }, + { + "title": "Database Access Controls", + "slug": "/database-access/auto-user-provisioning/database-access-controls/" } ] }, diff --git a/docs/cspell.json b/docs/cspell.json index 18df17f11bba4..97fc1ea36254c 100644 --- a/docs/cspell.json +++ b/docs/cspell.json @@ -134,6 +134,7 @@ "NOFILE", "NOKEY", "NOPASSWD", + "nonprod", "NVGJ", "Näme", "ODBC", @@ -918,4 +919,4 @@ "flagWords": [ "hte" ] -} +} \ No newline at end of file diff --git a/docs/pages/database-access/auto-user-provisioning.mdx b/docs/pages/database-access/auto-user-provisioning.mdx index f04d14b1278d1..8d65cdfd302b1 100644 --- a/docs/pages/database-access/auto-user-provisioning.mdx +++ b/docs/pages/database-access/auto-user-provisioning.mdx @@ -5,9 +5,14 @@ description: Configure automatic user provisioning for databases. (!docs/pages/includes/database-access/auto-user-provisioning/intro.mdx!) +Automatically created users will either receive a predefined set of roles, or can be granted permissions to specific database objects based on their required level of access using [Database Access Controls](./auto-user-provisioning/database-access-controls.mdx) feature. + Currently, automatic user provisioning is supported for the following databases: -- [Self-hosted and AWS RDS PostgreSQL databases](./auto-user-provisioning/postgres.mdx) -- [Self-hosted and AWS RDS MySQL databases](./auto-user-provisioning/mysql.mdx) -- [Self-hosted and AWS RDS MariaDB databases](./auto-user-provisioning/mariadb.mdx) +- [PostgreSQL databases (self-hosted and AWS RDS)](./auto-user-provisioning/postgres.mdx) +- [MySQL databases (self-hosted and AWS RDS)](./auto-user-provisioning/mysql.mdx) +- [MariaDB databases (self-hosted and AWS RDS)](./auto-user-provisioning/mariadb.mdx) - [AWS Redshift databases](./auto-user-provisioning/aws-redshift.mdx) -- [Self-hosted MongoDB](./auto-user-provisioning/mongodb.mdx) +- [MongoDB databases (self-hosted)](./auto-user-provisioning/mongodb.mdx) + + + diff --git a/docs/pages/database-access/auto-user-provisioning/database-access-controls.mdx b/docs/pages/database-access/auto-user-provisioning/database-access-controls.mdx new file mode 100644 index 0000000000000..4229967ad055c --- /dev/null +++ b/docs/pages/database-access/auto-user-provisioning/database-access-controls.mdx @@ -0,0 +1,213 @@ +--- +title: Database Access Controls +description: Understand the concepts behind Database Access Controls managed by Teleport. +--- + +{/*lint ignore messaging*/} +**Database Access Controls** is a Teleport feature that lets you configure +role-based access controls for database objects. A **database +object** can be a table, view, or stored procedure. With Database Access Controls, you can ensure that users only have permissions to manage the data +they need. + +You can define **import rules** that instruct the Teleport Database Service to +apply labels to database objects imported from databases that match labels configured within the import rule. When a user connects to a database, the Database Service selectively grants permissions by checking database object labels against the user's Teleport roles. + +The Database Service grants object-level permissions for the duration of a +connection and revokes them automatically when the connection ends. + +## Database object import rules + +A database object import rule in Teleport is a resource that defines the labels to be applied to database objects imported into Teleport. If a specific object does not match any of the rules, it will not be imported. + +By default, if no import rules are present (e.g. you create a fresh cluster or delete all your rules), Teleport will automatically create the `import_all_objects` rule on startup: + +```yaml +kind: db_object_import_rule +metadata: + name: import_all_objects +spec: + # Priority determines how important the rule is, with lower number indicating lower priority. + # In case of conflicts, when the same label is applied by two rules, + # the label applied by rule with higher priority wins. + priority: 0 + # database_labels is a filter specifying which database resources are in scope of this rule. + database_labels: + - name: '*' + values: + - '*' + # Each mapping, if matched, introduces a set of labels applied to database object. + # Database objects without labels are not imported. + mappings: + - add_labels: + database: '{{obj.database}}' + object_kind: '{{obj.object_kind}}' + name: '{{obj.name}}' + protocol: '{{obj.protocol}}' + schema: '{{obj.schema}}' + database_service_name: '{{obj.database_service_name}}' + # match adds objects to be imported; it cannot be empty. + match: + # list of all table names + table_names: + - '*' + # Additional mappings can be added here. + # - add_labels: ... +version: v1 +``` + +This rule will import all objects and label them by their inherent properties using the template syntax. + +Feel free to modify this rule with `tctl edit` to meet your specific requirements or add more rules. For instance, consider the following rule designed to designate particular tables as accessible to developers, either in a read-only or read-write capacity. + +```yaml +kind: db_object_import_rule +metadata: + name: ownership_nonprod +spec: + priority: 100 + database_labels: + # Affect `dev` and `staging` environments. + # Prod environment may have a different rule. + - name: 'env' + values: + - 'staging' + - 'dev' + - 'prod' + mappings: + # Apply project label + - add_labels: + project: horizon + # match section is mandatory and must contain at least one non-empty subsection + match: + table_names: + - '*' + # scope is the optional section which enables further filtering of objects by database and schema names. When omitted, this filtering is disabled. + scope: + database_names: + - 'horizon' + - 'horizon_v2' + schema_names: + - 'application' + - 'data_import' + # Add `dept: hr` label for respective tables. + - add_labels: + dept: hr + match: + table_names: + - '*' + scope: + schema_names: + - 'recruitment' + - 'salaries' + - 'pto' + - 'hr_scratchpad' +version: v1 +``` + +Save the rule to a file and execute `tctl create -f ownership_nonprod.yaml` to create it in Teleport. + +## Database permissions in roles + +To grant user permissions during a database connection, the user must be associated with a role that meets specific criteria: +- `spec.allow.db_labels` must match the database labels of particular database. +- Database user auto-provisioning should be enabled (`spec.options.create_db_user_mode` not set to `off` or `spec.options.create_db_user: true`). +- The label key/value pairs in `spec.allow.db_permissions.match` should correspond to the labels on the specific database object. + +The labels on the table must be matched with an appropriate role. Here's an example of a role that utilizes the `dept` label, applied by the `ownership_nonprod` rule, granting read-only access to HR records in the database. The `hr_scratchpad` table is further made editable. On the other hand, any objects labeled `dept: sales` are made unavailable by removing all permissions a user may have received for them. The wildcard permissions are only allowed in the `deny` part of the spec (`spec.deny.db_permissions`). + +```yaml +kind: role +metadata: + name: dept_hr_permissions +spec: + allow: + db_labels: + '*': '*' + db_names: + - '*' + db_permissions: + # default permission: read-only + - match: + object_kind: table + dept: hr + permissions: + - SELECT + # extra permissions for select tables + - match: + object_kind: table + dept: hr + name: hr_scratchpad + permissions: + - SELECT + - UPDATE + - DELETE + - INSERT + deny: + db_permissions: + # explicitly disallow any interaction with `dept: sales` tables. + - match: + dept: sales + permissions: + - '*' + options: + create_db_user_mode: keep +version: v7 +``` + +## Permissions lifecycle and consistency + +A user can maintain multiple simultaneous connections to the same database. All connections must possess identical permissions; otherwise, a new connection will be rejected. Upon the termination of the last active connection, all user permissions are automatically revoked. + +## Troubleshooting + + +### Checking the logs + +To diagnose the import process, refer to the Database Service logs to find details such as the number of objects fetched from the database, the number of imported objects (the difference comprising objects not matched by any import rule), and finally, the number of objects for which the user has been granted permissions. + +{/* spell-checker: disable */} +```code +INFO [DB:SERVIC] Database objects fetched from the database (table:75). db:my-postgres id:b4a33740-1d82-4a8d-b2be-2aa90ae9d2eb total:75 postgres/users.go:212 +INFO [DB:SERVIC] Database objects imported (table:75). db:my-postgres err_count:0 id:b4a33740-1d82-4a8d-b2be-2aa90ae9d2eb total:75 postgres/users.go:216 +INFO [DB:SERVIC] Calculated database permissions: "INSERT": 75 objects (table:75), "SELECT": 75 objects (table:75), "UPDATE": 75 objects (table:75). db:my-postgres id:b4a33740-1d82-4a8d-b2be-2aa90ae9d2eb user:teleport-user postgres/users.go:223 +``` +{/* spell-checker: enable */} + +### Invalid database admin user permissions + +The database admin user, referred to as `teleport-admin` in this documentation, is responsible for granting permissions to end users. Ensure that the admin user possesses the necessary permissions; otherwise, this action might fail. + +```bash +tsh db connect postgres-db --db-name postgres --db-user teleport-user +psql: error: connection to server at "localhost" (::1), port 50800 failed: Connection refused + Is the server running on that host and accepting TCP/IP connections? +connection to server at "localhost" (127.0.0.1), port 50800 failed: your Teleport role requires automatic database user provisioning but an attempt to activate database user "teleport-user" failed due to the following error: ERROR: permission denied for table pg_subscription (SQLSTATE 42501) +ERROR: exit status 2 +``` + +### Invalid import rules + +Import rules undergo validation upon creation. An invalid rule will trigger an error during tctl create. For instance: + +```yaml +... + mappings: + - add_labels: + invalid_label: '{{' +... +``` + +Will cause error: + +```shell +> tctl create -f import_all_objects_invalid.yaml +ERROR: validating rule + mapping value failed to parse as template + "{{" is using template brackets '{{' or '}}', however expression does not parse, make sure the format is {{expression}} +``` + +## Next steps + +- Read automatic user provisioning [RFD](https://github.com/gravitational/teleport/blob/master/rfd/0113-automatic-database-users.md). +- Read database permission management [RFD](https://github.com/gravitational/teleport/blob/master/rfd/0151-database-permission-management.md) + diff --git a/docs/pages/database-access/auto-user-provisioning/postgres.mdx b/docs/pages/database-access/auto-user-provisioning/postgres.mdx index cf67af9c17fac..b2636007c8e20 100644 --- a/docs/pages/database-access/auto-user-provisioning/postgres.mdx +++ b/docs/pages/database-access/auto-user-provisioning/postgres.mdx @@ -1,5 +1,5 @@ --- -title: PostgreSQL Automatic User Provisioning +title: PostgreSQL Automatic User Provisioning description: Configure automatic user provisioning for PostgreSQL. --- @@ -23,7 +23,7 @@ Automatic user provisioning is not compatible with RDS Aurora reader endpoints. Teleport will use the same authentication mechanism when connecting as an admin user as for regular user connections: X.509 for self-hosted databases and AWS IAM for RDS. The admin user must have privileges within the database to create -users and grant them privileges. +users and grant them privileges, either for roles or concrete database objects. @@ -60,6 +60,14 @@ to ensure that your configuration is correct. + +When [Database Access Controls](./database-access-controls.mdx) feature is in use, the `teleport-admin` should have permissions to relevant database objects. For example: + +```sql +GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA schema1, schema2, schema3 TO "teleport-admin"; +``` + + Users created by Teleport will be placed in the `teleport-auto-user` group in the database, which will be created automatically if it doesn't exist. @@ -71,16 +79,85 @@ the database, which will be created automatically if it doesn't exist. ## Step 2/3. Configure a Teleport role -(!docs/pages/includes/database-access/auto-user-provisioning/common-teleport-role.mdx!) +Database permissions are associated with a Teleport role, which can either allocate predefined database roles (configured in each database) or define specific database object permissions directly. Teleport grants these permissions for the duration of the connection. -Users created within the database will: +(!docs/pages/includes/database-access/auto-user-provisioning/db_users_ignored.mdx!) + +(!docs/pages/includes/database-access/auto-user-provisioning/modes.mdx!) + + + +To specify the database roles a user should be assigned within the database, +use the `db_roles` role option: + +```yaml +kind: role +version: v7 +metadata: + name: auto-db-users +spec: + options: + # create_db_user_mode enables automatic user provisioning for matching databases + create_db_user_mode: keep + allow: + db_labels: + "*": "*" + db_names: + - "*" + # db_roles is a list of roles the database user will be assigned + db_roles: + - reader + - "{{internal.db_roles}}" + - "{{external.db_roles}}" +``` + +The provisioned database user will be assigned all roles from the Teleport user's role set that match the database. +The role names must be valid and exist in the database. +See PostgreSQL [CREATE ROLE](https://www.postgresql.org/docs/current/sql-createrole.html) for information on how to create database roles. + + + +Required Teleport version: v15.2 or above. + +Use `spec.allow.db_permissions` section in a user role to specify object permissions given user should have. + +```yaml +kind: role +metadata: + name: read_all_tables +spec: + options: + # create_db_user_mode enables automatic user provisioning for matching databases + create_db_user_mode: keep + allow: + db_labels: + '*': '*' + db_names: + - '*' + db_permissions: + # grant `SELECT` on all tables in `public` schema. + - match: + # object labels to match + object_kind: table + schema: public + permissions: + - SELECT +version: v7 +``` + +You can define your own labels for database objects, applying them based on customizable import rules. These custom labels, such as `owner` or `environment`, can then be utilized when granting permissions. + +For additional information, refer to the [Database Access Controls](./database-access-controls.mdx) page. + + + + + +Users created within the database will: - Have the same username as the authenticated Teleport user. - Be a part of the `teleport-auto-user` role. -- Be assigned all roles from the Teleport user's role set that match the database. - The role names must be valid and exist in the database. See PostgreSQL - [CREATE ROLE](https://www.postgresql.org/docs/current/sql-createrole.html) - for information on how to create database roles. +- Will be assigned permissions according to the chosen mechanism. (!docs/pages/includes/database-access/auto-user-provisioning/username-conflict.mdx!) @@ -176,8 +253,8 @@ GRANT rds_iam TO "teleport-admin" WITH ADMIN OPTION; client](../../connect-your-client/gui-clients.mdx). - Learn about [role templating](../../access-controls/guides/role-templates.mdx#interpolation-rules). -- Read automatic user provisioning - [RFD](https://github.com/gravitational/teleport/blob/master/rfd/0113-automatic-database-users.md). +- Read automatic user provisioning [RFD](https://github.com/gravitational/teleport/blob/master/rfd/0113-automatic-database-users.md). +- Read database permission management [RFD](https://github.com/gravitational/teleport/blob/master/rfd/0151-database-permission-management.md). - The `internal.db_roles` traits we illustrated in this guide are replaced with values from the Teleport local user database. For full details on how variable expansion works in Teleport roles, see the [Teleport diff --git a/docs/pages/includes/database-access/auto-user-provisioning/database-object-import-rule-spec.mdx b/docs/pages/includes/database-access/auto-user-provisioning/database-object-import-rule-spec.mdx new file mode 100644 index 0000000000000..06ed2a18ccdbc --- /dev/null +++ b/docs/pages/includes/database-access/auto-user-provisioning/database-object-import-rule-spec.mdx @@ -0,0 +1,56 @@ +```yaml +kind: db_object_import_rule +metadata: + name: my_custom_rule +spec: + # Priority determines how important the rule is, with lower number indicating lower priority. + # In case of conflicts, when the same label is applied by two rules, + # the label applied by rule with higher priority wins. + priority: 123 + # database_labels is a filter specifying which database resources are in scope of this rule. + database_labels: + - name: 'env' + values: + - 'test' + - 'staging' + - name: 'dept' + values: + - '*' + # Each mapping, if matched, introduces a set of labels applied to database object. + # Database objects without labels are not imported. + mappings: + - add_labels: + # the following properties are supported; 'obj' stands for 'database object'. + database: '{{obj.database}}' + object_kind: '{{obj.object_kind}}' + name: '{{obj.name}}' + protocol: '{{obj.protocol}}' + schema: '{{obj.schema}}' + database_service_name: '{{obj.database_service_name}}' + # you may use fixed strings or mix with templates + fixed: const_value + template: 'foo-{{obj.name}}' + # match adds objects to be imported; it cannot be empty. + match: + # list of all table names + table_names: + - 'fixed_table_name' + - 'partial_wildcard_*' + # scope reduces scope of import; it may be empty. + scope: + database_names: + - Widget* + schema_names: + - public + - secret + # Additional mappings can be added here. + - add_labels: + confidential: true + match: + table_names: + - '*' + scope: + schema_names: + - secret +version: v1 +``` diff --git a/docs/pages/includes/role-spec.mdx b/docs/pages/includes/role-spec.mdx index dfef37e81e023..bef24664d079c 100644 --- a/docs/pages/includes/role-spec.mdx +++ b/docs/pages/includes/role-spec.mdx @@ -219,10 +219,26 @@ spec: # Functions transform variables. db_users: ['{{email.local(external.email)}}'] db_names: ['{{external.db_names}}'] - db_roles: ['{{external.db_roles}}'] db_labels: 'env': '{{regexp.replace(external.env, "^(staging)$", "$1")}}' + # List of database roles to grant. Mutually exclusive with `db_permissions`. + db_roles: ['{{external.db_roles}}'] + + # Grant all possible Postgres permissions for all tables. + # List of database permissions to grant. Mutually exclusive with `db_roles`. + db_permissions: + - match: + object_kind: table + permissions: + - SELECT + - INSERT + - UPDATE + - DELETE + - TRUNCATE + - REFERENCES + - TRIGGER + # app_labels: a user with this role will be allowed to connect to # applications with labels matching below. app_labels: diff --git a/docs/pages/reference/resources.mdx b/docs/pages/reference/resources.mdx index 248d774601869..ce8df7f663bbf 100644 --- a/docs/pages/reference/resources.mdx +++ b/docs/pages/reference/resources.mdx @@ -58,6 +58,7 @@ Here's the list of resources currently exposed via [`tctl`](./cli/tctl.mdx): | [device](#device) | A Teleport Trusted Device, see the [Device Trust guide](../access-controls/device-trust/guide.mdx) for more info. | | [ui_config](#ui-config) | Configuration for the Web UI served by the Proxy Service | | [cluster_auth_preference](#cluster-auth-preferences) | Configuration for the cluster's auth preferences. | +| [database_object_import_rule](#database-object-import-rule) | Database object import rules. | ## User @@ -186,6 +187,14 @@ Login rules contain logic to transform SSO user traits during login. (!docs/pages/includes/login-rule-spec.mdx!) +## Database object import rule + +Database object import rule define the labels to be applied to database objects imported into Teleport. + +See [Database Access Controls](../database-access/auto-user-provisioning/database-access-controls.mdx) for more details. + +(!docs/pages/includes/database-access/auto-user-provisioning/database-object-import-rule-spec.mdx!) + ## Device Device contains information identifying a trusted device.