Thread: Per-user schemas with inherited skeleton.

Per-user schemas with inherited skeleton.

From
alvarezp@alvarezp.ods.org
Date:
Hello.

I am testing different security models to use on a new database, where I
want different users to be restricted to their own data.

Users would be able to connect to the database through an interface, but
in particular cases of advanced users, they will be able to directly
connect to the database and create their own tables and extend existing
ones if needed.

I have currently discarded two other models: (1) per-user databases, as
getting the data from all users at once would be difficult and SELECTing
from another user would be next to impossible, and (2) per-user schemas
with "CREATE TABLE (LIKE parent_table)", as getting the data from all
users at once would also be difficult and modifying the column
definition on the user tables would be pretty much error-prone.

The model I am testing right now is per-user schemas with inheritance.
Non-user schemas are to be named with an underscore prefix. The model
includes a "_skel" schema that defines each table the user should have
on account creation, similar to /etc/skel for home directories.

On user account creation, the schema gets created and the interface
tries to do a "CREATE TABLE my_relation () INHERITS
(_skel.my_relation);" as the new role, but PostgreSQL returns the error
"must be owner of relation my_relations". I am assuming it refers to
_skel.my_relation.

I am aware that this can be done with views; it is my current
fallback. However, modification of the underlying table structure is
cumbersome as it implies updating the view and a number of rules and
the view for each modification to the table. Furthermore, I can't use
CREATE OR REPLACE VIEW to change the number of columns on a view.

The following questions arose during the preparation of this test:

Why is ownership needed on the parent table?

Is there a way to let PostgreSQL to allow inherited tables to be owned
by different roles?

If PostgreSQL were to let inherited tables to be owned by different
roles, what would be wrong on letting the parent table owner SELECT on
the parent table, and having PostgreSQL return all data from the
children tables as well, even if the owner of the children tables had
revoked the permissions? This possibility is similar to how views work.

Other than the previously mentioned, what other mechanisms are available
to enhance database security this way?

Thank you for your time, and best regards.

Octavio.


Re: Per-user schemas with inherited skeleton.

From
Craig Ringer
Date:
alvarezp@alvarezp.ods.org wrote:

> (2) per-user schemas
> with "CREATE TABLE (LIKE parent_table)", as getting the data from all
> users at once would also be difficult and modifying the column
> definition on the user tables would be pretty much error-prone.

I'd think about this one, personally. You can get data from all users
with a UNION that selects only the common subset of fields. It'll be
somewhat ugly, but effective, and wouldn't be hard to build
programatically with PL/PgSQL using user/schema data obtained from
pg_catalog.

There is the issue that users can alter/drop fields from what's supposed
to be the shared subset of fields, though.

> On user account creation, the schema gets created and the interface
> tries to do a "CREATE TABLE my_relation () INHERITS
> (_skel.my_relation);" as the new role, but PostgreSQL returns the error
> "must be owner of relation my_relations". I am assuming it refers to
> _skel.my_relation.

> The following questions arose during the preparation of this test:
>
> Why is ownership needed on the parent table?

I don't know for sure, but I'd say it's a security issue. Granting a
user ownership of a table that inherits another table gives them, as the
owner of the child table, the ability to (via the child table) INSERT
into the parent table, as well as DELETE/UPDATE/SELECT rows they've
previously inserted themselves.

What you CAN do is create the child table with the same owner as the
parent table, then GRANT appropriate rights to the user. This does not
give the user the ability to ALTER the child table, though, so they
can't add fields, constraints, etc.

You can also have the owner of the parent table CREATE the child table
with the inheritance relationship, then ALTER TABLE ... OWNER TO  to
give ownership of the child table away. You can wrap this within a
SECURITY DEFINER  Pl/PgSQL function if you want the eventual owning user
to be able to do it. eg, assuming for the sake of the example that
you're on a machine with `trust' authentication set to the db:

\c - super
CREATE SCHEMA super;
CREATE TABLE super.parent (x integer);

-- insert a dummy row, too. No end user should ever be able to see it,
-- and it's just here to demonstrate that one user shouldn't be able to
-- see another's data.

INSERT INTO super.parent(x) VALUES (99);

-- Now, the user setup function:

