☰
rinetd轻量端口转发:从原理到实操打通跨网段访问内网MySQL
2026/10/1 17:40:28 网站建设 项目流程

做运维这些年,跨网段访问内网MySQL一直是个高频需求。处理这种问题,我脑子里第一个浮现的往往不是frp这类功能全家桶,而是那个只有几十KB、配置文件几行就能跑起来的rinetd。很多人听到"rinetd"第一反应是"这年头还有人用这个?"——说实话,在端口转发这件事上,它恰恰是最省心的选择。

这篇文章就围绕"rinetd + 跨网段 + 内网MySQL"这条线,完整记录我从选型、配置、启动到排障的整个实操过程。也把原理讲透,告诉大家为什么这个轻量方案在很多场景下比上SSH隧道、搬frp更实在。如果你手头正好有一台带公网IP的跳板机、需要在别的网段连内网数据库,这篇可以直接照着抄作业。

1. 先说清楚:跨网段访问MySQL到底难在哪

1.1 你面对的真实环境是什么样

跨网段访问数据库,最常见的场景有三类。第一类是公司内网办公网段和数据库网段做了隔离——比如办公机在192.168.10.0/24,数据库在192.168.5.0/24,中间隔着一堆交换机,ACL只放行了特定端口。第二类是你在家办公,要从家里访问公司机房的数据库,但机房入口只有一台跳板机。第三类是云上业务,两个VPC之间要做单向数据同步,结果安全组规则卡得死死的。

这些场景的共同点是:**内网MySQL实例本身没有公网入口,你不能直接拿mysql -h去连。**数据库通常只监听内网网卡,外部客户端根本摸不到它。很多人的第一反应是去改数据库的bind-address,把它改成0.0.0.0,然后让云安全组放开端口——这条路在隔离网络里往往走不通,因为你改完数据库,防火墙那层还是过不去,而且直接把数据库暴露到公网也够不安全。

1.2 主流方案横向对比,为什么最后选了rinetd

处理跨网段访问,我测过不少方案,简单说下各自表现。

方案部署成本内存占用转发能力典型问题
SSH隧道低,一条命令低单端口或动态转发要维护长连接,容易断,且跳板机得开SSH权限
frp中,服务端+客户端中等多端口、TCP/UDP都行配置项多,跨平台客户端的维护有点琐碎
ngrok中,依赖官方/自建服务中等HTTP/TCP隧道依赖外部服务,生产环境一般不会用它去连数据库
iptables DNAT低,一行规则极低内核态转发,性能最好要求目标主机回包路由可达,跨网段场景常走不通
rinetd极低,单文件+单配置极低TCP端口转发不支持UDP,不支持加密

iptables DNAT在纯内网路由互通时很香,但跨网段场景下回包路由经常成问题,数据包被DNAT过去了,源主机的回复又不知道怎么回来,很折腾。SSH隧道在临时用一下挺方便,但要做成常驻服务,稳定性就靠不住了——隧道一旦断掉,所有连接全部中断,没人盯着的话只能等报障。

最终选rinetd,理由很直接:**它是一个纯用户态的TCP端口转发小工具,不做隧道、不加密、不搞域名绑定,就是把一个端口的TCP流量原封不动接到另一个主机的端口上。**整个程序通常不到100KB,配置文件几行就够。不需要在客户端装agent,不需要对数据库做改动,也不需要改路由。对"跨网段访问一个MySQL端口"这种单一需求来说,它是匹配度最高的方案。

1.3 rinetd能做什么,不能做什么

先划清边界。rinetd能做的是:监听本机某个端口,把进入的TCP连接转发到指定的目标IP和端口,支持同时配很多条规则,可以指定允许连接的来源地址。它的性能在单机端口转发里是够用的,一个rinetd进程转发几千个并发连接没太大问题,毕竟就是read/write搬数据。

它不能做的是:**UDP转发。**如果你要用Syslog、DNS这类UDP协议,rinetd派不上用场。它也没有TLS终结能力,数据是明文搬的,所以它适合放在可信网络中做访问入口,而不是当作加密隧道去用。别拿rinetd和frp比,两者定位不同——frp是"把内网服务变成一个可达的隧道体系",rinetd是"把A口的数据搬到B口",简单粗暴,用完即走。

2. 核心机制:看懂那个极简配置文件

2.1 一行规则拆解:bindaddress、bindport、connectaddress、connectport

rinetd的配置逻辑极其直白,每行一条规则,格式是四个字段:

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

文本里也就是一行:

0.0.0.0 3306 192.168.5.88 3306

