MongoDB 4.2——安全介绍

安全介绍

1、MongoDB的身份验证和授权

虽然身份验证和授权紧密相连,但明白二者的不同十分重要。身份验证的目的是验证用户的身份,而授权的目的是确定被验证用户对资源和操作的访问权限。

1.1、身份验证机制

在 MongoDB 集群上启用授权会强制进行验证,并确保用户只能执行授权的操作,这是由用户的角色决定的。MongoDB 的社区版本提供了对 SCRAM(SaltedChallenge Response Authentication Mechanism)和x.509 证书验证的支持。除了 SCRAM 和 x.509,MongoDB 企业版还支持 Kerberos 身份验证和 LDAP 代理身份验证。有关 MongoDB 所支持的各种身份验证机制的详细信息,请参阅相关文档。

身份验证。x.509 数字证书使用了被广泛接受的 x.509公钥基础设施(PKI)标准来验证公钥的所有人。

1.2、授权

在 MongoDB 中添加用户时,必须在指定的数据库中创建此用户。该数据库是针对此用户的身份验证数据库。对于身份验证,可以使用任何数据库。用户名和身份验证数据库一起作为用户的唯一标识符。然而,用户的权限并不局限在身份验证数据库中。在创建用户时,可以为其指定任何资源上的操作权限。这里的资源包括集群、数据库以及集合。

MongoDB 提供了许多内置的角色,可以为数据库用户授予通常所需要的权限,具体如下。

  • read:读取所有非系统集合及系统集合 system.indexes、system.js 和 system.namespaces 中的数据。
  • readWrite:提供与 read 相同的特权,以及修改所有非系统集合和 system.js 集合中数据的能力。
  • dbAdmin:执行管理任务,比如与模式相关的任务、索引和收集统计信息(不授予用户和角色管理权限)。
  • userAdmin:在当前数据库中创建和修改角色及用户。
  • dbOwner:结合了 readWrite、dbAdmin 和 userAdmin 这 3个角色的权限。
  • clusterManager:对集群进行管理和监控。
  • clusterMonitor:为监控工具,比如 MongoDB Cloud Manager 和Ops Manager 中的监控代理,提供只读访问权限。
  • hostManager:监控和管理服务器。
  • clusterAdmin:结合了 clusterManager、clusterMonitor 和hostManager 这 3 个角色的权限,以及 dropDatabase操作。
  • backup:提供足够的权限来使用 MongoDB Cloud Manager或 Ops Manager 备份代理,或者使用 MongoDB 备份整个 mongod 实例。
  • restore:提供从除去 system.profile 集合数据的备份中恢复数据所需的权限。
  • readAnyDatabase:除去 local 和 config,提供在所有数据库上与 read相同的权限,以及在整个集群上执行 listDatabases 操作的权限。
  • readWriteAnyDatabase:除去 local 和 config,提供在所有数据库上与readWrite 相同的权限,以及在整个集群上执行listDatabases 操作的权限。
  • userAdminAnyDatabase:除去 local 和 config,提供在所有数据库上与userAdmin 相同的权限(实际上就是超级用户的角色)。
  • dbAdminAnyDatabase:除去 local 和 config,提供在所有数据库上与dbAdmin 相同的权限,以及在整个集群上执行listDatabases 操作的权限。
  • root:提供结合了 readWriteAnyDatabase、dbAdminAnyDatabase、userAdminAnyDatabase、clusterAdmin、restore 和 backup 几个角色的操作和对所有资源的访问权限。

还可以创建所谓的"用户自定义角色"​,这实际上是将执行特定操作的权限组合在一起,并定义一个名称对其进行标记,这样可以方便地将这组权限授予多个用户。

对内置角色或用户自定义角色的深入研究超出了本章的范围。不过,本章的介绍应该可以让你很好地了解MongoDB 授权所能实现的功能。更详细的信息请参阅MongoDB 文档中对授权的介绍。

要确保可以自由添加新用户,必须首先创建一个 admin用户。无论使用的认证模式是什么(x.509 也不例外)​,MongoDB 在启用身份验证和授权时都不会创建默认的root 或 admin 用户。

MongoDB 默认不启用身份验证和授权。必须使用mongod 命令的 --auth 选项或在配置文件中将security.authorization 指定为 "enabled" 来显式启用它们。

要配置副本集,首先要在不启用身份验证和授权的情况下启动它,然后创建 admin 用户以及每个客户端所需的用户。

1.3、使用x.509证书对成员和客户端进行身份验证

由于所有生产环境中的 MongoDB 集群是由多个成员组成的,因此为了确保集群的安全,集群内所有服务的通信必须进行身份验证。为了交换数据,副本集中的每个成员必须与其他成员进行身份验证。同样,客户端必须与主节点以及任何与它们通信的从节点进行身份验证。

对于 x.509,由受信任的证书颁发机构(CA)对所有证书进行签名是很有必要的。签名可以证明证书的命名主题拥有与该证书相关联的公钥。CA 会充当受信的第三方,以防止中间人攻击。

