接手一台 CentOS7 的机器,第一件事永远是先把 SSH 这条路弄顺。系统版本老归老,但大量线上服务器、实验环境、培训沙盒都还跑在 CentOS7 上,很多团队的运维习惯也建立在它身上——网络调通、sshd 正常、能免密登录、权限可控,后面再谈部署什么 Docker、Java、MongoDB 都有底气。这篇文章我来把这套操作完整走一遍,从刚装完系统到日常使用中最容易卡住的几个点都过一遍,写给正在折腾 CentOS7 和 SSH 的你。
这次的内容不单是“敲命令”,我还会把配置背后的原因说清楚。比如为什么推荐用密钥而不是密码、为什么运维规范里经常强调“禁止 root 直接远程登录”、为什么 AllowGroups 和 PermitRootLogin 要配合着改。你可以把它当一份带着避坑记录的实操手册,也可以直接抄作业。
1. 准备工作:装好系统后,先把网络和 sshd 调顺
1.1 为什么还在大量环境里见到 CentOS7
看到“为什么那么多人还用 CentOS7”这个问题,用过的人都会会心一笑。原因不外乎这几点:存量业务跑得好好的,没人愿意为了追新把生产系统重新折腾一遍;CentOS7 的 systemd、firewalld、yum 体系非常成熟,网络上的资料多到几乎什么问题都能搜到;很多企业内部的自动化脚本、安全审计基线也都是照着 CentOS7 写的,迁移成本远比想象中高。我也经常在虚拟机里装 CentOS7 做实验,因为软件源里能直接装到的包很全,遇到问题很容易复现。
这不是说 CentOS7 是万灵药,但对个人学习和中小团队来说,它确实是一个稳定、顺手、试错成本低的操作系统。先别急着在上面部署业务,把 SSH 和网络基础打好,后面才不会被“远程连不上”这种问题耗掉半天。
1.2 先给网卡配置静态 IP
最小化安装的 CentOS7 默认可能开的是 DHCP,IP 地址说变就变。做服务器管理和 SSH 远程连接,第一步建议先把 IP 固定下来。网卡配置文件在 /etc/sysconfig/network-scripts/ 下,通常叫 ifcfg-ens33 或 ifcfg-eth0,先确认实际名字:
bash复制ip addr
nmcli device status
用 nmcli 能看到设备名和连接状态,比 ifconfig 直观。然后编辑对应的配置文件:
bash复制vi /etc/sysconfig/network-scripts/ifcfg-ens33
一份比较常见的静态 IP 配置如下:
ini复制TYPE=Ethernet
BOOTPROTO=static
NAME=ens33
DEVICE=ens33
ONBOOT=yes
IPADDR=192.168.1.10
NETMASK=255.255.255.0
GATEWAY=192.168.1.1
DNS1=223.5.5.5
DNS2=114.114.114.114
写完别忘了重启网络服务:
bash复制systemctl restart network
这里有个细节容易踩坑:CentOS7 里如果 NetworkManager 在运行,直接改 ifcfg-ens33 后光执行 systemctl restart network 有时不生效,最好再执行 nmcli connection reload 或用 nmtui 这种交互工具修改。我习惯在纯命令行环境里用 nmtui 先改一遍,再对照配置文件检查,省得被缓存配置坑到。
1.3 启动 sshd 并放行 22 端口
CentOS7 完成最小化安装后,openssh-server 通常是已经装好的,只是服务可能没启动,或者防火墙没放行。按顺序操作:
bash复制systemctl start sshd
systemctl enable sshd
systemctl status sshd
如果防火墙开着,需要把 22 端口加进去:
bash复制firewall-cmd --permanent --add-service=ssh
firewall-cmd --reload
firewall-cmd --list-all
CentOS7 默认的防火墙是 firewalld,很多新手装了 SSH 却连不上,查到最后往往就是 firewall-cmd 忘记放行。还要顺手看一眼 SELinux 的状态,虽然它很少会拦 SSH,但如果你改了非默认端口,就得同步调整 SELinux 策略:
bash复制sestatus
如果非要用 2222 这种自定义端口,除了改 /etc/ssh/sshd_config 里的 Port,还要执行:
bash复制semanage port -a -t ssh_port_t -p tcp 2222
不然 SELinux 会直接拒绝 sshd 绑定端口。这类问题在日志里能查到 AVC denied 的提示,但新手往往一头雾水。
1.4 连过去之前先做的自查
每次换新机器,我习惯先在本地执行几条命令,确定基础能通再开始下一步:
bash复制ping -c 4 192.168.1.10
telnet 192.168.1.10 22
如果 ping 不通,查网络和 IP;如果 ping 通但 telnet 22 不通,基本就是防火墙、sshd 没启动或者端口不是默认 22。telnet 22 能通说明端口已经监听,这时再 SSH 连不上,就重点看认证方式和用户权限。这是比较省时间的排查顺序。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 从密码到密钥:SSH 免密登录的正确姿势
2.1 用密钥替代密码解决的不只是“少输密码”
密码登录最大的问题是两个:一是容易暴力破解,只要端口暴露在公网,/var/log/secure 里就能看到大量尝试记录;二是密码本身存在泄露风险,团队里多人共用一台服务器时,密码周转一圈就不知道谁用过。SSH 密钥登录用的是公钥加密、私钥签名的机制,私钥只存在自己本机,安全性比密码高一个量级。
使用密钥后另一个附带好处是方便做自动化。批处理脚本、ansible、git 拉代码、VSCode 远程开发,全都省去了交互式输密码的麻烦。我把这一步称作“一劳永逸的小投资”,生成一次密钥能用很久。
不过要提醒一句:如果你在机房或者本机已经用密码登录了很长一段时间,别觉得换上密钥就够了。密钥虽然难以在线猜测,但本机私钥的保管同样重要。私钥文件的权限绝不能放开,建议 chmod 600 ~/.ssh/id_ed25519。
2.2 生成密钥并把公钥放进服务器
在本地机器上执行生成命令。现在的 SSH 客户端基本都支持 Ed25519 算法,比 RSA 更短更安全:
bash复制ssh-keygen -t ed25519 -C "your_email_or_remark"
如果不习惯,也可以继续用 RSA:
bash复制ssh-keygen -t rsa -b 4096
一路回车即可,如果不想每次连接都输私钥口令,就直接留空。生成后默认会在 ~/.ssh/ 下出现两个文件:id_ed25519(私钥)和 id_ed25519.pub(公钥)。接下来把公钥放到 CentOS7 上:
bash复制ssh-copy-id root@192.168.1.10
执行后会让你输入一次密码,然后自动把公钥追加到服务器 ~/.ssh/authorized_keys。如果用 root 登录,对应的目录是 /root/.ssh/;如果登录普通用户,就是 /home/用户名/.ssh/。之后再次 SSH 登录就不需要密码了。
没有 ssh-copy-id 命令的时候,可以手动追加:
bash复制cat ~/.ssh/id_ed25519.pub | ssh root@192.168.1.10 "mkdir -p ~/.ssh && chmod 700 ~/.ssh && cat >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys"
很多服务器连不上,就是因为 .ssh 目录或 authorized_keys 文件权限太宽松,sshd 会认为不安全而拒绝加载公钥。
2.3 多台机器批量分发公钥
当服务器数量开始变多,一个个执行 ssh-copy-id 仍然有点烦。可以写一个简单的循环批量操作。假设你有一批 CentOS7 机器,IP 写在 hosts.txt 里:
bash复制while read host; do
ssh-copy-id -i ~/.ssh/id_ed25519.pub root@$host
done < hosts.txt
前提是所有机器初始密码相同,或者你已经能免密登录第一台,后面靠它再跳转分发。提到批量登录,更专业的做法是配置 ~/.ssh/config 文件。当机器数量多且用户、端口不统一时,这个文件非常有用:
plaintext复制Host dev
HostName 192.168.1.10
User root
Port 22
IdentityFile ~/.ssh/id_ed25519
Host test1
HostName 192.168.1.11
User admin
Port 2222
配置完之后直接 ssh dev 就能连接,配合密钥登录,体验会顺畅很多。
2.4 服务端还需要确认的两个参数
有的 CentOS7 默认 PubkeyAuthentication 是注释状态,此时默认值是 yes,所以通常不用改。但如果你想彻底关闭密码登录,只保留密钥,需要把 /etc/ssh/sshd_config 改两个地方:
ini复制PubkeyAuthentication yes
PasswordAuthentication no
修改后一定先做个语法检查再重载:
bash复制sshd -t
systemctl reload sshd
sshd -t 这个命令能帮忙发现语法错误,等于给配置上了一道保险。即使真的把允许登录的方式改错了,只要当前 SSH 连接没断开,还能再改回来,所以改完不要立刻关窗口,先新开一个终端验证新的登录方式确实可用再做下一步。
3. 安全加固:只允许 wheel 组远程登录,禁掉 root 直连
3.1 需求场景分析
很多团队的服务器安全基线里写着这样一句话:禁止 root 用户直接通过 SSH 登录,远程操作用普通用户进入后,再通过 sudo 提权。这样做的价值在于:root 是系统里权限无限大的账号,一旦被爆破,攻击者直接就拿到了整个系统;而普通用户即使被攻破,默认权限也有限,sudo 的日志还能记录操作痕迹。
还有一个更细化的需求:只有 wheel 组用户可以 SSH 远程登录。wheel 组在 CentOS7 里是所有可 sudo 用户所在的管理组,这样既能阻止不明身份的人连进来,又能把日常管理入口收敛到一个明确的组范围里。如果之后有新人需要远程管理,不用单独改 SSH 策略,只要把他加入 wheel 组即可。
3.2 创建可远程登录的管理员用户
先创建一个普通用户并加入 wheel 组:
bash复制useradd admin
passwd admin
usermod -aG wheel admin
id admin
这里我用 usermod -aG 而不是 usermod -G,就是为了避免把用户原来所在的附属组覆盖掉。如果想把 root 用户本身纳管到 wheel 组里,也可以执行:
bash复制usermod -aG wheel root
但更多情况是我们不希望 root 能直接登录,而希望普通用户登录后再 sudo -i 切换成 root。CentOS7 默认 wheel 组里的用户在 /etc/sudoers 中是允许执行任意命令的,你只需要确认一行没被注释:
bash复制%wheel ALL=(ALL) ALL
一般来说无需额外配置。在测试时我先用 admin 登录,然后执行 sudo whoami,看看是不是能正常切换到 root。
3.3 修改 sshd_config 限制登录范围
下面进入主题。编辑 /etc/ssh/sshd_config,建议先备份:
bash复制cp /etc/ssh/sshd_config /etc/ssh/sshd_config.bak.$(date +%F)
然后添加或修改以下几项内容:
ini复制PermitRootLogin no
AllowGroups wheel
PasswordAuthentication yes
PubkeyAuthentication yes
解释一下这几个配置的含义:
PermitRootLogin no:禁止 root 用户直接通过 SSH 登录。AllowGroups wheel:只有主组或附加组是 wheel 的用户允许远程登录。PasswordAuthentication yes:允许密码登录,配合密钥登录用。如果你已经把所有公钥都导入,可以设置成 no。PubkeyAuthentication yes:开启公钥认证。
注意,如果 AllowGroups wheel 设置后,root 用户即便在 wheel 组里,只要 PermitRootLogin no 没有被放开,还是不能远程登录。如果业务上确实需要 root 远程登录,同时限定只有 wheel 组可以登录,那可以把 PermitRootLogin 改成 yes,但我不推荐这种做法,更安全的姿势是普通用户进系统后 su - 或者 sudo -i。
这里还要考虑一个细节:如果 /etc/ssh/sshd_config 里有类似 Match 的段落,AllowGroups 可能被后面的配置影响,建议把这些行放在文件靠后的位置,或者单独放在 /etc/ssh/sshd_config.d/ 下的配置文件中。CentOS7 较新版本的 OpenSSH 也会读取 Include 进来的片段,你需要在调整前先确认主配置里有没有相关 Include /etc/ssh/sshd_config.d/*.conf 这样的行。
3.4 验证与急救方案
这一步很重要。修改配置后,先在当前会话里确定自己能畅通地执行 sudo,然后再重载 sshd:
bash复制sshd -t
systemctl reload sshd
重载之后,一定要新开一个 SSH 会话做验证,别把现有的连接断开。如果新开连接失败,再用现有会话把配置改回备份,避免把自己锁在机器外面。
如果权限设置过于严格,导致所有用户都无法登录,而当前又没有保留活动会话,那就只能通过物理控制台、虚拟机控制台或者带外管理口进入系统,临时修改配置。所以在生产环境做这种操作,我的习惯是先在 A 终端保持一个长时间连接,然后在 B 终端测试新配置。B 终端确认成功后,A 终端再关闭。同时建议把密钥认证开启,这样就算密码策略出现了问题,管理员手里有私钥也能进系统。
4. 实操:VSCode 远程开发、SSH 隧道与 Docker 部署串起来
4.1 VSCode Remote-SSH 连接 CentOS7
日常管理服务器总不能一直黑屏敲命令,尤其是要改代码、看日志、调试服务时,VSCode 的 Remote-SSH 插件是首选工具。装好插件后按下 F1,输入 Remote-SSH: Connect to Host,填写用户和地址:
text复制root@192.168.1.10
# 或者已经配置 ~/.ssh/config 时直接填 Host 别名
dev
第一次连接会自动在服务器上安装 VSCode Server,要求服务器能访问外网或局域网源。如果你的 CentOS7 无法下载,插件会卡在下载阶段。解决办法是先手动下载对应版本的 vscode-server-linux-x64.tar.gz 传到服务器,再解压到 ~/.vscode-server/bin/ 下面。这个坑在离线环境里特别常见。
连接成功后,左侧的资源管理器直接就是远程目录,终端也自动变成远程终端,写代码、跑命令、看运行结果都在同一个界面里完成。配合我在第 2 节里配置的密钥登录,全程不需要第二次输密码,顺手很多。
4.2 连接失败排查实录
VSCode 连接远程服务器失败时,我通常按这个顺序排查:
- 先在系统终端里手动执行
ssh dev,如果能连上而 VSCode 连不上,问题大概率在 Remote-SSH 插件缓存或 VSCode Server 上。 - 查看 VSCode 输出面板的日志,看到
Failed to find remote server时可以试试删除服务器上的~/.vscode-server目录再重连。 - 如果看到
Permission denied,检查密钥是否已经加入 ssh-agent,Windows 下可以用ssh-add ~/.ssh/id_ed25519。 - 如果是公司网络环境,代理设置也可能影响连接,需要清理
proxyCommand相关配置。
我从实际使用中得到的经验是,VSCode Remote-SSH 对服务器端的 ~/.vscode-server 很敏感,一旦版本错乱就会出现类似“抱歉,没有奏效”的弹窗。处理方式通常是远程把目录重命名或者删除掉,触发重新安装。
4.3 SSH 隧道和端口转发的小应用
某些服务只监听本地端口,或者远程服务器在内网防火墙后面,最常见的做法就是用 SSH 隧道把远程端口映射到本机。一条命令就能做到:
bash复制ssh -L 8080:127.0.0.1:8080 user@centos7-host
这个命令的含义是:把本机的 8080 端口通过 SSH 隧道转发到远端服务器的 127.0.0.1:8080。如果远程有个管理面板只监听 localhost,这时就能在本地浏览器访问 http://localhost:8080 了。反向隧道也经常用:
bash复制ssh -R 9000:127.0.0.1:9000 user@public-host
这种用法可以临时把内网服务暴露到一台有公网 IP 的机器上,供别人调试。日常开发里,SSH 隧道搭配 VSCode 的端口转发界面,体验远比改防火墙规则快。我遇到很多项目里的中间件端口没有做访问控制,与其直接开放到网卡上,不如用 SSH 隧道给服务加一层“谁能连谁不能连”的限制。
4.4 SSH + Docker 部署开发环境
CentOS7 上部署 Docker 是很常见的组合。先安装依赖并配置国内 yum 源,再安装 Docker:
bash复制yum install -y yum-utils
yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
yum install -y docker-ce docker-ce-cli containerd.io
systemctl start docker
systemctl enable docker
配合 SSH 密钥登录,我可以直接在本地写好代码,用 scp 或 rsync 推到 CentOS7 上,再通过 SSH 远程执行 Docker 构建命令。举个例子,如果要在服务器上部署一个仓库管理或测试应用,可以先拉取项目代码,然后用 Docker Compose 把依赖装起来:
bash复制docker pull some-image
docker compose up -d
当然 CentOS7 自带的内核和旧版本 Docker 有时会有兼容问题,比如报 iptables failed,通常升级内核或调整 firewalld 即可解决。但这不是本文重点,我只是想说明,SSH 是整个部署链路的“操作入口”,只要 SSH 舒服了,后续 Docker、jdk、MongoDB 这些组件的安装管理都会顺手很多。
5. 高频问题与避坑记录
5.1 常见错误速查表
实践里踩坑多了,我整理了一张速查表,方便大家按图索骥。
| 现象 | 常见原因 | 处理方式 |
|---|---|---|
ssh: connect to host ... port 22: Connection refused |
sshd 没启动,或端口不是 22 | 检查 systemctl status sshd,确认端口监听 ss -lntp |
Connection timed out |
防火墙阻止,或 IP 路由不通 | 检查防火墙规则、网络配置,ping 和 telnet 分开验证 |
Permission denied (publickey,password) |
公钥未正确添加,或密码输错 | 确认 authorized_keys 文件和权限,查看 /var/log/secure |
WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED |
服务器重装后指纹变化 | 使用 ssh-keygen -R 192.168.1.10 清除旧指纹 |
Bad owner or permissions on ~/.ssh/config |
本机 ssh config 权限过宽 | 执行 chmod 600 ~/.ssh/config |
| 公钥登录没生效 | SELinux 对 ~/.ssh 上下文限制 |
用 restorecon -R -v /root/.ssh 恢复上下文 |
最后一项比较隐蔽。CentOS7 开启了 SELinux 后,如果把 root 的公钥是通过 cp 命令从别处复制过来的,文件的安全上下文可能不对。执行 restorecon 基本就能解决。
5.2 配置 sshd 时的三个“不要”
第一,不要在测试还没完成的时候就关闭当前 SSH 会话。所有和 sshd 相关的改动,都应该先保持现有连接不断开,新开连接验证成功后再关。第二,不要直接在 sshd_config 里删除所有可用的认证方式。一些人为了安全把密码登录关掉,结果公钥文件权限又没设置好,最后所有用户都进不去。第三,不要在不确定是否有额外 Include 配置时,改动主配置后以为立即生效。现代 OpenSSH 支持 /etc/ssh/sshd_config.d/*.conf 覆盖主配置,你需要清楚实际生效的是哪一份。
这些都不是理论问题,我在实际维护中见过很多次。最稳妥的流程是:备份配置、修改语法检查、重载服务、新会话验证、确认后清理备份。一步都不要跳。
5.3 服务器时间不同步引起的认证问题
SSH 登录遇到奇怪认证失败时,检查一下服务器时间。Kerberos 认证等场景对时间敏感,普通密码登录偶尔也会因为系统时间偏差导致日志记录混乱。CentOS7 上可以安装 chrony 保持时间同步:
bash复制yum install -y chrony
systemctl enable chronyd
systemctl start chronyd
chronyc sources -v
时间不同步还会影响日志排查。当你看到 /var/log/secure 里的登录失败记录时间和当前时间对不上时,很容易误判安全事件。把时间同步做好,日志才可信。
5.4 我的几个小习惯
操作 CentOS7 和 SSH 这么久,我慢慢养成了一些习惯。比如,所有重要系统都用单独的普通用户跑服务,不用 root 直接部署;每台机器的 SSH 端口尽量不暴露在公网上,如果必须暴露就配合 fail2ban 或防火墙 IP 白名单;公钥注释里写明用途,比如 admin@laptop-2024,日后清理不用的公钥时一眼就能看出来是哪台设备。
我还习惯在关键操作前用 history 记录时间戳,或者直接用 script 命令把整个 SSH 会话的操作录下来。这样出问题后能回看当时到底敲过什么。生产中很多“灵异事件”最后都证明是某条命令少了一个参数,留痕能省去不少扯皮。
最后再分享一个不算冷门但很容易被忽略的技巧:当你用 root 登录后执行 sudo 相关的命令如果一直提示找不到命令,多半是因为 /sbin 和 /usr/sbin 没在普通用户的 PATH 里。这和 SSH 配置关系不大,但会直接影响远程操作体验。在 wheel 组用户的 ~/.bashrc 里把路径补上,远程管理会更省心。
这几次折腾下来,最大的体会是 SSH 不是配好就一劳永逸的,它会随着你的使用场景不断变化。今天你可能只需要密码登录,明天就要给团队成员开密钥,后天也许就要接 VSCode 做远程开发,再往后还要考虑批量运维。把基础原理理解到位,后面遇到的问题都会迎刃而解。希望这篇记录能帮你少踩几个坑,少熬几次夜。
