Skip to content

Conversation

shayancanonical
Copy link
Contributor

@shayancanonical shayancanonical commented Apr 16, 2025

Issue

We would like to introduce pre-defined roles with the introduction of postgres 16 track in our charm
These pre-defined roles not only will help users be more expressive of the extra-user-roles that they can request, but also serve as a means to start hardening the security in our charm. Furthermore, the changes proposed in this PR take a step in the direction of supporting creation of custom roles via data interfaces.

Solution

Introduce the following instance level predefined roles:

  • charmed_stats (inherit from pg_monitor)
  • charmed_read (inherit from pg_read_all_data)
  • charmed_dml (inherit from pg_write_all_data)
  • charmed_backup (inherit from pg_checkpoint)
  • charmed_dba
  • charmed_instance_admin

Introduce, for every database created, the following roles:

  • <database_name>_owner
  • <database_name>_admin
    Additionally, create a function set_user_<database_name>_owner() which can be used by <database_name>_admin to escalate to <database_name>_owner using set_user extension
    All resources created in a database will be owned by <database_name>_owner -> or reassigned to <database_name>_owner when the provider relation is broken or when the first provider relation is formed (in the case that there is an existing database already)

Backup users have much less privileges than previously (we do not grant all privileges to all databases to the backup user anymore)

The following extra-user-roles still need to be maintained until we determine how to cleanly deprecate them:

  • admin: will inherit from charmed_dml with all privileges to all databases. additionally, will also have createdb attribute enabled for created user
  • createdb: will allow created user to create databases
  • createrole: will allow created user to create roles
  • pgbouncer: will enable createdb and createrole attributes for the requested user

The superuser role is being deprecated (albeit, silently. there will be no errors thrown when requested, the charm will simply not comply)

TODO

  • Implement the DDL role per database created as per the hardening guide
  • Determine if there is a way to use login_hook and set_user to auto-escalate <database_name>_admin to <database_name>_owner upon login to ensure that all resources created by the user are correctly owned by the owner role
  • Clean up unit tests

Checklist

  • I have added or updated any relevant documentation.
  • I have cleaned any remaining cloud resources from my accounts.

Copy link

codecov bot commented May 2, 2025

Codecov Report

Attention: Patch coverage is 46.37681% with 37 lines in your changes missing coverage. Please review.

Project coverage is 70.63%. Comparing base (93ae108) to head (e63be2c).
Report is 31 commits behind head on 16/edge.

Files with missing lines Patch % Lines
src/charm.py 44.44% 19 Missing and 1 partial ⚠️
src/upgrade.py 43.75% 9 Missing ⚠️
src/relations/postgresql_provider.py 33.33% 4 Missing and 4 partials ⚠️
Additional details and impacted files
@@             Coverage Diff             @@
##           16/edge     #837      +/-   ##
===========================================
- Coverage    71.03%   70.63%   -0.40%     
===========================================
  Files           15       15              
  Lines         3870     3903      +33     
  Branches       571      572       +1     
===========================================
+ Hits          2749     2757       +8     
- Misses         938      959      +21     
- Partials       183      187       +4     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copy link
Member

@marceloneppel marceloneppel left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice implementation, Shayan!

cur_user := (SELECT current_user);
EXECUTE 'SELECT current_database()' INTO db_name;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Out of curiosity: does this work in replicas? I had some errors in the past where I tried to use INTO variable in the replicas. I'm not sure if it was really related to the unit being a replica or another reason.

Comment on lines +364 to +366
if "superuser" in roles:
logger.warning("superuser privileges not allowed via extra-user-roles")
roles.remove("superuser")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Won't this break PgBouncer? I believe PgBouncer needs the SUPERUSER role to grant the SUPERUSER role to Landscape or another application when it connects to the PgBouncer legacy relation called db-admin. I know that it might not apply to this charm version (PG 16), but we may need to handle this on PgBouncer side, to avoid it breaking.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The PGB auth user also can't be created, since it needs access to user password hashes.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was wondering if in PG 16 only, we can translate SUPERUSER extra-user-role to CREATEDB,CREATEROLE in postgresql charm. if pgbouncer does not need superuser, it can continue to request superuser, and we can just provide it with what it needs in PG 16. we will also add support for pgbouncer extra-user-role which pgbouncer can request once we build in support for sharing PG versions through data interfaces

"""Grant the specified priviliges on the provided database for the user."""
try:
with self._connect_to_database() as connection, connection.cursor() as cursor:
cursor.execute(SQL("GRANT {} ON DATABASE {} TO {};").format(Identifier(", ".join(privileges)), Identifier(database), Identifier(user)))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PGB and create role users will need to be able to regrant at least connect privileges to created users.

@marceloneppel
Copy link
Member

Closing as #881 implemented the instance-level roles and #932, #931, and #921 implemented the remaining roles and the cleanup of old roles.

@marceloneppel marceloneppel deleted the feature/16_predefined_roles branch June 5, 2025 19:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants