postgresqlpostgres-10

How to create user(read-write & readonly) and admin roles for an existing postgres database?


We have an existing postgres database in production with a superuser adm that is being used to do everything. Our web application connects to the database using the same user and also the administrators(for patching/updating etc.) use the same credentials.

We have to fix this to have roles so that we can have read-write, readonly and admin roles. We don't want our web application and admin to connect to the database as superuser.

With that being said, I have created the following sql script to make the appropriate roles. I am not a database expert(not yet) so wanted to know the issues or better ways to solve this.

ALTER ROLE adm NOLOGIN;

CREATE role user_role NOINHERIT;
CREATE role readonlyuser_role NOINHERIT;
CREATE role admin_role CREATEDB CREATEROLE NOINHERIT;
CREATE ROLE u_service LOGIN PASSWORD '<some password>' INHERIT;
CREATE ROLE u_admin LOGIN PASSWORD '<some password>' INHERIT;
CREATE ROLE u_reader LOGIN PASSWORD '<some password>' INHERIT;

GRANT SELECT ON ALL TABLES IN SCHEMA public TO readonlyuser_role;
GRANT ALL PRIVILEGES ON SCHEMA public TO admin_role;
GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO user_role, admin_role;
GRANT ALL PRIVILEGES ON ALL FUNCTIONS IN SCHEMA public TO user_role, admin_role;
GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA public TO user_role, admin_role;
GRANT ALL PRIVILEGES ON ALL PROCEDURES IN SCHEMA public TO user_role, admin_role;
GRANT ALL PRIVILEGES ON ALL ROUTINES IN SCHEMA public TO user_role, admin_role;

GRANT ALL PRIVILEGES ON SCHEMA audit TO admin_role;
GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA audit TO admin_role;
GRANT ALL PRIVILEGES ON ALL FUNCTIONS IN SCHEMA audit TO admin_role;
GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA audit TO admin_role;
GRANT ALL PRIVILEGES ON ALL PROCEDURES IN SCHEMA audit TO admin_role;
GRANT ALL PRIVILEGES ON ALL ROUTINES IN SCHEMA audit TO admin_role;

GRANT admin_role TO u_admin;
GRANT user_role TO u_service;
GRANT readonlyuser_role TO u_reader;

Solution

  • A few things to consider.

    Spell out what user_role and readonlyuser_role can do

    Start by revoking all privileges from both these roles, then add them back only as needed. This makes it both clearer in your intentions about what the roles should do, and safer in practice because higher privileges than intended won't accidentally sneak in.

    REVOKE ALL ON SCHEMA public FROM public;  --only authorized roles can do anything here.
    REVOKE ALL ON SCHEMA public FROM user_role;
    REVOKE ALL ON SCHEMA public FROM readonlyuser_role;
    
    
    GRANT ...
    

    The Database Owner is a local Superuser

    We usually make the db owner an additional role; one who only logs in to create or alter the schema, then gracefully exits. If your admin_role does more than this, consider adding an owner_role.

    Does a public role need to connect?

    Consider adding

     REVOKE CONNECT ON DATABASE yourdb FROM public;
    

    This blocks the loophole where any role created on the same DB server could log into this database.

    Do all this in a transaction block

    Stopping privilege assignment half-way through the job can lead to all sorts of trouble, much akin to locking your keys in your car. Make the privilege assignments a single transaction where possible, so a missed semicolon doesn't lock you out.