☰
rinetd轻量TCP转发实现跨网段访问MySQL实战
2026/10/2 2:56:43 网站建设 项目流程

1. 为什么要做跨网段访问MySQL:一个真实的网段隔离场景

1.1 网段隔离与数据库访问的矛盾

我最早遇到这个需求,是在一次开发联调中。办公室的测试网段是192.168.30.0/24,数据库服务器却被安排在内网10.0.1.0/24,两个网段在路由层面被隔开,开发机上连数据库工具、跑后端服务、写临时脚本,全都因为这层隔离而失败。网段隔离本身是安全基线的常规操作,能防止内网服务被无关设备直接扫到,但带来的副作用也很明显:任何一次"按需打通",都得走防火墙策略申请流程。

流程走下来可能要一两周,而业务往往只需要"今天下午让三个人临时连一下测试库"。这种短周期、小规模、用完即撤的诉求,决定了你不可能为它专门架一台复杂的网关设备,更不适合让网络团队为你在核心路由上做一次大改。需要的只是一个能在短时间内生效、规则清晰、能随手撤掉的端口转发工具。

当时我手头的条件是这样的:有一台双网卡的跳板机,办公网和内网它都能摸到,系统是CentOS 7,资源极其有限,跑着一个轻量监控脚本已经占了小半内存。在这种条件下,我用rinetd做跨网段访问内网MySQL的端口转发,前后不到十分钟就通了。今天这篇就完整记录一下这个过程,包括选型思路、配置细节、验证步骤和几个我踩过的坑。

1.2 主流打通方案的取舍

在做方案对比之前,我先说清楚"跨网段访问MySQL"的本质:它就是一个TCP连接转发问题。MySQL的通信协议跑在TCP 3306端口上,只要能把客户端的TCP连接从办公网转发到内网数据库端口,应用层根本不需要关心中间发生了什么。基于这一点,候选方案有下面几种。

方案配置复杂度资源占用适用场景主要短板
SSH本地端口转发中需维护SSH连接偶尔手动连接连接断开需要重建,不适合长期后台运行
iptables DNAT高极低网关或主机层长期转发需要root权限,持久化配置麻烦
frp等隧道工具高较高长期、多路、复杂拓扑太重,为一条规则引一堆配置
rinetd低极低临时/轻量/单条规则只做TCP转发,无健康检查

SSH本地转发的问题在于它是一个"前台交互式"的东西,即使加了-f参数后台运行,SSH连接本身仍然可能因为网络抖动、服务器重启等原因断开,断开之后不会自动恢复。iptables则是另一个极端,它的DNAT功能确实强大,但一个不熟悉内核网络栈的运维同事很难一眼看出配置问题,而且它改的是本机网络栈的转发规则,影响面比单进程转发大得多。frp这类工具本身是好东西,但它引入了一个客户端一个服务端,配置文件和端口约定都要维护,对一条3306转发来说属于杀鸡用牛刀。

rinetd最讨喜的地方,是它把"监听某个端口、把数据转发到另一个地址的端口"这件事压缩成了一个配置文件里的一行规则。没有客户端、没有额外的服务端、没有复杂的握手协议,它就是一个纯粹的用户态TCP转发程序。MySQL场景下完全够用,而且你不需要它为你做更多事。

2. rinetd是什么:轻量TCP转发背后的工作原理

2.1 用户态转发的基本模型

rinetd的原理其实很简单,简单到可以用一句话讲完:它在指定网卡IP上的指定端口进行TCP监听,收到连接后主动向目标地址的端口发起另一个TCP连接,然后在这两个socket之间搬运字节流。

有两个细节值得展开说。

第一,它是"字节流搬运"而不是"协议解析"。rinetd不关心你传输的内容是MySQL握手包、HTTP请求还是自定义的二进制流,它只管把一段数据原样从A口搬到B口。这意味着使用rinetd转发MySQL,你不需要安装任何MySQL相关驱动或客户端组件在转发机上,转发机本身甚至可以完全没有MySQL库。我见过有人误以为转发MySQL需要在跳板机上也装一套MySQL,这是完全没必要的,纯属误解。

第二,用户态转发和内核态转发(比如iptables)的区别。iptables的DNAT一旦配置好,数据包在内核里就被改写了目标地址,整个转发过程几乎不消耗用户态资源。rinetd则要把每一个字节从内核态复制到用户态,再写回内核态,性能上肯定不如内核态方案。但对于几十个并发连接、单个连接偶尔跑几条查询这种MySQL联调场景,这点性能损耗完全感知不到。我实测过连续跑一小时批量查询,跳板机的CPU占用几乎在0附近浮动。

2.2 安装的两种方式和验证

在CentOS/RHEL系上,安装很简单:

yum install -y rinetd

Ubuntu/Debian系对应的是:

apt install -y rinetd

如果你的机器没有开外网软件源,或者是一个精简容器,那源码编译也很快。你需要gcc和基础的libc头文件,然后在有网的环境下载源码包传进去:

wget http://www.boutell.com/rinetd/http/rinetd.tar.gz tar -zxvf rinetd.tar.gz cd rinetd make && make install

编译完成后,可执行文件会落在/usr/sbin/rinetd,配置文件默认是/etc/rinetd.conf。验证一下:

rinetd -v

能看到版本号就说明装好了。需要说明的是,CentOS包管理器里带的版本老,但这不是问题——这个工具这么多年没啥大变化,稳定压倒一切。

2.3 能力边界先讲清楚

在用rinetd之前,我心里给它画了一个边界,这能避免后续很多不切实际的期望。

  • 它只做TCP转发。MySQL是TCP协议,所以没问题,但如果你要转的是UDP流量(比如某些DNS查询),那rinetd在大多数版本里并不支持。
  • 它没有应用层健康检查。后端MySQL挂了,rinetd照样监听端口,客户端连接时才会发现连不上。
  • 它没有负载均衡能力。同一个源端口只能定义一条规则,不存在把请求分散到多台MySQL的做法。
  • 它的并发能力是"够用但不豪华"。几百个并发连接实测没问题,几千个上万个大流量传输会吃力,那种场景应该换HAProxy或Nginx stream。

这些边界让我在做方案决策时很清醒:rinetd是一个合格的"轻量实现",不是一个全功能网关。认清这一点,后面所有配置和对结果的预期都会准确很多。

3. 设计转发规则:一次典型的MySQL跨网段映射

3.1 配置文件的四元组格式

rinetd的配置文件极简,每行一条规则,格式是固定的四个字段:

[源地址] [源端口] [目标地址] [目标端口]

一个最典型的MySQL转发规则长这样:

192.168.30.5 3306 10.0.1.10 3306

这行的含义是:在192.168.30.5这个IP的3306端口上监听TCP连接,凡是连上来的流量,原样转发给10.0.1.10的3306端口。

这里有个容易搞混的概念,我想特别纠正一下:源地址指的是rinetd进程监听在哪个IP上,不代表客户端来源限制。虽然在效果上,如果你只监听办公网的那个网卡IP,那办公网之外确实连不上这个端口,但它不是真正的来源IP白名单。真正的来源IP过滤,需要配合防火墙来做,或者利用它监听在特定IP上的特性达到类似目的。我在做生产配置时,会把这两层结合起来用,后面安全加固部分会细说。

3.2 一个能直接照抄的配置示例

假设我的办公网客户端IP是192.168.30.50,跳板机双网卡IP分别是办公网192.168.30.5和内网10.0.1.5,MySQL服务器内网IP是10.0.1.10。那么一个合适的/etc/rinetd.conf是这样:

# rinetd.conf # 办公网跳板机IP 3306 -> 内网MySQL 3306 192.168.30.5 3306 10.0.1.10 3306 # 日志配置 logfile /var/log/rinetd.log

注意,配置文件里每一行字段之间用空格或制表符分隔都可以,但不要混用出奇怪效果。我最开始手写规则时在末尾多打了一个空格,后面又有一次少写了一个字段,两次都导致规则没生效。排查方法很简单,前台调试模式跑起来就知道问题在哪:

rinetd -c /etc/rinetd.conf -d

-d参数会让rinetd在前台运行并把日志输出到终端,端口有没有被监听、规则有没有加载,一眼就能看出来。这个调试习惯帮我省了很多时间。

3.3 多网段同时访问的配置方式

如果不止办公网一个网段需要访问,还有其他运维网段,规则就多写几行:

192.168.30.5 3306 10.0.1.10 3306 192.168.40.5 3306 10.0.1.10 3306

每个网段挑一个跳板机IP,各自监听相同端口,转发目标一致。这里有个需要留意的体验细节:如果两个网段用了同一个转发端口,客户端的连接串就可以完全一致,只是入口IP不同。如果你希望统一入口,也可以让两个网段都通过同一个物理IP进入,那就得考虑反正端口够用,让不同网段用不同入口端口会更清晰:

192.168.30.5 3306 10.0.1.10 3306 192.168.30.5 13306 10.0.1.10 3306

第二种写法适合在同一个入口上做区分,比如3306给开发组用,13306给测试脚本用。从审计角度讲,端口分开,日志里也好查是谁在访问。

4. 实测全程:从办公网连到内网MySQL的每一步验证

4.1 双网卡跳板机的基线与连通性预检

写配置之前,我先做了一轮环境和网络预检,这一步不能省。在跳板机上执行:

ip addr show ip route show ping -c 2 10.0.1.10

确认两个网卡都有IP、默认路由和到MySQL服务器的内网路由都存在,再开始下一步。很多人跨网段转发失败,不是rinetd的问题,而是跳板机到MySQL服务器本身就不通。

然后检查MySQL服务器的监听状态。在MySQL服务器上执行:

ss -tlnp | grep 3306

看到0.0.0.0:3306或者10.0.1.10:3306才是正常的,说明MySQL接受来自内网其他主机的连接。如果输出显示127.0.0.1:3306,那说明MySQL只绑定了回环地址,这种情况下即使rinetd通了,MySQL也会拒绝转发机发来的连接,这是一个特别典型的坑,我在下一章会专门展开。

4.2 启动rinetd并验证监听状态

配置文件准备好之后,启动服务:

rinetd -c /etc/rinetd.conf

启动后立刻验证监听:

ss -tlnp | grep 3306

正常输出应该能看到192.168.30.5:3306处于LISTEN状态,进程名显示为rinetd。再确认一下进程本身还存在:

ps -ef | grep rinetd

这里我要提醒一件事:rinetd默认不会生成pid文件,启动后就是个后台进程,管理起来比较粗糙。直接用pkill rinetd停掉虽然能行,但不优雅,也不利于重启时保留规则。业内常规做法是把它交给systemd管理,我会在本章末尾给出一个现成的unit配置。

4.3 MySQL客户端连通性验证与抓包分析

基本监听验证通过后,回到办公网那台客户端上,执行一次真实的MySQL连接:

mysql -h 192.168.30.5 -P 3306 -u appuser -p

输入密码后能正常进入mysql>命令行,说明整条链路已经通了。但"能连上"还不够,我还需要确认连接确实经过了rinetd的转发,而不是跳板机上本来就开了什么奇怪的转发规则。最有说服力的方式是在跳板机的内网网卡上抓包。在跳板机上执行:

tcpdump -i eth1 -nn host 10.0.1.10 and port 3306

然后在客户端上重新执行一次数据库查询,比如SELECT 1。抓包结果里会看到跳板机的内网IP(10.0.1.5)向10.0.1.10:3306发起的新TCP连接,这就实证了rinetd在中间完成了搬运工作。

还有一个细节值得记录。登录MySQL后查一下进程列表:

SELECT id, user, host, db, command, time FROM information_schema.processlist;

此时你会看到连接的host字段显示的是10.0.1.5,而不是办公网的客户端IP。这是因为rinetd做了字节流搬运,MySQL看到的新连接来自转发机的内网IP。这个行为直接影响MySQL侧的权限设计:授权账号的主机范围应该写成转发机的IP,而不是客户端的原始网段。

4.4 加入systemd托管,避免重启后失联

