如何在Ubuntu16.04上使用Stunnel加密到Redis的流量
介绍
Redis 是一个开源的键值对数据存储,使用内存存储模型和可选的磁盘写入来实现持久性。 它具有事务、发布/订阅消息模式和自动故障转移等功能。 Redis 有用大多数语言编写的客户端, 他们的网站 上有推荐的客户端。
Redis 本身不提供任何加密功能。 它的运行假设是它已部署到一个隔离的专用网络,只有受信任的方可以访问。 如果您的环境不符合该假设,您将不得不单独将 Redis 流量包装在加密中。
在本指南中,我们将演示如何使用称为 stunnel
的安全隧道程序加密 Redis 流量。 Redis 客户端和服务器之间的流量将通过专用的 SSL 加密隧道进行路由。 我们将使用两台 Ubuntu 16.04 服务器进行演示。
先决条件
首先,您应该在每台机器上配置一个具有 sudo
权限的非 root 用户。 此外,本指南将假设您有一个基本的防火墙。 您可以按照我们的 Ubuntu 16.04 初始服务器设置指南 来满足这些要求。
当您准备好继续时,请按照以下说明进行操作。
什么是stunnel?
对于基本的加密通信,stunnel
实用程序易于安装和配置。 它支持两台机器之间的加密转发。 客户端连接到本地端口,stunnel
在将其转发到远程服务器之前对其进行加密包装。 在服务器端,stunnel
侦听配置的端口并解密流量,然后将其转发到本地端口(在我们的例子中,Redis 服务器侦听的端口)。
使用 stunnel
的一些优点是:
- Ubuntu 在其默认存储库中维护
stunnel
的软件包 - Ubuntu 包含一个 init 脚本,用于在启动时自动启动进程
- 配置简单直观
- 每个目的都使用一条新隧道。 在某些情况下,这可能是一个缺点,但它提供了对访问的精细控制。
一些缺点是:
- 客户端通过附加到非默认本地端口来连接到远程机器,这起初可能不直观。
- 如果连接两台 Redis 服务器进行复制或集群,则必须在每台机器上配置两条隧道用于服务器到服务器的通信(一条用于出站流量,一条用于入站流量)。
考虑到这些特征,让我们开始吧。
安装 Redis 服务器和客户端软件包
在开始之前,我们应该在一台机器上安装 Redis 服务器,在另一台机器上安装客户端包。 如果您已经配置了其中一项或两项,请随时跳过。
注意: Redis 服务器指令设置了一个测试密钥,稍后将用于测试连接。 如果您已经安装了 Redis 服务器,您可以继续设置此密钥或在我们测试连接时使用任何其他已知密钥。
安装 Redis 服务器
我们将使用 Chris Lea 的 Redis 服务器 PPA 来安装最新版本的 Redis。 使用第三方存储库时请务必小心。 在这种情况下,Chris Lea 是一个值得信赖的打包者,他为几个流行的开源项目维护高质量、最新的包。
添加 PPA 并在您的第一台计算机上安装 Redis 服务器软件,方法是键入:
sudo apt-add-repository ppa:chris-lea/redis-server sudo apt-get update sudo apt-get install redis-server
在此过程中输入 Enter 接受提示。
安装完成后,输入以下命令测试是否可以在本地连接到 Redis 服务:
redis-cli ping
如果软件已安装并正在运行,您应该会看到:
Redis server outputPONG
让我们设置一个稍后可以使用的密钥:
redis-cli set test 'success'
我们已将 test 键设置为值 success
。 在配置 stunnel
后,我们将尝试从我们的客户端计算机访问此密钥。
安装 Redis 客户端
另一台 Ubuntu 16.04 机器将充当客户端。 我们需要的所有软件都可以在默认存储库的 redis-tools
包中找到:
sudo apt-get update sudo apt-get install redis-tools
由于远程 Redis 服务器的默认配置和防火墙处于活动状态,我们目前无法连接到远程 Redis 实例进行测试。
在每台计算机上安装和启用 stunnel
接下来,您需要在每个服务器和客户端上安装 stunnel
。 Ubuntu 在其默认存储库中包含该实用程序的第四版,称为 stunnel4
。 如果您不需要在上一节中安装任何东西,请确保在安装前包含 sudo apt-get update
命令以刷新您的包索引:
# sudo apt-get update sudo apt-get install stunnel4
Ubuntu 上的 stunnel
服务使用较旧的 SysVinit 脚本启动,可由 systemd 管理。 而不是使用本机 systemd 方法,要将服务配置为在启动时启动,您必须修改 /etc/default/stunnel4
文件:
sudo nano /etc/default/stunnel4
通过将 ENABLED
选项设置为“1”,使服务在启动时启动:
/etc/default/stunnel4
. . . ENABLED=1 . . .
保存并关闭每台服务器上的文件。
接下来,我们将创建用于加密通信的自签名 SSL 证书和密钥。
在 Redis 服务器上创建自签名 SSL 证书和密钥
在您的 Redis 服务器上,在 /etc/stunnel
目录中创建一个自签名 SSL 证书和密钥。 这将用于加密 stunnel
的两个实例之间的连接。 我们将使用名称 redis-server
来指代证书和密钥文件:
sudo openssl req -x509 -nodes -days 3650 -newkey rsa:2048 -keyout /etc/stunnel/redis-server.key -out /etc/stunnel/redis-server.crt
系统将提示您输入有关您正在创建的证书的信息。 由于这只会在内部使用,因此值并不重要,因此请填写您想要的任何内容。 您可以在下面看到一个示例:
Redis server output. . . ----- Country Name (2 letter code) [AU]:US State or Province Name (full name) [Some-State]:New York Locality Name (eg, city) []:New York City Organization Name (eg, company) [Internet Widgits Pty Ltd]:DigitalOcean Organizational Unit Name (eg, section) []:Community Common Name (e.g. server FQDN or YOUR name) []:redis-server Email Address []:admin@example.com
通过键入以下内容来限制对生成的 .key
文件的访问:
sudo chmod 600 /etc/stunnel/redis-server.key
现在我们有了 SSL 证书和密钥,我们可以创建 Redis 服务器的 stunnel
配置文件。
创建 Redis 服务器 stunnel 配置文件
在 Redis 服务器上的 /etc/stunnel
目录中打开一个以 .conf
结尾的文件以开始使用:
sudo nano /etc/stunnel/redis.conf
在里面,指定一个位置以在主要部分中写入 PID 文件。 /run
目录旨在存储这些类型的文件,因此我们将使用它:
/etc/stunnel/redis.conf
pid = /run/stunnel-redis.pid
接下来,创建一个部分来配置对 Redis 服务的访问。 您可以随意调用它(我们将其称为 redis-server
)。 该部分将此配置与您以后可能需要在此计算机上配置的任何其他隧道分开。
我们需要分别使用 cert
和 key
指令指定 Redis 服务器自己的证书和密钥的位置。
我们还将在这里定义传入数据的隧道。 我们希望 accept
加密流量到 Redis 服务器的外部 IP 地址上的默认 Redis 端口(端口 6379)。 然后,我们希望 connect
将该流量发送到 local 接口上的默认 Redis 端口,以存放解密的流量。 这是 Redis 服务实际监听的地方:
/etc/stunnel/redis.conf
pid = /run/stunnel-redis.pid [redis-server] cert = /etc/stunnel/redis-server.crt key = /etc/stunnel/redis-server.key accept = redis_servers_public_IP:6379 connect = 127.0.0.1:6379
完成后,保存并关闭文件。
重新启动 stunnel 并配置防火墙
现在在 Redis 服务器上配置了 stunnel
,我们可以通过键入以下内容来重启服务:
sudo systemctl restart stunnel4.service
如果您检查 Redis 服务器上侦听连接的服务,您应该会在公共接口上看到 stunnel
正在侦听端口 6379。 您还应该看到 Redis 正在侦听本地接口上的同一端口:
sudo netstat -plunt
Redis server outputActive Internet connections (only servers) Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name tcp 0 0 public_IP:6379 0.0.0.0:* LISTEN 4292/stunnel4 tcp 0 0 127.0.0.1:6379 0.0.0.0:* LISTEN 2679/redis-server 1 tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 1720/sshd tcp6 0 0 :::22 :::* LISTEN 1720/sshd
尽管 stunnel
正在侦听公共接口,但防火墙可能尚未配置为允许流量通过。
要允许所有流量到端口 6379,请键入:
sudo ufw allow 6379
这将打开对 stunnel
正在侦听的公共接口上的端口 6379 的访问。 stunnel
端口只接受加密流量。
将证书分发给客户端
每个 Redis 客户端都需要一份 Redis 服务器证书文件的副本。 分发 .crt
文件的最简单方法是简单地将文件内容输出到服务器上,然后将内容复制到连接机器上的相应文件中。
通过键入以下内容在 Redis 服务器上输出 .crt
文件的内容:
cat /etc/stunnel/redis-server.crt
Redis server output-----BEGIN CERTIFICATE----- MIIEGTCCAwGgAwIBAgIJALUdz8P8q8UPMA0GCSqGSIb3DQEBCwUAMIGiMQswCQYD VQQGEwJVUzERMA8GA1UECAwITmV3IFlvcmsxFjAUBgNVBAcMDU5ldyBZb3JrIENp . . . Tq7WJk77tk4nPI8iGv1WuK8xTAm5aOncxP16VoMpsDMV+GB1p3nBkMQ/GKF8pPXU fn6BnDWKmeZqAlBM+MGYAfkbZWdBslrWasCJzs+tehTqL0LLJ6d3Gi9biBPb -----END CERTIFICATE-----
将显示的证书 包括标记为 BEGIN CERTIFICATE 和 END CERTIFICATE 的行复制到剪贴板。
在客户端机器上,打开/etc/stunnel
目录下的同名文件:
sudo nano /etc/stunnel/redis-server.crt
粘贴您从 Redis 服务器复制的内容。 完成后保存并关闭文件。
创建 Redis 客户端 stunnel 配置文件
现在客户端已经有了服务器证书的副本,我们可以配置客户端的 stunnel
配置。
在客户端机器上的 /etc/stunnel
目录中打开一个以 .conf
结尾的文件。 我们将再次调用文件 redis.conf
:
sudo nano /etc/stunnel/redis.conf
在里面,指定一个 PID 文件,服务将在其中再次存储其进程 ID:
/etc/stunnel/redis.conf
pid = /run/stunnel-redis.pid
接下来,添加一个部分来配置出站数据的隧道。 您可以随意命名(我们将其命名为 redis-client
)。 该部分将此配置与您以后可能需要在此计算机上配置的任何其他隧道分开。
我们需要使用 client
指令将此部分显式标记为客户端配置。 设置 accept
指令以侦听本地接口上未使用的端口,以处理来自本地 Redis 客户端的连接(在本例中我们将使用端口 8000)。 将 connect
指令设置为 Redis 服务器的公共 IP 地址和我们打开的端口。
然后使用 CAfile
指向 Redis 服务器证书的副本。 我们还必须将 verify
设置为 4,这使得 stunnel
只检查证书而不考虑证书链(因为我们自签名证书):
/etc/stunnel/redis.conf
pid = /run/stunnel-redis.pid [redis-client] client = yes accept = 127.0.0.1:8000 connect = remote_server_IP_address:6379 CAfile = /etc/stunnel/redis-server.crt verify = 4
完成后保存并关闭文件。
重新启动客户端服务并测试连接
重启客户端的stunnel
服务,实现修改:
sudo systemctl restart stunnel4.service
检查客户端上的隧道是否设置正确:
sudo netstat -plunt
Redis client outputActive Internet connections (only servers) Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name tcp 0 0 127.0.0.1:8000 0.0.0.0:* LISTEN 3809/stunnel4 tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 1714/sshd tcp6 0 0 :::22 :::* LISTEN 1714/sshd
如您所见,stunnel
正在本地端口 8000 上侦听连接。
现在,您应该能够通过将客户端指向本地接口上的端口 8000 来连接到远程 Redis 实例:
redis-cli -p 8000 ping
Redis client outputPONG
查询我们在本指南开头设置的测试密钥:
redis-cli -p 8000 get test
Redis client output"success"
这确认我们能够成功访问远程数据库。
为了确认我们无法不使用隧道与远程Redis服务器通信,我们可以尝试直接连接远程端口:
redis-cli -h redis_server_public_IP -p 6379 ping
Redis client outputError: Connection reset by peer
如您所见,只有在通过隧道正确加密的情况下,远程 Redis 端口才接受流量。
将上述示例扩展为多客户端和服务器到服务器通信
我们上面概述的示例使用了单个 Redis 服务器和单个客户端的简单示例。 但是,这些相同的方法可以应用于更复杂的交互。
扩展此示例以处理多个客户端很简单。 您需要执行上述操作。
- 在新客户端上安装 Redis 客户端软件和
stunnel
包 - 使
stunnel
软件开机启动 - 将服务器的证书文件复制到
/etc/stunnel
目录下 - 将
stunnel
客户端配置文件复制到新的客户端机器 - 重启
stunnel
服务
要设置安全的服务器到服务器通信(例如,用于复制或集群),您需要设置两个并行隧道:
- 在新服务器上,安装 Redis 服务器包和
stunnel
- 使
stunnel
软件开机启动 - 为新的 Redis 服务器生成新的证书和密钥文件(为文件使用唯一名称)
- 将每个证书文件从一台服务器复制到另一台服务器到
/etc/stunnel
目录 - 在每个服务器(包括现有服务器)上编辑或创建
stunnel
配置文件,使其包含:
- 在新的 Redis 服务器上打开防火墙中的外部端口
- 通过调整 Redis 配置文件配置每个 Redis 实例连接到本地映射的端口以访问远程服务器(所需的指令取决于服务器的关系。 有关更多详细信息,请参阅 Redis 文档)。
两台服务器的 stunnel
配置文件如下所示:
用于服务器到服务器通信的 stunnel 配置文件
pid = /run/stunnel-redis.pid [redis-server] cert = /etc/stunnel/this_servers_certificate.crt key = /etc/stunnel/this_servers_key.key accept = this_servers_public_IP:6379 connect = 127.0.0.1:6379 [redis-client] client = yes accept = 127.0.0.1:arbitrary_local_port connect = remote_servers_public_IP:6379 CAfile = /etc/stunnel/remote_servers_certificate.crt verify = 4
如有必要,可以在每台机器上配置多个客户端部分,以将本地端口映射到远程服务器。 在这些情况下,请务必使用 accept
指令为每个远程服务器选择不同的未使用本地端口。
结论
Redis 是一个功能强大且灵活的工具,对于许多部署来说都是无价的。 但是,在不安全的环境中运行 Redis 是一项巨大的责任,会使您的服务器和数据容易受到攻击或盗窃。 如果您没有仅由受信任方组成的隔离网络,则必须通过其他方式保护流量。 本指南中概述的方法只是保护 Redis 各方之间通信的一种方法。 其他选项包括 隧道与 spiped 或 设置 VPN 。