下图描述了用于保护一个 MongoDB 三成员副本集的x.509 身份验证信任架构。注意客户端和副本集成员之间的身份验证,以及与 CA 之间的信任关系。

成员和客户端都有自己的证书,由 CA 进行签发。对于生产环境,MongoDB 部署应该使用由单个证书颁发机构生成和签发的有效证书。你或你的组织可以生成并维护一个独立的证书颁发机构,也可以使用第三方 TLS/SSL 供应商生成的证书。

用于内部身份验证以验证集群成员身份的证书被称为成员证书。成员证书和客户端证书(用于对客户端进行身份验证)都具有下面这样的结构。

js 复制代码
Certificate:
    Data:
        Version: 1 (0x0)
        Serial Number: 1 (0x1)
    Signature Algorithm: sha256WithRSAEncryption
        Issuer: C=US, ST=NY, L=New York, O=MongoDB, CN=CA-SIGNER
        Validity
            Not Before: Nov 11 22:00:03 2018 GMT
            Not After : Nov 11 22:00:03 2019 GMT
        Subject: C=US, ST=NY, L=New York, O=MongoDB, OU=MyServers, CN=server1
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                Public-Key: (2048 bit)
                Modulus:
                    00:d3:1c:29:ba:3d:29:44:3b:2b:75:60:95:c8:83:
                    fc:32:1a:fa:29:5c:56:f3:b3:66:88:7f:f9:f9:89:
                    ff:c2:51:b9:ca:1d:4c:d8:b8:5a:fd:76:f5:d3:c9:
                    95:9c:74:52:e9:8d:5f:2e:6b:ca:f8:6a:16:17:98:
                    dc:aa:bf:34:d0:44:33:33:f3:9d:4b:7e:dd:7a:19:
                    1b:eb:3b:9e:21:d9:d9:ba:01:9c:8b:16:86:a3:52:
                    a3:e6:e4:5c:f7:0c:ab:7a:1a:be:c6:42:d3:a6:01:
                    8e:0a:57:b2:cd:5b:28:ee:9d:f5:76:ca:75:7a:c1:
                    7c:42:d1:2a:7f:17:fe:69:17:49:91:4b:ca:2e:39:
                    b4:a5:e0:03:bf:64:86:ca:15:c7:b2:f7:54:00:f7:
                    02:fe:cf:3e:12:6b:28:58:1c:35:68:86:3f:63:46:
                    75:f1:fe:ac:1b:41:91:4f:f2:24:99:54:f2:ed:5b:
                    fd:01:98:65:ac:7a:7a:57:2f:a8:a5:5a:85:72:a6:
                    9e:fb:44:fb:3b:1c:79:88:3f:60:85:dd:d1:5c:1c:
                    db:62:8c:6a:f7:da:ab:2e:76:ac:af:6d:7d:b1:46:
                    69:c1:59:db:c6:fb:6f:e1:a3:21:0c:5f:2e:8e:a7:
                    d5:73:87:3e:60:26:75:eb:6f:10:c2:64:1d:a6:19:
                    f3:0b
                Exponent: 65537 (0x10001)
    Signature Algorithm: sha256WithRSAEncryption
        5d:dd:b2:35:be:27:c2:41:4a:0d:c7:8c:c9:22:05:cd:eb:88:
        9d:71:4f:28:c1:79:71:3c:6d:30:19:f4:9c:3d:48:3a:84:d0:
        19:00:b1:ec:a9:11:02:c9:a6:9c:74:e7:4e:3c:3a:9f:23:30:
        50:5a:d2:47:53:65:06:a7:22:0b:59:71:b0:47:61:62:89:3d:
        cf:c6:d8:b3:d9:cc:70:20:35:bf:5a:2d:14:51:79:4b:7c:00:
        30:39:2d:1d:af:2c:f3:32:fe:c2:c6:a5:b8:93:44:fa:7f:08:
        85:f0:01:31:29:00:d4:be:75:7e:0d:f9:1a:f5:e9:75:00:9a:
        7b:d0:eb:80:b1:01:00:c0:66:f8:c9:f0:35:6e:13:80:70:08:
        5b:95:53:4b:34:ec:48:e3:02:88:5c:cd:a0:6c:b4:bc:65:15:
        4d:c8:41:9d:00:f5:e7:f2:d7:f5:67:4a:32:82:2a:04:ae:d7:
        25:31:0f:34:e8:63:a5:93:f2:b5:5a:90:71:ed:77:2a:a6:15:
        eb:fc:c3:ac:ef:55:25:d1:a1:31:7a:2c:80:e3:42:c2:b3:7d:
        5e:9a:fc:e4:73:a8:39:50:62:db:b1:85:aa:06:1f:42:27:25:
        4b:24:cf:d0:40:ca:51:13:94:97:7f:65:3e:ed:d9:3a:67:08:
        79:64:a1:ba