我最初用命令行直接启动rinetd,后来跳板机因为内核补丁重启了一次,转发规则就没了,办公室同事立刻来问"数据库怎么连不上了"。从那以后我所有的rinetd都交给systemd管理。Unit文件放在/etc/systemd/system/rinetd.service:

[Unit] Description=rinetd tcp port forwarder After=network-online.target Wants=network-online.target [Service] ExecStart=/usr/sbin/rinetd -c /etc/rinetd.conf Restart=on-failure RestartSec=5s [Install] WantedBy=multi-user.target

启用并启动:

systemctl daemon-reload systemctl enable --now rinetd

After=network-online.target是为了确保网络已经完全起来后再监听端口。之后每次改配置,只需要:

systemctl restart rinetd

有了systemd之后,进程的退出重启、开机自启、日志查看都能用一套标准命令管理,比裸跑进程安心太多。

5. 生产环境里的安全加固与常见坑

5.1 最小化暴露:不要一上来就写0.0.0.0

我见过很多rinetd教程里给出这样的规则:

0.0.0.0 3306 10.0.1.10 3306

这行的效果是在所有网卡上监听3306,等于把内网MySQL暴露到了这台跳板机的所有网络接口上。如果跳板机有公网IP,这个规则会把数据库直接暴露到公网,风险极大。我的习惯是:源地址永远写成具体的跳板机内网或办公网IP,不给0.0.0.0留机会。

如果确实有多个不同网段的客户端要通过同一台跳板机访问,那就把每个入口IP都显式写出来,宁可规则多几行,也不要把整个监听面铺开。配合iptables再做一层源IP限制,连接来源能精确到主机。例如只允许192.168.30.50访问:

iptables -A INPUT -p tcp --dport 3306 -s 192.168.30.50 -j ACCEPT iptables -A INPUT -p tcp --dport 3306 -j DROP

这两条规则组合在一起就是白名单效果,其他来源一律拒绝。如果用的是firewalld,写成rich rule也是一样的逻辑:

firewall-cmd --permanent --add-rich-rule="rule family=ipv4 source address=192.168.30.50 port port=3306 protocol=tcp accept" firewall-cmd --reload

5.2 MySQL自身的bind-address设置对转发的影响

这应该是rinetd转发MySQL最隐蔽的一个坑。MySQL服务器如果配置了:

bind-address = 127.0.0.1

那么它只会监听回环地址,任何从外部发起的TCP连接都会被拒绝。即使你的rinetd规则完全正确、跳板机到MySQL的网络也通,客户端连接时依然会报错。检查方法很直接,在MySQL服务器上看监听状态:

ss -tlnp | grep 3306

如果是127.0.0.1:3306,就得把bind-address改成0.0.0.0或内网IP10.0.1.10。改成后者的好处是只在内网网卡上监听,比0.0.0.0更收敛。修改后重启MySQL,再确认监听状态。

在MySQL账号层面也要注意,授权语句里的host需要覆盖转发机的来源IP,也就是跳板机的内网IP。比如:

CREATE USER 'appuser'@'10.0.1.5' IDENTIFIED BY 'your_password'; GRANT SELECT, INSERT, UPDATE, DELETE ON yourdb.* TO 'appuser'@'10.0.1.5'; FLUSH PRIVILEGES;

如果账号当初只授权给了192.168.30.%,通过rinetd转发后反而会因为来源IP变为10.0.1.5而无法登录。这个因果很多人没想明白,以为转发成功了账号就一定能用,其实账号权限判断发生在MySQL收到连接之后,它看到的永远是从哪里来就算哪里。

5.3 MySQL连接慢的隐藏原因:反向DNS解析

转发链路通了之后,还可能遇到一个现象:连接能建立,但每次输入密码后要等几秒才进入命令行。这不是rinetd的转发慢,很可能是MySQL在握手阶段对客户端IP做反向DNS解析,而跳板机的内网IP没有对应的PTR记录,解析超时拖慢了连接建立。

解决方式有两种。如果确认业务不需要用主机名做权限判断,直接在MySQL配置文件里加上:

[mysqld] skip-name-resolve

