ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Ubuntu 16.04服务器安全加固:彻底禁用scp、sftp与WinSCP传输通道

Ubuntu 16.04服务器安全加固:彻底禁用scp、sftp与WinSCP传输通道 1. 一台老服务器为什么要同时封住scp、sftp和winscp先说背景。我近半年一直在折腾一批Ubuntu 16.04老机器原因是公司有些内部系统当年就部署在这些机器上业务还在跑但系统本身已经过了生命周期。安全团队做合规检查的时候给了整改清单里面有一条就是“关闭不必要的文件传输通道禁止scp、sftp以及Winscp访问”。刚开始我挺不理解scp和sftp不是基于SSH的安全协议吗为什么还要禁后来才明白审计关注的是风险面收缩只要机器还开着文件传输能力任何拿到合法账号的人都能快速把敏感文件拷走攻击活动也特别喜欢利用这类通道批量下载数据。运维层面可以记录日志但日志只是事后追溯更稳妥的做法是从协议层直接封死非必要通道只保留最小可用的管理入口。所以这篇不是教你怎么用scp或Winscp连sftp而是反过来——在一台Ubuntu 16.04服务器上把scp、sftp和Winscp的访问路径逐层封堵干净。适合的对象是需要做安全加固的系统管理员、负责老系统运维的同学、以及准备修改sshd_config但想先把后果搞清楚的人。1.1 先理清这三个名字背后的依赖关系经常有人问“怎么禁用Winscp”这个问法本身就有问题。Winscp是客户端软件跑在Windows上你不可能在Linux服务器上把它“禁用”。你能做的是让Winscp可用的所有协议路径在服务端全部失效。所以在动手之前必须把依赖关系画清楚。项目SSH终端SCPSFTPWinscp依赖服务sshdsshdsshd客户端工具默认端口222222连接服务端开放端口协议本质远程交互shell基于ssh的远程文件复制SSH子系统基于SFTP/SCP/FTP的GUI客户端关键配置项sshd_config全局ForceCommand / scp二进制Subsystem sftp无服务端配置能否单独禁用不能除非停ssh可以可以只能从协议侧封堵从这张表能看出来封禁的核心对象是sshd提供的能力也就是SFTP子系统和SCP命令通道。Winscp只是在这两条通道上行驶的车路封了车自然进不来。另外还要注意一个点Ubuntu 16.04默认安装openssh-server之后22端口同时承载了SSH终端、SCP、SFTP三种流量。它们共用端口、共用认证体系、共用同一个sshd进程。这意味着你做任何封禁动作时都要反复确认自己没有把SSH终端登录一并误伤。我见过不少教程让你“把sshd_config里所有Match块删掉”或“直接禁用root登录”结果操作者把自己也关在外面的情况。1.2 封禁前必须确定的两件事对象与范围第一件事想清楚要封谁。如果是整台机器彻底关闭scp和sftp能力只需要动sshd_config全部用户一刀切。如果只是某些用户不能传输文件、其他人不受影响那就要用Match User或Match Group条件块按用户维度分别下发策略。这两种需求在配置上差别很大后面我会分开讲。第二件事想清楚是否需要保留管理通道。改sshd_config有一个非常容易忽略的副作用如果写错了你会把正在远程登录的自己踢下线。Ubuntu 16.04的sshd默认监听22端口所有SSH会话都在同一棵树上原则上推荐生产服务器至少保留一个本机控制台或带外管理口。哪怕没有也一定要保证配置语法检查通过后再重启服务。明确这两点之后就可以动手了。下文先从最直观的SFTP切口讲起。2. 禁用SFTP从Subsystem配置动手最直接2.1 认识sshd_config里的Subsystem sftp行Ubuntu 16.04安装openssh-server之后默认配置文件/etc/ssh/sshd_config里会有一行Subsystem sftp /usr/lib/openssh/sftp-server这一行的意思是当客户端发起sftp请求时sshd启动/usr/lib/openssh/sftp-server这个外部程序来处理文件传输。换句话说sftp不是一个独立的监听服务它挂在sshd下面通过“子系统Subsystem”机制对外提供服务。在Ubuntu 16.04对应的OpenSSH 7.2p2中默认还支持internal-sftp模式。internal-sftp是内置在sshd进程内部的SFTP实现不依赖外部程序好处是可以用ChrootDirectory把用户关在指定目录里这在给合作方开临时账号时非常实用。不过今天我们的主题是“封”所以重点要搞清楚默认配置到底怎么改。确认当前配置用这个命令sudo grep -n Subsystem /etc/ssh/sshd_config如果看到上面那行Subsystem sftp说明sftp功能处于开启状态。如果机器是精简安装的openssh-server有时连这行都没有那说明sftp本来就不可用不需要处理。2.2 完全封禁SFTP注释Subsystem行之后会怎样最直接的封禁方法是把这一行注释掉sudo cp /etc/ssh/sshd_config /etc/ssh/sshd_config.bak.20260116 sudo sed -i s/^Subsystem sftp/#Subsystem sftp/ /etc/ssh/sshd_config sudo sshd -t sudo service ssh restart注意Ubuntu 16.04上的服务名是ssh而不是sshd。虽然内部二进制是sshd但service命令注册名是ssh。如果写惯了CentOS的systemctl restart sshd在这台机器上会报Unit sshd.service not found。注释掉这行之后sftp子系统不再被加载。客户端再用sftp连接时典型报错是subsystem request failed on channel 0 Couldnt read packet: Connection reset by peer整个过程看起来很快客户端会觉得“连上了又立刻断开”。很多新手第一反应是查网络、查防火墙其实问题就出在服务端不提供sftp这个子系统。这里要补充一个关键认知注释Subsystem只影响sftp不影响SSH终端登录也不影响scp。很多人改了这行之后发现scp还能用就以为封禁失败其实只是没搞清楚scp那套工作机制。scp的问题留到第三章单独讲。如果想做到更彻底也可以直接把整行删除而不是注释。效果几乎一样。删除的好处是配置干净坏处是以后想恢复还要重新写一遍。我自己的习惯是保留注释状态这样一眼就能看出这里是“故意关掉”而不是“配置缺失”。2.3 用Match块实现“只对部分用户禁用SFTP”如果不想全国一刀切只想让个别账号不能用sftp、其他账号照常那就别动全局的Subsystem行直接加Match条件块。例如给临时用户temp_upload设置成只能使用internal-sftp、不能普通SSH登录、不能跳出指定目录Subsystem sftp /usr/lib/openssh/sftp-server Match User temp_upload ForceCommand internal-sftp ChrootDirectory /srv/upload/%u PasswordAuthentication yes X11Forwarding no AllowTcpForwarding no这段配置的意思是temp_upload这个用户即使尝试建立普通SSH会话也会被ForceCommand强行指定为internal-sftp只能执行SFTP操作再加上ChrootDirectory直接把他关进/srv/upload/temp_upload目录里目录外什么都看不到。但要注意这种玩法是“把用户限制成只能sftp”而不是“禁止该用户sftp”。想反过来限制某个用户只能用SSH、不能用sftp在OpenSSH 7.2里没有一条配置叫“禁sftp”最常见的做法是把该用户加入一个组在Match Group块里处理或者干脆用防火墙按IP来限制。这一点必须提前想清楚别配完才发现方向反了。还有种特殊情况有些机器把Subsystem sftp写成了internal-sftp形式Subsystem sftp internal-sftp这个配置下sftp处理由sshd主进程内部接管不再启动外部程序。它的好处是配合ChrootDirectory时更稳定但这个细节对封禁没有影响——不管是外部sftp-server还是internal-sftp都属于sftp子系统一样可以注释。3. 禁用SCP比改两行配置麻烦一点3.1 为什么Ubuntu 16.04的scp不是独立服务这是整个封禁操作里最让人困惑的地方。我们用scp命令的时候总觉得它是一个“程序在跑”本地敲scp远端应该有个scp进程接收。这个直觉对一半。在OpenSSH 7.2p2Ubuntu 16.04默认版本里scp协议的工作方式是这样的本地scp客户端通过SSH连接到远端在远端执行一个scp命令进程这个进程负责读取文件内容并通过标准输入输出传回。整个过程没有经过任何独立的“scp子系统”它只是SSH会话里的一条命令执行通道。这意味着你哪怕把Subsystem sftp注释掉scp该传还是能传因为scp根本不走sftp子系统。要从服务端禁止scp思路就得从“禁协议”转向“禁命令”和“限制会话行为”。这里也顺便提一个版本差异OpenSSH 9.0之后scp默认改用SFTP协议实现文件传输行为发生了变化。但Ubuntu 16.04停留在7.2p2属于传统scp模式。如果你在查资料时看到别人说“禁用scp就是禁用sftp”要留意他用的OpenSSH版本是否比你新。版本不同结论直接不通用。3.2 用ForceCommand限制远程命令执行最常用的做法是在sshd_config里针对需要限制的用户或组强制指定会话命令。比如让某个用户连上来之后只能执行internal-sftp其他命令一概不响应Match User temp_upload ForceCommand internal-sftp这段配置的效果是该用户用scp试图连接时服务端本来要执行用户请求的scp命令但被ForceCommand拦下强制替换成internal-sftp。结果就是scp在认证完成后立刻失败客户端报错多半是scp: Connection closed同时这个用户也无法通过SSH终端执行普通命令因为他能执行的只有internal-sftp。这个方案非常适合“临时账号只允许传文件”的场景也是很多公司给合作方开账号的标准做法。如果你觉得某个用户组的成员都应该受到限制就把Match User换成Match Group。另外ForceCommand的值也可以是自定义脚本Match User uploadbot ForceCommand /usr/local/bin/restrict-upload.sh脚本里可以做更复杂的判断比如只允许从固定来源IP上传、只允许向指定目录写入。这种写法比单纯禁止scp灵活得多代价是要自己维护脚本逻辑而且脚本的所有输出必须小心处理——一旦脚本往标准输出写了任何非协议内容反而会引发sftp协议解析错误。这个坑我后面细细说。3.3 把scp可执行文件“收起来”的做法与代价还有一种看似粗暴但实践里很常见的做法直接处理/usr/bin/scp这个二进制。sudo chmod 000 /usr/bin/scp或者sudo mv /usr/bin/scp /usr/bin/scp.bak这样做的好处是简单直观本机用户再敲scp时要么提示权限不足要么找不到命令。但它的前提是你只是想让“这台机器上的人不能主动对外scp”。如果你想禁止“外部机器scp连到这台服务器”只删本机二进制是没有用的——因为服务端接收scp请求靠的是sshd并不是服务器上的/usr/bin/scp。真正的全面封禁必须走服务端配置ForceCommand、Match块配合而不是只动客户端二进制。很多文章把这两个概念混在一起导致读者一顿操作猛如虎最后发现远端照样能连。我自己的习惯是对老机器做整改时服务端配置和客户端二进制都处理。服务端封堵远端连接的可能客户端把scp收起来防止本机用户顺手就拷贝数据出去。双管齐下互相补漏。3.4 一个补充rssh和scponly这类受限shellUbuntu 16.04的软件源里有rssh和scponly两个包专门把用户的登录shell限制在文件传输范围内。装好之后把用户的登录shell换成/usr/bin/rssh或/usr/bin/scponly用户就只能在允许的功能里操作比如只能rsync、只能sftp、只能scp其他一律拒绝。配置rssh需要编辑/etc/rssh.conf放开对应功能user temp_upload:011:00011:/srv/uploads allowscp allowsftp这个方案的好处是天然防止用户误入shell执行命令对于“给不信任的人开一个定向传输账号”非常合适。但说实话如果只是要“禁用”rssh有点偏重还要额外安装和配置包增加维护面。我当时选择的是sshd_config原生方案因为整改要求里强调的是“不要额外依赖”配置尽量少、影响面尽量小。如果你以后要管的是大量共享账号倒是可以考虑rssh这类工具。4. 封堵Winscp服务端协议全关之后还差网络层最后一环4.1 Winscp连接模式拆解SFTP、SCP、FTP三条路径Winscp作为Windows下最常用的图形化文件传输工具它的会话类型下拉框里有SFTP、SCP、FTP三个选项。很多人被“禁用Winscp”这个说法带偏以为服务器上装个Winscp服务端再关掉就行——真没有这种东西。Winscp只是客户端它连上服务器后服务端看到的是sftp子系统请求或scp命令请求跟Linux下用命令行连过来的效果完全一样。所以封堵Winscp的逻辑就变成把Winscp可能用到的三条路径全部在服务端关掉。路径一SFTP协议。对应配置是sshd_config里的Subsystem sftp按第二章的方法处理。 路径二SCP协议。对应配置是ForceCommand和scp命令通道按第三章的方法处理。 路径三FTP协议。这一条不依赖sshd而是依赖vsftpd或proftpd这类独立FTP服务。Winscp同样支持普通FTP和FTPS如果服务器恰好装vsftpd且还在运行光堵22端口是堵不住这条路的。这里就出现了一个很容易让人上头的疏漏管理员把Subsystem sftp注释了也把ForceCommand配好了结果发现Winscp还能连一查原来是服务器上跑着vsftpdWinscp自动切到FTP模式登进来了。所以在封Winscp之前先检查服务器上还有没有其他文件服务在监听端口。4.2 先扫清残留文件服务别给Winscp留后门扫端口和进程用sudo netstat -tlnp重点看21、22这些端口。21端口是经典FTP如果出现vsftpd或proftpd监听就要特别小心。另外80/443也可能被Web服务占用但也要留意有没有开WebDAV模块比如Apache的mod_dav。Winscp支持WebDAV协议如果有人把Web服务器配了WebDAV那也能成为文件传输通道。发现vsftpd在运行直接停掉sudo service vsftpd stop sudo systemctl disable vsftpdUbuntu 16.04上如果用的是SysV风格的启动脚本systemctl disable有时会提示找不到service文件。这种情况用update-rc.d -f vsftpd remove来移除开机启动更稳妥。这里有个值得注意的老系统特性16.04是upstart/SysV和systemd混用的时期不同机器启用的初始化系统可能不一样别一上来就按印象敲systemctl先看服务由谁托管。4.3 用防火墙限制22端口访问来源协议层面全关之后还有一层防护可以加用iptables或ufw限制22端口的来源。例如只允许公司固定IP段访问sudo ufw allow from 192.168.10.0/24 to any port 22 proto tcp sudo ufw deny 22/tcp注意deny要放在allow后面或者直接编辑/etc/ufw/ufw.rules调整顺序。ufw的规则是按文件里的排列从上到下匹配先允许后拒绝。如果用iptables同理。设置完防火墙Winscp从非白名单IP尝试连接时在客户端看到的就是“连接超时”而不是“协议错误”。这反而有个好处暴露面变小日志里不会出现一堆协议协商失败的噪音。但我强烈建议先做协议层封禁并验收再动防火墙。否则同时改太多变量一旦出差错很难判断是配置写错还是网络策略拦截。我把这个顺序当铁律执行每次都能快速定位问题。4.4 配合审计手段观察连接尝试最后别忘了看日志。Ubuntu 16.04的SSH登录记录在/var/log/auth.logsftp和scp连接尝试也会留下痕迹sudo tail -f /var/log/auth.log | grep -E sftp|scp|subsystem封禁之后正常的sftp连接会留下sbsubsystem request for sftp failed这类记录。Winscp如果尝试走sftp同样会被挡下来。如果发现仍有大量认证成功后的异常连接那大概率是某种隧道或端口转发这已经超出文件传输范畴级别上属于更严重的问题需要单独调查。5. 验证封禁效果时踩过的那些坑5.1 本机模拟验证sftp、scp分别应该报什么错配置改完最重要的就是验证。不要等Winscp用户来反馈才知道结果你自己先在服务器上模拟一遍。验证sftpsftp rootlocalhost如果Subsystem被注释会看到类似rootlocalhosts password: subsystem request failed on channel 0 Couldnt read packet: Connection reset by peer Connection closed这个结果就是封禁成功的标志SSH认证本身通过但sftp子系统的请求被服务端拒绝。这里要理解系统提示“password”是正常的因为用户认证还是走SSH别以为输完密码后断线就是密码不对。验证scpscp /etc/hosts rootlocalhost:/tmp/正常封禁场景下如果ForceCommand生效scp会显示连接被关闭scp: Connection closed如果你只改过/usr/bin/scp的权限本机执行会提示“Permission denied”或“command not found”但远端机器的scp仍然可以连到这台服务器。这一点务必分清楚——scp本机和远端是两个角色。5.2 一个经典误报sftp received message too long 1416128883排查这个问题的过程中我还踩过一个和封禁完全无关、但容易让人误判封禁失败的经典坑sftp连接时报sftp received message too long 1416128883。这个报错的意思是SFTP客户端从远端收到了一条“消息”但这条消息的长度异常巨大。通常原因不是sshd配置而是用户的登录shell在加载时往标准输出打了一堆欢迎语或无关内容。例如~/.bashrc里写了个echo或者/root/.profile里调了个输出时间的脚本这些内容混在SSH会话的标准输出里SFTP子系统无法解析就把它当成一个超长协议消息来读取。1416128883转成十六进制是0x54697265刚好是ASCII字符“Tire”在我的场景里是bashrc中一个失败的motd脚本造成的。排查方法sudo cat /root/.bashrc | grep -n echo sudo cat /root/.profile | grep -n echo把登录shell的输出清理干净或者把该用户的shell从/bin/bash改成/bin/sh问题一般就消失了。这也是为什么在前文反复强调用自定义ForceCommand脚本时脚本本身绝不能往标准输出写任何东西否则你会亲手制造一个“sftp received message too long”。5.3 改配置前后必做的三件事回到主题所有配置改动之前我建议你强制走一轮固定流程先备份、再检查、后重启。第一备份。把旧的sshd_config复制一份带时间戳sudo cp /etc/ssh/sshd_config /etc/ssh/sshd_config.bak.$(date %Y%m%d)第二语法检查。任何SSH配置改动之后、重启之前执行sudo sshd -t没有任何输出就代表语法合格。这一步能拦住九成配置错误。第三不要直接restart最好用reload。Ubuntu 16.04上可以执行sudo service ssh reloadreload会让sshd重新读取配置但保留现有连接不断开。对生产环境来说这比restart友好太多——restart会把所有在线会话全部踢下线包括你正在打字的这台远程窗口。如果reload过程中发现不对劲你还有机会马上改回来而不是被关在门外再想办法。如果改的是防火墙规则又是另一套流程。我的习惯是每条iptables规则增量添加先测试再固化绝不一次性改动所有链然后立刻保存。ufw的enable操作会把已有规则全部载入稍有不慎就会阻断当前SSH连接所以建议先允许当前IP段进22端口再启用ufw。5.4 这次整改做完后我的实际体会禁用scp、sftp和Winscp在Linux管理里算不上复杂操作但它非常考验一个人对SSH协议栈的理解深度。你如果只是照着网上命令复制粘贴大概率会遇到“明明sftp关了scp还能用”或“Winscp还能连上”这类困惑而当你理解了Subsystem、ForceCommand、Match块、防火墙规则之间的协作关系就会发现所谓封禁就是一层层地缩小面协议层面关掉子系统会话层面限制命令网络层面缩小来源。另外还有一点私人经验分享Ubuntu 16.04虽然已经停止维护但很多企业内部还在用。对这些老机器做任何安全改动时一定要比新系统更温和、更保守。原因很简单缺少官方补丁背景下你的每一步操作都可能让系统进入不稳定状态。先备份、先测试、留控制台这三条习惯在CentOS、Debian乃至其他版本Ubuntu上同样适用。修修补补老机器的过程虽然麻烦但每一次小心确认都是给自己少埋一个雷。
返回列表