-----BEGIN CERTIFICATE----
MIIDODCCAiACAQEwDQYJKoZIhvcNAQELBQAwWTELMAkGA1UEBhMCQ04xCzAJBgNV
BAgMAkdEMREwDwYDVQQHDAhTaGVuemhlbjEWMBQGA1UECgwNTW9uZ29EQiBDaGlu
YTESMBAGA1UEAwwJQ0EtU0lHTkVSMB4XDTE4MTExMTIyMDAwM1oXDTE5MTExMTIy
MDAwM1owazELMAkGA1UEBhMCQ04xCzAJBgNVBAgMAkdEMREwDwYDVQQHDAhTaGVu
emhlbjEWMBQGA1UECgwNTW9uZ29EQiBDaGluYTESMBAGA1UECwwJTXlTZXJ2ZXJz
MRAwDgYDVQQDDAdzZXJ2ZXIxMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
AQEA0xwpuj0pRDsrdWCVyIP8Mhr6KVxW87NmiH/5+Yn/wlG5yh1M2Lha/Xb108mV
nHRS6Y1fLmvK+GoWF5jcqr800EQzM/OdS37dehkb6zueIdnZugGcixaGo1Kj5uRc
9wyrehq+xkLTpgGOCleyzVso7p31dsp1esF8QtEqfxf+aRdJkUvKLjm0peADv2SG
yhXHsvdUAPcC/s8+EmsoWBw1aIY/Y0Z18f6sG0GRT/IkmVTy7Vv9AZhlrHp6Vy+o
pVqFcqae+0T7Oxx5iD9ghd3RXBzbYoxq99qrLnasr219sUZpwVnbxvtv4aMhDF8u
jqfVc4c+YCZ1628QwmQdphnzCwIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQBd3bI1
vifCQUoNx4zJIgXN64idcU8owXlxPG0wGfScPUg6hNAZALHsqRECyaacdOdOPDqf
IzBQWtJHU2UGpyILWXGwR2FiiT3Pxtiz2cxwIDW/Wi0UUXlLfAAwOS0dryzzMv7C
xqW4k0T6fwiF8AExKQDUvnV+Dfka9el1AJp70OuAsQEAwGb4yfA1bhOAcAhblVNL
NOxI4wKIXM2gbLS8ZRVNyEGdAPXn8tf1Z0oygioErtclMQ806GOlk/K1WpBx7Xcq
phXr/MOs71Ul0aExeiyA40LCs31emvzkc6g5UGLbsYWqBh9CJyVLJM/QQMpRE5SX
f2U+7dk6Zwh5ZKG6
-----END CERTIFICATE-----

在 MongoDB 中使用 x.509 进行身份验证时,成员证书必须具有以下属性。

  • 必须由单个 CA 来签发集群中成员的所有 x.509 证书。
  • 成员证书主题中的 Distinguished Name(DN)必须为以下属性中的至少一个指定非空值:Organization(O)、Organizational Unit(OU)或 Domain Component(DC)。
  • O、OU 和 DC 属性必须与其他集群成员证书中的属性匹配。
  • Common Name(CN)或 Subject Alternative Name(SAN)必须与集群其他成员所使用的服务器主机名匹配。

2、MongoDB的认证和传输层加密教程

本教程将设置根 CA 和中间 CA。在最佳实践中,建议使用中间 CA 对服务器和客户端证书进行签名。

2.1、建立CA

在为副本集的成员生成签名证书之前,必须首先解决证书颁发机构的问题。如前所述,我们可以生成并维护一个独立的证书颁发机构,也可以使用第三方 TLS/SSL 供应商生成的证书。在本章的示例中,我们会生成自己的 CA。

2.1.1、生成根CA

我们会使用 OpenSSL 生成 CA。为了继续后面的内容,请确保你能够访问本地机器上的 OpenSSL。

根 CA 位于证书链的顶端。这是信任的最终来源。理想情况下,应该使用第三方 CA。然而,在网络隔离的情况下(通常是在大型企业环境中)或出于测试目的,就需要使用本地 CA。

首先,需要初始化一些变量:

js 复制代码
dn_prefix="/C=US/ST=NY/L=New York/O=MongoDB"
ou_member="MyServers"
ou_client="MyClients"
mongodb_server_hosts=( "server1" "server2" "server3" )
mongodb_client_hosts=( "client1" "client2" )
mongodb_port=27017

然后,创建一个密钥对,并将其存储在 root-ca.key 文件中:

js 复制代码
# !!! 在生产环境中,需要对密钥进行加密保护
# openssl genrsa -aes256 -out root-ca.key 4096
openssl genrsa -out root-ca.key 4096

接下来,创建一个配置文件来保存 OpenSSL 设置,我们使用它来生成证书:

js 复制代码
# 对于CA策略
[ policy_match ]
countryName = match
stateOrProvinceName = match
organizationName = match
organizationalUnitName = optional
commonName = supplied
emailAddress = optional

[ req ]
default_bits        = 4096
default_keyfile     = server-key.pem
default_md      = sha256
distinguished_name  = req_dn
req_extensions = v3_req
x509_extensions = v3_ca # 要添加到自签名证书的扩展名

[ v3_req ]
subjectKeyIdentifier  = hash
basicConstraints = CA:FALSE
keyUsage = critical, digitalSignature, keyEncipherment
nsComment = "OpenSSL Generated Certificate"
extendedKeyUsage = serverAuth, clientAuth

