如何在Ubuntu16.04上为MySQL配置SSL/TLS

来自菜鸟教程
跳转至:导航、​搜索

介绍

MySQL 是世界上最流行的开源关系数据库管理系统。 现代包管理器已经减少了启动和运行 MySQL 的一些阻力,但是在安装后仍然需要进行一些配置。 花一些额外时间的最重要的领域之一是安全性。

默认情况下,MySQL 配置为仅接受本地连接。 如果您需要允许远程连接,请务必安全地执行此操作。 在本指南中,我们将演示如何在 Ubuntu 16.04 上配置 MySQL 以接受使用 SSL/TLS 加密的远程连接。

先决条件

要遵循本指南,您将需要 两个 Ubuntu 16.04 服务器。 我们将使用一个作为 MySQL 服务器,另一个作为客户端。 在每个服务器上创建一个具有 sudo 权限的非 root 用户。 按照我们的 Ubuntu 16.04 初始服务器设置指南 使您的服务器进入适当的初始状态。

在第一台机器上,您应该安装并配置了 MySQL 服务器。 按照我们的 MySQL Ubuntu 16.04 安装指南安装和配置软件。

在第二台机器上,安装 MySQL 客户端 包。 您可以通过键入以下命令更新 apt 软件包索引并安装必要的软件:

sudo apt-get update
sudo apt-get install mysql-client

当您的服务器和客户端准备就绪后,请继续下面的操作。

检查当前 SSL/TLS 状态

在开始之前,我们可以检查 MySQL 服务器 实例上 SSL/TLS 的当前状态。

使用 root MySQL 用户登录 MySQL 会话。 我们将使用 -h 来指定 IPv4 本地环回接口,以强制客户端使用 TCP 连接,而不是使用本地套接字文件。 这将允许我们检查 TCP 连接的 SSL 状态:

mysql -u root -p -h 127.0.0.1

系统将提示您输入您在安装过程中选择的 MySQL root 密码。 之后,您将进入交互式 MySQL 会话。

通过键入以下内容显示 SSL/TLS 变量的状态:

SHOW VARIABLES LIKE '%ssl%';
[environment second]
Output+---------------+----------+
| Variable_name | Value    |
+---------------+----------+
| have_openssl  | DISABLED |
| have_ssl      | DISABLED |
| ssl_ca        |          |
| ssl_capath    |          |
| ssl_cert      |          |
| ssl_cipher    |          |
| ssl_crl       |          |
| ssl_crlpath   |          |
| ssl_key       |          |
+---------------+----------+
9 rows in set (0.01 sec)

have_opensslhave_ssl 变量都标记为 DISABLED。 这意味着 SSL 功能已编译到服务器中,但尚未启用。

检查我们当前连接的状态以确认:

\s
Output--------------
mysql  Ver 14.14 Distrib 5.7.17, for Linux (x86_64) using  EditLine wrapper

Connection id:      30
Current database:   
Current user:       root@localhost
SSL:          Not in use
Current pager:      stdout
Using outfile:      ''
Using delimiter:    ;
Server version:     5.7.17-0ubuntu0.16.04.1 (Ubuntu)
Protocol version:   10
Connection:       127.0.0.1 via TCP/IP
Server characterset:    latin1
Db     characterset:    latin1
Client characterset:    utf8
Conn.  characterset:    utf8
TCP port:       3306
Uptime:         3 hours 38 min 44 sec

Threads: 1  Questions: 70  Slow queries: 0  Opens: 121  Flush tables: 1  Open tables: 40  Queries per second avg: 0.005
--------------

正如上面的输出所示,我们的连接当前没有使用 SSL,即使我们是通过 TCP 连接的。

完成后关闭当前 MySQL 会话:

exit

现在我们可以开始为 SSL 配置 MySQL 以保护我们的连接。

生成 SSL/TLS 证书和密钥