这条规则读起来就是:这台机器的任意网卡的3306端口收到TCP请求后,原样转发给192.168.5.88这台主机的3306端口。源地址写0.0.0.0表示监听本机所有IP,如果只想让特定网卡上的请求进来,就写那个网卡的地址。

确切地说,源地址还支持用CIDR形式限制允许访问的来源网段:

10.2.0.0/16 3306 192.168.5.88 3306

这样写的意思就是只有10.2.0.0/16这个网段的机器可以连接本机3306,其他来源一律拒绝。这个特性对安全控制非常有用,之后我会反复强调。

默认安装后,配置文件在/etc/rinetd.conf,日志默认写到/var/log/rinetd.log。很多手册没细说的一点是:配置里的注释用#,每行规则字段之间用空格或tab分隔都可以。修改完配置不用重启进程,执行rinetd -c /etc/rinetd.conf重新加载即可,这一点在调试多条规则时非常方便。

2.2 安装是小事,但别小看这两个细节

Linux发行版自带的仓库基本都有rinetd包。Ubuntu/Debian直接:

apt-get install rinetd

CentOS/RHEL如果默认仓库没有,需要先装EPEL:

yum install epel-release yum install rinetd

安装完先看版本:

rinetd --version

如果你需要较新版本,可以走源码编译。从GitHub拉取最新发布的源码:

wget https://github.com/samhocevar/rinetd/archive/refs/tags/v0.70.tar.gz tar xzf v0.70.tar.gz cd rinetd-0.70 make sudo make install

源码编译的成功率其实很高,依赖只有libc,不像其他软件要装一堆开发库。但日常使用我建议直接用系统包,原因是系统包自带init脚本或者可以直接配systemd,升级维护省心。

这里有个新手容易踩的坑:**rinetd绑定端口需要root权限。**如果配置里写的源端口小于1024,比如转发3306,必须以root用户启动。用systemd管理时,默认就是root跑,没什么问题。你要是手动用普通用户执行rinetd,它会直接报bind权限错误,别到时候一脸懵。

2.3 防火墙和内核转发:责任边界要分清

很多第一次用rinetd的人,会在跳板机上把net.ipv4.ip_forward打开,以为要开启内核转发才能转发端口。这个其实没必要。ip_forward是内核做IP路由转发时用的,比如当作路由器转发数据包。rinetd是用户态程序,它自己accept连接之后,再主动connect目标端口,然后把两边的数据流互相copy,整个过程根本不经过内核的IP转发逻辑。所以就算ip_forward=0,rinetd照样正常工作。

真正要盯的是防火墙。跳板机上如果有iptables或firewalld,需要把源端口放行。以firewalld为例:

firewall-cmd --permanent --add-port=3306/tcp firewall-cmd --reload

iptables的放行方式:

iptables -A INPUT -p tcp --dport 3306 -j ACCEPT

顺便说,如果跳板机部署了云安全组,云平台的安全组规则也得放行对应的入方向端口。三层关系要理清:**云安全组 → 本机防火墙 → rinetd进程,三层全部放行才能连上。**很多人排查半天,最后发现是云安全组没加规则,这个坑我踩过一次之后就长记性了。

3. 完整实操:把内网MySQL暴露给跨网段客户端

3.1 先画清楚拓扑

用一个真实例子来讲。假设场景如下:

  • 内网有一台MySQL服务器,IP是192.168.5.88,监听3306端口。
  • 有一台带公网IP的跳板机,公网IP为1.2.3.4,内网IP为192.168.5.99,它和内网MySQL是互通的。
  • 你人在办公网,办公机的IP是10.2.0.55,想直接用mysql -h 1.2.3.4 -P 3306 -u app_user -p连上内网那台MySQL。

目标就一句话:**让办公网这台机器能连上内网MySQL。**数据库不动,路由不动,只在跳板机上做一次端口转发。

3.2 安装并验证

先登录跳板机,用root执行安装。我这里演示CentOS 7环境,Debian系把包管理命令换成apt-get即可。

uname -a cat /etc/redhat-release yum -y install rinetd rinetd --version

这条版本命令如果正常输出版本号,安装就过关了。如果你发现仓库里找不到rinetd,那就去装EPEL源,执行上面说的epel-release。装完顺手确认一下二进制路径:

which rinetd

正常输出会是/usr/sbin/rinetd。

3.3 写配置:一行规则的完整过程

编辑配置文件:

vim /etc/rinetd.conf

我最终的配置内容如下:

# 将本机3306端口转发到内网MySQL 192.168.5.88:3306 0.0.0.0 3306 192.168.5.88 3306

如果只允许办公网段访问,就把源地址改成CIDR形式:

