记一次 OpenVPN 配置解决 RDS 安全问题

记一次 OpenVPN 配置解决 RDS 安全问题

1. 问题背景

出于某些原因,线上 RDS 数据库的访问凭证(用户名密码)泄露到了公司内部。这导致了几个潜在的安全隐患:

  1. 公网访问风险:公司网络环境下,由于 RDS 公网地址的 IP 白名单包含了公司出口 IP,无权限的同事获取公网 RDS 地址后可以直接连接线上数据库
  2. VPN 访问风险:连接公司内网 VPN 后,可以通过 RDS 内网地址连接到数据库
  3. 跳板机风险:测试服务器可以作为跳板,通过内网 RDS 地址连接线上库

本文记录了如何通过配置来逐一解决这些问题,确保只有授权人员可以访问线上数据库。

2. 解决方案

2.1 剔除 RDS 公网访问白名单

问题:公司网络环境下可以通过公网 RDS 地址访问线上数据库

解决方法

在阿里云 RDS 控制台的安全设置中,将公司的外网出口 IP 从白名单中移除,这样公司网络环境下就无法通过公网地址连接数据库了。

2.2 阻断 VPN 连接内网 RDS 的通道

问题:连接公司 VPN 后可通过内网地址访问 RDS

当前配置:OpenVPN 服务端配置文件中的路由配置如下:

# 该配置使整个网段的流量都通过 VPN 隧道
push "route 172.18.20.0 255.255.255.0"

这行配置的作用是让整个网段(172.18.20.0/24)的请求都走 VPN 流量。由于 RDS 内网地址为 172.18.20.235,属于该网段,所以任何连接了 VPN 的用户都可以通过内网地址连接数据库。

解决方案:添加一条更精确的路由规则,优先级更高:

# 强制 RDS IP 流量走本地网关而非 VPN 隧道
push "route 172.18.20.235 255.255.255.255 net_gateway"

该配置会精准匹配 RDS IP 地址的流量,并强制其走 VPN 客户端本地的网关,而不是 VPN 隧道,从而阻断了通过 VPN 访问内网 RDS 的可能性。

2.3 防止测试服务器作为跳板机

问题:由于测试服务器与 RDS 在同一网段且可以内网通信,无权限同事仍可通过测试服务器作为跳板连接线上数据库。

解决方案:修改 DNS 解析,阻断连接

在测试服务器的 /etc/hosts 文件中添加以下配置:

# 将 RDS 内网域名解析至本地回环地址,阻断连接
127.0.0.1  rds内网域名

这样,当有人尝试在测试服务器上连接 RDS 内网域名时,会被解析到本地回环地址 127.0.0.1,导致连接失败。

3. 进阶解决方案:基于用户的精细化权限控制

以上三个解决方案在解决安全问题的同时,也限制了有权限用户的正常访问。为此,我们可以通过修改 OpenVPN 服务端配置,实现基于用户的精细化权限控制:只允许特定用户通过 VPN 访问内网 RDS。

3.1 修改 OpenVPN 服务器配置

/etc/openvpn/server/server.conf 配置文件中添加以下内容:

# 启用脚本执行权限(级别 3 允许调用外部程序)
script-security 3

# 设置客户端连接时执行的脚本
client-connect /etc/openvpn/client-connect.sh

3.2 创建客户端连接脚本

创建并设置脚本权限:

cd /etc/openvpn
touch client-connect.sh
chmod 755 client-connect.sh  # 使用更安全的权限设置

脚本内容如下:

#!/bin/bash

# 获取参数和用户名
CONFIG_FILE="$1"  # OpenVPN 生成的临时配置文件
TIMESTAMP=$(date "+%Y-%m-%d %H:%M:%S")
LOG_FILE="$(dirname "$0")/client-connect.log"

# 从环境变量获取用户名(优先使用 username,其次是 common_name)
USERNAME="${username:-${common_name:-UNKNOWN_USER}}"

# 创建日志文件(如果不存在)
if [ ! -f "$LOG_FILE" ]; then
    echo "Timestamp | Username | Config File | Action Taken" > "$LOG_FILE"
    echo "------------------------------------------------" >> "$LOG_FILE"
fi

# 记录连接基本信息
LOG_ENTRY="$TIMESTAMP | $USERNAME | $CONFIG_FILE"

