办公室那台老服务器又被人扫端口了。端口的配翻日志的复用时候,我盯着22和3389两条记录发呆——这俩口子就像俩没锁门的置过房间,谁路过都想推一把。端口的配索性把SSH挪到443上去,复用让防火墙以为我在逛HTTPS网站。置过这就是端口的配端口复用,听着玄乎,复用其实就一句话:让一个端口同时干两件事。置过
先说说最土的端口的配配置方法,用iptables做个端口转发。复用假设你有个服务监听在2222,置过想让它伪装成443。端口的配规则很简单:iptables -t nat -A PREROUTING -p tcp --dport 443 -j REDIRECT --to-port 2222。复用但这破绽太明显了,置过明眼人一抓一个准——因为443上本该跑TLS握手,你回个SSH的banner,等于自己掀了底牌。
真正的复用得靠协议识别。我用的方案是sslh,一个小巧的协议分流器。它监听443端口,先瞅一眼进来的第一个数据包——如果是TLS ClientHello,就扔给nginx;如果是SSH的版本字符串,就丢给sshd。配置起来跟改错题似的:protocols: ssh: "22" tls: "443",然后让sslh监听443,nginx改到8443。这么一搞,外网访问ssh -p 443 user@host,跟正常连SSH没两样,但防火墙看到的全是加密流量。
有个坑得提一嘴。sslh对TLS的识别依赖SNI,你要是用IP直连而不是域名,它可能认不出来。我调试时卡了半小时,最后发现curl得加--resolve参数指定域名才分流正确。还有,如果服务器上已经跑了Apache或Nginx占着443,你得先把它们挪走,不然端口冲突,sslh直接罢工。
比sslh更野的路子是搞个systemd socket激活。把SSH和HTTP都挂到同一个socket上,让内核帮你分流。配置起来稍麻烦:先定义一个ssh-http.socket,监听443,然后让两个service都Sockets=指向它。但内核只认端口不认协议,你得自己写个小程序判断第一个字节是0x16还是0x53。这活儿我干过一次,代码倒是好写,就是调试时老得抓包看分流对不对。
对了,如果你只是临时用用,不想装额外软件,还有个土办法:用xinetd的portmap。把443端口交给xinetd,然后配两个服务——一个匹配SSH特征,一个匹配HTTP特征。但这法子太笨,每来一个连接都得fork一个进程,并发一高准卡死。我劝你别试。
配置完了得验证。先本地测:ssh -p 443 localhost能通,再用浏览器访问https://localhost也正常。然后从外网扫一遍,用nmap的-sV版本探测,看到的结果应该是“ssl/http”而不是“ssh”——这就对了。要是还暴露了SSH版本号,说明sslh没生效,回去查日志。
端口复用这活儿,本质上是跟扫描器玩捉迷藏。但别指望它绝对安全——真正的行家会用tcpdump抓包分析流量特征,你的SSH会话时长和HTTP明显不一样。所以这招只能防普通扫描,挡不住定向渗透。我一般拿它当临时应急手段,比如出差时借用公司防火墙的允许端口,省得申请额外的安全策略。
最后说个有意思的细节。有次我把SSH藏在443上,结果公司IT部门的安全扫描报告里,这台服务器的443端口被标记为“弱加密”。原因是我用的老版本OpenSSH不支TLS1.3,握手特征太老。后来升级了sshd才消停。你看,连复用端口都得跟上时代。