10.2.0.0/16 3306 192.168.5.88 3306

还要确认配置开头的日志路径设置。默认配置里有行logfile /var/log/rinetd.log,保留即可。有了日志后面排障会轻松很多。

保存退出后,需要让rinetd重新加载配置。最稳妥的做法是重启服务,或者直接执行:

systemctl restart rinetd

如果你用的是旧版系统没有systemd,就执行:

/etc/init.d/rinetd restart

查看进程是否起来:

ps -ef | grep rinetd netstat -ant | grep 3306

netstat能看到tcp 0.0.0.0:3306在监听,说明转发规则已经生效。

3.4 配置systemd实现开机自启

系统包装的rinetd通常会自带service文件。如果缺,或者你想彻底掌控启动参数,可以自己写一个。这个unit值得存一下:

[Unit] Description=rinetd TCP port forwarder After=network-online.target Wants=network-online.target [Service] Type=simple ExecStart=/usr/sbin/rinetd -c /etc/rinetd.conf Restart=always RestartSec=3 [Install] WantedBy=multi-user.target

保存到/etc/systemd/system/rinetd.service,然后:

systemctl daemon-reload systemctl enable rinetd systemctl start rinetd

启用开机自启后,还要检查一下日志目录的权限。rinetd默认以root身份写/var/log/rinetd.log,这个一般没问题。但如果你把logfile指定到别的位置,比如/var/log/rinetd/,就要确保目录存在且权限可写,否则进程起不来,这种小坑会让很多人排查半天。

3.5 客户端侧连库验证

回到办公网那台机器,先做端口连通性测试:

telnet 1.2.3.4 3306

如果通了,telnet窗口通常会显示MySQL的版本欢迎信息或者直接黑屏无报错,按Ctrl+]然后quit退出。这一步确认网络层面通了,接下来用真实客户端连:

mysql -h1.2.3.4 -P3306 -uapp_user -p

如果连上并能执行show databases;,事情就成了一大半。要确认流量确实走了rinetd转发,去跳板机上同时看连接和日志:

ss -ant | grep 3306 tail -f /var/log/rinetd.log

你会看到两条关键连接:一条是客户端IP连到跳板机3306,另一条是跳板机主动连到192.168.5.88的3306。看到这两条,就说明rinetd把数据搬起来了。

日志里通常会出现一行类似:

CONNECT 10.2.0.55 55555 192.168.5.88 3306

这行记录说明一条转发连接成功建立。日志默认记录新连接、正常关闭、拒绝事件。如果你看到大量连接记录但客户端说卡,就要看是不是目标MySQL性能慢,而不是转发有问题。

3.6 场景扩展:一个跳板转发多个MySQL实例

生产环境经常遇到不止一台MySQL要转发。比如有三套环境,分别是192.168.5.88、192.168.5.89、192.168.5.90,端口都是3306。你当然可以写三个规则都用3306,但那会冲突,因为跳板机只有一个3306端口。更稳妥的方案是外部用不同端口,比如33061、33062、33063,分别映射到三台内网主机的3306。

配置这样写:

0.0.0.0 33061 192.168.5.88 3306 0.0.0.0 33062 192.168.5.89 3306 0.0.0.0 33063 192.168.5.90 3306

客户端连接就是:

mysql -h1.2.3.4 -P33061 -uapp_user -p

这个思路在管理多个数据库实例的时候很好使,一组端口对应一个实例,不用维护一堆SSH隧道。有些团队甚至用rinetd对外暴露统一的3306端口,配合内网DNS做流量分发,效果也还不错。

4. 实战中的坑:连接失败、权限异常与安全问题

4.1 客户端telnet通,但mysql客户端报错

这是最经典的现象:telnet能通,说明rinetd端口在听,链路没问题。但mysql客户端直接报错或者卡住。原因通常是这几个方向:

检查MySQL授权。在MySQL服务端执行:

SELECT user, host FROM mysql.user WHERE user = 'app_user';

确认app_user的host里包含了跳板机的内网IP,也就是192.168.5.99。因为从MySQL的角度看,连接来源不是办公机的10.2.0.55,而是跳板机的192.168.5.99——rinetd转发后,源IP会被替换成跳板机IP,这个点99%的踩坑都是因为它。

如果授权缺失,在MySQL里补上:

CREATE USER 'app_user'@'192.168.5.99' IDENTIFIED BY 'your_password'; GRANT SELECT, INSERT, UPDATE, DELETE ON your_db.* TO 'app_user'@'192.168.5.99'; FLUSH PRIVILEGES;

另外检查MySQL的skip-networking是否被禁用。如果配置里有skip-networking,MySQL只监听本地socket,外部TCP连接一律拒绝,rinetd转发得再勤也没用。