# 基于用户名进行权限控制
# 这里可以扩展为从数据库或配置文件读取授权用户列表
if [ "$USERNAME" = "xingcy" ]; then
    # 允许特定用户访问 RDS 内网地址
    echo "push \"route 172.18.20.235 255.255.255.255 vpn_gateway\"" >> "$CONFIG_FILE"
    echo "$LOG_ENTRY | Added VPN route for 172.18.20.235" >> "$LOG_FILE"
else
    # 其他用户不添加特殊路由
    echo "$LOG_ENTRY | No special routing applied" >> "$LOG_FILE"
fi

# 记录环境变量(调试用)
echo "[$TIMESTAMP] Environment variables:" >> "$LOG_FILE"
env | grep -E 'username|common_name|script_' >> "$LOG_FILE"

# 记录最终配置(调试用)
echo "[$TIMESTAMP] Final config file content:" >> "$LOG_FILE"
cat "$CONFIG_FILE" >> "$LOG_FILE"
echo "----------------------------------------" >> "$LOG_FILE"

exit 0

脚本工作原理

  1. 脚本在客户端连接 VPN 时被调用
  2. 获取连接用户的用户名
  3. 只有特定用户(本例中为 xingcy)才会接收到允许访问 RDS 内网地址的路由配置
  4. 其他用户不会收到此路由配置,因此无法通过 VPN 访问内网 RDS
  5. 所有连接尝试都被记录到日志文件,方便审计

3.3 验证配置效果

当不同用户连接 VPN 时,client-connect.log 日志文件会记录不同的路由配置:

# 普通用户连接记录
----------------------------------------
2025-04-11 21:42:54 | yujianghua | /tmp/openvpn_cc_fde8421074b05294e17573fd0810e9a.tmp | No special routing applied
[2025-04-11 21:42:54] Environment variables:
common_name=yujianghua
script_type=client-connect
script_context=init
username=yujianghua
[2025-04-11 21:42:54] Config file content:
----------------------------------------

# 授权用户连接记录
2025-04-11 22:07:02 | xingcy | /tmp/openvpn_cc_6d7bd9d7f217217afbf8ef016b73381.tmp | Added VPN route for 172.18.20.235
[2025-04-11 22:07:02] Environment variables:
common_name=xingcy
script_type=client-connect
script_context=init
username=xingcy
[2025-04-11 22:07:02] Config file content:
push "route 172.18.20.235 255.255.255.255 vpn_gateway"
----------------------------------------

3.4 路由表验证

使用 netstat 命令检查客户端路由表,可以看到授权用户和非授权用户的区别:

授权用户的路由表:

(base) ➜  Downloads netstat -rn|grep 172.18
172.18.20/24       10.0.10.1          UGSc            utun3  # 网段流量走 VPN
172.18.20.235/32   10.0.10.1          UGSc            utun3  # RDS IP 流量也走 VPN

非授权用户的路由表:

(base) ➜  Downloads netstat -rn|grep 172.18
172.18.20/24       10.0.10.1          UGSc            utun3  # 网段流量走 VPN
172.18.20.235/32   192.168.110.1      UGSc            en0    # RDS IP 流量走本地网络接口

在非授权用户的路由表中,RDS IP 的流量会被导向本地网络接口(en0),而不是 VPN 接口(utun3),因此无法访问内网 RDS。

4. 总结

通过以上配置,我们成功实现了多层次的安全防护:

  1. 关闭了公网访问入口:移除 IP 白名单
  2. 阻断了通用 VPN 访问:通过路由规则控制
  3. 防止了跳板攻击:修改 DNS 解析
  4. 实现了精细权限控制:基于用户身份的动态路由配置

这种多层次的安全防护策略,可以有效应对凭证泄露的安全风险,确保即使数据库凭证泄露,也只有授权人员能够实际访问数据库。同时,通过完善的日志记录,也为安全审计提供了有力支持。

在网络安全领域,这种”纵深防御”的思想非常重要 —— 不依赖单点安全措施,而是构建多层次、多角度的安全防护体系。

本作品采用《CC 协议》,转载必须注明作者和本文链接
《L04 微信小程序从零到发布》
从小程序个人账户申请开始,带你一步步进行开发一个微信小程序,直到提交微信控制台上线发布。
《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!