要启用与 MySQL 的 SSL 连接,我们首先需要生成适当的证书和密钥文件。 MySQL 5.7 及更高版本提供了一个名为 mysql_ssl_rsa_setup 的实用程序来简化此过程。 Ubuntu 16.04 有一个兼容版本的 MySQL,所以我们可以使用这个命令来生成必要的文件。

这些文件将在 MySQL 的数据目录中创建,位于 /var/lib/mysql。 我们需要 MySQL 进程能够读取生成的文件,因此我们将传递 mysql 作为应该拥有生成文件的用户:

sudo mysql_ssl_rsa_setup --uid=mysql

该生成将产生如下所示的输出:

OutputGenerating a 2048 bit RSA private key
...................................+++
.....+++
writing new private key to 'ca-key.pem'
-----
Generating a 2048 bit RSA private key
......+++
.................................+++
writing new private key to 'server-key.pem'
-----
Generating a 2048 bit RSA private key
......................................................+++
.................................................................................+++
writing new private key to 'client-key.pem'
-----

通过键入以下内容检查生成的文件:

sudo find /var/lib/mysql -name '*.pem' -ls
Output   256740      4 -rw-r--r--   1 mysql    mysql        1078 Mar 17 17:24 /var/lib/mysql/server-cert.pem
   256735      4 -rw-------   1 mysql    mysql        1675 Mar 17 17:24 /var/lib/mysqlsql/ca-key.pem
   256739      4 -rw-r--r--   1 mysql    mysql         451 Mar 17 17:24 /var/lib/mysqlsql/public_key.pem
   256741      4 -rw-------   1 mysql    mysql        1679 Mar 17 17:24 /var/lib/mysqlsql/client-key.pem
   256737      4 -rw-r--r--   1 mysql    mysql        1074 Mar 17 17:24 /var/lib/mysqlsql/ca.pem
   256743      4 -rw-r--r--   1 mysql    mysql        1078 Mar 17 17:24 /var/lib/mysqlsql/client-cert.pem
   256736      4 -rw-------   1 mysql    mysql        1675 Mar 17 17:24 /var/lib/mysqlsql/private_key.pem
   256738      4 -rw-------   1 mysql    mysql        1675 Mar 17 17:24 /var/lib/mysqlsql/server-key.pem

最后一列显示生成的文件名。 显示“mysql”的中心列表示生成的文件具有正确的用户和组所有权。

这些文件是证书颁发机构(以“ca”开头)、MySQL 服务器进程(以“server”开头)和 MySQL 客户端(以“client”开头)的密钥和证书对。 此外,MySQL 使用 private_key.pempublic_key.pem 文件在不使用 SSL 时安全地传输密码。

在 MySQL 服务器上启用 SSL 连接

当服务器启动时,现代 MySQL 版本将在 MySQL 数据目录中查找适当的证书文件。 因此,我们实际上不需要修改 MySQL 配置来启用 SSL。

我们可以直接重启 MySQL 服务:

sudo systemctl restart mysql

重新启动后,使用与以前相同的命令打开一个新的 MySQL 会话。 如果服务器支持,MySQL 客户端将自动尝试使用 SSL 进行连接:

mysql -u root -p -h 127.0.0.1

让我们看一下我们上次请求的相同信息。 检查 SSL 相关变量的值:

SHOW VARIABLES LIKE '%ssl%';
Output+---------------+-----------------+
| Variable_name | Value           |
+---------------+-----------------+
| have_openssl  | YES             |
| have_ssl      | YES             |
| ssl_ca        | ca.pem          |
| ssl_capath    |                 |
| ssl_cert      | server-cert.pem |
| ssl_cipher    |                 |
| ssl_crl       |                 |
| ssl_crlpath   |                 |
| ssl_key       | server-key.pem  |
+---------------+-----------------+
9 rows in set (0.00 sec)

这次 have_opensslhave_ssl 变量读取“YES”而不是“DISABLED”。 此外,ssl_cassl_certssl_key 变量已填充了我们生成的相关证书的名称。

接下来,再次检查连接详细信息:

\s
Output--------------
. . .
SSL:            Cipher in use is DHE-RSA-AES256-SHA
. . .
Connection:       127.0.0.1 via TCP/IP
. . .
--------------