[ req_dn ]
countryName = Country Name (2-letter code)
countryName_default = US
countryName_min = 2
countryName_max = 2

stateOrProvinceName = State or Province Name (full name)
stateOrProvinceName_default = NY
stateOrProvinceName_max = 64

localityName = Locality Name (eg, city)
localityName_default = New York
localityName_max = 64

organizationName = Organization Name (eg, company)
organizationName_default = MongoDB
organizationName_max = 64

organizationalUnitName = Organizational Unit Name (eg, section)
organizationalUnitName_default = Education
organizationalUnitName_max = 64

commonName = Common Name (eg, YOUR name)
commonName_max = 64

[ v3_ca ]
# 典型CA的扩展

subjectKeyIdentifier = hash
basicConstraints = critical,CA:true
authorityKeyIdentifier = keyid:always,issuer:always

# 密钥使用:这是CA证书的典型用法
# 不过,由于它会防止被用作测试自签名证书,因此最好在默认情况下忽略它
keyUsage = critical,keyCertSign,cRLSign

接下来,创建一个配置文件来保存 OpenSSL 设置,我们使用它来生成证书:

js 复制代码
# 对于CA策略
[ policy_match ]
countryName = match
stateOrProvinceName = match
organizationName = match
organizationalUnitName = optional
commonName = supplied
emailAddress = optional

[ req ]
default_bits        = 4096
default_keyfile     = server-key.pem
default_md      = sha256
distinguished_name  = req_dn
req_extensions = v3_req
x509_extensions = v3_ca # 要添加到自签名证书的扩展名

[ v3_req ]
subjectKeyIdentifier  = hash
basicConstraints = CA:FALSE
keyUsage = critical, digitalSignature, keyEncipherment
nsComment = "OpenSSL Generated Certificate"
extendedKeyUsage = serverAuth, clientAuth

[ req_dn ]
countryName = Country Name (2-letter code)
countryName_default = US
countryName_min = 2
countryName_max = 2

stateOrProvinceName = State or Province Name (full name)
stateOrProvinceName_default = NY
stateOrProvinceName_max = 64

localityName = Locality Name (eg, city)
localityName_default = New York
localityName_max = 64

organizationName = Organization Name (eg, company)
organizationName_default = MongoDB
organizationName_max = 64

organizationalUnitName = Organizational Unit Name (eg, section)
organizationalUnitName_default = Education
organizationalUnitName_max = 64

commonName = Common Name (eg, YOUR name)
commonName_max = 64

[ v3_ca ]
# 典型CA的扩展

subjectKeyIdentifier = hash
basicConstraints = critical,CA:true
authorityKeyIdentifier = keyid:always,issuer:always

# 密钥使用:这是CA证书的典型用法
# 不过,由于它会防止被用作测试自签名证书,因此最好在默认情况下忽略它
keyUsage = critical,keyCertSign,cRLSign

最后,使用 openssl req 命令来创建根证书。由于根是权威链的最顶端,因此使用上一步创建的私钥(存储在 root-ca.key 中)对此证书进行自签名。-x509选项会通知 openssl req 命令我们希望使用提供给 -key 选项的私钥对证书进行自签名。输出是一个名为root-ca.crt 的文件:

bash 复制代码
openssl req -new -x509 -days 1826 -key root-ca.key -out root-ca.crt \
  -config openssl.cnf -subj "$dn_prefix/CN=ROOTCA"

如果看一下 root-ca.crt 文件,你会发现它包含了根CA 的公共证书。可以查看由以下命令生成的一个可读版本来验证其内容:

js 复制代码
openssl x509 -noout -text -in root-ca.crt

该命令的输出如下所示。

