TiDB 证书鉴权使用指南

TiDB 支持基于证书鉴权的登录方式。采用这种方式,TiDB 对不同用户签发证书,使用加密连接来传输数据,并在用户登录时验证证书。相比 MySQL 用户常用的用户名密码验证方式,与 MySQL 相兼容的证书鉴权方式更安全,因此越来越多的用户使用证书鉴权来代替用户名密码验证。

在 TiDB 上使用证书鉴权的登录方法,可能需要进行以下操作:

  • 创建安全密钥和证书
  • 配置 TiDB 和客户端使用的证书
  • 配置登录时需要校验的用户证书信息
  • 更新和替换证书

本文介绍了如何进行证书鉴权的上述几个操作。

创建安全密钥和证书

目前推荐使用 OpenSSL 来生成密钥和证书,生成证书的过程和为 TiDB 客户端服务端间通信开启加密传输过程类似,下面更多演示如何在证书中配置更多需校验的属性字段。

生成 CA 密钥和证书

  1. 执行以下命令生成 CA 密钥:

    sudo openssl genrsa 2048 > ca-key.pem

    命令执行后输出以下结果:

    Generating RSA private key, 2048 bit long modulus (2 primes) ....................+++++ ...............................................+++++ e is 65537 (0x010001)
  2. 执行以下命令生成该密钥对应的证书:

    sudo openssl req -new -x509 -nodes -days 365000 -key ca-key.pem -out ca-cert.pem
  3. 输入证书细节信息,示例如下:

    Country Name (2 letter code) [AU]:US State or Province Name (full name) [Some-State]:California Locality Name (eg, city) []:San Francisco Organization Name (eg, company) [Internet Widgits Pty Ltd]:PingCAP Inc. Organizational Unit Name (eg, section) []:TiDB Common Name (e.g. server FQDN or YOUR name) []:TiDB admin Email Address []:s@pingcap.com

生成服务端密钥和证书

  1. 执行以下命令生成服务端的密钥:

    sudo openssl req -newkey rsa:2048 -days 365000 -nodes -keyout server-key.pem -out server-req.pem
  2. 输入证书细节信息,示例如下:

    Country Name (2 letter code) [AU]:US State or Province Name (full name) [Some-State]:California Locality Name (eg, city) []:San Francisco Organization Name (eg, company) [Internet Widgits Pty Ltd]:PingCAP Inc. Organizational Unit Name (eg, section) []:TiKV Common Name (e.g. server FQDN or YOUR name) []:TiKV Test Server Email Address []:k@pingcap.com Please enter the following 'extra' attributes to be sent with your certificate request A challenge password []: An optional company name []:
  3. 执行以下命令生成服务端的 RSA 密钥:

    sudo openssl rsa -in server-key.pem -out server-key.pem

    输出结果如下:

    writing RSA key
  4. 使用 CA 证书签名来生成服务端的证书:

    sudo openssl x509 -req -in server-req.pem -days 365000 -CA ca-cert.pem -CAkey ca-key.pem -set_serial 01 -out server-cert.pem

    输出结果示例如下:

    Signature ok subject=C = US, ST = California, L = San Francisco, O = PingCAP Inc., OU = TiKV, CN = TiKV Test Server, emailAddress = k@pingcap.com Getting CA Private Key

生成客户端密钥和证书

生成服务端密钥和证书后,需要生成客户端使用的密钥和证书。通常需要为不同的用户生成不同的密钥和证书。

  1. 执行以下命令生成客户端的密钥:

    sudo openssl req -newkey rsa:2048 -days 365000 -nodes -keyout client-key.pem -out client-req.pem
  2. 输入证书细节信息,示例如下:

    Country Name (2 letter code) [AU]:US State or Province Name (full name) [Some-State]:California Locality Name (eg, city) []:San Francisco Organization Name (eg, company) [Internet Widgits Pty Ltd]:PingCAP Inc. Organizational Unit Name (eg, section) []:TiDB Common Name (e.g. server FQDN or YOUR name) []:tpch-user1 Email Address []:zz@pingcap.com Please enter the following 'extra' attributes to be sent with your certificate request A challenge password []: An optional company name []:
  3. 执行以下命令生成客户端 RSA 证书:

    sudo openssl rsa -in client-key.pem -out client-key.pem

    以上命令的输出结果如下:

    writing RSA key
  4. 执行以下命令,使用 CA 证书签名来生成客户端证书:

    sudo openssl x509 -req -in client-req.pem -days 365000 -CA ca-cert.pem -CAkey ca-key.pem -set_serial 01 -out client-cert.pem

    输出结果示例如下:

    Signature ok subject=C = US, ST = California, L = San Francisco, O = PingCAP Inc., OU = TiDB, CN = tpch-user1, emailAddress = zz@pingcap.com Getting CA Private Key

