如何在Ubuntu18.04上为MySQL配置SSL/TLS
介绍
MySQL是世界上最受欢迎开源关系型数据库管理系统。 虽然现代包管理器已经减少了启动和运行 MySQL 的一些阻力,但在安装它之后仍然需要执行一些进一步的配置。 花一些额外时间的最重要的方面之一是安全性。
默认情况下,MySQL 被配置为只接受本地连接,或者来自安装 MySQL 的同一台机器的连接。 如果您需要从远程位置访问您的 MySQL 数据库,请务必安全地进行。 在本指南中,我们将演示如何在 Ubuntu 18.04 上配置 MySQL 以接受使用 SSL/TLS 加密的远程连接。
先决条件
要完成本指南,您需要:
- 两台 Ubuntu 18.04 服务器。 我们将使用其中一台服务器作为 MySQL 服务器,而将另一台服务器用作客户端机器。 创建具有
sudo
权限的非 root 用户,并在每个服务器上启用具有ufw
的防火墙。 按照我们的 Ubuntu 18.04 初始服务器设置指南 使两台服务器都进入适当的初始状态。 - 在 一台机器 上,安装和配置 MySQL 服务器。 按照我们的 MySQL Ubuntu 18.04 安装指南的 步骤 1 到 3 执行此操作。 当您遵循本指南时,请务必配置您的 root MySQL 用户以使用密码进行身份验证,如指南的 Step 3 中所述,因为这是使用连接到 MySQL 所必需的TCP 而不是本地 Unix 套接字。
请注意,在本指南中,您安装 MySQL 的服务器将被称为 MySQL 服务器,应该在这台机器上运行的任何命令都将以蓝色背景显示,如下所示:
同样,本指南将另一台服务器称为 MySQL 客户端 ,并且必须在该机器上运行的任何命令都将以红色背景显示:
请在学习本教程时牢记这些,以免造成任何混淆。
第 1 步 — 检查 MySQL 当前的 SSL/TLS 状态
在进行任何配置更改之前,您可以检查 MySQL 服务器 实例上当前的 SSL/TLS 状态。
使用以下命令以 root MySQL 用户身份开始 MySQL 会话。 此命令包括 -p
选项,它指示 mysql
提示您输入密码以便登录。 它还包括 -h
选项,用于指定要连接的主机。 在这种情况下,它指向 127.0.0.1
,即 IPv4 环回接口,也称为 localhost。 这将强制客户端使用 TCP 连接,而不是使用本地套接字文件。 默认情况下,MySQL 尝试通过 Unix 套接字文件 建立连接。 这通常更快、更安全,因为这些连接只能在本地进行,而不必经过 TCP 连接必须执行的所有检查和路由操作。 但是,使用 TCP 连接允许我们检查连接的 SSL 状态:
mysql -u root -p -h 127.0.0.1
系统将提示您输入安装和配置 MySQL 时选择的 MySQL root 密码。 输入后,您将进入交互式 MySQL 会话。
显示发出以下命令的 SSL/TLS 变量的状态:
SHOW VARIABLES LIKE '%ssl%';
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_openssl
和 have_ssl
变量都标记为 DISABLED
。 这意味着 SSL 功能已编译到服务器中,但尚未启用。
检查您当前连接的状态以确认这一点:
\s
Output-------------- mysql Ver 14.14 Distrib 5.7.26, for Linux (x86_64) using EditLine wrapper Connection id: 9 Current database: Current user: root@localhost SSL: Not in use Current pager: stdout Using outfile: '' Using delimiter: ; Server version: 5.7.26-0ubuntu0.18.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: 40 min 11 sec Threads: 1 Questions: 33 Slow queries: 0 Opens: 113 Flush tables: 1 Open tables: 106 Queries per second avg: 0.013 --------------
如上面的输出所示,此连接当前未使用 SSL,即使您是通过 TCP 连接的。
完成后关闭当前 MySQL 会话:
exit
现在您已经确认您的 MySQL 服务器没有使用 SSL,您可以继续下一步,您将通过生成一些证书和密钥开始启用 SSL 的过程。 这些将允许您的服务器和客户端安全地相互通信。
第 2 步 — 生成 SSL/TLS 证书和密钥
要启用与 MySQL 的 SSL 连接,您首先需要生成适当的证书和密钥文件。 MySQL 5.7 及更高版本提供了一个名为 mysql_ssl_rsa_setup
的实用程序,可帮助简化此过程。 您按照必备MySQL教程安装的MySQL版本包含此实用程序,因此我们将在此处使用它来生成必要的文件。
MySQL 进程必须能够读取生成的文件,因此使用 --uid
选项将 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' -----
这些新文件将存储在 MySQL 的数据目录中,默认位于 /var/lib/mysql
。 通过键入以下内容检查生成的文件:
sudo find /var/lib/mysql -name '*.pem' -ls
Output 258930 4 -rw-r--r-- 1 mysql mysql 1107 May 3 16:43 /var/lib/mysql/client-cert.pem 258919 4 -rw-r--r-- 1 mysql mysql 451 May 3 16:43 /var/lib/mysql/public_key.pem 258925 4 -rw------- 1 mysql mysql 1675 May 3 16:43 /var/lib/mysql/server-key.pem 258927 4 -rw-r--r-- 1 mysql mysql 1107 May 3 16:43 /var/lib/mysql/server-cert.pem 258922 4 -rw------- 1 mysql mysql 1675 May 3 16:43 /var/lib/mysql/ca-key.pem 258928 4 -rw------- 1 mysql mysql 1675 May 3 16:43 /var/lib/mysql/client-key.pem 258924 4 -rw-r--r-- 1 mysql mysql 1107 May 3 16:43 /var/lib/mysql/ca.pem 258918 4 -rw------- 1 mysql mysql 1679 May 3 16:43 /var/lib/mysql/private_key.pem
这些文件是证书颁发机构(以“ca”开头)、MySQL 服务器进程(以“server”开头)和 MySQL 客户端(以“client”开头)的密钥和证书对。 此外,MySQL 使用 private_key.pem
和 public_key.pem
文件在不使用 SSL 时安全地传输密码。
现在您已拥有必要的证书和密钥文件,请继续在您的 MySQL 实例上启用 SSL。
第 3 步 — 在 MySQL 服务器上启用 SSL 连接
每当服务器启动时,现代版本的 MySQL 都会在 MySQL 数据目录中查找适当的证书文件。 因此,您无需修改 MySQL 的配置即可启用 SSL。
相反,通过重新启动 MySQL 服务来启用 SSL:
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_openssl
和 have_ssl
变量现在读取 YES
而不是 DISABLED
。 此外,ssl_ca
、ssl_cert
和 ssl_key
变量已填充了我们刚刚生成的相应文件的名称。
接下来,再次检查连接详细信息:
\s
Output-------------- . . . SSL: Cipher in use is DHE-RSA-AES256-SHA . . . Connection: 127.0.0.1 via TCP/IP . . . --------------
这一次,显示特定的 SSL 密码,表明 SSL 正在用于保护连接。
退出到外壳:
exit
您的服务器现在能够使用加密,但需要一些额外的配置以允许远程访问并强制使用安全连接。
第 4 步 — 为远程客户端配置安全连接
现在您已经在 MySQL 服务器上启用了 SSL,您可以开始配置安全远程访问。 为此,您将配置 MySQL 服务器以要求通过 SSL 进行任何远程连接,绑定 MySQL 以侦听公共接口,并调整系统的防火墙规则以允许外部连接
目前,MySQL 服务器配置为接受来自客户端的 SSL 连接。 但是,如果客户端请求,它仍将允许未加密的连接。 我们可以通过打开 require_secure_transport
选项来改变它。 这需要使用 SSL 或本地 Unix 套接字进行所有连接。 由于 Unix 套接字只能从服务器内部访问,因此远程用户可用的唯一连接选项将是 SSL。
要启用此设置,请在首选文本编辑器中打开 MySQL 配置文件。 在这里,我们将使用 nano
:
sudo nano /etc/mysql/my.cnf
内部将有两个 !includedir
指令用于获取其他配置文件。 您必须在这些行下面添加自己的配置 ,以便它覆盖在这些附加配置文件中发现的任何冲突设置。
首先创建一个 [mysqld]
部分来定位 MySQL 服务器进程。 在该部分标题下,将 require_secure_transport
设置为 ON
,这将强制 MySQL 仅允许安全连接:
/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 配置为仅侦听来自 127.0.0.1
的连接,即表示 localhost 的环回 IP 地址。 这意味着 MySQL 被配置为仅侦听来自安装 MySQL 服务器的机器的连接。
为了允许 MySQL 侦听外部连接,您必须将其配置为侦听 external IP 地址上的连接。 为此,您可以添加 bind-address
设置并将其指向 0.0.0.0
,即代表所有 IP 地址的通配符 IP 地址。 本质上,这将迫使 MySQL 监听每个接口上的连接:
/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
注意:您也可以将bind-address
设置为MySQL服务器的公网IP地址。 但是,如果您曾经将数据库迁移到另一台机器,则需要记住更新您的 my.cnf
文件。
添加这些行后,保存并关闭文件。 如果您使用 nano
编辑文件,您可以按 CTRL+X
、Y
,然后按 ENTER
进行编辑。
接下来,重新启动 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 13317/mysqld tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 1293/sshd tcp6 0 0 :::22 :::* LISTEN 1293/sshd
上面输出中突出显示的 0.0.0.0
表明 MySQL 正在侦听所有可用接口上的连接。
接下来,允许 MySQL 连接通过服务器的防火墙。 通过键入以下内容为您的 ufw
规则添加例外:
sudo ufw allow mysql
OutputRule added Rule added (v6)
有了它,远程连接尝试现在可以到达您的 MySQL 服务器。 但是,您当前没有配置任何可以从远程计算机连接的用户。 我们将在下一步创建和配置一个可以从您的客户端计算机连接的 MySQL 用户。
第 5 步 — 创建专用 MySQL 用户
此时,您的 MySQL 服务器将拒绝任何从远程客户端计算机连接的尝试。 这是因为现有的 MySQL 用户都只配置为从 MySQL 服务器本地连接。 要解决此问题,您将创建一个只能从您的客户端计算机连接的专用用户。
要创建这样的用户,请以 root 用户身份重新登录 MySQL:
mysql -u root -p
在提示符下,使用 CREATE USER
命令创建一个新的远程用户。 您可以随意命名此用户,但在本指南中,我们将其命名为 mysql_user。 请务必在用户规范的主机部分指定您的客户端机器的 IP 地址,以限制与该机器的连接,并将 password
替换为您选择的安全密码。 此外,对于将来关闭 require_secure_transport
选项的一些冗余,请通过包含 REQUIRE SSL
子句来指定此用户需要 SSL,如下所示:
CREATE USER 'mysql_user'@'your_mysql_client_IP' IDENTIFIED BY 'password' REQUIRE SSL;
接下来,授予新用户对他们应该有权访问的任何数据库或表的权限。 为了演示,创建一个 example
数据库:
CREATE DATABASE example;
然后让您的新用户访问此数据库及其所有表:
GRANT ALL ON example.* TO 'mysql_user'@'your_mysql_client_IP';
接下来,刷新权限以立即应用这些设置:
FLUSH PRIVILEGES;
完成后退出到 shell:
exit
您的 MySQL 服务器现在已设置为允许来自远程用户的连接。 要测试是否可以成功连接到 MySQL,您需要在 MySQL 客户端 上安装 mysql-client
包。
使用 ssh
登录到您的客户端计算机
ssh sammy@your_mysql_client_ip
然后更新客户端机器的包索引:
sudo apt update
并使用以下命令安装 mysql-client
:
sudo apt install mysql-client
出现提示时,按 ENTER
确认安装。
一旦 APT 完成安装包,运行以下命令测试是否可以成功连接到服务器。 此命令包括 -u
用户选项以指定 mysql_user 和 -h
选项以指定 MySQL 服务器的 IP 地址:
mysql -u mysql_user -p -h your_mysql_server_IP
提交密码后,您将登录到远程服务器。 使用 \s
检查服务器状态并确认您的连接是安全的:
\s
Output-------------- . . . SSL: Cipher in use is DHE-RSA-AES256-SHA . . . Connection: your_mysql_server_IP via TCP/IP . . . --------------
退出到外壳:
exit
您已确认您能够通过 SSL 连接到 MySQL。 但是,您尚未确认 MySQL 服务器拒绝不安全的连接。 要对此进行测试,请尝试再次连接,但这次将 --ssl-mode=disabled
附加到登录命令。 这将指示 mysql-client
尝试未加密的连接:
mysql -u mysql_user -p -h mysql_server_IP --ssl-mode=disabled
在提示时输入密码后,您的连接将被拒绝:
OutputERROR 1045 (28000): Access denied for user 'mysql_user'@'mysql_server_IP' (using password: YES)
这表明允许 SSL 连接,而拒绝未加密的连接。
此时,您的 MySQL 服务器已配置为接受安全远程连接。 如果这满足您的安全要求,您可以在这里停下来,但是您可以添加一些额外的部分来增强两台服务器之间的安全性和信任。
第 6 步 - (可选)为 MySQL 连接配置验证
目前,您的 MySQL 服务器配置有由本地生成的证书颁发机构 (CA) 签名的 SSL 证书。 服务器的证书和密钥对足以为传入连接提供加密。
但是,您还没有完全利用证书颁发机构可以提供的信任关系。 通过将 CA 证书以及客户端证书和密钥分发给客户端,双方可以提供证明,证明他们的证书是由相互信任的证书颁发机构签署的。 这有助于防止来自恶意服务器的欺骗连接。
为了实现这个额外的可选保护,我们将适当的 SSL 文件传输到客户端机器,创建客户端配置文件,并更改远程 MySQL 用户以要求受信任的证书。
注意: 以下段落中概述的将 CA 证书、客户端证书和客户端密钥传输到 MySQL 客户端的过程包括使用 cat
显示每个文件的内容,并将这些内容复制到剪贴板,并将它们粘贴到客户端计算机上的新文件中。 虽然可以使用 scp
或 sftp
之类的程序直接复制这些文件,但这也需要您为两台服务器 设置 SSH 密钥 以允许它们通过 SSH 进行通信。
我们的目标是将连接到 MySQL 服务器的潜在途径的数量降至最低。 虽然这个过程比直接传输文件稍微费力一些,但它同样安全,并且不需要您在两台机器之间打开 SSH 连接。
首先在非 root 用户的主目录中的 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 CERTIFICATE
和 END 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 服务器所需的所有凭据。 但是,MySQL 服务器仍未设置为客户端连接需要受信任的证书。
要更改此设置,请在 MySQL 服务器 上再次登录 MySQL root 帐户:
mysql -u root -p
从这里,更改远程用户的安全要求。 代替 REQUIRE SSL
子句,应用 REQUIRE X509
子句。 这意味着 REQUIRE SSL
子句提供的所有安全性,但还需要连接客户端提供由 MySQL 服务器信任的证书颁发机构签名的证书。
要调整用户要求,请使用 ALTER USER
命令:
ALTER USER 'mysql_user'@'mysql_client_IP' REQUIRE X509;
然后刷新更改以确保立即应用它们:
FLUSH PRIVILEGES;
完成后退出到 shell:
exit
然后,检查您是否可以在连接时验证双方。
在 MySQL 客户端 上,首先尝试在不提供客户端证书的情况下进行连接:
mysql -u mysql_user -p -h mysql_server_IP
OutputERROR 1045 (28000): Access denied for user 'mysql_user'@'mysql_client_IP' (using password: YES)
正如预期的那样,当没有提供客户端证书时,服务器会拒绝连接。
现在,连接时使用 --ssl-ca
、--ssl-cert
和 --ssl-key
选项指向 ~/client-ssl
目录中的相关文件:
mysql -u mysql_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 客户端 机器上的主目录中,创建一个名为 ~/.my.cnf
的隐藏配置文件:
nano ~/.my.cnf
在文件的顶部,创建一个名为 [client]
的部分。 在下方,添加 ssl-ca
、ssl-cert
和 ssl-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-cert
和 ssl-key
选项指向需要向 MySQL 服务器证明它也具有已由同一证书颁发机构签名的证书的文件。 如果您希望 MySQL 服务器验证客户端是否也受 CA 信任,您将需要这个。
完成后保存并关闭文件。
现在,您无需在命令行中添加 --ssl-ca
、--ssl-cert
和 --ssl-key
选项即可连接到 MySQL 服务器:
mysql -u remote_user -p -h mysql_server_ip
您的客户端和服务器现在将在协商连接时各自提供证书。 每一方都配置为根据其本地拥有的 CA 证书验证远程证书。
结论
您的 MySQL 服务器现在配置为需要来自远程客户端的安全连接。 此外,如果您按照步骤使用证书颁发机构验证连接,则双方都会建立某种程度的信任,即远程方是合法的。