js 复制代码
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            1e:83:0d:9d:43:75:7c:2b:d6:2a:dc:7e:a2:a2:25:af:5d:3b:89:43
        Signature Algorithm: sha256WithRSAEncryption
        Issuer: C = US, ST = NY, L = New York, O = MongoDB, CN = ROOTCA
        Validity
            Not Before: Sep 11 21:17:24 2019 GMT
            Not After : Sep 10 21:17:24 2024 GMT
        Subject: C = US, ST = NY, L = New York, O = MongoDB, CN = ROOTCA
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                RSA Public-Key: (4096 bit)
                Modulus:
                    00:e3:de:05:ae:ba:c9:e0:3f:98:37:18:77:02:35:
                    e7:f6:62:bc:c3:ae:38:81:8d:04:88:da:6c:e0:57:
                    c2:90:86:05:56:7b:d2:74:23:54:f8:ca:02:45:0f:
                    38:e7:e2:0b:69:ea:f6:c8:13:8f:6c:2d:d6:c1:72:
                    64:17:83:4e:68:47:cf:de:37:ed:6e:38:b2:ab:3a:
                    e4:45:a8:fa:08:90:a0:f3:0d:3a:14:d8:9a:8d:69:
                    e7:cf:93:1a:71:53:4f:13:29:50:b0:2f:b6:b8:19:
                    2a:40:21:15:90:43:e7:d8:d8:f3:51:e5:95:58:87:
                    6c:45:9f:61:fc:b5:97:cf:5b:4e:4a:1f:72:c9:0c:
                    e9:8c:4c:d1:ca:df:b3:a4:da:b4:10:83:81:01:b1:
                    c8:09:22:76:c7:1e:96:c7:e6:56:27:8d:bc:fb:17:
                    ed:d9:23:3f:df:9c:ef:03:20:cc:c3:c4:55:cc:9f:
                    ad:d4:8d:81:95:c3:f1:87:f8:d4:5a:5e:e0:a8:41:
                    27:c8:0d:52:91:e4:2b:db:25:d6:b7:93:8d:82:33:
                    7a:a7:b8:e8:cd:a8:e2:94:3d:d6:16:e1:4e:13:63:
                    3f:77:08:10:cf:23:f6:15:7c:71:24:97:ef:1c:a2:
                    68:0f:82:e2:f7:24:b3:aa:70:1a:4a:b4:ca:4d:05:
                    92:5e:47:a2:3d:97:82:f6:d8:c8:04:a7:91:6c:a4:
                    7d:15:8e:a8:57:70:5d:50:1c:0b:36:ba:78:28:f2:
                    da:5c:ed:4b:ea:60:8c:39:e6:a1:04:26:60:b3:e2:
                    ee:4f:9b:f9:46:3c:7e:df:82:88:29:c2:76:3e:1a:
                    a4:81:87:1f:ce:9e:41:68:de:6c:f3:89:df:ae:02:
                    e7:12:ee:93:20:f1:d2:d6:3d:36:58:ee:71:bf:b3:
                    c5:e7:5a:4b:a0:12:89:ed:f7:cc:ec:34:c7:b2:28:
                    a8:1a:87:c6:8b:5e:d2:c8:25:71:ba:ff:d0:82:1b:
                    5e:50:a9:8a:c6:0c:ea:4b:17:a6:cc:13:0a:53:36:
                    c6:9d:76:f2:95:cc:ac:b9:64:d5:72:fc:ab:ce:6b:
                    59:b1:3a:f2:49:2f:2c:09:d0:01:06:e4:f2:49:85:
                    79:82:e8:c8:bb:1a:ab:70:e3:49:97:9f:84:e0:96:
                    c2:6d:41:ab:59:0c:2e:70:9a:2e:11:c8:83:69:4b:
                    f1:19:97:87:c3:76:0e:bb:b0:2c:92:4a:07:03:6f:
                    57:bf:a9:ec:19:85:d6:3d:f8:de:03:7f:1b:9a:2f:
                    6c:02:72:28:b0:69:d5:f9:fb:3d:2e:31:8f:61:50:
                    59:a6:dd:43:4b:89:e9:68:4b:a6:0d:9b:00:0f:9a:
                    94:61:71
                Exponent: 65537 (0x10001)
        X509v3 extensions:
            X509v3 Subject Key Identifier:
                8B:D6:F8:BD:B7:82:FC:13:BC:61:3F:8B:FA:84:24:3F:A2:14:C8:27
            X509v3 Basic Constraints: critical
                CA:TRUE
            X509v3 Authority Key Identifier:
                keyid:8B:D6:F8:BD:B7:82:FC:13:BC:61:3F:8B:FA:84:24:3F:A2:14:C8:27
                DirName:/C=US/ST=NY/L=New York/O=MongoDB/CN=ROOTCA
                serial:1E:83:0D:9D:43:75:7C:2B:D6:2A:DC:7E:A2:A2:25:AF:5D:3B:89:43
            X509v3 Key Usage: critical
                Certificate Sign, CRL Sign
    Signature Algorithm: sha256WithRSAEncryption
        c2:cc:79:40:8b:7b:a1:87:3a:ec:4a:71:9d:ab:69:00:bb:6f:
        56:0a:25:3b:8f:bd:ca:4d:4b:c5:27:28:3c:7c:e5:cf:84:ec:
        2e:2f:0d:37:35:52:6d:f9:4b:07:fb:9b:da:ea:5b:31:0f:29:
        1f:3c:89:6a:10:8e:ae:20:30:8f:a0:cf:f1:0f:41:99:6a:12:
        5f:5c:ce:15:d5:f1:c9:0e:24:c4:81:70:df:ad:a0:e1:0a:cc:
        52:d4:3e:44:0b:61:48:a9:26:3c:a3:3d:2a:c3:ca:4f:19:60:
        da:f7:7a:4a:09:9e:26:42:50:05:f8:74:13:4b:0c:78:f1:59:
        39:1e:eb:2e:e1:e2:6c:cc:4d:96:95:79:c2:8b:58:41:e8:7a:
        e6:ad:37:e4:87:d7:ed:bb:7d:fa:47:dd:46:dd:e7:62:5f:e9:
        fe:17:4b:e3:7a:0e:a1:c5:80:78:39:b7:6c:a6:85:cf:ba:95:
        d2:8d:09:ab:2d:cb:be:77:9b:3c:22:12:ca:12:86:42:d8:c5:
        3c:31:a0:ed:92:bc:7f:3f:91:2d:ec:db:01:bd:26:65:56:12:
        a3:56:ba:d8:d3:6e:f3:c3:13:84:98:2a:c7:b3:22:05:68:fa:
        8e:48:6f:36:8e:3f:e5:4d:88:ef:15:26:4c:b1:d3:7e:25:84:
        8c:bd:5b:d2:74:55:cb:b3:fa:45:3f:ee:ef:e6:80:e9:f7:7f:
        25:a6:6e:f2:c4:22:f7:b8:40:29:02:f1:5e:ea:8e:df:80:e0:
        60:f1:e5:3a:08:81:25:d5:cc:00:8f:5c:ac:a6:02:da:27:c0:
        cc:4e:d3:f3:14:60:c1:12:3b:21:b4:f7:29:9b:4c:34:39:3c:
        2a:d1:4b:86:cc:c7:de:f3:f7:5e:8f:9d:47:2e:3d:fe:e3:49:
        70:0e:1c:61:1c:45:a0:5b:d6:48:49:be:6d:f9:3c:49:26:d8:
        8b:e6:a1:b2:61:10:fe:0c:e8:44:2c:33:cd:3c:1d:c2:de:c2:
        06:98:7c:92:7b:c4:06:a5:1f:02:8a:03:53:ec:bd:b7:fc:31:
        f3:2a:c1:0e:6a:a5:a8:e4:ea:4d:cc:1d:07:a9:3f:f6:0e:35:
        5d:99:31:35:b3:43:90:f3:1c:92:8e:99:15:13:2b:8f:f6:a6:
        01:c9:18:05:15:2a:e3:d0:cc:45:66:d3:48:11:a2:b9:b1:20:
        59:42:f7:88:15:9f:e0:0c:1d:13:ae:db:09:3d:bf:7a:9d:cf:
        b2:41:1e:7a:fa:6b:35:20:03:58:a1:6c:02:19:21:5f:25:fc:
        ba:2f:fc:79:d7:92:e7:37:77:14:10:d9:33:b6:e5:fb:7a:46:
        ab:d1:86:70:88:92:59:c3

