几天前,我们(Artem和Chris)正在与两个不同的客户合作,他们有同样的要求:他们需要对数据库中的某些表进行加密。
这并不罕见。加密会带来一点性能上的损失,所以许多公司宁愿不对整个数据库进行加密。相反,最好的做法是只对包含敏感信息的表进行加密,这样就可以在没有加密产生的开销的情况下访问不太敏感的数据。
但是,你如何实际设置一个按表选择性加密的数据库?在这篇文章中,我们将介绍如何建立一个CockroachDB集群,对敏感表进行加密,而对其他表进行明文存储。
为了达到这个目的,我们将在CockroachDB节点上创建多个存储,然后使用CockroachDB的定位属性标志来指定哪些数据需要使用加密的存储。这是一个非常简单的过程,我们将一步一步地走下去。
高级的步骤。
第1步:创建一个加密密钥
首先,让我们分配一些变量,以脚本的方式管理这个设置。我们将创建一个$storepath 变量,它将告诉CockroachDB在哪里保存它的数据。我们还将使用一个$keypath 变量,它将拥有我们加密密钥的位置。
export keypath="${PWD}/workdir/key"
export storepath="${PWD}/workdir/data"
mkdir -p "${keypath}"
mkdir -p "${storepath}"
接下来,我们将创建一个AES-128加密密钥。这个功能是内置在CockroachDB中的。
cockroach gen encryption-key -s 128 $keypath/aes-128.key
运行上述命令后,你会得到一个确认信息,说你的密钥已经被创建,并列出了它的目录路径。
successfully created AES-128 key: /Users/username/Documents/workdir/key/aes-128.key
第2步:启动一个带有加密和非加密存储的CockroachDB集群
接下来,让我们创建一个同时具有加密存储和明文存储的集群。这方面的语法分别是--store=path=${dir}/1e/data,attrs=encrypt 和--store=path=${dir}/1o/data,attrs=open 。我们将建立一个三个节点的集群,所以我们将为每个节点创建加密和非加密的存储。
当把表钉在加密存储中时,存储创建结束时的属性将被使用。此外,我们在上一步中创建的加密密钥也被引用到--enterprise-encryption 标志中。
cockroach start \
--insecure \
--store=path=$storepath/1e/data,attrs=encrypt \
--store=path=$storepath/1o/data,attrs=open \
--enterprise-encryption=path=$storepath/1e/data,key=$keypath/aes-128.key,old-key=plain \
--listen-addr=127.0.0.1 \
--port=26257 \
--http-port=8080 \
--locality=region=local,zone=local \
--join=127.0.0.1:26257 \
--background
cockroach start \
--insecure \
--store=path=$storepath/2e/data,attrs=encrypt \
--store=path=$storepath/2o/data,attrs=open \
--enterprise-encryption=path=$storepath/2e/data,key=$keypath/aes-128.key,old-key=plain \
--listen-addr=127.0.0.1 \
--port=26259 \
--http-port=8081 \
--locality=region=local,zone=local \
--join=127.0.0.1:26257 \
--background
cockroach start \
--insecure \
--store=path=$storepath/3e/data,attrs=encrypt \
--store=path=$storepath/3o/data,attrs=open \
--enterprise-encryption=path=$storepath/3e/data,key=$keypath/aes-128.key,old-key=plain \
--listen-addr=127.0.0.1 \
--port=26261 \
--http-port=8082 \
--locality=region=local,zone=local \
--join=127.0.0.1:26257 \
--background
现在集群已经创建,让我们来初始化它。
cockroach init --insecure
运行这个命令应该会产生一个Cluster successfully initialized 消息。现在我们已经创建并初始化了一个有加密和明文存储的三节点CockroachDB集群。
现在到了最酷的部分了!
第3步:创建PII和非PII表并将其分配给相应的存储空间
让我们创建PII和非PII表,并把它们分别放在适当的加密或非加密存储中。
首先,我们将创建pii 表,并配置一个区域约束,限制它使用带有encrypt 属性的存储。我们还将插入一些样本数据,以便我们以后可以确认我们的设置。
cockroach sql --insecure \
-e "create table pii (k int primary key, v string);" \
-e "alter table pii configure zone using constraints='[+encrypt]';" \
-e "insert into pii (k,v) values (1,'bob');"
运行上述命令应该可以确认我们已经创建了一个表,配置了一个区域,并在表中插入了一条记录。
现在,让我们为我们的non_pii 表重复这个过程,只是这一次,我们将把它设置为使用带有open 属性的存储。
cockroach sql --insecure \
-e "create table non_pii (k int primary key, v string);" \
-e "alter table non_pii configure zone using constraints='[+open]';" \
-e "insert into non_pii (k,v) values (1,'bob');"
一旦这两个表都被创建,我们就可以查看表的范围,看看它们是否已经被移到加密的存储空间。
cockroach sql --insecure -e "SHOW ALL ZONE CONFIGURATIONS;"
确认输出有正确的约束。如果一切设置正确,你会在运行上述命令的输出中看到以下内容。
`TABLE defaultdb.public.pii | ALTER TABLE defaultdb.public.pii CONFIGURE ZONE USING | constraints = '[+encrypt]'`
`TABLE defaultdb.public.non_pii | ALTER TABLE defaultdb.public.non_pii CONFIGURE ZONE USING | constraints = '[+open]'`
注意,pii 表确实有encrypt 约束,non_pii 表有open 约束。这正是我们应该看到的,但让我们采取额外的步骤来验证所有的东西,以确保!
第四步:验证我们的加密设置是否正确
验证这些表是否在正确的位置,需要我们查询CockroachDB的一些内部元数据。具体来说,我们需要做以下工作。
- 找到表的范围
- 找到加密的和未加密的商店ID
- 确认表的范围被映射到了正确的商店中
在本教程中,我们之前一直是通过bash来执行SQL命令的,但是现在,让我们在CockroachDB的内置SQL shell中启动一个会话来运行我们的命令。在终端中运行以下命令来打开CockroachDB的SQL shell。
步骤4a:找到表的范围
回顾一下,CockroachDB中的数据被分割成若干个范围,然后被复制到多个节点上。我们想弄清楚我们的pii 表的range_id是什么,我们可以用SHOW RANGES 命令来完成这个任务。
SELECT range_id, replicas FROM [SHOW RANGES FROM TABLE pii];
运行该命令将产生输出,显示pii 表的range_id。
range_id | replicas
-----------+-----------
37 | {1,3,5}
我们可以看到,我们有一个范围需要验证(range_id=37),这个范围有三个副本(我们集群中的每个节点都有一个)。
找到加密和未加密的商店ID
接下来,我们需要查询一个名为crdb_internal.kv_store_status 的内部表,以了解更多关于我们集群上正在使用的商店以及它们有哪些属性。
SELECT node_id, store_id, attrs
FROM crdb_internal.kv_store_status;
上述命令将返回以下输出。
node_id | store_id | attrs
----------+----------+--------------
1 | 1 | ["encrypt"]
1 | 2 | ["open"]
2 | 3 | ["encrypt"]
2 | 4 | ["open"]
3 | 5 | ["encrypt"]
3 | 6 | ["open"]
我们可以看到,总共有六个存储空间。其中三个有一个**["加密"]属性,三个有一个["开放"]**属性。每个节点有一个加密的存储和一个未加密的存储。
这都是意料之中的事--这就是我们在第2步中设置的内容现在我们需要确保来自我们表的数据被正确地存储,我们可以通过检查表的范围如何被映射到这些存储来做到这一点。
步骤4b:确认表的范围被映射到正确的存储区
最后,我们要生成一个表,显示我们的pii 表的range_id和它所对应的store_ids。回顾前面的步骤,该表的范围ID是37 ,加密的存储空间ID是1 、3 、5 ,所以如果一切按预期工作,我们将看到一个表格,显示范围37 ,被存储在这三个存储空间。
我们可以使用下面的查询生成该输出,并确认来自表的范围被映射到正确的存储空间。
这个查询比我们在这篇文章中使用的其他查询要复杂一些,但它将使我们得到我们需要的答案。基本上,我们正在查询CockroachDB的一些元数据,为range_id 、node_id 、store_id ,为pii 表。
SELECT range_id, node_id, repls.store_id
FROM
(
SELECT range_id, unnest(replicas) AS store_id
FROM crdb_internal.ranges_no_leases
WHERE table_name = 'pii'
) AS repls
JOIN crdb_internal.kv_store_status AS ss ON (ss.store_id = repls.store_id)
ORDER BY range_id;
正如预期的那样,当我们运行这个查询时,我们可以看到范围37,也就是我们带有个人身份信息(PII)的表,被分配到了商店1, 3 ,和5 ,这就是我们创建的三个加密的商店。
range_id | node_id | store_id
-----------+---------+-----------
37 | 1 | 1
37 | 2 | 3
37 | 3 | 5
(3 rows)
我们可以重复同样的过程来确认未加密的表non_pii 被分配给未加密的商店。
找到non_pii 表的范围ID。
SELECT range_id, replicas FROM [SHOW RANGES FROM TABLE non_pii];
range_id | replicas
-----------+-----------
38 | {2,4,6}
检查non_pii 表被分配到哪些商店。
SELECT range_id, node_id, repls.store_id
FROM
(
SELECT range_id, unnest(replicas) AS store_id
FROM crdb_internal.ranges_no_leases
WHERE table_name = 'non_pii'
) AS repls
JOIN crdb_internal.kv_store_status AS ss ON (ss.store_id = repls.store_id)
ORDER BY range_id;
range_id | node_id | store_id
-----------+---------+-----------
38 | 1 | 2
38 | 2 | 4
38 | 3 | 6
(3 rows)
同样,我们可以看到,range_id38 的范围被存储在商店2,4 和6!换句话说,我们做到了--我们已经为我们的pii 表设置了静态加密,但没有为我们的non_pii 表设置加密。
这给我们提供了两全其美的方法--对需要加密的表进行安全加密,而对不需要加密的表则提供明文性能!
第5步(可选):清理
如果你一直在关注这个教程,你可以运行下面的命令来杀死CockroachDB进程,并删除我们上面创建的目录。
pkill -9 cockroach
rm -Rf "${keypath}"
rm -Rf "${storepath}"
关于CockroachDB加密的更多信息,请参考我们的文档,并查看我们的免费课程,以帮助你的CockroachDB或CockroachCloud启动和运行!
免责声明:本教程特意使用了一个不安全的CockroachDB测试实例,无论表是否加密,都不会阻止对数据的未经授权的访问。对于真实的生产场景,你会希望建立一个安全的集群。此外,请注意,这种静态加密不会阻止管理用户和默认根用户的数据访问。