How to create PostgreSQL read-only role with Ansible? - postgresql

I am creating a PostgreSQL user for a in internal dashboard where I want the user's access to the db to default to read-only.
I did some research, and it seems to be a bit complex to create a read-only user who also inherits access to any new tables, materialized views, etc.
So I think the simplest thing is just to set:
ALTER USER readuser SET default_transaction_read_only = on;
How do I set this using the Ansible PostgreSQL modules?
I'm not sure if I should be using the postgresql_privs or postgresql_user module. I tried experimenting with different values for the priv attribute, but couldn't get anything to work.
I'm aware that this is not guaranteed read-only, as the user could just change the type of transaction at runtime. However, it will be just fine for my usecase since security isn't an issue, I just want to protect against ignorant users accidentally modifying data. If someone is knowledgable enough to change their transaction, I'd rather assume they know what they're doing and have a legitimate reason for it.

I'm not sure if I should be using the postgresql_privs or
postgresql_user module. I tried experimenting with different values
for the priv attribute, but couldn't get anything to work.
Did you tried to revoke privs?:
# REVOKE INSERT, UPDATE ON ALL TABLES IN SCHEMA public FROM reader
# "public" is the default schema. This also works for PostgreSQL 8.x.
- postgresql_privs: >
db=library
state=absent
privs=INSERT,UPDATE
objs=ALL_IN_SCHEMA
role=reader
Note:
To revoke only GRANT OPTION for a specific object, set state to
present and grant_option to no (see examples http://docs.ansible.com/ansible/postgresql_privs_module.html).
Note that when revoking privileges from a role R, this role may still
have access via privileges granted to any role R is a member of
including PUBLIC.
Note that when revoking privileges from a role R, you do so as the
user specified via login. If R has been granted the same privileges by
another user also, R can still access database objects via these
privileges.

Related

Granting via role in Postgres

I'd like to ask Postgres experts about a confusing sentence which I found in the documentation. In particular, I refer to the GRANT command, where the documentation states:
If the role executing GRANT holds the required privileges indirectly via more than one role membership path, it is unspecified which containing role will be recorded as having done the grant. In such cases it is best practice to use SET ROLE to become the specific role you want to do the GRANT as.
If I understand correctly, this is related to role inheritance. In particular, you might have a role C which inherits a permission from both role A and role B. In this case, if a user has role C and grants the permission, then Postgres might non-deterministically stipulate that the permission was granted by either A or B. To avoid this ambiguity, the user can issue SET ROLE A or SET ROLE B to force a lesser role and clarify how the granting should occur.
Some questions about this and a more general one:
Is this reading correct or does the sentence mean something different?
What if, in the prior example, a user has the permission via A and C (as opposed to A and B)? In that case, the recommended practice of using "SET ROLE to become the specific role you want to do the GRANT as" does not seem to help, because setting the role to C still leaves an ambiguity due to inheritance.
More generally, is there any good reference documentation where the role system of Postgres is compared against traditional SQL grant diagrams? Some parts of the Postgres implementation are not straightforward and I'd like to learn more about it, which is complicated to do via experiments alone.
Thanks in advance.
Yes, in my opinion, you are right:
You understand it correctly.
It is no matters if the role that grants a permission has innerited the privilege or not. For each permission that you have postgress registers the role that granted the privilege.
I don't know. If you find it, please post a comment.

Is it possible in PostgreSQL to give a specific user or group on edit rights only to one schema?

Is it possible in PostgreSQL to give a specific user or group on edit rights only to one schema?
I need the user to only change the objects in the schema. Similar to superuser rights, but only for one schema.
Thank for advance
The best setup in your case is probably the following.
Let's assume that the schema and the objects therein are owned by object_owner and the user that should have “superuser” privileges is called wannabe.
Then you can do:
ALTER ROLE wannabe NOINHERIT;
GRANT object_owner TO wannabe;
That allows wannabe to become object_owner by running
SET ROLE object_owner;
similar to using su in UNIX. Use RESET ROLE to step down again.

PostgreSQL authorization with Access ODBC Linked Tables

For the impatient - I can summarize this question as:
What practical approach can be used to leverage role-based privileges
in PostgreSQL when using an Access Front End that employs ODBC linked-tables?
And now for the longer version:
I've inherited the unsavory task of upgrading an Access 2000 / PG 7 application to Access 2013 / PG 9. I'm new to PostgreSQL but have used Oracle and Microsoft Access quite a bit.
EDIT: The production server is running PostgreSQL on Mac OS X Lion. My Test machine is running PostgreSQL on Oracle Linux 7.
This Access DB is linking to tables in the PG Database via ODBC, connecting using an single PG login role (application_user). Every user connects with this login role, and it is only the conditions in the Forms / VBA that limits the user's rights. If, however, a user can get into the navigation pane - they can access the linked tables directly and bypass all security restrictions. While upgrading this database, I'd like to see if I can tighten that up.
I could set up each user with their own login role on PostgreSQL, but then it would mean (from the way I'm looking at it) a hefty amount of retooling the database. I'd rather not make such large changes on a production database - incremental changes are more desired.
Looking at the database's security needs - I can think of only five roles that would be needed.
Order Entry
Customer Entry
Order and Customer Entry
Read-Only
Not Authorized - No Access
I can set up these as Group Roles in PGSQL and each table with the necessary ACL for each role.
What I'm missing is how I can go from a single login-role (application_user) to all of the above roles?
My initial thought was to set the application_user (logon role) to have no group roles (essentially resulting in "Not Authorized - No Access"), and then use a call to a PL/pgSQL function authorize(Username, MD5PassWord) to authorize and elevate the role. The function would check if the supplied MD5 hash matches the MD5 hash stored in the users table - and if so - it would issue a SET SESSION ROLE for the appropriate Group Role.
If this would work, it would let me track user names that are logging in, and then using the pg_backend_pid() function, I can associate it back with the user for the business logic or logging or whatever. It also means I don't need to worry if some user goes into the Linked Table - because their access would be restricted by whatever role they are currently authorized for in that database session.
So I whipped up a plpgsql script, set its owner to OrderCustomerEntryGroup and gave it SECURITY DEFINER rights.
DECLARE
v_Status integer;
BEGIN
v_Status := 0;
IF pin_username = 'username' AND MD5('foo') = pin_pwmd5 THEN
SET SESSION AUTHORIZATION OrderEntryGroup;
v_Status := 1;
END IF;
RETURN v_Status;
END;
Only problem however with my implementation is that
SELECT authenticate('username',MD5('foo'));
gives:
ERROR: cannot set parameter "session_authorization" within security-definer function
SQL state: 42501
Context: SQL statement "SET SESSION AUTHORIZATION OrderEntryGroup"
PL/pgSQL function authenticate(character varying,text) line 7 at SQL statement
So I read up on this - and from what I can tell, you used to be able to do this, but for whatever reason it was removed. I haven't been able to find an alternative - other than using the built in roles on a per-user level.
So what I'm asking is .. What am I missing to make my approach (an easy solution) work, or is there a better way of doing this that won't involve ripping apart the existing access database?
If you want to restrict access to the database from a direct connection then you'll need to do a certain amount of "retooling" on the back-end regardless. The best approach is almost always to have each user connect with their own credentials and then restrict what that user can do based on the groups (sometimes referred to as "roles") to which they belong in the database.
If you want to avoid having to set up separate database userids/passwords for each network user then you should investigate using integrated Windows authentication (SSPI) as discussed in another question here. You'll still need to define the users (in addition to the groups/roles) at the database level, but you'd have to do most of that work anyway.

Why did PostgreSQL merge users and groups into roles?

From the PostgreSQL docs:
The concept of roles subsumes the concepts of "users" and "groups". In
PostgreSQL versions before 8.1, users and groups were distinct kinds
of entities, but now there are only roles. Any role can act as a user,
a group, or both.
Why did they make this change in 8.1?
Perhaps it's easier from the C coders point of view, with a single Role class (struct)?
More details:
CREATE USER is equivalent to CREATE ROLE except that CREATE USER gives the LOGIN permission to the user/role.
(I'm about to design a permission system for my webapp, hence I'm interested in this.)
The merge has many advantages and no disadvantages. For instance, you can now seamlessly convert a "user" to a "group" and vice versa by adding / removing the LOGIN privilege.
ALTER ROLE myrole LOGIN;
ALTER ROLE myrole NOLOGIN;
Or you can GRANT membership in any other login ("user") or non-login role ("group") to a role:
GRANT joe TO sue;
You can still:
CREATE USER james;
That's just a role with login privilege now. Or:
CREATE GROUP workers;
That's effectively the same as CREATE ROLE now.
The manual has it all.
I found this thread in the PostgreSQL-Hackers list, from June 6, 2003, that in the end suggests that users and groups and roles be consolidated. (Thanks Craig Ringer for suggesting that I check the pgsql-hackers list archives.)
Here are some benefits mentioned (those that I found).
allow groups to have groups as members
the ACL code would be simplified
the GRANT/REVOKE syntax and the display format for ACL lists could be
simplified, since there'd be no need for a syntactic marker as to
whether a given name is a user or a group.
In some circumstances I could see it making sense to allow logging in
directly as a group/role/whatchacallit
This would also solve the problem that information_schema views will
show only owned objects
[makes it easier to] representing privileges granted to groups [since
you'd simply reuse the role related code?]
From the manual:
The SQL standard defines the concepts of users and roles, but it
regards them as distinct concepts and leaves all commands defining
users to be specified by each database implementation. In PostgreSQL
we have chosen to unify users and roles into a single kind of entity.
Roles therefore have many more optional attributes than they do in the
standard.
Having a distinction between users and groups doesn't gain you anything.
AFAIK the motivation for changing it was to simplify uses like:
One user masquerading as another, eg a superuser simulating a reduced permissions user. With unified roles this becomes just another change of current role, no different to changing primary group.
Groups that are members of other groups to implement granular access permissions.
If you want the details, though, you're best off checking out the archives of the pgsql-hackers list for the period, and the git history (converted from CVS).

Creating another

I was trying to create new role that will have all privileges of the PUBLIC role and then remove all of the privileges from PUBLIC role afterwards. This is for security purposes.
This is the problem. I couldn't grant SYS./1005bd30_LnkdConstant, and others with the same format, to my new role.
sample:
SYS./10076b23_OraCustomDatumClosur
SYS./100c1606_StandardMidiFileRead
ORDSYS./1013c29d_PlanarImageServerPro
.
.
.
Do I really need these or my new "public" role can do without those?
Any help will be much appreciated.
Let me take a quick guess here. The problem you are having is that the object names are case sensitive. The quick fix is to enclose the object names in double quotes, like this.
GRANT EXECUTE ON SYS."/1005bd30_LnkdConstant" TO mynewpublicrole;
You indicate that you "couldn't grant [EXECUTE ON] SYS./1005bd30_LnkdConstant" to a role.
I take that to mean that when you ran the GRANT statement, Oracle raised an exception, most likely,
ORA-00903: invalid table name
Enclosing the objectname in double quotes (as shown in the example) should fix that problem.
It's not possible to answer the question whether your new role needs EXECUTE privilege on those objects or not. Well, the role doesn't necessarily need them. The question is whether the user needs them or not (whether granted directly, or granted indirectly through roles.) That can be determined through thorough testing.
Some other comments.
If your intention is to create a new role and grant that role to all users, I don't see that security is changed or improved. So, I'm going to assume that is not the case.
It would appear you are trying to apply the principle of "least privilege". I applaud that effort.
One of the most common patterns I see application developers follow is to have the application connect to the database as the owner of the schema objects. What that means is that the application has all sorts of privileges it probably doesn't need, e.g. DROP TABLE, ALTER PROCEDURE, etc.
The pattern we use is to have an "OWNER" user that owns the schema objects, and a separate "APP" user that has specific privileges it needs on the "OWNER" objects, and synonyms for the "OWNER" objects. (The synonyms allow the OWNER.object to be referenced without being qualified with the OWNER.) It almost goes without saying, we do not grant privileges to PUBLIC, we grant to roles where needed.
I mention this because it's a pattern we use for implementing the principle of "least privilege".
For other security concerns, I recommend you review the "Oracle Security Checklist" white paper:
http://www.oracle.com/technology/deploy/security/database-security/pdf/twp_security_checklist_database.pdf
Some other possible exceptions you may have encountered when executing the GRANT statement:
ORA-01031: insufficient privileges
or
ORA-04042: procedure, function, package, or package body does not exist.
In either of those cases, make sure you connect to the database as SYS (SYSDBA) to grant the privileges. We almost always grant privileges as the owner of the object, rather than have some other user as the GRANTEE. I almost never use the "WITH GRANT OPTION" on object privileges. It's a simpler model, and avoids any potential problem with dependency trees.