验证证书

执行以下命令验证证书:

openssl verify -CAfile ca-cert.pem server-cert.pem client-cert.pem

如果验证通过,会显示以下信息:

server-cert.pem: OK client-cert.pem: OK

配置 TiDB 和客户端使用证书

在生成证书后,需要在 TiDB 中配置服务端所使用的证书,同时让客户端程序使用客户端证书。

配置 TiDB 服务端

修改 TiDB 配置文件中的 [security] 段。这一步指定 CA 证书、服务端密钥和服务端证书存放的路径。可将 path/to/server-cert.pempath/to/server-key.pempath/to/ca-cert.pem 替换成实际的路径。

[security] ssl-cert ="path/to/server-cert.pem" ssl-key ="path/to/server-key.pem" ssl-ca="path/to/ca-cert.pem"

启动 TiDB 日志。如果日志中有以下内容,即代表配置生效:

[INFO] [server.go:286] ["mysql protocol server secure connection is enabled"] ["client verification enabled"=true]

配置客户端程序

配置客户端程序,让客户端使用客户端密钥和证书来登录 TiDB。

以 MySQL 客户端为例,可以通过指定 ssl-certssl-keyssl-ca 来使用新的 CA 证书、客户端密钥和证书:

mysql -utest -h0.0.0.0 -P4000 --ssl-cert /path/to/client-cert.new.pem --ssl-key /path/to/client-key.new.pem --ssl-ca /path/to/ca-cert.pem

配置登录时需要校验的用户证书信息

使用客户端连接 TiDB 进行授权配置。先获取需要验证的用户证书信息,再对这些信息进行配置。

获取用户证书信息

用户证书信息可由 REQUIRE SUBJECTREQUIRE ISSUERREQUIRE SANREQUIRE CIPHER 来指定,用于检查 X.509 certificate attributes。

  • REQUIRE SUBJECT:指定用户在连接时需要提供客户端证书的 subject 内容。指定该选项后,不需要再配置 require ssl 或 x509。配置内容对应生成客户端密钥和证书中的录入信息。

    可以执行以下命令来获取该项的信息:

    openssl x509 -noout -subject -in client-cert.pem | sed 's/.\{8\}//' | sed 's/, /\//g' | sed 's/ = /=/g' | sed 's/^/\//'
  • REQUIRE ISSUER:指定签发用户证书的 CA 证书的 subject 内容。配置内容对应生成 CA 密钥和证书中的录入信息。

    可以执行以下命令来获取该项的信息:

    openssl x509 -noout -subject -in ca-cert.pem | sed 's/.\{8\}//' | sed 's/, /\//g' | sed 's/ = /=/g' | sed 's/^/\//'
  • REQUIRE SAN:指定签发用户证书的 CA 证书的 Subject Alternative Name 内容。配置内容对应生成客户端证书使用的 openssl.cnf 配置文件的 alt_names 信息

    • 可以执行以下命令来获取已生成证书中的 REQUIRE SAN 项的信息:

      openssl x509 -noout -extensions subjectAltName -in client.crt
    • REQUIRE SAN 目前支持以下 Subject Alternative Name 检查项:

      • URI
      • IP
      • DNS
    • 多个检查项可通过逗号连接后进行配置。例如,对用户 u1 进行以下配置:

      CREATE USER 'u1'@'%' REQUIRE SAN 'DNS:d1,URI:spiffe://example.org/myservice1,URI:spiffe://example.org/myservice2';

      以上配置只允许用户 u1 使用 URI 项为 spiffe://example.org/myservice1spiffe://example.org/myservice2、DNS 项为 d1 的证书登录 TiDB。

  • REQUIRE CIPHER:配置该项检查客户端支持的 cipher method。可以使用以下语句来查看支持的列表:

    SHOW SESSION STATUS LIKE 'Ssl_cipher_list';

配置用户证书信息

获取用户证书信息(REQUIRE SUBJECTREQUIRE ISSUERREQUIRE SANREQUIRE CIPHER)后,可在创建用户、赋予权限或更改用户时配置用户证书信息。将以下命令中的 <replaceable> 替换为对应的信息。可以选择配置其中一项或多项,使用空格或 and 分隔。

  • 可以在创建用户 (CREATE USER) 时配置登录时需要校验的证书信息:

    CREATE USER 'u1'@'%' REQUIRE ISSUER '<replaceable>' SUBJECT '<replaceable>' SAN '<replaceable>' CIPHER '<replaceable>';
  • 可以在修改已有用户 (ALTER USER) 时配置登录时需要校验的证书信息:

    ALTER USER 'u1'@'%' REQUIRE ISSUER '<replaceable>' SUBJECT '<replaceable>' SAN '<replaceable>' CIPHER '<replaceable>';

