如何在 Google 云中设置保险库和 Postgres 以获得正确的权限?
How to setup vault and Postgres in Google cloud to have the correct permissions?
问题
我正在尝试在 Google 云中设置 Hashicorp vault 和 Postgres。
我正在使用 liquibase 来管理架构,当它执行迁移时,它会从保管库中检索 username/password,然后在该用户下运行以执行迁移。
但是,默认的 postgres 用户或任何其他试图使用该 table 的用户不能并收到 ERROR: permission denied for relation
.
如果我理解正确,用户是在以 postgres
的角色登录时创建的。这一切都在本地工作,但 google 云的东西似乎以截然不同的方式设置。
这里有一些输出,可以查看数据库的设置:
\du
List of roles
Role name | Attributes | Member of
----------------------------------+------------------------------------
------------------------+---------------------
cloudsqladmin | Superuser, Create role, Create DB,
Replication, Bypass RLS | {}
cloudsqlagent | Create role, Create DB
| {cloudsqlsuperuser}
cloudsqlreplica | Replication
| {}
cloudsqlsuperuser | Create role, Create DB
| {}
postgres | Create role, Create DB
| {cloudsqlsuperuser}
test | Create role, Create DB
| {cloudsqlsuperuser}
v-token-power-watc-p079t4r13s85w | Password valid until
| {cloudsqlsuperuser}
\dt
List of relations
Schema | Name | Type | Owner
--------+-----------------------+-------+------------------------------
public | databasechangelog | table | v-token-power-watc-p079t4r13s85w
public | databasechangeloglock | table | v-token-power-watc-p079t4r13s85w
public | test | table | v-token-power-watc-p079t4r13s85w
保险库创建 SQL 看起来像这样:
CREATE ROLE "{{name}}" WITH LOGIN PASSWORD '{{password}}' VALID UNTIL '{{expiration}}';
GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO "{{name}}";
GRANT cloudsqlsuperuser to "{{name}}";"
我试过以多种方式玩这些角色,但似乎我创建的所有新角色总是放在它们自己的沙盒中,不能玩任何其他角色。在这一点上,我完全不知道下一步该怎么做,似乎无论我可以通过 grant
选项,它都无济于事。
当前问题
是否所有者需要是cloudsqlsuperuser 组而不是temp ownwer?
有没有办法将其设置为默认值,而不必确保我的所有 table 都是由正确的所有者创建的?
使用本地 docker 实例进行复制
Docker 启动 Postgres 数据库的命令
docker run --rm --name database -v $(pwd)/setup.sql:/docker-entrypoint-initdb.d/setup.sql -e POSTGRES_USER=temp -e POSTGRES_PASSWORD=secret -e POSTGRES_DB=mydb postgres:9.6
setup.sql
的内容
ALTER ROLE postgres RENAME TO cloudsqladmin;
CREATE ROLE cloudsqlsuperuser WITH CREATEDB CREATEROLE;
ALTER DATABASE mydb OWNER TO cloudsqlsuperuser;
CREATE ROLE "postgres" WITH LOGIN CREATEDB CREATEROLE IN ROLE cloudsqlsuperuser;
在postgres下创建用户模拟vault用户创建
docker exec -ti database psql -U postgres -d mydb -c "CREATE ROLE testuser WITH LOGIN IN ROLE cloudsqlsuperuser"
从测试用户
创建测试table
docker exec -ti database psql -U testuser -d mydb -c "CREATE TABLE test (col1 text)"
尝试 select table 演示错误
docker exec -ti database psql -U postgres -d mydb -c "SELECT * FROM test"
我认为这里的问题与在该临时用户下创建的新 table 有关 - 您的其他用户将无权访问它。有几种方法可以尝试解决这个问题。
首先是在每个 table 创建后授予权限。你必须根据你想要授予访问权限的角色相应地调整它,但如果你只想 all 用户(当前和未来)拥有完全访问权限),你可以使用 GRANT ALL PRIVILEGES ON <table> TO public;
如所述 here.
更好的解决方案可能是仅使用一个用户创建所有 table。您仍然可以让 Vault 按需为其生成新密码并自动撤销对它的访问,即使用户名相同也是如此。为此,您必须 手动 创建用户并首先赋予它您想要的权限 - 假设您将其命名为 migrator
:
CREATE ROLE migrator;
GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO "migrator";
然后更改您的 Vault 配置以在请求凭据时添加登录权限,并在撤销凭据时将其删除:
vault write <role path here, e.g. database/roles/migrator> \
db_name=<db name here> \
creation_statements="ALTER ROLE migrator LOGIN PASSWORD '{{password}}' VALID UNTIL '{{expiration}}';" \
revocation_statements="ALTER ROLE migrator NOLOGIN;"
这样做的好处是您可以使用 ALTER DEFAULT PRIVILEGES
(docs here) 来避免为每个新创建的 table 授予权限。例如,如果您知道所有用户都应该有权访问所有内容:
ALTER DEFAULT PRIVILEGES FOR ROLE migrator GRANT ALL PRIVILEGES ON TABLES TO PUBLIC
我不确定如果删除一个 table 所有者的用户,postgres 会有什么反应,但如果有问题,那么这个解决方案也会解决这个问题。
简短摘要
您必须在 table/sequence/function 创建过程后 运行 REASSIGN OWNED BY "$VAULT_USERNAME" TO "cloudsqlsuperuser"
才能重新分配回普通角色。
更长的答案
Postgres 9.6 CREATE TABLE
will by default create the table under the user's role。因为保险库用户被设计为临时凭证,我们还需要确保在临时角色下创建的对象被重新分配回某种普通角色。在我在 Google Cloud 中概述的特定情况下,我们将重新分配回 cloudsqlsuperuser
。
我发现可以使用的一个命令是 REASSIGN OWNED BY current_user to "cloudsqlsuperuser"
命令,该命令然后将获取该临时用户拥有的所有对象并分配回该公共角色。
奖励:liquibase 配置
我正在使用 liquibase 进行迁移,这在我的更改日志中,用于在我们每次 运行 迁移时修复权限:
<databaseChangeLog>
<changeSet author="elindblom" id="fix-permissions" context="" logicalFilePath="fix-permissions" runAlways="true" runOrder="last">
<sqlFile path="baselines/sql/fix-permissions.sql" splitStatements="false" relativeToChangelogFile="true" stripComments="false"/>
</changeSet>
... rest of your change logs after ...
</databaseChangeLog>
SQL 看起来像这样:
REASSIGN OWNED BY current_user TO "cloudsqlsuperuser";
一切完成后 changeSet
运行 秒,每次 运行 秒。非常重要的位是 runAlways
和 runOrder
元素。
问题
我正在尝试在 Google 云中设置 Hashicorp vault 和 Postgres。
我正在使用 liquibase 来管理架构,当它执行迁移时,它会从保管库中检索 username/password,然后在该用户下运行以执行迁移。
但是,默认的 postgres 用户或任何其他试图使用该 table 的用户不能并收到 ERROR: permission denied for relation
.
如果我理解正确,用户是在以 postgres
的角色登录时创建的。这一切都在本地工作,但 google 云的东西似乎以截然不同的方式设置。
这里有一些输出,可以查看数据库的设置:
\du
List of roles
Role name | Attributes | Member of
----------------------------------+------------------------------------
------------------------+---------------------
cloudsqladmin | Superuser, Create role, Create DB,
Replication, Bypass RLS | {}
cloudsqlagent | Create role, Create DB
| {cloudsqlsuperuser}
cloudsqlreplica | Replication
| {}
cloudsqlsuperuser | Create role, Create DB
| {}
postgres | Create role, Create DB
| {cloudsqlsuperuser}
test | Create role, Create DB
| {cloudsqlsuperuser}
v-token-power-watc-p079t4r13s85w | Password valid until
| {cloudsqlsuperuser}
\dt
List of relations
Schema | Name | Type | Owner
--------+-----------------------+-------+------------------------------
public | databasechangelog | table | v-token-power-watc-p079t4r13s85w
public | databasechangeloglock | table | v-token-power-watc-p079t4r13s85w
public | test | table | v-token-power-watc-p079t4r13s85w
保险库创建 SQL 看起来像这样:
CREATE ROLE "{{name}}" WITH LOGIN PASSWORD '{{password}}' VALID UNTIL '{{expiration}}';
GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO "{{name}}";
GRANT cloudsqlsuperuser to "{{name}}";"
我试过以多种方式玩这些角色,但似乎我创建的所有新角色总是放在它们自己的沙盒中,不能玩任何其他角色。在这一点上,我完全不知道下一步该怎么做,似乎无论我可以通过 grant
选项,它都无济于事。
当前问题
是否所有者需要是cloudsqlsuperuser 组而不是temp ownwer?
有没有办法将其设置为默认值,而不必确保我的所有 table 都是由正确的所有者创建的?
使用本地 docker 实例进行复制
Docker 启动 Postgres 数据库的命令
docker run --rm --name database -v $(pwd)/setup.sql:/docker-entrypoint-initdb.d/setup.sql -e POSTGRES_USER=temp -e POSTGRES_PASSWORD=secret -e POSTGRES_DB=mydb postgres:9.6
setup.sql
的内容ALTER ROLE postgres RENAME TO cloudsqladmin;
CREATE ROLE cloudsqlsuperuser WITH CREATEDB CREATEROLE;
ALTER DATABASE mydb OWNER TO cloudsqlsuperuser;
CREATE ROLE "postgres" WITH LOGIN CREATEDB CREATEROLE IN ROLE cloudsqlsuperuser;
在postgres下创建用户模拟vault用户创建
docker exec -ti database psql -U postgres -d mydb -c "CREATE ROLE testuser WITH LOGIN IN ROLE cloudsqlsuperuser"
从测试用户
创建测试tabledocker exec -ti database psql -U testuser -d mydb -c "CREATE TABLE test (col1 text)"
尝试 select table 演示错误
docker exec -ti database psql -U postgres -d mydb -c "SELECT * FROM test"
我认为这里的问题与在该临时用户下创建的新 table 有关 - 您的其他用户将无权访问它。有几种方法可以尝试解决这个问题。
首先是在每个 table 创建后授予权限。你必须根据你想要授予访问权限的角色相应地调整它,但如果你只想 all 用户(当前和未来)拥有完全访问权限),你可以使用 GRANT ALL PRIVILEGES ON <table> TO public;
如所述 here.
更好的解决方案可能是仅使用一个用户创建所有 table。您仍然可以让 Vault 按需为其生成新密码并自动撤销对它的访问,即使用户名相同也是如此。为此,您必须 手动 创建用户并首先赋予它您想要的权限 - 假设您将其命名为 migrator
:
CREATE ROLE migrator;
GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO "migrator";
然后更改您的 Vault 配置以在请求凭据时添加登录权限,并在撤销凭据时将其删除:
vault write <role path here, e.g. database/roles/migrator> \
db_name=<db name here> \
creation_statements="ALTER ROLE migrator LOGIN PASSWORD '{{password}}' VALID UNTIL '{{expiration}}';" \
revocation_statements="ALTER ROLE migrator NOLOGIN;"
这样做的好处是您可以使用 ALTER DEFAULT PRIVILEGES
(docs here) 来避免为每个新创建的 table 授予权限。例如,如果您知道所有用户都应该有权访问所有内容:
ALTER DEFAULT PRIVILEGES FOR ROLE migrator GRANT ALL PRIVILEGES ON TABLES TO PUBLIC
我不确定如果删除一个 table 所有者的用户,postgres 会有什么反应,但如果有问题,那么这个解决方案也会解决这个问题。
简短摘要
您必须在 table/sequence/function 创建过程后 运行 REASSIGN OWNED BY "$VAULT_USERNAME" TO "cloudsqlsuperuser"
才能重新分配回普通角色。
更长的答案
Postgres 9.6 CREATE TABLE
will by default create the table under the user's role。因为保险库用户被设计为临时凭证,我们还需要确保在临时角色下创建的对象被重新分配回某种普通角色。在我在 Google Cloud 中概述的特定情况下,我们将重新分配回 cloudsqlsuperuser
。
我发现可以使用的一个命令是 REASSIGN OWNED BY current_user to "cloudsqlsuperuser"
命令,该命令然后将获取该临时用户拥有的所有对象并分配回该公共角色。
奖励:liquibase 配置
我正在使用 liquibase 进行迁移,这在我的更改日志中,用于在我们每次 运行 迁移时修复权限:
<databaseChangeLog>
<changeSet author="elindblom" id="fix-permissions" context="" logicalFilePath="fix-permissions" runAlways="true" runOrder="last">
<sqlFile path="baselines/sql/fix-permissions.sql" splitStatements="false" relativeToChangelogFile="true" stripComments="false"/>
</changeSet>
... rest of your change logs after ...
</databaseChangeLog>
SQL 看起来像这样:
REASSIGN OWNED BY current_user TO "cloudsqlsuperuser";
一切完成后 changeSet
运行 秒,每次 运行 秒。非常重要的位是 runAlways
和 runOrder
元素。