这一次,显示了特定的 SSL 密码,表明 SSL 正在用于保护我们的连接。

退出到外壳:

exit

我们的服务器现在能够使用加密,但是需要一些额外的配置来允许远程访问和强制使用安全连接。

为远程客户端配置安全连接

现在我们在服务器上拥有了可用的 SSL,我们可以开始配置安全远程访问。 为此,我们需要:

  • 远程连接需要 SSL
  • 绑定到公共接口
  • 为远程连接创建 MySQL 用户
  • 调整我们的防火墙规则以允许外部连接

使用强制 SSL 配置远程访问

目前,MySQL 服务器配置为接受来自客户端的 SSL 连接。 但是,如果客户端请求,它仍将允许未加密的连接。

我们可以通过打开 require_secure_transport 选项来解决这个问题。 这需要使用 SSL 或本地 Unix 套接字进行所有连接。 由于 Unix 套接字只能从服务器内部访问,因此对远程用户开放的唯一连接选项将是 SSL。

要启用此设置,请在文本编辑器中打开 /etc/mysql/my.cnf 文件:

sudo nano /etc/mysql/my.cnf

在内部,将有两个 !includedir 指令用于获取其他配置文件。 我们需要将我们自己的配置 放在这些行的 之下,以便它们覆盖任何冲突的设置。

首先创建一个 [mysqld] 部分来定位 MySQL 服务器进程。 在该部分标题下,将 require_secure_transport 设置为 ON

/etc/mysql/my.cnf

. . .

!includedir /etc/mysql/conf.d/
!includedir /etc/mysql/mysql.conf.d/

[mysqld]
# Require clients to connect either using SSL
# or through a local socket file
require_secure_transport = ON

该行是实施安全连接所需的唯一设置。

默认情况下,MySQL 配置为仅侦听源自本地计算机的连接。 要将其配置为侦听远程连接,我们可以将 bind-address 设置为不同的接口。

为了允许 MySQL 在其任何接口上接受连接,我们可以将 bind-address 设置为“0.0.0.0”:

/etc/mysql/my.cnf

. . .

!includedir /etc/mysql/conf.d/
!includedir /etc/mysql/mysql.conf.d/

[mysqld]
# Require clients to connect either using SSL
# or through a local socket file
require_secure_transport = ON
bind-address = 0.0.0.0

完成后保存并关闭文件。

接下来,重新启动 MySQL 以应用新设置:

sudo systemctl restart mysql

通过键入以下命令验证 MySQL 是否正在侦听“0.0.0.0”而不是“127.0.0.1”:

sudo netstat -plunt
OutputActive Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name
tcp        0      0 0.0.0.0:3306            0.0.0.0:*               LISTEN      4330/mysqld     
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN      1874/sshd       
tcp6       0      0 :::22                   :::*                    LISTEN      1874/sshd

上述输出中的“0.0.0.0”表示 MySQL 正在侦听所有可用接口上的连接。

接下来,我们需要允许 MySQL 连接通过我们的防火墙。 通过键入以下内容创建异常:

sudo ufw allow mysql
OutputRule added
Rule added (v6)

远程连接尝试现在应该能够到达我们的 MySQL 服务器。

配置远程 MySQL 用户

MySQL 服务器现在正在侦听远程连接,但我们目前没有配置任何可以从外部计算机连接的用户。

root 用户身份登录 MySQL 以开始使用:

mysql -u root -p

在内部,您可以使用 CREATE USER 命令创建一个新的远程用户。 我们将在用户规范的主机部分中使用客户端机器的 IP 地址来限制与该机器的连接。

为了在将来关闭 require_secure_transport 选项的情况下提供一些冗余,我们还将在帐户创建期间通过包含 REQUIRE SSL 子句来指定此用户需要 SSL:

CREATE USER 'remote_user'@'mysql_client_IP' IDENTIFIED BY 'password' REQUIRE SSL;