配置完成后,用户在登录时 TiDB 会验证以下内容:

  • 使用 SSL 登录,且证书为服务器配置的 CA 证书所签发
  • 证书的 issuer 信息和权限配置里的 REQUIRE ISSUER 信息相匹配
  • 证书的 subject 信息和权限配置里的 REQUIRE CIPHER 信息相匹配
  • 证书的 Subject Alternative Name 信息和权限配置里的 REQUIRE SAN 信息相匹配

全部验证通过后用户才能登录,否则会报 ERROR 1045 (28000): Access denied 错误。登录后,可以通过以下命令来查看当前链接是否使用证书登录、TLS 版本和 Cipher 算法。

连接 MySQL 客户端并执行:

\s

返回结果如下:

-------------- mysql Ver 8.3.0 for Linux on x86_64 (MySQL Community Server - GPL) Connection id: 1 Current database: test Current user: root@127.0.0.1 SSL: Cipher in use is TLS_AES_128_GCM_SHA256

然后执行:

SHOW VARIABLES LIKE '%ssl%';

返回结果如下:

+---------------+----------------------------------+ | Variable_name | Value | +---------------+----------------------------------+ | have_openssl | YES | | have_ssl | YES | | ssl_ca | /path/to/ca-cert.pem | | ssl_cert | /path/to/server-cert.pem | | ssl_cipher | | | ssl_key | /path/to/server-key.pem | +---------------+----------------------------------+ 6 rows in set (0.06 sec)

更新和替换证书

证书和密钥通常会周期性更新。下文介绍更新密钥和证书的流程。

CA 证书是客户端和服务端相互校验的依据,所以如果需要替换 CA 证书,则需要生成一个组合证书来在替换期间同时支持客户端和服务器上新旧证书的验证,并优先替换客户端和服务端的 CA 证书,再替换客户端和服务端的密钥和证书。

更新 CA 密钥和证书

  1. 以替换 CA 密钥为例(假设 ca-key.pem 被盗),将旧的 CA 密钥和证书进行备份:

    mv ca-key.pem ca-key.old.pem && \ mv ca-cert.pem ca-cert.old.pem
  2. 生成新的 CA 密钥:

    sudo openssl genrsa 2048 > ca-key.pem
  3. 用新的密钥生成新的 CA 证书:

    sudo openssl req -new -x509 -nodes -days 365000 -key ca-key.pem -out ca-cert.new.pem
  4. 生成组合 CA 证书:

    cat ca-cert.new.pem ca-cert.old.pem > ca-cert.pem

之后使用新生成的组合 CA 证书并重启 TiDB Server,此时服务端可以同时接受和使用新旧 CA 证书。

之后先将所有客户端用的 CA 证书也替换为新生成的组合 CA 证书,使客户端能同时和使用新旧 CA 证书。

更新客户端密钥和证书

  1. 生成新的客户端 RSA 密钥:

    sudo openssl req -newkey rsa:2048 -days 365000 -nodes -keyout client-key.new.pem -out client-req.new.pem && \ sudo openssl rsa -in client-key.new.pem -out client-key.new.pem
  2. 使用新的组合 CA 证书和新 CA 密钥生成新客户端证书:

    sudo openssl x509 -req -in client-req.new.pem -days 365000 -CA ca-cert.pem -CAkey ca-key.pem -set_serial 01 -out client-cert.new.pem
  3. 让客户端使用新的客户端密钥和证书来连接 TiDB (以 MySQL 客户端为例):

    mysql -utest -h0.0.0.0 -P4000 --ssl-cert /path/to/client-cert.new.pem --ssl-key /path/to/client-key.new.pem --ssl-ca /path/to/ca-cert.pem

更新服务端密钥和证书

  1. 生成新的服务端 RSA 密钥:

    sudo openssl req -newkey rsa:2048 -days 365000 -nodes -keyout server-key.new.pem -out server-req.new.pem && \ sudo openssl rsa -in server-key.new.pem -out server-key.new.pem
  2. 使用新的组合 CA 证书和新 CA 密钥生成新服务端证书:

    sudo openssl x509 -req -in server-req.new.pem -days 365000 -CA ca-cert.pem -CAkey ca-key.pem -set_serial 01 -out server-cert.new.pem
  3. 配置 TiDB 使用上面新生成的服务端密钥和证书并重启。参见配置 TiDB 服务端