创建用于签名的中间CA

创建根 CA 之后,就可以创建用于对成员证书和客户端证书进行签名的中间 CA 了。中间 CA 其实就是一个使用根证书签名的证书。最佳实践是使用中间 CA对服务器证书(成员证书)和客户端证书进行签名。通常,一个 CA 会使用不同的中间 CA 对不同类别的证书进行签名。如果中间 CA 遭到破坏,并且需要撤销证书,那么只会对信任树的一部分(而不是由 CA签名的所有证书)产生影响。如果使用根 CA 对所有证书进行签名,那么发生这种情况时,所有证书都会受到影响。

js 复制代码
# 再次强调,在生产环境中,需要对密钥进行加密保护:
# openssl genrsa -aes256 -out signing-ca.key 4096
openssl genrsa -out signing-ca.key 4096

openssl req -new -key signing-ca.key -out signing-ca.csr \
  -config openssl.cnf -subj "$dn_prefix/CN=CA-SIGNER"
openssl x509 -req -days 730 -in signing-ca.csr -CA root-ca.crt -CAkey \
  root-ca.key -set_serial 01 -out signing-ca.crt -extfile openssl.cnf \
  -extensions v3_ca

注意,通过使用 openssl req 和 openssl ca 命令,上面的语句使用根证书来对签名证书进行签名。openssl req 命令创建了签名请求,openssl ca 命令使用该请求作为输入创建了经过签名的中间(签名)证书。

作为创建签名 CA 的最后一步,需要将根证书(包含根公钥)和签名证书(包含签名公钥)连接到一个pem 文件中。这个文件稍后将作为 --tlsCAFile 选项的值提供给 mongod 或客户端进程。

js 复制代码
cat root-ca.crt > root-ca.pem
cat signing-ca.crt >> root-ca.pem

设置好根 CA 和签名 CA 之后,就可以创建用于MongoDB 集群中身份验证的成员证书和客户端证书了。

2.2、生成并签名成员证书

成员证书通常称为 x.509 服务器证书。应该对 mongod和 mongos 进程使用该类型的证书。MongoDB 集群成员会使用这些证书来验证集群中的成员身份。换句话说,mongod 就是用服务器证书来为副本集的其他成员提供身份验证的。

要为副本集的所有成员生成证书,需要使用一个 for 循环来生成多个证书。

js 复制代码
# 注意openssl req命令中主题的OU部分
for host in "${mongodb_server_hosts[@]}"; do
    echo "Generating key for $host"
    openssl genrsa -out ${host}.key 4096
        openssl req -new -key ${host}.key -out ${host}.csr -config openssl.cnf \
        -subj "$dn_prefix/OU=$ou_member/CN=${host}"
        openssl x509 -req -days 365 -in ${host}.csr -CA signing-ca.crt -CAkey \
        signing-ca.key -CAcreateserial -out ${host}.crt -extfile openssl.cnf \
        -extensions v3_req
    cat ${host}.crt > ${host}.pem
    cat ${host}.key >> ${host}.pem