接下来,授予新用户对他们应该有权访问的数据库或表的权限。 为了演示,我们将创建一个 example 数据库并赋予我们的新用户所有权:

CREATE DATABASE example;
GRANT ALL ON example.* TO 'remote_user'@'mysql_client_IP';

接下来,刷新权限以立即应用这些设置:

FLUSH PRIVILEGES;

完成后退出到 shell:

exit

我们的服务器设置为允许连接到我们的远程用户。

测试远程连接

MySQL 客户端 机器上,进行测试以确保可以成功连接到服务器。 使用 -u 选项指定远程用户,使用 -h 选项指定 MySQL 服务器的 IP 地址:

mysql -u remote_user -p -h mysql_server_IP

指定密码后,您将登录到远程服务器。

检查以确保您的连接是安全的:

\s
Output--------------
. . .
SSL:          Cipher in use is DHE-RSA-AES256-SHA
. . .
Connection:       mysql_server_IP via TCP/IP
. . .
--------------

退出到外壳:

exit

接下来,尝试不安全地连接:

mysql -u remote_user -p -h mysql_server_IP --ssl-mode=disabled

在提示您输入密码后,您的连接应该被拒绝:

OutputERROR 1045 (28000): Access denied for user 'remote_user'@'mysql_server_IP' (using password: YES)

这就是我们正在努力的方向。 它表明允许 SSL 连接,而拒绝未加密的连接。

此时,我们的 MySQL 服务器已配置为安全地接受远程连接。 如果这满足您的安全要求,您可以在这里停下来,但是我们可以采取一些额外的措施来进一步提高我们的安全性和信任度。

为 MySQL 连接配置验证(可选)

目前,我们的 MySQL 服务器配置了由本地生成的证书颁发机构 (CA) 签名的 SSL 证书。 服务器的证书和密钥对足以为传入连接提供加密。

但是,我们目前没有利用证书颁发机构可以提供的信任关系。 通过将 CA 证书以及客户端证书和密钥分发给客户端,双方可以提供证明,证明他们的证书是由相互信任的证书颁发机构签署的。 这有助于防止与恶意服务器的欺骗连接。

为了实施这种额外的可选保护措施,我们需要:

  • 将适当的 SSL 文件传输到客户端计算机
  • 创建客户端配置文件
  • 更改我们的远程用户以要求受信任的证书

将客户端证书传输到客户端计算机

首先,我们需要从 MySQL 服务器获取 MySQL CA 和客户端证书文件并将它们放在 MySQL 客户端上。

首先在您将用于连接的用户的主目录中的 MySQL 客户端 上创建一个目录。 称之为 client-ssl

mkdir ~/client-ssl

由于证书密钥是敏感的,我们应该锁定对该目录的访问,以便只有当前用户可以访问它:

chmod 700 ~/client-ssl

现在,我们可以将证书信息复制到新目录。

MySQL 服务器 机器上,通过键入以下内容显示 CA 证书的内容:

sudo cat /var/lib/mysql/ca.pem
Output-----BEGIN CERTIFICATE-----

. . .

-----END CERTIFICATE-----

将整个输出,包括 BEGIN CERTIFICATEEND CERTIFICATE 行复制到剪贴板。

MySQL 客户端 上,在新目录中创建一个同名文件:

nano ~/client-ssl/ca.pem

在里面,从剪贴板粘贴复制的证书内容。 完成后保存并关闭文件。

接下来,在 MySQL 服务器 上显示客户端证书:

sudo cat /var/lib/mysql/client-cert.pem
Output-----BEGIN CERTIFICATE-----

. . .

-----END CERTIFICATE-----

再次,将内容复制到剪贴板。 请记住包括第一行和最后一行。

client-ssl目录下的MySQL客户端上打开同名文件:

nano ~/client-ssl/client-cert.pem

粘贴剪贴板中的内容。 保存并关闭文件。

最后,在MySQL服务器上显示客户端密钥文件的内容:

sudo cat /var/lib/mysql/client-key.pem
Output-----BEGIN RSA PRIVATE KEY-----

