
做运维这些年跨网段访问内网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、connectportrinetd的配置逻辑极其直白每行一条规则格式是四个字段[源地址] [源端口] [目标地址] [目标端口]文本里也就是一行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 rinetdCentOS/RHEL如果默认仓库没有需要先装EPELyum 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_forward0rinetd照样正常工作。真正要盯的是防火墙。跳板机上如果有iptables或firewalld需要把源端口放行。以firewalld为例firewall-cmd --permanent --add-port3306/tcp firewall-cmd --reloadiptables的放行方式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 3306netstat能看到tcp 0.0.0.0:3306在监听说明转发规则已经生效。3.4 配置systemd实现开机自启系统包装的rinetd通常会自带service文件。如果缺或者你想彻底掌控启动参数可以自己写一个。这个unit值得存一下[Unit] Descriptionrinetd TCP port forwarder Afternetwork-online.target Wantsnetwork-online.target [Service] Typesimple ExecStart/usr/sbin/rinetd -c /etc/rinetd.conf Restartalways RestartSec3 [Install] WantedBymulti-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_user192.168.5.99 IDENTIFIED BY your_password; GRANT SELECT, INSERT, UPDATE, DELETE ON your_db.* TO app_user192.168.5.99; FLUSH PRIVILEGES;另外检查MySQL的skip-networking是否被禁用。如果配置里有skip-networkingMySQL只监听本地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_user192.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配置里加Restartalways并把LimitNOFILE调大应对大量短连接导致的文件描述符不足[Service] LimitNOFILE65535 Restartalways4.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 目标端口基本能还原所有连接的全貌。排查效率会高很多下次遇到数据库卡了是不是转发问题这种疑问一分钟就能给出结论。