done

每个证书涉及 3 个步骤:

  • 使用 openssl genrsa 命令创建新的密钥对;
  • 使用 openssl req 命令为密钥生成签名请求;
  • 使用 openssl x509 命令来通过签名 CA 对证书进行签名和输出。

注意变量 $ou_member。这个变量标识出了服务器证书和客户端证书之间的区别。服务器证书和客户端证书在Distinguished Name 的组织部分必须不同。更具体地说,它们在 O、OU 或 DC 的值中至少要有一个不同。

2.3、生成并签名客户端证书

客户端证书用于 mongo shell、MongoDB Compass、MongoDB 实用程序和工具,当然,也用于使用了MongoDB 驱动的应用程序。生成客户端证书的过程与生成成员证书的过程基本相同,唯一的区别是其使用了变量$ou_client。这样可以确保 O、OU 和 DC 值的组合与上面生成的服务器证书不同。

bash 复制代码
# 注意openssl req命令中主题的OU部分
for host in "${mongodb_client_hosts[@]}"; do
    echo "Generating key for $host"
    openssl genrsa -out ${host}.key 4096
    openssl req -new -key ${host}.key -out ${host}.csr -config openssl.cnf \
-subj "$dn_prefix/OU=$ou_client/CN=${host}"
    openssl x509 -req -days 365 -in ${host}.csr -CA signing-ca.crt -CAkey \
      signing-ca.key -CAcreateserial -out ${host}.crt -extfile openssl.cnf \
      -extensions v3_req
    cat ${host}.crt > ${host}.pem
    cat ${host}.key >> ${host}.pem
done

2.4、在不启用身份验证和授权的情况下启动副本集

如下所示,可以在不启用身份验证的情况下启动副本集的每个成员。在之前使用副本集的例子中都没有启用身份验证,因此这种方式对我们来说应该很熟悉。这里再次使用了 2.1 节定义的几个变量(或参阅本章的完整脚本)和一个循环来启动副本集的每个成员(mongod)​:

bash 复制代码
mport=$mongodb_port
for host in "${mongodb_server_hosts[@]}"; do
    echo "Starting server $host in non-auth mode"
    mkdir -p ./db/${host}
    mongod --replSet set509 --port $mport --dbpath ./db/$host \
        --fork --logpath ./db/${host}.log
    let "mport++"
done

在每个 mongod 都被启动之后,就可以使用这些mongod 来初始化一个副本集了:

bash 复制代码
myhostname=`hostname`
cat > init_set.js <<EOF
rs.initiate();
mport=$mongodb_port;
mport++;
rs.add("localhost:" + mport);
mport++;
rs.add("localhost:" + mport);
EOF
mongo localhost:$mongodb_port init_set.js

注意,上面的代码构造了一系列命令。将这些命令存储在一个 JavaScript 文件中,然后运行 mongo shell 来执行所创建的这一小段脚本。总体来说,这些命令在 mongoshell 中执行时,会连接到运行在 27017 端口上的mongod(由 19.2.1 节中 $mongodb_port 变量的值设置的)​,启动副本集,然后将其他两个 mongod(27018端口和 27019 端口)添加到副本集中。

2.5、创建admin用户

当从 mongo shell 或其他客户端连接来执行管理任务时,会使用这个用户进行身份验证。要使用客户端证书进行身份验证,必须首先作为 MongoDB 用户从客户端证书中添加主题的值。由于每个唯一的 x.509客户端证书对应一个 MongoDB 用户,因此不能使用同一个客户端证书来验证多个 MongoDB 用户。必须在external 数据库中添加用户,也就是说,身份验证数据库就是 external 数据库。

首先,使用 openssl x509 命令从客户端证书中获取主题:

bash 复制代码
openssl x509 -in client1.pem -inform PEM -subject -nameopt RFC2253 | grep subject

输出如下:

bash 复制代码
subject= CN=client1,OU=MyClients,O=MongoDB,L=New York,ST=NY,C=US

要创建 admin 用户,首先使用 mongo shell 连接到副本集的主节点:

bash 复制代码
mongo --norc localhost:27017

在 mongo shell 中,使用以下命令:

js 复制代码
db.getSiblingDB("$external").runCommand(
    {
        createUser: "CN=client1,OU=MyClients,O=MongoDB,L=New York,ST=NY,C=US",
        roles: [
            { role: "readWrite", db: 'test' },
            { role: "userAdminAnyDatabase", db: "admin" },
            { role: "clusterAdmin", db:"admin"}
            ],
        writeConcern: { w: "majority" , wtimeout: 5000 }
    }
);

注意,我们在这个命令中使用了 $external 数据库,并且已经将客户端证书的主题指定为用户名了。

2.6、启用身份验证和授权并重新启动副本集

既然有了 admin 用户,那么就可以启用身份验证和授权来重新启动副本集,并使用客户端进行连接了。如果什么类型的用户都没有,则无法连接到启用了身份验证的副本集。

