数据库是黑客眼中的“圣杯”,须要咱们像看待“花朵”一样精心呵护它。本文次要介绍数据库爱护的最佳实际。首先,从最罕用的开源数据库 PostgreSQL 开始,咱们将一一介绍诸位须要思考的几个平安层级:
PostgreSQL 中网络层的平安
防火墙
现实状况下,PostgreSQL 服务器该当是齐全隔离,不容许任何入站申请、SSH 或 psql 的。然而,PostgreSQL 没有对这类网闸设置提供开箱即用的反对。
咱们最多也只能通过设置防火墙,锁定数据库所在节点的端口级拜访来晋升数据库服务器的安全性。默认状况下,PostgreSQL 监听 TCP 端口 5432。而依据操作系统的不同,锁定其余端口的形式也会有所不同。以 Linux 最罕用的防火墙为例,上面这几行代码就可轻松实现工作:
确保已有连贯不被 drop
iptables -A INPUT -m state –state ESTABLISHED,RELATED -j ACCEPT
容许 SSH.
iptables -A INPUT -p tcp -m state –state NEW –dport 22 -j ACCEPT
容许 PostgreSQL.
iptables -A INPUT -p tcp -m state –state NEW –dport 5432 -j ACCEPT
容许所有出站,drop 所有入站
iptables -A OUTPUT -j ACCEPT
iptables -A INPUT -j DROP
iptables -A FORWARD -j DROP
在更新 iptables 的规定时,倡议应用工具。这样,哪怕你不小心把本人也锁在里面了,它也能主动回滚更改。
这条 PostgreSQL 规定会容许所有人连贯到端口 5432 上。当然,你也能够将其批改为只承受特定 IP 地址或子网让限度更加严格:
仅容许本地子网拜访 PostgreSQL 端口
iptables -A INPUT -p tcp -m state –state NEW –dport 5432 -s 192.168.1.0/24 -j ACCEPT
持续探讨咱们的现实状况。想要齐全限度到 5432 端口的所有入站连贯须要一个某种类型的本地代理,由它维持一个到客户端节点的长久出站连贯,并且能将流量代理到本地 PostgreSQL 实例。
这种代理被称作是“反向通道”。具体应用办法能够通过 SSH 近程端口转发的性能进行演示。运行下列指令能够在 PostgreSQL 数据库运行的节点上关上一个反向通道:
ssh -f -N -T -R 5432:localhost:5432 user@<client-host>
PostgreSQL 的节点须要能拜访 <client-host>,其上的 SSH 守护过程(daemon)也要处于运行状态。下列指令会将数据库服务器上端口 5432 转发到客户端机器的端口 5432 上,这样你就能够通过这个通道连贯数据库了:
psql “host=localhost port=5432 user=postgres dbname=postgres”
PostgreSQL 监听地址
通过配置文件指令来限度服务器监听客户端连贯的地址是个好习惯。如果运行 PostgreSQL 的节点上有多个网络接口,能够确保服务器只会监听客户端所连贯的一个或多个接口:
listen_addresses = ‘localhost, 192.168.0.1’
如果连贯到数据库的客户端总是驻留在同一节点上,或是与数据库独特驻留在同一个 Kubernetes pod 上,PostgreSQL 作为 sidecar 容器运行,禁用套接字监听(socket)能够齐全打消网络的影响。将监听地址设置为空字符串能够使服务器只承受 Unix 域的套接字连贯:
listen_addresses = ”
PostgreSQL 中传输层级的平安
当世界上大部分的网络都转投向 HTTP 的怀抱时,为数据库连贯抉择强传输加密也变成了一个必备我的项目。PostgreSQL 自身即反对 TLS(因为历史遗留问题,在文档、配置文件,以及 CLI 中仍被称作是 SSL),咱们也能够应用它进行服务器端和客户端认证。
服务器端 TLS
对于服务器的认证,咱们首先须要为服务器筹备一份用于和相连接的客户端认证的证书。在 Let’s Encrypt 上,咱们能够找到收费提供的 X.509 证书,具体应用办法以的命令行工具为例:
certbot certonly –standalone -d postgres.example.com
须要留神的是,certbot 默认应用 ACME 标准的 HTTP-01 挑战来验证证书申请,这里咱们就须要确保申请域指向节点的 DNS 无效,并且端口 80 处于凋谢状态。
除了 Let’s Encrypt 之外,如果想在本地生成所有的信息,咱们还能够抉择 openssl 命令行工具:
生成一个自签名的服务器 CA
openssl req -sha256 -new -x509 -days 365 -nodes \
-out server-ca.crt \
-keyout server-ca.key
生成服务器 CSR,CN 里填须要连贯到数据库的主机名
openssl req -sha256 -new -nodes \
-subj "/CN=postgres.example.com" \
-out server.csr \
-keyout server.key
签证书
openssl x509 -req -sha256 -days 365 \
-in server.csr \
-CA server-ca.crt \
-CAkey server-ca.key \
-CAcreateserial \
-out server.crt
在生产环境中记得在证书过期前更新。
客户端 TLS
通过验证客户端提供的 X.509 证书是由可信的证书颁发机构(CA)签名,服务器便能够验证连贯客户端的身份。
倡议应用不同的 CA 来别离给客户端和服务器端发证书。下列代码创立了一个客户端 CA,并用它来给客户签证书:
生成一个自签名的客户端 CA
openssl req -sha256 -new -x509 -days 365 -nodes \
-out client-ca.crt \
-keyout client-ca.key
生成客户端 CSR。CN 必须填用于连贯的数据库角色名
openssl req -sha256 -new -nodes \
-subj "/CN=alice" \
-out client.csr \
-keyout server.key
签证书
openssl x509 -req -sha256 -days 365 \
-in client.csr \
-CA client-ca.crt \
-CAkey client-ca.key \
-CAcreateserial \
-out client.crt
这里须要留神,客户端证书里的 CommonName(CN)字段必须要蕴含用于连贯的数据库账号。PostgreSQL 服务器将用 CN 来创立客户端的身份。
TLS 配置
小结一下,咱们当初能够配置 PostgreSQL 服务器来接管 TLS 连贯了:
ssl = on
ssl_cert_file = ‘/path/to/server.crt’
ssl_key_file = ‘/path/to/server.key’
ssl_ca_file = ‘/path/to/client-ca.crt’
这里默认是 on,但出于平安思考,特意写进去总是没错的
ssl_prefer_server_ciphers = on
TLS 1.3 能提供最强的平安爱护。在管制服务器和客户端时倡议应用 ssl_min_protocol_version = ‘TLSv1.3’
咱们还须要再配置的就只剩下用于更新 PostgreSQL 服务器的基于主机的认证文件()了。它能够要求所有的连贯应用 TLS,并通过 X.509 证书对客户端进行认证。
TYPE DATABASE USER ADDRESS METHOD
hostssl all all ::/0 cert
hostssl all all 0.0.0.0/0 cert
当初,连贯到数据库服务器的客户端就须要提供由客户端 CA 签名的无效证书了:
psql “host=postgres.example.com \
user=alice \
dbname=postgres \
sslmode=verify-full \
sslrootcert=/path/to/server-ca.crt \
sslcert=/path/to/client.crt \
sslkey=/path/to/client.key"
须要留神的是,默认状况下,psql 不会进行服务器证书认证,所以咱们须要将“sslmode”设置为“”或者“”。具体设置要看你是否应用了编码后的 X.509 中 CN 字段的主机名对服务器进行连贯。
为了简化指令,并且防止在每次连贯到数据库时都要从新输出一遍到 TLS 的门路,能够通过应用 PostgreSQL 的连贯服务文件来解决。它能够将连贯参数分组到“services”中,通过连贯字符串中的“service”参数进行援用。
用下列代码创立“”文件:
[example]
host=postgres.example.com
user=alice
sslmode=verify-full
sslrootcert=/path/to/server-ca.crt
sslcert=/path/to/client.crt
sslkey=/path/to/client.key
当初,当连贯到数据库时,咱们只须要指定服务名和想要连贯的数据库名即可:
psql “service=example dbname=postgres”
PostgreSQL 中数据库层级平安
角色
目前为止,咱们曾经探讨的内容有以下几点:如何从未认证的网络连接中爱护 PostgreSQL 数据库,如何应用强加密进行数据传输,以及如何通过独特的 TLS 认证让服务器与客户相互信赖对方身份。接下来,咱们将持续剖析用户在这里能做什么,连贯到数据库后他们能够拜访什么,以及如何验证他们的身份。这一步通常被称作是“受权”。
PostgreSQL 有一套残缺的、根据角色(role)建设的用户权限零碎。在古代的 PostgreSQL(8.1 及以上版本)中,“角色”是“用户”的同义词。无论你用什么样的数据库账户名,比方 psql 中的 ”user=alice”,它其实都是一个能够连贯数据库的、领有 LOGIN 属性的角色。也就是说,上面两条指令其实成果一样:
CREATE USER alice;
CREATE ROLE alice LOGIN;
除了登入的权限外,角色还能够领有其余属性:能够通过所有的权限查看的,能够创立数据库的,能够创立其余角色的,等等。
除了属性外,角色被授予的权限能够分为两大类:一是其余角色的成员身份(membership),二是数据库对象的权限。上面咱们将介绍这两类都是如何工作的。
授予角色权限
假如,咱们须要跟踪服务器清单:
CREATE TABLE server_inventory (
id int PRIMARY KEY,
description text,
ip_address text,
environment text,
owner text,
);
默认状况下,PostgreSQL 装置会蕴含一个用于疏导数据库的超级用户角色,通常被称作是“postgres”。应用这一角色进行所有的数据库操作相当于在 Linux 零碎中常常应用“root”登入,永远不是个好主见。所以,咱们要创立一个无权限角色,依据须要为其授予最小权限。
通过创立一个“组角色”并受权其余角色(角色与用户一一对应)为组内成员,能够防止为每一个用户或角色独自调配权限的麻烦。如果说,咱们想要授予开发者 Alice 和 Bob 查看服务器清单,但不容许其批改的权限:
— 创立一个本身没有登入能力的组角色,授予其在服务器清单表中进行 SELECT 的权限
GRANT SELECT ON server_inventory TO developer;
— 创立两个用户账号,在登入权限的根底上继承“developer”权限
CREATE ROLE alice LOGIN INHERIT;
CREATE ROLE bob LOGIN INHERIT;
— 调配给两个用户账号“developer”组角色
GRANT developer TO alice, bob;
当初,当连贯到数据库时,Alice 和 Bob 都会继承“developer”组角色中的权限,并且能够查询数据库清单表。
权限默认对表的所有列无效,但这一点也是能够更改的。假如咱们只想让实习生查看服务器的大抵信息但又不想让他们连贯到服务器,那么就能够抉择暗藏 IP 地址:
CREATE ROLE intern;
GRANT SELECT(id, description) ON server_inventory TO intern;
CREATE ROLE charlie LOGIN INHERIT;
GRANT intern TO charlie;
其余罕用数据库对象权限还有“”、“”、“”,以及“”,别离对应它们各自的 SQL 语句。咱们还能够为连贯到特定数据库、新建模式或模式中新建对象、执行函数等等调配权限。PostgreSQL 文档中的章节提供了残缺列表。
行级安全策略
PostgreSQL 权限零碎的更高级的玩法是行级安全策略(RLS),它容许你为表中的局部行调配权限。这既包含能够用查问的行,也包含能够、以及的行。
想要应用行级平安,咱们须要筹备两件事件:在表中启用 RLS,为其定义一个用于管制行级拜访的策略。
持续之前的例子,假如咱们只想容许用户更新他们本人的服务器。那么,第一步,启用表中的 RLS:
ALTER TABLE server_inventory ENABLE ROW LEVEL SECURITY;
如果没有定义任何策略的话,PostgreSQL 会默认到“回绝”策略上,这就意味着除了表的创建者 / 所有者之外,没有任何角色可能拜访。
行安全策略是一个布尔表达式,是 PostgreSQL 用于断定所有须要返回或更新的行的条件。SELECT 语句返回的即将对照 USING 子语句所指定的表达式进行查看,而通过 INSERT,UPDATE 或 DELETE 语句更新的即将对照 WITH CHECK 表达式进行查看。
首先,让咱们定义几个策略,容许用户查看所有服务器,但只能更新他们本人服务器,这个“本人”是由表中的“owner”字段决定的。
CREATE POLICY select_all_servers
ON server_inventory FOR SELECT
USING (true);
CREATE POLICY update_own_servers
ON server_inventory FOR UPDATE
USING (current_user = owner)
WITH CHECK (current_user = owner);
注,只有表的所有者能够创立或更新 RLS。
审计
到目前为止,咱们次要都在探讨先手进攻的安全措施。依据其中一项根本的平安准则——深度进攻,咱们剖析了这些措施是如何通过相互叠加,帮忙减缓(假想中的)攻击者防御零碎的过程。
留存精确且具体的审计追踪记录是系统安全属性中经常被忽视的一点。监控数据库服务器端的网络层或节点层拜访并不在本文的探讨范畴内,但当波及到 PostgreSQL 服务器自身时,无妨先来看看咱们有什么可选项。
想要更清晰地观测数据库内状况时,最罕用的办法是启用具体日志记录。在服务器配置文件中增加以下指令,开启对所有连贯尝试以及已执行 SQL 的日志记录。
; 记录胜利与非胜利连贯尝试
log_connections = on
; 记录终止对话
log_disconnections = on
; 记录所有执行过的 SQL 语句
log_statement = all
可怜的是,对于规范的自托管 PostgreSQL,这简直是在不装置其余插件的状况下你能做到的全副了。尽管总比没有强,但在多数数据库服务器和简略的“grep”之外,它的延展性并不好。
如果想要更高级的 PostgreSQL 审计解决方案,诸如这样的第三方插件是个不错的抉择。自托管的 PostgreSQL 须要手动装置,但对于一些托管版本的 PostgreSQL,比方 AWS RDS 这些,自身就有,咱们只需启用即可。
对于记录的语句,pgAudit 提供了更多的构造和粒度。但要记住,它并没有脱离日志的范畴,也就是说,如果须要将审计日志以结构化的格局传输到内部 SIEM 零碎以作更具体的剖析时,恐怕会很难。
PostgreSQL 中基于证书的拜访
(Teleport for Database Access)是一款开源我的项目,在它的帮忙下,咱们能够实现本文中介绍的所有用于爱护 PostgreSQL 和其余数据库的最佳实际。
结语
与任何以平安为前提设计的零碎一样,正确地爱护对数据库实例的拜访须要在网络协议栈的多个档次上采取保护措施。在这篇文章中,咱们从网络和传输平安开始,探讨了如何应用 PostgreSQL 灵便的用户权限零碎。