在WSL中用Alpine Linux搭建SSH跳板机:配置与安全禁忌

在WSL中用Alpine Linux搭建SSH跳板机:配置与安全禁忌 手头管着几台服务器的人多半都经历过那种“想连个机器还得翻半天笔记”的烦躁。IP、用户名、密码散落各处今天记在这个文档里明天存在那个聊天记录里真到要用的时候百分之百找不到。我在这个问题上折腾了大半年最后沉淀下来的方案相当朴素在WSL里放一个Alpine Linux把它配置成固定的SSH门户也就是跳板机/入口机所有远程连接都先落在这台机器上再从它往目标机走。这套方案帮我省下的时间远超配置它花的时间。标题里写“门户”其实就是跳板机、堡垒机这类角色“忌”字则是我这次要重点聊的——配置过程中我踩过的坑、以及SSH门户最容易忽略的安全禁忌。接下来从选型、安装、SSH配置、安全加固到客户端使用完整过一遍我的做法和踩坑记录希望能让你少走几趟弯路。1. 门户角色为什么落在Alpine头上1.1 门户的三个硬指标在SSH语境里门户机承担的工作很单纯集中入口、转发连接、留存日志、做第一道认证。它不像业务服务器那样要跑各种服务也不像开发机那样要装一堆依赖它最需要的是稳定、轻量、少惹事。这三个需求翻译成技术指标就是占用资源够低别干扰Windows日常使用、系统够简单更新和安全维护省心、服务面够小暴露的攻击面可控。我最早试过用Ubuntu虚拟机当门户配置倒是熟悉但开机几GB内存没了更新一次几百兆风扇转得我心疼。后来换成WSL里的Ubuntu比虚拟机轻不少可一个常规Ubuntu发行版的包袱还是在。直到一次偶然机会试了Alpine才确认这就是门户该有的样子。1.2 Alpine为什么比Ubuntu更适合当门户Alpine和最常见的Ubuntu发行版差别集中体现在三件事上。第一是体积。Alpine的minirootfs压缩包只有几十MB装完系统本身也就几百MB级别而Ubuntu的base系统往往要以GB为单位去算。这个差距落在WSL2里最直观的感受就是启动Alpine基本秒开内存占用常年在几十MB徘徊Windows侧几乎感觉不到它的存在。第二是架构。Alpine默认用musl libc加busybox用户态工具全是精简过的服务管理用OpenRC而不是systemd。很多人第一次用会不习惯但这正是它轻量、行为可预期的根源。对门户这种“越简单越好”的角色来说系统越薄出问题的概率越低排查问题也越容易。第三是安全面。Alpine默认装的东西很少意味着没有一堆预装服务和端口暴露。门户最怕的就是自己身上漏洞百出还替别人守门。Alpine这种极简底子天然把安全边界做得比较干净。1.3 前置思维转变在Alpine里别找systemd这是给从Ubuntu/CentOS转过来的人最重要的心理建设。习惯Ubuntu的人上手Alpine第一反应往往是敲systemctl start sshd然后收到一个不存在的命令。Alpine用的OpenRC完全是另一套用法服务脚本放在/etc/init.d/下管理命令是rc-service和rc-update。打个比方systemd像是整栋楼的物业中心统一管水电、门禁、监控OpenRC更像是楼里每一户自己搞定灯泡和门锁各管各的规则简单直接。对跑在WSL里的门户来说OpenRC这种“管得少、管得明白”的风格反而更靠谱。后面配置SSH服务时你会反复和rc-service打交道。2. 把Alpine装进WSL两条路线与开箱三件事2.1 路线一从Microsoft Store一键安装最简单的办法打开Microsoft Store搜索Alpine选择官方那个发行版点安装。安装完成后从开始菜单启动一次它会自动注册到WSL里进入命令行终端就可以开始用了。首次进入会让你设置root密码之后就能自由切换普通用户。这条路线适合只是想先体验一下Alpine、不想折腾底层细节的人。缺点也很明显发行版版本由商店决定rootfs存放位置不透明之后想精细控制、做备份迁移会比较别扭。如果你确定要把这个Alpine长期当门户用我更推荐下面这条手动路线整个过程是透明的任何一步都能复现。2.2 路线二手拉minirootfs并用wsl --import导入手动导入的核心是拿到Alpine的minirootfs压缩包然后交给wsl --import打造成一个发行版。minirootfs是Alpine官方提供的最小根文件系统没有内核、没有引导正好可以被WSL拿来当发行版根目录用。先建一个存放发行版的目录比如在D盘下建一个wsl/alpine目录再执行wsl --import Alpine D:\wsl\alpine D:\downloads\alpine-minirootfs-3.19.1-x86_64.tar.gz --version 2几个参数说明一下Alpine后面跟的是发行版名称自定义即可第二个参数是rootfs存放路径必须是空目录第三个参数是minirootfs压缩包路径可以在Alpine官方站点的releases目录下找对应版本--version 2指定用WSL2这一步别省略WSL1的网络和服务兼容性都不适合当门户导入完成后启动wsl -d Alpine此时你直接就是root默认没密码第一步先执行passwd设置root密码然后创建一个日常用的普通用户。用非root身份跑SSH门户是最基本的底线后面讲安全配置时我会再次强调。2.3 开箱三件事换源、设密码、校准时间先把源换成国内镜像。Alpine的镜像源配置在/etc/apk/repositories文件里默认指向官方源在国内网络环境下经常慢得让人想砸键盘。以清华源为例替换成https://mirrors.tuna.tsinghua.edu.cn/alpine/v3.19/main https://mirrors.tuna.tsinghua.edu.cn/alpine/v3.19/community换完执行apk update刷新索引。接着是时间校准服务器上时间不准日志时间对不上排查起来非常折磨。Alpine默认只有UTC时间需要装tzdata包并设置时区apk add tzdata cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime以后看/var/log/messages里的日志时间就是本地时间不用再心算八小时时差。这三件事每件都很小但漏掉任何一件都会在后面某个环节给你添堵。2.4 OpenRC服务管理的正确姿势清空先前的systemd执念记住三组命令rc-update add sshd default # 把sshd服务加入开机自启 rc-service sshd start # 立即启动sshd服务 rc-service sshd restart # 重启sshd服务rc-update管理的是开机自启项rc-service管理的是服务运行状态。对于门户来说把SSH服务的自启配上是最关键的一步否则WSL实例被回收后重新进入SSH服务不会自己跑起来。这里顺带提一个WSL的细节WSL2实例默认在特定条件下会自动终止下次进入时是全新开机状态所以依赖rc-update这类自启机制来拉起服务不要指望“上次开着就一直开着”。3. 配置sshd从安装到真正能连通的链路3.1 安装openssh并启动服务Alpine的包管理器是apk安装SSH服务端和客户端只需要一条命令apk update apk add openssh-server openssh-client装完以后先不要急着启动先改配置。Alpine的sshd配置文件和主流Linux发行版一样都在/etc/ssh/sshd_config。直接用vi或者sed改都行改完再用rc-service sshd start启动。提示建议把大家熟知的“用root直接登录”留到密钥配置完之后再决定开不开。我的习惯是PermitRootLogin prohibit-password意思是root只能用密钥登录禁止密码登录。这样兼顾了管理便利又堵住了暴力破解root密码的路。3.2 sshd_config关键项逐条拆解每条配置如果不能说出为什么这么写就先不要写。我贴一份我目前用的核心配置附上我的理解Port 2222 PermitRootLogin prohibit-password PubkeyAuthentication yes PasswordAuthentication no MaxAuthTries 3 AllowUsers alice ClientAliveInterval 30 ClientAliveCountMax 3Port 2222不解释默认22端口每天都在被全网扫描器问候。换个高位端口能拦截掉绝大多数无差别扫描。PermitRootLogin prohibit-passwordroot可以用密钥登录但不能用密码登录。PasswordAuthentication no这是门户的硬底线。配好密钥后关闭密码认证等于直接对密码爆破说再见。MaxAuthTries 3限制单次连接认证尝试次数防暴力尝试。AllowUsers alice只允许alice这个用户通过SSH登录其它用户一概拒绝。ClientAliveInterval 30和ClientAliveCountMax 3每30秒向客户端发一次心跳包连续3次没响应才断开。这条对跳板场景特别重要保持长连接时不会因为一时网络抖动就被踢掉。改完配置别忘重启rc-service sshd restart3.3 从Windows验证连通localhost的坑先在Windows终端里试一下ssh -p 2222 alicelocalhost如果你能连上说明WSL2的localhost转发机制正常工作。WSL2为NAT网络模式提供了一个便捷特性Windows侧访问localhost时会把流量转发到WSL2实例中监听的端口。这让你从Windows直连WSL里的SSH服务感觉就像连本机服务一样。但这里藏着一个很多人栽跟头的坑WSL2的IP不是固定不变的每次重启实例IP都可能变化。如果你在某个文档里记了WSL的IP过几天再用完全可能连不上。所以Windows侧要连WSL里的SSH请一律用localhost而不是IP否则某次重启后它会变成玄学问题。3.4 局域网访问端口转发和mirrored网络二选一如果只有你这一台Windows机器用门户localhost就够了。但如果想从办公室的另一台电脑、或者手机上的Termius/Sw64连到这个门户就要解决局域网可达的问题。WSL2默认的NAT模式虚拟机有自己独立的虚拟网络局域网内其他机器是看不到WSL实例的IP的。两个解决办法第一个是Windows端口转发。在Windows PowerShell管理员权限里执行netsh interface portproxy add v4tov4 listenport2222 listenaddress0.0.0.0 connectport2222 connectaddresswsl-ip这里的connectaddress填WSL当前的IP可以在Alpine里执行ip addr查看。这个方案有效但WSL IP变化后要跟着更新portproxy规则比较繁琐。第二个方案更省心微软在较新的Windows 11版本里为WSL2提供了mirrored镜像网络模式。在Windows用户目录下建一个.wslconfig文件写入[wsl2] networkingModemirrored然后在PowerShell里执行wsl --shutdown再重新进入Alpine。镜像模式下WSL共享Windows的网络栈不再有独立的虚拟IP局域网机器可以直接通过Windows的IP和端口访问到WSL里的sshd。这才是门户该有的网络形态我强烈建议有条件就切到mirrored模式。注意这个功能需要较新的Windows 11版本支持老版本Windows 10还是老老实实用portproxy方案。4. 把门户焊死密钥认证与暴力破解防护4.1 ed25519密钥生成与authorized_keys部署密钥认证是门户安全的核心没有之一。我推荐用ed25519算法生成密钥比传统RSA 2048更短更快安全性还更高。在Windows侧或者Alpine里都可以生成我的习惯是在Windows侧生成然后把公钥拷进Alpine这样私钥始终留在客户端。ssh-keygen -t ed25519 -a 100 -C wsl-portal-key-a 100指定KDF迭代次数提高私钥被暴力破解的门槛。生成的公钥id_ed25519.pub内容是要放进服务器的东西私钥id_ed25519必须留在本地谁都不能给。在Alpine里把公钥写入alice用户的authorized_keys文件mkdir -p /home/alice/.ssh chmod 700 /home/alice/.ssh echo ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAA... /home/alice/.ssh/authorized_keys chmod 600 /home/alice/.ssh/authorized_keys chown -R alice:alice /home/alice/.ssh权限设置是这里最容易踩的坑。如果authorized_keys文件权限是644或者.ssh目录权限是777sshd会直接忽略这个密钥文件让你怎么连都失败。SSH对权限的要求是.ssh目录不能对组和其他用户开放写权限authorized_keys文件同理。我一开始就栽在这上面配好密钥怎么都连不上排查了半小时发现是节外生枝的权限问题。4.2 关掉密码登录门户的“禁入名单”密钥文件部署到位并确认能通过密钥登录之后回到sshd_config里把PasswordAuthentication改成no然后重启sshd。这一步在时间上一定要放在密钥验证没问题之后再做否则把密码登录关了密钥又连不上那就只能进WSL里把配置改回来来回折腾很容易逼疯自己。关闭密码登录之后你的门户就对“密码爆破”彻底关了门。没有密钥的人哪怕猜对了密码也进不来。搜索热搜里经常有人问“ubuntu ssh无法连接怎么办”相当一部分案例就是因为改了这一项之后没想清楚导致自己把自己锁在外面。我的建议是改这项之前先把密钥登录跑通并且确保你能从客户端免密登录再回头关密码登录这个顺序千万别颠倒。4.3 fail2ban在Alpine里的正确用法密钥认证已经挡掉大部分攻击者了但还缺一道主动防线——fail2ban。它做的事情很简单监控认证日志发现某个IP在短时间内多次认证失败就临时封禁这个IP。在Alpine里安装apk add fail2ban配置文件在/etc/fail2ban/jail.local为sshd单独写一个jail[sshd] enabled true maxretry 3 findtime 600 bantime 3600含义是10分钟内认证失败3次封禁1小时。配置好后启动fail2ban服务rc-update add fail2ban default rc-service fail2ban start需要说明的是在WSL2默认NAT网络下fail2ban封禁的是进入WSL虚拟网卡的IP如果流量经过Windows端口转发实际封禁效果会打折扣。切换到mirrored网络模式后SSH连接直接走Windows网络栈fail2ban的封禁才能完整生效。这也是我前面推荐mirrored模式的另一个原因。4.4 日志监控看懂sshd在说什么SSH被暴力破解的情况在日志里是有迹可循的。当你在网上看到有人问“网络攻击ssh大量连接怎么办”大多数情况下就是SSH日志里刷满了Failed password记录来源是大量陌生IP的机器人扫描。在Alpine里默认的syslog会把ssh日志写到/var/log/messages用一条tail命令实时看tail -f /var/log/messages | grep sshd日志里最典型的几类内容Failed password for invalid user xxx有扫描器在枚举用户名说明有人在试探你的服务器。Connection closed by invalid user xxx同样是无害但烦人的试探。Accepted publickey for alice from x.x.x.x port xxx正常人登录成功了这部分应该都是你自己或者可信来源。看到“invalid user”刷屏不用慌端口改了、密码登录关了、fail2ban在跑这三层防护已经能挡住99%的扫描。判断是否需要进一步处理看有没有“Accepted”之外的可疑成功认证以及有没有大量不断变化的源IP。如果某天发现日志量突然爆发、封禁名单一直在更新可以考虑在Windows防火墙里加一条只允许指定来源IP访问2222端口的规则把暴露面缩到最小。5. “门户忌”自查手册与我的实战经验5.1 五个真实踩过的坑第一个坑默认22端口裸奔。第一次搭好SSH后我没改端口当晚日志里就有几千次Failed password尝试。改端口不是绝对安全但能让你的日志安静好几个数量级属于性价比最高的安全操作。第二个坑authorized_keys权限不对。前面提过.ssh目录和authorized_keys文件权限太宽松sshd会直接忽略这个文件结果就是密钥认证死活不生效。记住两个数字700给.ssh目录600给authorized_keys文件。第三个坑在Alpine里用systemd命令。这个坑基本是Ubuntu转Alpine的人必踩。Alpine没有systemd不要执行任何systemctl命令用rc-service和rc-update。这个思维一旦转过来Alpine的OpenRC其实简单到让人舒适。第四个坑WSL2 IP变化导致配置失效。用portproxy做局域网端口转发时WSL2重启后IP会变portproxy规则跟着失效。一次重装系统后我整整浪费了一个晚上排查这个问题。后来切到mirrored网络模式彻底解决了这个问题。如果你还在用NAT模式建议把获取WSL IP的操作写进一个脚本或者干脆日常只用localhost访问。第五个坑在门户上乱装东西。Alpine当门户的精髓是轻薄你装的东西越多出问题的概率越大需要更新的面越大。我的门户上只装了openssh fail2ban tzdata再多都不装。要转发到目标机的连接走命令行就行不需要在门户上装任何图形化工具。我用一个表格把你可能遇到的现象和根因对照一下现象最可能的根因解决办法连不上提示Connection refusedsshd没起来或端口配错rc-service sshd status检查服务状态认证失败密码和密钥都进不去密钥权限不对或PasswordAuthentication已关闭chmod 600 authorized_keys确认密钥文件权限正常局域网其他机器连不上WSL2 NAT网络限制或Windows防火墙拦截换mirrored模式或检查Windows入站规则明明开了自启重启后SSH还是没监听WSL实例被终止后重新进入自启项没生效检查rc-update show确认sshd在default运行级别里ssh连接后没过一会儿就断开网络不稳定或心跳配置太激进加大ClientAliveInterval降低误杀概率5.2 客户端侧配置用ssh config把门户用顺门户的体验好不好一半在服务端一半在客户端。Windows侧把SSH客户端配置好整个跳板体验能丝般顺滑。在Windows的C:\Users\你的用户名.ssh\config里写这样一段Host portal HostName localhost Port 2222 User alice Host srv1 HostName 10.0.0.47 User root ProxyJump portal Host srv2 HostName 10.0.0.58 User deploy ProxyJump portal配置写好后在Windows终端里直接执行ssh srv1客户端会自动经过门户连到目标机中间不需要任何额外操作。这个体验和直接连目标机几乎没有区别但所有入口、认证、日志都被门户管着目标机的SSH服务甚至都不需要直接暴露在网络里。如果目标机较多还可以用通配符简化比如Host *.example.internal配合ProxyJump portal一台台写也很快。另外通过门户操作目标机时如果打算在目标机上跑长时间任务建议在目标机上装个tmux防止网络抖动导致任务中断这个经验是从一次次断连丢任务里学来的。5.3 别忘了门户自身的边界与保养门户用久了很多人会渐渐忽略它本身也需要维护。清单就三件事定期更新apk update apk upgrade这个习惯和更新目标机一样重要。定期看日志不用天天看但偶尔扫一眼/var/log/messages里的SSH记录能让你对当前外部扫描态势和访问状况心里有数。不要存储敏感信息不要在门户上保存私钥、密码表、数据库连接串这类东西。门户被攻破后这些才是最致命的财产。我的门户上连环境变量都尽量少配能临时输入的就临时输入。5.4 最后分享一个保持了半年的小技巧我在Windows侧把常用的目标机命令精简成了一条条别名装进PowerShell的$PROFILE里function srv1 { ssh srv1 } function srv2 { ssh srv2 }现在日常操作基本都是三五个字母搞定。入口收敛到WSL里这个Alpine客户端配置收敛到.ssh/config一个文件整条链路清清楚楚出了任何问题排查思路都特别直客户端配置看没看对门户通不通目标机通不通。这套Alpine WSL的SSH门户方案我已经稳定用了大半年期间除了偶尔升级系统几乎没有给我添过乱。如果你也是那种手里有几台服务器、不想再被连接信息搞晕的人照着这条链路搭一遍大概率能体会到跟我一样的轻松感。