然后重启MySQL。这会关掉所有基于域名的解析行为,连接速度立刻恢复。另一种方式是在内网DNS里把跳板机IP和主机名做好反向记录,但增加一套DNS记录的维护成本不太值得。我个人在跨网段转发场景下默认开启skip-name-resolve,因为账号授权全都用IP,不依赖主机名。

5.4 日志、并发连接数与进程守护

rinetd默认不输出访问日志,需要在配置文件里显式指定:

logfile /var/log/rinetd.log

日志会记录每条转发的连接来源和目标,配合防火墙白名单,能做出最基本的审计链路。日志文件会一直增长,需要在系统层配置logrotate防止撑爆磁盘。在/etc/logrotate.d/rinetd里写一份:

/var/log/rinetd.log { daily rotate 7 compress missingok notifempty }

并发连接方面,rinetd的连接处理模型不算复杂,实测一两百个并发MySQL连接完全扛得住。但要注意它没有空闲超时配置,如果一个客户端连上之后长时间不操作,连接会一直占用转发机的一个文件描述符。这种问题在"测试机连上就跑路忘记关闭"的场景特别常见,解决思路是让客户端侧设置连接超时,或者在跳板机上用systemd的ExecStartPre做一次端口占用检查,避免多实例互相抢端口。

6. 踩坑实录:三种典型故障的完整排查链路

6.1 规则写对了但端口就是没监听

有一次我改完配置文件重启rinetd,ss -tlnp | grep 3306却什么都看不到。第一反应是进程没起来,但ps -ef | grep rinetd显示进程确实在。后来用前台调试模式跑了一下:

rinetd -c /etc/rinetd.conf -d

终端立刻打印出错误:配置文件里某一行只有三个字段。回看那行,原来是在复制规则时丢了最后一个目标端口字段。这个教训的价值在于:力所能及的范围先不猜,直接前台跑,错误信息会告诉你答案。rinetd的错误提示虽然简洁,但足够直接。

6.2 转发通了但MySQL一直拒绝连接

还有一次规则和监听都正常,客户端telnet跳板机3306端口也是通的,但用mysql -h连接时返回Access denied。我查了一圈,最后发现MySQL的账号授权表里根本没有允许来自10.0.1.5的访问,而在跳板机上直接连MySQL服务器测试恰好用的是root账号,所以一直没暴露这个问题。这个坑在第5章已经讲过了,这里是它真实的复现现场。补充一句排查口诀:转发通了不代表数据库权限对了,把MySQL侧看到的来源IP列出来,再对着授权表逐条核对。

6.3 端口被占用的隐蔽场景

跳板机上如果本身装过MySQL或者其它监控服务,3306端口可能已经被占用。但启动rinetd时它不会报"端口被占用"这么直白的话,有时候是日志里出现bind失败提示,有时候是干脆静默退出。我在systemd托管后遇到的场景更隐蔽:一个旧版本的rinetd进程还活着,新的systemd实例启动时端口冲突失败,systemctl status rinetd显示的却是Active: failed。排查路径是这样:先用ss -tlnp | grep 3306看哪个进程占着端口,确认身份后再统一停掉旧进程,起新实例。

6.4 什么时候应该放弃rinetd改用更重的方案

最后说点实际的边界判断。rinetd的价值在于"轻",但轻的代价是它没有健康检查、没有负载均衡、没有高可用能力。如果出现下面几种情况,我建议直接换方案:

  • 后端MySQL有主从切换,希望客户端始终连到当前主库,那应该上HAProxy或者Nginx stream的upstream,配合端口检测做自动摘除。
  • 连接规模上千、单连接传输大量数据,跳板机CPU可能成为瓶颈,内核态方案或负载均衡设备更合适。
  • 需要多台跳板机承担同一个转发入口,实现故障转移,那单机rinetd满足不了。
  • 要转发的协议不止TCP,还涉及UDP或动态端口范围,那应该找专业工具而不是硬扩rinetd。

这个项目的大部分时间其实不是花在rinetd上,而是花在确认网络可达、MySQL权限匹配、防火墙放行这几件看似外围的事上。rinetd只是链路里最不起眼却最关键的一环——它负责把两个不通的网段悄悄连起来,而你负责的,是判断它什么时候该出现、什么时候该离场。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询