尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Linux服务器异常端口排查实战:从端口告警到定位Java进程

Linux服务器异常端口排查实战:从端口告警到定位Java进程 1. 一个只有数字编号的工单从端口告警说起值班手机在凌晨两点半震了两下报障信息只有一行“10.10.x.x 出现异常监听端口 22133进程未知请排查。”又是这种最让人头疼的情况——报警器只给了一个端口号没有上下文不知道是哪个业务、哪个团队起的服务甚至不知道它是刚出现还是已经跑了一周。更巧的是这个问题的工单编号恰好也是 22133于是这个数字在接下来三个小时里反复出现在屏幕上我想忘都忘不掉。先说结论22133 这个端口号本身没有任何特殊含义。它既不是 /etc/services 里登记过的知名端口也不是某些框架的默认端口纯粹是一个 1024 到 65535 之间的高位端口。在 Linux 服务器上这类端口通常意味着三种情况一是某个开发同学随手指定的业务服务端口二是一些中间件或客户端程序启动时动态绑定的端口三是被入侵或伪装成正常进程的异常程序。排查的难点不在端口本身而在“确认是什么进程在监听、它从哪里来、该不该存在”。这篇文章的受众是那些需要在 Linux 服务器上独立排查问题的运维、后端开发、SRE以及被临时拉去救火的人。我会按我自己实际处理的流程把从接到告警到闭环的完整思路拆开讲包括命令怎么选、输出怎么看、什么时候该恐慌、什么时候可以先睡一觉。这不是教程式的功能罗列而是记录了我会在真实环境里怎么做判断的全过程。2. 初次侦察定位监听端口的三大命令2.1 先用 ss 还是 netstat这是个习惯问题接到告警后的第一件事永远是先确认端口到底有没有在监听、监听在哪个 IP 上。我最常用的命令是ss -lntup而不是 netstat。不是因为它看起来高端而是因为在连接数多的业务服务器上ss的响应速度比 netstat 快得多且它直接读内核的 socket 信息不需要遍历 /proc 下每个进程的文件描述符。在端口数量几千上万的生产环境里netstat 偶尔会卡上几秒甚至十几秒而 ss 基本是瞬间出结果。让我还原一下当时的现场。登录服务器后第一时间执行ss -lntup | grep 22133输出长这样tcp LISTEN 0 128 0.0.0.0:22133 0.0.0.0:* users:((java,pid18666,fd101))这行信息已经告诉我们三件事这是一个 TCP 服务监听地址是0.0.0.0意味着它对所有网卡生效占用进程是 javaPID 是 18666。看到 java 两个字心里先松了一口气——至少大概率是某个 Java 应用而不是常见的挖矿程序或恶意脚本。挖矿程序通常用 Python 或编译过的二进制木马伪装直接看到 java 进程基本可以先排除最坏情况。但这里有一个新手很容易踩的坑如果直接在ss后面加| grep 22133有时会漏掉信息因为进程名和端口不在一行。遇到监听数量极多的情况我更习惯先执行ss -lntup把结果存到文件里再 grep或者干脆先按端口排序防止由于输出截断导致误判。另外别忘了 UDP 端口有些服务用 UDP 做心跳ss -lnup也要看一眼。排查阶段不要只盯着 TCP。2.2 lsof 的魔法以及权限的坑如果 ss 的输出里进程信息为空白比如只有 IP 和端口没有users:((xxx,pidyyyy))这一段那就得请出 lsoflsof -i :22133正常会输出 COMMAND、PID、USER、FD、TYPE、DEVICE、SIZE/NODE、NAME 这些列。比如java 18666 root 101u IPv6 123456789 0t0 TCP *:22133 (LISTEN)有些同学会遇到奇怪的现象自己的普通账号执行ss -lntup能看到端口但看不到 PID换成lsof -i :22133却提示 Permission denied。这通常不是因为权限不够导致 ss 看不到进程而是 ss 默认也会尝试读取进程信息只是失败时静默忽略。解决方式是加 sudo或者用ss -lntup -p配合sudo执行。别小看这一条很多人排查到这一步卡住以为端口被内核或诡异的进程占用实际上只是权限不够。另一个细节lsof -i :22133只精确匹配端口号 22133但如果你机器上同时监听了 IPv4 和 IPv6 的同一个端口输出会显示两行PID 通常是同一个。这时不用慌这是同一个 socket 的 dual-stack 监听不是两个进程。3. 顺着 PID 挖进程画像3.1 定位可执行文件路径与启动参数端口查到了PID 是 18666进程是 java接下来要回答的问题变成了三个这个 java 是从哪个目录启动的、启动参数是什么、谁启动了它。ss已经回答不了这些得往 /proc 里看。第一个命令是检查这个进程的可执行文件路径sudo ls -l /proc/18666/exe/proc 下每个进程目录里的 exe 是一个符号链接指向这个进程实际运行的可执行文件。输出像这样lrwxrwxrwx 1 root root 0 Jul 29 02:31 /proc/18666/exe - /usr/local/java/bin/java这只告诉我们用的是哪个 JDK还回答不了“跑的是哪个应用”。真正能定位应用的是命令行参数sudo cat /proc/18666/cmdline | tr \0 这里有个细节cmdline 文件里的参数之间是用\0分隔的直接 cat 会看到一串粘连的字符所以要用tr把空字符转成空格否则可读性很差。我见过不少新手 cat 完之后一脸懵以为进程没有参数。输出大概率是/usr/local/java/bin/java -Xms512m -Xmx2g -jar /data/app/api-gateway-2.3.1.jar --server.port22133 --spring.profiles.activeprod看到--server.port22133时基本就明白了这是一个 Spring Boot 应用端口是显式指定的。这里就能得出一个判断如果这个应用本身是有备案的、属于公司的服务目录那就不是异常进程而是告警系统没有把它纳入白名单。反之如果生产环境里压根没人部署过这个 jar那就要进入真正的安全排查流程了。3.2 工作目录、启动时间与运行身份拿到 cmdline 之后我会顺手再看另外三样东西进程的工作目录、启动时间、运行用户。sudo ls -ld /proc/18666/cwd sudo ps -p 18666 -o pid,lstart,user,group,etime,pcpu,pmem工作目录cwd非常有用因为它常常暴露应用是从哪个目录拉起脚本的。很多运维事故排查到最后发现是有人手动在 /tmp 下解压了一个 Java 包然后直接nohup java -jar xxx.jar 启动导致应用的工作目录是 /tmp。如果发现生产服务器的进程 cwd 指向 /tmp、/var/tmp、/dev/shm 这种目录那就要高度警惕了因为正常业务部署不会把可执行目录放在这种临时目录里。启动时间和运行时长也要记录。假设这个进程是三个月前启动的说明服务已经稳定运行了很长时间大概率是备份的旧业务或测试遗留环境如果启动时间恰好是告警前五分钟那就要思考是谁刚刚部署了它。结合ps信息里的 CPU 和内存占用也可以做初步判断长时间高 CPU 的 java 进程在业务上可能是计算密集型任务但配合未知端口和陌生 jar也可能是在跑某些高强度计算脚本。3.3 分析启动方式与父进程最后一个关键线索是父进程也就是这个 java 进程是被谁拉起来的。使用sudo cat /proc/18666/status | grep PPid或者直接ps -o pid,ppid,cmd -p 18666如果 PPID 是 1说明它已经被 init/systemd 收养可能是守护进程或启动脚本在 fork 之后父进程退出了如果 PPID 是某个 bash那说明大概率是有人在终端里敲了启动命令退出去早的执行者也许就是上次发布时手工启动的人。这种情况下我会顺着 PPID 继续往下追甚至看看那个 bash 进程的命令行历史。结合 systemd 的话则是另一个思路可以直接用sudo systemctl status 18666不过 systemctl status 后面跟 PID 不一定能查出服务名更可靠的是查看进程的命令行然后反向搜 systemd 单元文件sudo grep -r 22133 /etc/systemd/system/ /usr/lib/systemd/system/如果能搜到某个 service 文件说明是正经部署的服务告警大概率只是白名单缺失的问题。搜不到并且 PPID 是 1那就是裸进程要重点排查启动脚本来源。4. 网络行为深挖与业务还原4.1 监听地址和连接状态判断暴露面定位完进程本身接下来要从网络层看一看这个端口到底是怎么暴露的。这里要区分几个不同的“暴露”层次0.0.0.0:22133、127.0.0.1:22133、公网IP:22133三者含义不同。从刚才 ss 的输出看22133 监听在0.0.0.0也就是所有网卡上的请求都会到达这个服务。这意味着只要机器有网卡暴露在外网这个服务就可能被外部扫描到。进一步确认实际连接情况看当前有哪些客户端连了它ss -tnp | grep 22133 | grep ESTAB这一步可以确认是否有活跃的外部连接。如果端口虽然监听但长时间没有 ESTAB 连接说明这是一个临时服务或者被大家遗忘了如果存在大量来自陌生 IP 的 ESTAB 连接就要警觉了。用来判断对端来源我通常会再查一下对端 IP 的归属用 IP 归属库查或者直接whois。这个方法在生产环境比较实用一个内部服务如果对端 IP 全部是公司内网地址那还算合理如果出现了大量住宅宽带 IP那大概率是被外部扫描工具盯上了。4.2 外联行为与访问关系除了被动接收连接之外还应该检查这个进程是否有主动外联行为。方法是ss -tnp | grep 18666注意这里 grep 不写端口而是直接按 PID 筛进程的所有连接不管它连到 22133 还是连接到其他端口。Java 应用启动后通常会有一些必要的注册中心连接、数据库连接、日志上报等这些外联看着眼熟就行。如果发现这个进程主动连了某些非常规端口比如 4444、5555、8088 这类可疑端口那就要结合应用代码或日志一起判断了。一个非常有用的排查维度是看 DNS有的恶意程序会定期向某些域名发起解析请求观察服务器的 DNS 日志或通过tcpdump抓包可以看到这个进程有没有尝试连接可疑域名。4.3 结合日志、配置还原业务真相网络行为分析得再细最终还是要落到“这到底是不是一个正经业务”上。到了这一步我会把排查路径切换到业务视角查看应用日志、配置文件、服务注册信息。如果这个 Java 进程对应一个 Spring Boot 应用第一件事是去它的 jar 包里看配置文件unzip -p /data/app/api-gateway-2.3.1.jar BOOT-INF/classes/application.yml | grep -A 8 port只要是打包规范的 Spring Boot 应用配置文件一定在 BOOT-INF/classes 下面通过unzip -p可以不经解压直接输出内容不需要把 jar 复制出来解压一遍。这样就能看到端口、数据库连接、第三方服务地址等关键配置。看一眼配置里的数据库地址对照是否属于公司的数据库网段就能快速确认身份。同时如果应用连接了注册中心比如 Nacos、Eureka去注册中心控制台搜这个 IP 加端口能直接查到服务名和所属团队。这一步实在太重要了。我处理过的很多“神秘端口”案例最后都在注册中心里找到了答案——某个兄弟团队新上了一个服务但是忘了同步给告警平台。5. 处置决策与安全加固清单5.1 三种情况对应的处置路径到了这个阶段我已经拿到了完整信息端口 22133、PID 18666、进程 java、启动路径 /data/app、启动参数、连通记录、配置文件。接下来要做的决策看似简单其实是整个排查过程中最容易出错的地方——要不要停这个进程这不能拍脑袋决定要根据证据分类处理。第一种情况进程是注册在案的正规业务服务只是告警平台没收录端口。这种最简单处置方式是把端口加入告警白名单然后通知业务方确认是否需要更新告警规则。相应地我也要检查安全组和防火墙策略是否只允许特定来源访问。这类情况常见的毛病是防火墙规则留得太宽线上直接暴露到任何 IP 都能访问哪怕业务本身正常暴露面过大也是隐患。第二种情况进程不是正规部署的但能确认属于某个内部团队可能是测试机残留、临时起的手工服务、或某人为了调试起的端口。这种我会先保留进程通过网络联系到团队负责人确认是否可以停。注意不要自己直接 kill。因为你不知道这个进程背后是不是有定时任务在拉起来也不知道它是否被当作某个未公开的依赖。如果贸然 kill 并且后续没人管可能会引发其他诡异故障。第三种情况经过排查确认这是一个完全未知的进程不在任何团队的服务目录中且行为可疑比如 cwd 在 /tmp、主动外联陌生地址、配置文件指向不存在的路径。这时要做的是先保留现场做进程内存转储和二进制备份再停进程然后查 logs、查定时任务、查 ssh 登录记录。如果确认恶意按应急响应流程做隔离处理包括断网、快照、保存证据。5.2 加固清单从端口到服务全链路无论处置方式是什么只要是暴露在公网或半公网网段的服务端口我都会顺手做一轮最小化加固。进口的第一步永远是云安全组或者本机防火墙策略sudo firewall-cmd --permanent --add-rich-rulerule familyipv4 source address10.0.0.0/8 port port22133 protocoltcp accept sudo firewall-cmd --reload如果这是内网服务只允许内网源 IP 段访问如果它有独立的管理后台再单独加一个堡垒机网段的白名单。不要为了方便就放行0.0.0.0/0这是生产环境的大忌。即使业务方说“没事我们靠应用层鉴权”我也建议至少限到服务所在机房网段毕竟端口本身就是攻击面的一部分。此外还有一个容易被忽略的点确认服务有没有做监听拆分。一些业务其实只需要本机访问或通过网关转发没必要监听 0.0.0.0。Spring Boot 可以通过server.address127.0.0.1或server.address内网IP强制绑定到指定网卡。很多企业内部服务默认监听0.0.0.0这不是需求而是启动配置没细看。这类问题在排查“问题端口”时很容易暴露出来我会顺便把配置改掉降低暴露面。5.3 从告警到预防让下次排查不再重演排查完这次真正重要的是把防再次发生的手段落地。不要觉得“报过一次警处理完了就完了”这类问题如果不做收尾下个月还会再出来。我的收尾动作是把当前发现的端口、进程、启动方式、依赖关系、所属团队整理成一个服务登记表。如果公司已有配置管理库或 CMDB就把信息补录进去如果没有至少在一个共享表格里记录。告警平台那边把已经确认合法的端口加进白名单同时把可疑端口设为高优告警策略。这个动作看起来笨但能极大减少后续值班同事的重复工作量——因为导致这次排查耗费时间的根源不是端口自己出现了而是谁也没法立刻确认这个端口是什么。另外要把启动进程的提交者找到。如果是从部署平台拉起的一般会在平台记录里留下操作日志如果是手工启动的就查~/.bash_history和 sudo 日志找到是谁在什么时候启动的。这步不为了追责而是为了还原如果你能找到部署者就能让他告诉你应用是做什么的比任何痕迹分析都高效。6. 常用命令速查与排坑实录6.1 端口明明有服务却查不到进程这个情况很常见尤其是使用容器或者 Kubernetes 时。在宿主机上用 ss 只能看到 docker-proxy 或 kube-proxy 进程监听在端口上真正的业务进程在容器的网络命名空间里。如果ss -lntup显示的用户是docker-proxy或者 PID 是某个kube-proxy直接进容器里再看一遍端口占用docker ps | grep 容器名 docker exec -it 容器名 /bin/bash ss -lntup | grep 22133同样的道理也适用于排查 Java 之外的语言写的服务比如 Nginx 反代到后端、Keepalived 的 VIP 转发等情况。在宿主机层面看到的端口不一定直接对应业务进程。6.2 PID 一直在变抓不到元凶有些进程带守护机制比如脚本里写了 while 循环进程挂了就拉起新的PID 每几秒就变一次。遇到这种情况不要追着 PID 跑换一个思路抓进程的启动路径或者启动命令。可以用pgrep -f按完整命令行匹配pgrep -af 22133这样即使 PID 换了也能不断把新 PID 拉出来。然后再看它的父进程是不是一个supervisord或者systemd托管的脚本。如果是脚本守护循环去找脚本文件把脚本里的启动逻辑和轮询间隔看清楚。但要注意看脚本别贸然删除先备份再处理。6.3 老版本的 CentOS 命令兼容问题现在很多新机器默认没有 netstat因为 iproute2 已经覆盖了它的功能。但老一点的脚本或者同事的肌肉记忆还是习惯用 netstat遇到command not found不用慌yum install -y net-tools更推荐直接学 ss因为它不需要额外安装而且输出结构更适合脚本处理。另有一些发行版里lsof默认也没装需要手动补装。排查开始前最好确认一下机器上有哪些工具没有就先装上免得做到一半卡壳。这三大命令——ss、lsof、/proc 文件系统基本可以覆盖 90% 的端口排查场景。剩下的部分则是靠经验做判断端口本身不是问题问题永远在于这个端口背后的进程、路径、行为是否属于业务本身。我处理过不少类似的事故最深刻的体会是排查流程本身要像剥洋葱一样先从网络层看到端口再从进程层看到命令再从文件层看到配置和日志最后落到人身上。把每一步的证据链串起来才能在这个只有编号的告警里还原出完整的业务真相。
返回列表