暂无图片
暂无图片
1
暂无图片
暂无图片
暂无图片

[译]PostgreSQL15 public shema权限增强

yanzongshuaiDBA 2022-09-12
782

PostgreSQL 15对用户权限这块进行了增强。默认情况下,不再设置public schema的CREATE权限

2021年9月,PG15的版本提交了一个patch:默认情况下不再设置public schema的CREATE权限。该建议来自:https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2018-1058Commit为:https://git.postgresql.org/gitweb/?p=postgresql.git;a=commitdiff;h=b073c3ccd06e4cb845e121387a43faa8c68a7b62

这对于普通用户来说(非超级用户)意味着什么呢?

先看下PG14的操作:

    postgres=# SELECT version();
    version
    -------------------------------------------------------------------------------------------------------
    PostgreSQL 14.5 on x86_64-pc-linux-gnu, compiled by gcc (Ubuntu 9.4.0-1ubuntu1~20.04.1) 9.4.0, 64-bit
    (1 row)
    postgres=# CREATE ROLE unprivileged WITH LOGIN;
    CREATE ROLE
    postgres=# CREATE DATABASE priv_test;
    CREATE DATABASE
    postgres=# \c priv_test
    You are now connected to database "priv_test" as user "ads".
    priv_test=# \dn+ public
    List of schemas
    Name | Owner | Access privileges | Description
    --------+-------+-------------------+------------------------
    public | ads | ads=UC/ads +| standard public schema
    | | =UC/ads |
    (1 row)

    可以看到,public(访问权限中的第二行)具有USAGE(U)和CREATE(C)的权限。普通用户可以在public schema模式下创建表:

      priv_test=# SET SESSION ROLE unprivileged;
      SET
      priv_test=> SHOW search_path;
      search_path
      -----------------
      "$user", public
      (1 row)
      priv_test=> CREATE TABLE priv_test (id INT);
      CREATE TABLE
      priv_test=> \dp priv_test
      Access privileges
      Schema | Name | Type | Access privileges | Column privileges | Policies
      --------+-----------+-------+-------------------+-------------------+----------
      public | priv_test | table | | |
      (1 row)

      下面是PG15的操作及其影响:

        postgres=# SELECT version();
        version
        ----------------------------------------------------------------------------------------------------------
        PostgreSQL 15beta3 on x86_64-pc-linux-gnu, compiled by gcc (Ubuntu 9.4.0-1ubuntu1~20.04.1) 9.4.0, 64-bit
        (1 row)
        postgres=# CREATE ROLE unprivileged WITH LOGIN;
        CREATE ROLE
        postgres=# CREATE DATABASE priv_test;
        CREATE DATABASE
        postgres=# \c priv_test
        You are now connected to database "priv_test" as user "ads".
        priv_test=# \dn+ public
        List of schemas
        Name | Owner | Access privileges | Description
        --------+-------------------+----------------------------------------+------------------------
        public | pg_database_owner | pg_database_owner=UC/pg_database_owner+| standard public schema
        | | =U/pg_database_owner |
        (1 row)
        缺少C(CREATE)。默认情况下,普通用户的CREATE TABLE不再工作:
        priv_test=# SET SESSION ROLE unprivileged;
        SET
        priv_test=> SHOW search_path;
        search_path
        -----------------
        "$user", public
        (1 row)
        priv_test=> CREATE TABLE priv_test (id INT);
        ERROR: permission denied for schema public
        LINE 1: CREATE TABLE priv_test (id INT);

        升级中如何应对这种情况呢?

        这里有2种方法可以升级。当然还有其他更多方法,例如重新按照您的应用程序。但是处于本文目的,我们只关注数据从旧版本传输到新版本的方法。

        1)从老版本(14或更老)中将数据dump出来,并恢复到新版本中(15及以上)

        2)运行pg_upgrade.

        两种方式的工作方式有点不同,我们看下细节:

        Dump和Restore

        使用自带的pg_dump工具来完成。最好使用自定义或者目录格式。并且需要使用较新版本(15)中的pg_dump来转储旧数据库:

          /path/to/15/bin/pg_dump -F c -f /tmp/backup.dump priv_test

          通过创建一个空数据库(使用template0作为源/模板)来完成恢复,然后使用pg_restore将转储恢复到新数据库中。

            /path/to/15/bin/dropdb --if-exists priv_test
            /path/to/15/bin/createdb -T template0 priv_test
            /path/to/15/bin/pg_restore -d priv_test -e /tmp/backup.dump

            然而:

              priv_test=# \dn+ public
              List of schemas
              Name | Owner | Access privileges | Description
              --------+-------------------+----------------------------------------+------------------------
              public | pg_database_owner | pg_database_owner=UC/pg_database_owner+| standard public schema
              | | =U/pg_database_owner |
              (1 row)

              为什么public schema中具有“new”权限,而CREATE缺失?因为public模式包含在template0中,并被复制到新创建的数据库中,在PG15中,template0中的public模式具有“new”权限。我们看下:

                priv_test=# SELECT datname, datallowconn FROM pg_database;
                datname | datallowconn
                -----------+--------------p
                postgres | t
                template1 | t
                template0 | f
                priv_test | t
                (4 rows)
                priv_test=# UPDATE pg_database SET datallowconn = TRUE WHERE datname = 'template0';
                UPDATE 1
                priv_test=# \c template0
                You are now connected to database "template0" as user "ads".
                template0=# \dn
                List of schemas
                Name | Owner
                --------+-------------------
                public | pg_database_owner
                (1 row)
                template0=# \dn+ public
                List of schemas
                Name | Owner | Access privileges | Description
                --------+-------------------+----------------------------------------+------------------------
                public | pg_database_owner | pg_database_owner=UC/pg_database_owner+| standard public schema
                | | =U/pg_database_owner |
                (1 row)

                如我们所见,template0中的public少了CREATE权限,然后将其复制到新数据库中。任何期望其他情况并依赖于普通用户可写的public应用程序都会遇到问题。可以通过在新数据库中为public添加CREATE权限来解决此问题:

                  priv_test=# GRANT CREATE ON SCHEMA public TO PUBLIC;
                  GRANT
                  priv_test=# \dn+ public
                  List of schemas
                  Name | Owner | Access privileges | Description
                  --------+-------------------+----------------------------------------+------------------------
                  public | pg_database_owner | pg_database_owner=UC/pg_database_owner+| standard public schema
                  | | =UC/pg_database_owner |
                  (1 row)

                  如果您希望这是新数据库的默认值,就将此更改应用于template1.

                  pg_upgrade

                  升级的第二种方式是pg_upgrade。这会将目录从就数据库复制到新数据库中。然后复制或链接数据文件。这是将服务器升级到新版本的顺畅方法。

                    /path/to/14/bin/pg_ctl -m fast -D /data/14/data stop
                    /path/to/15/bin/pg_ctl -m fast -D /data/15/data stop
                    rm -rf /data/15/data/*
                    /path/to/15/bin/initdb --pgdata=/data/15/data
                    /path/to/15/bin/pg_upgrade -b /path/to/14/bin -B /path/to/15/bin -d /data/14/data -D /data/15/data -p 5454 -P 5455 -v

                    这将运行pg_upgrade,将14版本更新到15,之后public模式看起来与14相同:

                      priv_test=# \dn+ public
                      List of schemas
                      Name | Owner | Access privileges | Description
                      --------+-------+-------------------+------------------------
                      public | ads | ads=UC/ads +| standard public schema
                      | | =UC/ads |
                      (1 row)

                      PG不应用pg_database_owner也不用设置(撤销)“新”权限,所有内容都像以前一样被复制。如果想拥有15的新型为,需要从public撤销CREATE:

                        priv_test=# REVOKE CREATE ON SCHEMA public FROM PUBLIC;
                        REVOKE

                        还可以将所有者设置为新的pg_database_owner:

                          priv_test=# ALTER SCHEMA public OWNER TO pg_database_owner;
                          ALTER SCHEMA
                          priv_test=# \dn+ public
                          List of schemas
                          Name | Owner | Access privileges | Description
                          --------+-------------------+----------------------------------------+------------------------
                          public | pg_database_owner | pg_database_owner=UC/pg_database_owner+| standard public schema
                          | | =U/pg_database_owner |
                          (1 row)

                          总结

                          PostgreSQL 数据库从版本 <= 14 升级到版本 15 或更高版本会给public模式带来一些挑战。两种最常见的升级方式在处理更改时表现不同。

                          最好不要依赖可写的public模式。

                          原文

                          https://andreas.scherbaum.la/blog/archives/1120-Changes-to-the-public-schema-in-PostgreSQL-15-and-how-to-handle-upgrades.html

                          文章转载自yanzongshuaiDBA,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

                          评论