4.2 连接被拒绝:bind地址和防火墙的博弈

telnet直接connection refused,多数情况是rinetd进程没监听端口或者防火墙拦了。先到跳板机上确认:

ss -ant | grep 3306

如果看到0.0.0.0:3306在LISTEN,那就查防火墙。如果什么都没有,说明rinetd没起来,看日志。

还有一种隐蔽情况是配置里的bind地址写成了127.0.0.1,结果外部机器根本访问不到。配置里写127.0.0.1和0.0.0.0的区别,可以类比成"只有小区内部能走的小门"和"临街大门"。Cross网段访问必须从外面进来,源地址写0.0.0.0是常态。

4.3 MySQL 8.0与客户端认证插件不一致

MySQL 8默认认证插件是caching_sha2_password,老版本的Navicat或MySQL客户端连上去会报认证错误。这个不算rinetd的问题,但既然走的是跨网段链路,报错往往让人误以为是转发问题。

在MySQL里查看用户插件:

SELECT user, host, plugin FROM mysql.user WHERE user = 'app_user';

如果plugin是caching_sha2_password,而客户端不支持,有两个办法。一是升级客户端驱动到支持新插件;二是把用户改回兼容插件:

ALTER USER 'app_user'@'192.168.5.99' IDENTIFIED WITH mysql_native_password BY 'your_password';

顺手说明下,mysql_native_password在新版本MySQL里属于兼容模式,生产环境要不要动它,需要你们DBA评估。但作为跨网段转发的排障点,值得留意。

4.4 rinetd进程异常退出与日志定位

进程挂掉是运维中必然会遇到的事。常见原因有三类:配置写错导致解析失败、端口被其他服务占用、日志目录不可写。判别方法都聚在日志里。

立刻查看:

journalctl -u rinetd --no-pager -n 50 tail -n 50 /var/log/rinetd.log

如果journal里没有任何输出,而系统日志也没记录,那很可能进程被OOM Killer干掉了。查一下:

dmesg | grep -i rinetd grep -i rinetd /var/log/messages

这种情况建议在systemd配置里加Restart=always,并把LimitNOFILE调大,应对大量短连接导致的文件描述符不足:

[Service] LimitNOFILE=65535 Restart=always

4.5 转发性能与大连接数问题

rinetd本质上就是在用户态搬数据,性能上限不算高,但应对MySQL这种OLTP场景完全够用。实测一个rinetd进程承载几百个并发连接,CPU占用也就个位数。需要注意的反而是单个客户端的长时间空闲连接,它会占住rinetd的一个进程线程。默认配置下,rinetd每个连接会fork一个子进程来处理(新版也有线程模式),大量空闲连接会把进程数顶上去。监控时留意一下:

ps -eLf | grep rinetd | wc -l

想控制空闲连接,可以在MySQL侧设置wait_timeout和interactive_timeout,让闲置连接自动断开,减轻rinetd压力。

4.6 安全加固:别让公网裸奔一个3306

最后说说安全。rinetd只做转发,没有任何认证能力,你把它暴露在公网,等于把自己的MySQL入口挂到网上。虽然流量最终由MySQL密码保护,但暴力破解和扫描的压力会非常大。我一般至少做三重加固:

第一,配置里限制源网段。把bind地址写成CIDR形式,只允许办公网段访问:

10.2.0.0/16 3306 192.168.5.88 3306

第二,配合iptables限制来源IP。就算rinetd配置先放行,防火墙层再罩一层:

iptables -A INPUT -p tcp --dport 3306 -s 10.2.0.0/16 -j ACCEPT iptables -A INPUT -p tcp --dport 3306 -j DROP

第三,不在公网暴露默认端口。把外部端口改成高位端口,比如33061,降低被扫描命中的概率。这些措施叠加起来,才比较放心。

最后再分享一个经验

用rinetd这几次最深的体会是,很多网络问题根本不是"不通",而是"走了两条不同的路"。比如你从办公网telnet跳板机通,但从客户端机器直连却不通,这时候就要想到NAT和路由的差异,别一头扎进rinetd配置里反复改。先把链路分段画出来,每段单独测,问题范围一下就缩小了。

还有一个小技巧收尾:如果跳板机上要临时看当前有哪些转发连接正在使用,不需要抓包,直接看/var/log/rinetd.log里的CONNECT记录,再配合ss -ant | grep 目标端口,基本能还原所有连接的全貌。排查效率会高很多,下次遇到"数据库卡了是不是转发问题"这种疑问,一分钟就能给出结论。

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

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

立即咨询