. . .

-----END RSA PRIVATE KEY-----

将显示的内容(包括第一行和最后一行)复制到剪贴板。

MySQL客户端上,打开client-ssl目录下的同名文件:

nano ~/client-ssl/client-key.pem

粘贴剪贴板中的内容。 保存并关闭文件。

客户端计算机现在应该具有访问 MySQL 服务器所需的所有凭据。 接下来,我们需要更改我们的远程用户。

需要来自受信任 CA 的远程用户证书

目前,MySQL 客户端具有可用于在连接时向服务器提供其证书的文件。 但是,服务器仍未设置为需要来自受信任 CA 的客户端证书。

要更改此设置,请在 MySQL 服务器 上再次登录 MySQL root 帐户:

mysql -u root -p

接下来,我们需要更改远程用户的要求。 我们需要应用 REQUIRE X509 子句,而不是 REQUIRE SSL 子句。 这意味着前面要求提供的所有安全性,但还需要连接客户端提供由 MySQL 服务器信任的证书颁发机构签名的证书。

要调整用户要求,请使用 ALTER USER 命令:

ALTER USER 'remote_user'@'mysql_client_IP' REQUIRE X509;

刷新更改以确保立即应用它们:

FLUSH PRIVILEGES;

完成后退出到 shell:

exit

接下来,我们可以测试以确保我们仍然可以连接。

连接时测试证书验证

现在是检查我们是否可以在连接时验证双方的好时机。

MySQL 客户端 上,首先尝试在不提供客户端证书的情况下进行连接:

mysql -u remote_user -p -h mysql_server_IP
OutputERROR 1045 (28000): Access denied for user 'remote_user'@'mysql_client_IP' (using password: YES)

如果不提供客户端证书,服务器将拒绝连接。

现在,连接时使用 --ssl-ca--ssl-cert--ssl-key 选项指向 ~/client-ssl 目录中的相关文件:

mysql -u remote_user -p -h mysql_server_IP --ssl-ca=~/client-ssl/ca.pem --ssl-cert=~/client-ssl/client-cert.pem --ssl-key=~/client-ssl/client-key.pem

您应该已成功登录。 注销以重新获得对 shell 会话的访问权限:

exit

现在我们已经确认了对服务器的访问,我们可以实现一个小的可用性改进。

创建 MySQL 客户端配置文件

为了避免每次连接时都必须指定证书文件,我们可以创建一个简单的 MySQL 客户端配置文件。

MySQL 客户端 机器上的主目录中,创建一个名为 ~/.my.cnf 的隐藏文件:

nano ~/.my.cnf

在文件的顶部,创建一个名为 [client] 的部分。 在下面,我们可以设置 ssl-cassl-certssl-key 选项指向我们从服务器复制过来的文件。 它应该如下所示:

~/.my.cnf

[client]
ssl-ca = ~/client-ssl/ca.pem
ssl-cert = ~/client-ssl/client-cert.pem
ssl-key = ~/client-ssl/client-key.pem

ssl-ca 选项告诉客户端验证 MySQL 服务器提供的证书是否由我们指向的证书颁发机构签名。 这允许客户端相信它正在连接到受信任的 MySQL 服务器。

ssl-certssl-key 选项指向向 MySQL 服务器证明它也具有已由同一证书颁发机构签名的证书所需的文件。 如果我们希望 MySQL 服务器验证客户端是否也被 CA 信任,我们需要这个。

完成后保存并关闭文件。

现在,您无需在命令行中添加 --ssl-ca--ssl-cert--ssl-key 选项即可连接到 MySQL 服务器:

mysql -u remote_user -p -h mysql_server_ip

您的客户端和服务器现在应该在协商连接时都提供证书。 每一方都配置为根据其本地拥有的 CA 证书验证远程证书。

结论

您的 MySQL 服务器现在应该配置为需要远程客户端的安全连接。 此外,如果您按照步骤使用证书颁发机构验证连接,则双方都会建立某种程度的信任,即远程方是合法的。