CREATE OR REPLACE FUNCTION user_setup(newuser text) RETURNS void AS $$
BEGIN
   EXECUTE 'CREATE SCHEMA ' || newuser;
   EXECUTE 'GRANT ALL ON SCHEMA ' || newuser || ' TO ' || newuser;
   EXECUTE 'ALTER USER ' || newuser ||
           ' SET search_path TO ''' || newuser || '''';
   -- Now create newuser's inherited tables and grant them ownership.
   EXECUTE 'CREATE TABLE ' || newuser || '.' ||
           'tablename(y integer) inherits (super.parent)';
   EXECUTE 'ALTER TABLE ' || newuser || '.' ||
           'tablename OWNER TO ' || newuser;
END;
$$ LANGUAGE 'plpgsql';

-- Note that the above function could also be written with
-- SECURITY DEFINER rights, where it gets the user name to set
-- up from the `user' built-in sql variable instead of as a
-- parameter. That way the end user could run it to set their
-- account up. However, since you're going to have to
-- CREATE USER for them anyway, you may as well just
-- SELECT user_setup('fred'). Maybe even bundle the CREATE USER
-- into the user_setup function...

-- In this case, we just create a demo user:

CREATE USER test WITH PASSWORD 'test';
SELECT user_setup('test');

-- now the end user connects and has the ability to mess with
-- their tables, schema, etc.


\c test test 127.0.0.1
Password for user test:
You are now connected to database "test" on host "127.0.0.1" as user "test".
test=> \d
          List of relations
  Schema |   Name    | Type  | Owner
--------+-----------+-------+-------
  test   | tablename | table | test
(1 row)

test=> select * from tablename;
  x | y
---+---
(0 rows)

test=> insert into tablename (x,y) values (4,7);
INSERT 0 1
test=> select * from tablename;
  x | y
---+---
  4 | 7
(2 rows)

test=> select * from super.parent;
ERROR:  permission denied for schema super

test=> ALTER TABLE tablename ADD COLUMN z integer;
ALTER TABLE
test=> \d tablename
     Table "test.tablename"
  Column |  Type   | Modifiers
--------+---------+-----------
  x      | integer |
  y      | integer |
  z      | integer |
Inherits: super.parent

test=> ALTER TABLE tablename DROP COLUMN x;
ERROR:  cannot drop inherited column "x"





I guess it might be handy for a new right might be created called say
'INHERIT'. This would let a table owner delegate the right to inherit
from the table to other users. Such a right does not presently exist.
There might be good reasons for it that I don't know about, or it might
simply be that nobody has wanted it - or at least, wanted it enough to
bother implementing it.

Do you? To me, it seems pretty easy to just create the table with the
same ownership as the parent then ALTER TABLE ... OWNER TO it away.

> Is there a way to let PostgreSQL to allow inherited tables to be owned
> by different roles?

Not that I know of, and given the security implications I'd be a bit
nervous about it unless it was done via an explicitly GRANTed right.

> If PostgreSQL were to let inherited tables to be owned by different
> roles, what would be wrong on letting the parent table owner SELECT on
> the parent table, and having PostgreSQL return all data from the
> children tables as well, even if the owner of the children tables had
> revoked the permissions? This possibility is similar to how views work.

The problem is that if a newuser can create an table they own with an
inheritance relationship to a parent it doesn't own, the newuser can
"steal" rights to the parent table. Even if you require that the newuser
has select/insert/update/delete rights at time of relationship creation,
that doesn't help that much since revoking those rights later won't
delete the relationship.

The only right they can really steal, as such, is INSERT, since they can
only SELECT/UPDATE/DELETE those records they added via their own child
table. That's quite bad enough, though, and testing for INSERT rights
before permitting inherited table creation isn't good enough (as noted
above) because of issues with revocation.

> Other than the previously mentioned, what other mechanisms are available
> to enhance database security this way?

Doing all your access through a functional interface where you enforce
your security rules in PL/PgSQL in cases where the existing privelege
model is insufficient.

--
Craig Ringer

Re: Per-user schemas with inherited skeleton.

From
alvarezp@alvarezp.ods.org
Date:
On Mon, 2009-01-05 at 13:05 +0900, Craig Ringer wrote:
> alvarezp@alvarezp.ods.org wrote:
>
> > On user account creation, the schema gets created and the interface
> > tries to do a "CREATE TABLE my_relation () INHERITS
> > (_skel.my_relation);" as the new role, but PostgreSQL returns the error
> > "must be owner of relation my_relations". I am assuming it refers to
> > _skel.my_relation.
>
> > The following questions arose during the preparation of this test:
> >
> > Why is ownership needed on the parent table?
>
> I don't know for sure, but I'd say it's a security issue. Granting a
> user ownership of a table that inherits another table gives them, as the
> owner of the child table, the ability to (via the child table) INSERT
> into the parent table, as well as DELETE/UPDATE/SELECT rows they've
> previously inserted themselves.

I see. So, in other words, inserting a record in a table they don't have
INSERT privileges from would be just a matter of inheriting that table.

For the tables given in the _skel schema from this model, this is be the
way to go. The _skel tables are empty, and have foreign keys to other
secured-tables that prevent two users from having the same value in the
primary keys.

It is also possible to list the records from all users with a simple
SELECT directly from the the parent table in the _skel schema.

> You can also have the owner of the parent table CREATE the child
> table
> with the inheritance relationship, then ALTER TABLE ... OWNER TO  to
> give ownership of the child table away. You can wrap this within a
> SECURITY DEFINER  Pl/PgSQL function if you want the eventual owning user
> to be able to do it. eg, assuming for the sake of the example that
> you're on a machine with `trust' authentication set to the db:

This is what bewildered me. So if parent and child tables CAN have
different owners, this is perfect for the model. Users can extend their
use of the database with an app of their own connecting with their own
role and permissions, it is still secure (or so it looks), and
administration is relatively simple.

> \c - super
> CREATE SCHEMA super;
> CREATE TABLE super.parent (x integer);
>
> -- insert a dummy row, too. No end user should ever be able to see it,
> -- and it's just here to demonstrate that one user shouldn't be able
> << snipped rest of SQL demonstration >>

Thank you for this detailed example. It proves to be secure enough.

CHECK constraints or FOREIGN keys to secured tables are present so the
users don't fill up the tables with dummy rows to perform a DoS. This
can or can not be ultimately desired, though.

> I guess it might be handy for a new right might be created called say
> 'INHERIT'. This would let a table owner delegate the right to inherit
> from the table to other users. Such a right does not presently exist.
> There might be good reasons for it that I don't know about, or it might
> simply be that nobody has wanted it - or at least, wanted it enough to
> bother implementing it.
>
> Do you? To me, it seems pretty easy to just create the table with the
> same ownership as the parent then ALTER TABLE ... OWNER TO it away.

What *would* be even nicer is for PostgreSQL to expose the owner of the
row as a special column (say, "__owner_user") in a SELECT statement to
the parent table.

An INHERITS privilege is a very nice proposal for extension, though
through the use of SECURITY DEFINER we can achieve the same result and
it is good enough for this model. SECURITY DEFINER is comparable to
having a SUID-root program in a Unix system, with everything it implies.

Also, if this privilege would exist, PostgreSQL would need to provide
the owner of the parent table a way to use the before-mentioned special
__owner_user column as part of primary and unique keys.

This would be a good on the way of having a native way of setting up
row-level security based on user ownership (a la virtual private
database with some shared data).

Octavio.



Re: Per-user schemas with inherited skeleton.

From
Craig Ringer
Date:
Octavio Alvarez wrote:

> CHECK constraints or FOREIGN keys to secured tables are present so the
> users don't fill up the tables with dummy rows to perform a DoS. This
> can or can not be ultimately desired, though.

Beware here. IIRC PostgreSQL's inheritance support has some major
limitations when it comes to enforcing constraints on a parent table on
data inserted/updated via a child table. See the documentation for details.

> What *would* be even nicer is for PostgreSQL to expose the owner of the
> row as a special column (say, "__owner_user") in a SELECT statement to
> the parent table.

That would be interesting, though I don't think "owner user" would be
the way to do it. If anything could be obtained without significant
cost, it'd probably be the relid of the leaf table from which that
particular record came. You could get an owning role name etc from that
relatively easily, of course.

That seems like something that'd have a performance hit, though (as each
record would be wider by one relid) and, like the oid column, might
confuse apps that don't expect extra columns not present in the table
definition to be returned.

You can get vaguely the same effect yourself in any case, though
unfortunately not with any sort of guaranteed enforcement. Add an ON
INSERT ... FOR EACH ROW trigger to the child table that sets the value
of a "username" field to the value of the "user" SQL variable, ignoring
whatever the user may have supplied for that field. Since you're giving
your users ownership of the tables they can always just drop or replace
that trigger, so it's not a security measure or suitable for accounting,
but it's good enough for informational reporting.

> An INHERITS privilege is a very nice proposal for extension, though
> through the use of SECURITY DEFINER we can achieve the same result and
> it is good enough for this model. SECURITY DEFINER is comparable to
> having a SUID-root program in a Unix system, with everything it implies.

Yep .. which is why the example I posted didn't use it. If it can be
avoided it should be.

Note that I'm VERY far from an expert in Pg in general and Pg security
in particular, so of course you need to evaluate all this in detail
yourself.

> Also, if this privilege would exist, PostgreSQL would need to provide
> the owner of the parent table a way to use the before-mentioned special
> __owner_user column as part of primary and unique keys.
>
> This would be a good on the way of having a native way of setting up
> row-level security based on user ownership (a la virtual private
> database with some shared data).

I'm not entirely sure I see where this would be useful. I'd be
interested in some examples. As I mentioned before, though,

--
Craig Ringer

Re: Per-user schemas with inherited skeleton.

From
Octavio Alvarez
Date:
On Mon, 2009-01-05 at 13:05 +0900, Craig Ringer wrote:
> alvarezp@alvarezp.ods.org wrote:
>
> > On user account creation, the schema gets created and the interface
> > tries to do a "CREATE TABLE my_relation () INHERITS
> > (_skel.my_relation);" as the new role, but PostgreSQL returns the error
> > "must be owner of relation my_relations". I am assuming it refers to
> > _skel.my_relation.
>
> > The following questions arose during the preparation of this test:
> >
> > Why is ownership needed on the parent table?
>
> I don't know for sure, but I'd say it's a security issue. Granting a
> user ownership of a table that inherits another table gives them, as the
> owner of the child table, the ability to (via the child table) INSERT
> into the parent table, as well as DELETE/UPDATE/SELECT rows they've
> previously inserted themselves.

I see. So, in other words, inserting a record in a table they don't have
INSERT privileges from would be just a matter of inheriting that table.

For the tables given in the _skel schema from this model, this is be the
way to go. The _skel tables are empty, and have foreign keys to other
secured-tables that prevent two users from having the same value in the
primary keys.

It is also possible to list the records from all users with a simple
SELECT directly from the the parent table in the _skel schema.

> You can also have the owner of the parent table CREATE the child
> table
> with the inheritance relationship, then ALTER TABLE ... OWNER TO  to
> give ownership of the child table away. You can wrap this within a
> SECURITY DEFINER  Pl/PgSQL function if you want the eventual owning user
> to be able to do it. eg, assuming for the sake of the example that
> you're on a machine with `trust' authentication set to the db:

This is what bewildered me. So if parent and child tables CAN have
different owners, this is perfect for the model. Users can extend their
use of the database with an app of their own connecting with their own
role and permissions, it is still secure (or so it looks), and
administration is relatively simple.

> \c - super
> CREATE SCHEMA super;
> CREATE TABLE super.parent (x integer);
>
> -- insert a dummy row, too. No end user should ever be able to see it,
> -- and it's just here to demonstrate that one user shouldn't be able
> << snipped rest of SQL demonstration >>

Thank you for this detailed example. It proves to be secure enough.

CHECK constraints or FOREIGN keys to secured tables are present so the
users don't fill up the tables with dummy rows to perform a DoS. This
can or can not be ultimately desired, though.

> I guess it might be handy for a new right might be created called say
> 'INHERIT'. This would let a table owner delegate the right to inherit
> from the table to other users. Such a right does not presently exist.
> There might be good reasons for it that I don't know about, or it might
> simply be that nobody has wanted it - or at least, wanted it enough to
> bother implementing it.
>
> Do you? To me, it seems pretty easy to just create the table with the
> same ownership as the parent then ALTER TABLE ... OWNER TO it away.

What *would* be even nicer is for PostgreSQL to expose the owner of the
row as a special column (say, "__owner_user") in a SELECT statement to
the parent table.

An INHERITS privilege is a very nice proposal for extension, though
through the use of SECURITY DEFINER we can achieve the same result and
it is good enough for this model. SECURITY DEFINER is comparable to
having a SUID-root program in a Unix system, with everything it implies.

Also, if this privilege would exist, PostgreSQL would need to provide
the owner of the parent table a way to use the before-mentioned special
__owner_user column as part of primary and unique keys.

This would be a good on the way of having a native way of setting up
row-level security based on user ownership (a la virtual private
database with some shared data).

Octavio.


Re: Per-user schemas with inherited skeleton.

From
"Scott Marlowe"
Date:
On Sun, Jan 4, 2009 at 9:05 PM, Craig Ringer
<craig@postnewspapers.com.au> wrote:
> alvarezp@alvarezp.ods.org wrote:
>
>> (2) per-user schemas
>> with "CREATE TABLE (LIKE parent_table)", as getting the data from all
>> users at once would also be difficult and modifying the column
>> definition on the user tables would be pretty much error-prone.

If you are just replacing a couple of tables, and they don't need most
of the tables but can use the public schema this might be worthwhile
for some projects.  Everyone uses the public schema as the common one.

I agree with everything you posted after this, and a nice example of doing it.

>> Is there a way to let PostgreSQL to allow inherited tables to be owned
>> by different roles?
>
> Not that I know of, and given the security implications I'd be a bit nervous
> about it unless it was done via an explicitly GRANTed right.

I hope here you're meaning to have tables that are inheritable by
various non-role members.  It works as long as everyone's in the same
group role with the right permissions.  Since you'd have to change
ownership to the group role for the parent table, everyone would, in
effect, own it now.  But if you wanna do it...

The admin creates a group called sharing, all users are granted
sharing, so when they create a table they can share it or not.  If
they share it in the public schema then any user in the group can
inherit it, and search_path can make it the "default" table so to
speak.

The child tables are automatically created with the role of the user
not the group, so your user would have to remember to grant select to
the sharing role when they created a table.

Re: Per-user schemas with inherited skeleton.

From
Craig Ringer
Date:
Scott Marlowe wrote:

>>> Is there a way to let PostgreSQL to allow inherited tables to be owned
>>> by different roles?
>> Not that I know of, and given the security implications I'd be a bit nervous
>> about it unless it was done via an explicitly GRANTed right.
>
> I hope here you're meaning to have tables that are inheritable by
> various non-role members.  It works as long as everyone's in the same
> group role with the right permissions.  Since you'd have to change
> ownership to the group role for the parent table, everyone would, in
> effect, own it now.  But if you wanna do it...

That's right - I refer to inheritance by a user that's not a member of
the role that owns the table.

If the inheriting users *are* a member of the owning role of the parent
table, then they can select and update the shared-structure part of
OTHER users' records via the parent table, as well as their own. IIRC
they can delete other users records via the parent table, too. Not ideal
if the various users are supposed to be blind to each others' data, as
appears to be the case here.

--
Craig Ringer

Re: Per-user schemas with inherited skeleton.

From
"Scott Marlowe"
Date:
On Wed, Jan 7, 2009 at 11:39 PM, Craig Ringer
<craig@postnewspapers.com.au> wrote:
> Scott Marlowe wrote:
>
>>>> Is there a way to let PostgreSQL to allow inherited tables to be owned
>>>> by different roles?
>>>
>>> Not that I know of, and given the security implications I'd be a bit
>>> nervous
>>> about it unless it was done via an explicitly GRANTed right.
>>
>> I hope here you're meaning to have tables that are inheritable by
>> various non-role members.  It works as long as everyone's in the same
>> group role with the right permissions.  Since you'd have to change
>> ownership to the group role for the parent table, everyone would, in
>> effect, own it now.  But if you wanna do it...
>
> That's right - I refer to inheritance by a user that's not a member of the
> role that owns the table.
>
> If the inheriting users *are* a member of the owning role of the parent
> table, then they can select and update the shared-structure part of OTHER
> users' records via the parent table, as well as their own. IIRC they can
> delete other users records via the parent table, too. Not ideal if the
> various users are supposed to be blind to each others' data, as appears to
> be the case here.

No, it doesn't work that way.  Perms on the parent table are just
that, perms on the parent table.  I just tested it.  Given a role
called sharing, and two members stan and ted, and a parent table owned
by sharing, the users have to grant select on their tables to the
other users or the role they belong to or the other users can't see it
or change it.

Re: Per-user schemas with inherited skeleton.

From
"Scott Marlowe"
Date:
On Wed, Jan 7, 2009 at 11:39 PM, Craig Ringer
<craig@postnewspapers.com.au> wrote:
> Scott Marlowe wrote:
>
>>>> Is there a way to let PostgreSQL to allow inherited tables to be owned
>>>> by different roles?
>>>
>>> Not that I know of, and given the security implications I'd be a bit
>>> nervous
>>> about it unless it was done via an explicitly GRANTed right.
>>
>> I hope here you're meaning to have tables that are inheritable by
>> various non-role members.  It works as long as everyone's in the same
>> group role with the right permissions.  Since you'd have to change
>> ownership to the group role for the parent table, everyone would, in
>> effect, own it now.  But if you wanna do it...
>
> That's right - I refer to inheritance by a user that's not a member of the
> role that owns the table.
>
> If the inheriting users *are* a member of the owning role of the parent
> table, then they can select and update the shared-structure part of OTHER
> users' records via the parent table, as well as their own. IIRC they can
> delete other users records via the parent table, too. Not ideal if the
> various users are supposed to be blind to each others' data, as appears to
> be the case here.

Also alter table gets locked by the child tables.  If stan, a member
of sharing, tries to change the inherited table top, which user ted
has inherited, he gets an error saying he has to have alter perms on
ted's table.