在当前状态下(不启用身份验证)停止副本集:

bash 复制代码
kill $(ps -ef | grep mongod | grep set509 | awk '{print $2}')

现在我们已经准备好在启用身份验证的情况下重新启动副本集了。在生产环境中,需要把每个证书和密钥文件复制到它们对应的主机上。为简单起见,这里在 localhost 上做所有这些操作。为了启动一个安全的副本集,需要在每次调用 mongod 时添加以下命令行选项。

  • --tlsMode
  • --clusterAuthMode
  • --tlsCAFile------根 CA 文件(root-ca.key)
  • --tlsCertificateKeyFile------mongod 的证书文件
  • --tlsAllowInvalidHostnames------仅用于测试,允许无效的主机名

这里,作为 tlsCAFile 选项提供的文件用于建立信任链。回想一下,root-ca.key 文件包含了根 CA 以及签名 CA的证书。向 mongod 进程提供此文件,就表示希望信任 此文件中包含的证书以及由这些证书签名的所有其他证书。

bash 复制代码
mport=$mongodb_port
for host in "${mongodb_server_hosts[@]}"; do
    echo "Starting server $host"
    mongod --replSet set509 --port $mport --dbpath ./db/$host \
        --tlsMode requireTLS --clusterAuthMode x509 --tlsCAFile root-ca.pem \
        --tlsAllowInvalidHostnames --fork --logpath ./db/${host}.log \
        --tlsCertificateKeyFile ${host}.pem --tlsClusterFile ${host}.pem \
        --bind_ip 127.0.0.1
    let "mport++"
done

这样,我们就有了一个三成员副本集,其使用 x.509 证书进行身份验证和传输层加密。剩下唯一要做的就是使用mongo shell 进行连接。这里使用 client1 证书进行身份验证,因为之前为该证书创建了一个 admin 用户:

bash 复制代码
mongo --norc --tls --tlsCertificateKeyFile client1.pem --tlsCAFile root-ca.pem \
--tlsAllowInvalidHostnames --authenticationDatabase "\$external" \
--authenticationMechanism MONGODB-X509

建立连接后,可以尝试向集合中插入一些数据。同样应该尝试使用其他用户(如 client2.pem)进行连接。这样的连接尝试会导致如下错误:

bash 复制代码
mongo --norc --tls --tlsCertificateKeyFile client2.pem --tlsCAFile root-ca.pem \
--tlsAllowInvalidHostnames --authenticationDatabase "\$external" \
--authenticationMechanism MONGODB-X509
MongoDB shell version v4.2.0
2019-09-11T23:18:31.696+0100 W NETWORK [js] The server certificate does not match
the host name. Hostname: 127.0.0.1 does not match
2019-09-11T23:18:31.702+0100 E QUERY   [js] Error: Could not find user
"CN=client2,OU=MyClients,O=MongoDB,L=New York,ST=NY,C=US" for db "$external" :
connect@src/mongo/shell/mongo.js:341:17
@(connect):3:6
2019-09-11T23:18:31.707+0100 F -         [main] exception: connect failed
2019-09-11T23:18:31.707+0100 E -         [main] exiting with code 1

本章展示了一个基于 x.509 证书来进行身份验证,并对客户端和副本集成员之间的通信进行加密的示例。同样的过程也适用于分片集群。关于 MongoDB 集群的安全保护,请记住以下两点。

  • 应该保护目录、根 CA 和签名 CA,以及为成员或客户端生成及签名证书的主机本身,防止未经授权的访问。
  • 为简单起见,本教程中的根 CA 和签名 CA 密钥不受密码保护。在生产环境中,必须用密码来保护密钥以防未经授权的使用。
相关推荐
zzq77972 小时前
别把大模型 API Key 写进 APK:移动 AI 应用接口防盗刷实践
android·人工智能·安全·app加固·御盾安全·安卓加固
龙仔7253 小时前
人大金仓OS_Core数据库自动备份实施笔记(银河麒麟Linux)
linux·数据库·笔记·备份·人大金仓
sunxr.2273 小时前
Mysql-----最后一次作业
数据库·mysql
普通网友3 小时前
Python FastAPI 异步数据库管理
数据库·fastapi
晓子文集4 小时前
Tushare接口文档:期货合约信息表(fut_basic)
大数据·数据库·金融·金融数据·量化投资
山东科恩光电4 小时前
如何通过穆柯隐卫MZS-01确保生产安全与人员保护?
安全
三言老师4 小时前
CentOS7.9:Redis‑Cluster集群部署结构化实战教程
linux·运维·服务器·数据库
旺仔学长 哈哈5 小时前
Spring Boot 智能停车场管理系统---附源码+数据库文档
数据库·spring boot·后端·智能停车场
ClickHouseDB5 小时前
ClickHouse托管Postgres:OLTP+OLAP,新能力解锁最佳数据平台
java·前端·数据库
数智化管理手记6 小时前
主数据重复、错漏频发?一站式主数据管理平台如何落地?
大数据·运维·数据库·人工智能·数据挖掘