如果你也遇到过“git push 半天没反应,最后卡在ssh: connect to host github.com port 22: Connection refused”,那你大概率和我当初一样,对 SSH 和 GitHub 的关系一知半解。SSH 是远程登录和传输文件的加密协议,GitHub 是全世界的代码托管平台,两者一配合,就成了开发者日常最熟悉的操作链路。这篇东西不打算讲教科书,我直接从实际使用出发,把 SSH 密钥生成、GitHub 配置、远程仓库推送、VSCode 远程开发、服务器端 SSH 权限管控这些场景全部串起来讲一遍,你只要照着敲,基本不会再被这些连接问题卡住。
这篇文章适合刚接触 Git 的初学者,也适合被各种 SSH 连接报错折磨过、想系统搞懂原理的人。我会把常见的Connection refused、Permission denied、443 超时、多账号切换、远程服务器连不上等现象全部拆开,把命令背后的逻辑说清楚,顺便附上我实际踩过的坑和解决办法。
1. 为什么我建议你用 SSH 方式操作 GitHub
1.1 从一次“连接被拒”说起
前阵子帮同事调一个 CI 发布流程,他每次跑git pull都报错,日志里写的是:
ssh: connect to host github.com port 22: Connection refused fatal: Could not read from remote repository.他第一反应是“GitHub 挂了”,但我让他先跑一下ssh -T git@github.com,发现同样是 22 端口被拒。后来查了一圈,是那台机器所在网络把外网 22 端口给限制掉了,改成走 SSH over 443 端口(GitHub 官方支持ssh.github.com:443)之后,问题立刻解决。
这个例子说明一件事:用 SSH 连 GitHub 时,真正影响你的往往不是 GitHub 本身,而是网络环境、密钥配置、端口开放情况这些环节。你要是不理解 SSH 的组成,出了问题就只能瞎猜。
1.2 SSH 和 HTTPS,到底差在哪里
很多教程会让你用 HTTPS 克隆仓库,比如git clone https://github.com/xxx/yyy.git。这种方式对临时下载项目确实方便,不需要提前配置任何东西。但如果你要长期推送代码,HTTPS 会让你反复输入用户名和密码(或者 Personal Access Token),体验很割裂。
而 SSH 用的是公钥和私钥配对:私钥留在本地,公钥放到 GitHub 账号里。每次连接时,GitHub 通过加密握手验证你的身份,整个过程不用输入密码,体验更顺滑,也更容易写进自动化脚本。
| 对比项 | HTTPS | SSH |
|---|---|---|
| 克隆匿名仓库 | 直接可用 | 需要先生成密钥并配置 |
| 推送身份验证 | 用户名 + Token/密码 | 私钥自动完成 |
| 密码过期/失效 | 会频繁中断操作 | 基本不涉及 |
| 自动化部署友好性 | 一般 | 很好 |
| 常见端口 | 443 | 22(也支持 443) |
实际操作下来,我所有需要长期维护的仓库都统一改成 SSH 协议,只有偶尔下载一次性项目时才用 HTTPS。如果你还在反复输密码,建议尽早切换到 SSH。
2. 从生成密钥到第一次 push:完整 SSH 配置
2.1 密钥生成的参数到底怎么选
生成 SSH 密钥的标准命令是:
ssh-keygen -t ed25519 -C "your_email@example.com" -f ~/.ssh/id_ed25519先说-t ed25519。以前很多教程用rsa -b 4096,但现在 GitHub 已经支持 Ed25519 算法,它密钥更短、生成更快、安全性也比传统 RSA 更现代。如果你的 Git 版本比较老,或者要兼容旧服务器,可以退回去用 RSA;普通场景直接 Ed25519 就行。
-C参数是注释,通常填邮箱,作用是让你以后能认出这把密钥是谁的。-f指定密钥保存路径,如果不写会默认存到~/.ssh/id_ed25519。运行之后会让你设置 passphrase,也就是私钥口令。
这里有个常见分歧:到底要不要设置 passphrase?如果设了,每次 SSH 连接可能都要输入一次口令;不设则更省事,但私钥一旦泄露,对方就能直接使用。我个人的折中方案是:个人电脑上设置一个简单的 passphrase,同时开启ssh-agent来缓存;CI 或服务器上用独立生成的密钥且不设 passphrase,权限按最小化原则控制。
生成完成之后,你会看到两个文件:id_ed25519是私钥,id_ed25519.pub是公钥。公钥可以随便给别人,私钥必须留在自己手里。
2.2 把公钥添加到 GitHub,并验证连接
查看公钥内容:
cat ~/.ssh/id_ed25519.pub输出是一长串以ssh-ed25519开头的字符串。把它复制下来,打开 GitHub 网页,进入Settings->SSH and GPG keys->New SSH key,粘贴保存即可。标题可以写“my-laptop”之类,方便以后区分设备。
然后验证连接:
ssh -T git@github.com第一次连接会提示确认主机指纹,输入yes回车。如果看到类似下面这行输出,说明配置成功:
Hi yourname! You've successfully authenticated, but GitHub does not provide shell access.注意这里用的是git@github.com,不是你的邮箱,也不是你的用户名。GitHub 通过公钥来识别你是哪个账号,所以公钥添加到位比什么都重要。
2.3 第一次克隆与推送的完整流程
配置好密钥之后,克隆一个仓库就简单了。在 GitHub 仓库页面找到 SSH 格式的地址,形如:
git@github.com:yourname/repo-name.git克隆到本地:
git clone git@github.com:yourname/repo-name.git cd repo-name修改文件之后推送到远端:
git add . git commit -m "first commit" git push origin main你要是在这一步遇到Permission denied (publickey),基本可以断定是公钥没配对成功。这时候不要慌,按三件事排查:
- 本地正在使用的私钥和 GitHub 上保存的公钥是否成对;
ssh-agent里是否加载了这把私钥;- 是否用了自定义路径或自定义
~/.ssh/config,导致 Git 没找到预期密钥。
2.4 多账号、多仓库的 SSH 配置细节
很多人手上有不止一个 GitHub 账号,或者既要连 GitHub 又要连公司的 GitLab。此时如果每把密钥都叫id_ed25519,就会互相覆盖,连接时也会混淆。
解决办法是给不同账号生成不同文件名的密钥,并在~/.ssh/config里做映射。例如:
# 个人 GitHub Host github.com HostName github.com User git IdentityFile ~/.ssh/id_ed25519_personal # 公司 GitLab Host gitlab.company.com HostName gitlab.company.com User git IdentityFile ~/.ssh/id_ed25519_work这里的关键逻辑是:Host后面写你实际要访问的别名或域名,IdentityFile指定对应私钥路径。配置好之后,Git 会自动根据远程地址匹配到合适的密钥,不用每次手动指定。
我见过最坑的场景是:有人把公司密钥和个人密钥都命名为默认文件名,然后在 GitHub 上添加了公司公钥,结果 GitHub 报“key already in use”。这是因为一把公钥只能绑定一个账号。遇到这种问题,直接把密钥拆开、重新命名,按上面的 config 方式分工,就彻底清净了。
3. GitHub 日常高频操作与连接排障
3.1 从 GitHub 拉取一个具体项目:以 qzonearchive 为例
很多人问我:“GitHub 上看到一个项目,怎么快速在本地跑起来?”这里以热词里反复出现的gaoshu705/qzonearchive为例。简单说一下,这是一个用于本地备份 QQ 空间数据的开源项目,说白了就是把自己发过的说说、日志等数据导出成文件保存下来。对这种个人数据备份工具,我的习惯是先看 README,再 clone 到本地,最后按说明执行。
SSH clone 命令:
git clone git@github.com:gaoshu705/qzonearchive.git如果项目比较老,依赖安装在当前环境有问题,我一般会先建一个干净的虚拟环境,再按仓库里的requirements.txt安装。下载完数据后,通常会在本地生成一个快照目录,方便以后导入或存档。
这类“个人数据备份”的项目有一个共同点:它们大多依赖你主动登录第三方平台来获取数据。在使用前,建议先弄清楚它会把数据传到哪、是否只存在本地、后续能否删除。我对所有第三方工具都坚持一条原则:可确认的本地数据胜过一切云端的承诺。qzonearchive 我实际体验下来,核心流程就是把备份内容落盘到本地目录,相对可控。
3.2 反复遇到的 443 和 22 连接问题
在 GitHub 使用过程中,最常见的两类报错:
第一类是 22 端口直接被拒:
ssh: connect to host github.com port 22: Connection refused这种情况多半是本地网络禁止外连 22 端口,或者 GitHub 的 22 端口在你的网络环境下不可达。GitHub 官方早就提供了 SSH over 443 的备用方式,你只需要在~/.ssh/config里加一段:
Host github.com HostName ssh.github.com Port 443 User git之后再用ssh -T git@github.com验证连接,能通就一切照旧。这个方法是官方支持的,完全合规,也是我在受限网络下最常用的备案。
第二类是连接超时:
ssh: connect to host github.com port 443: Connection timed out这种情况通常不是 SSH 本身的问题,而是到 GitHub 的网络链路不稳定。我的建议是先确认本地网络整体是否正常,比如访问其他网站是否流畅;如果只是 GitHub 偶发超时,可以稍后重试,或者切换热点/网络环境。不要一遇到超时就去折腾密钥,先分清是网络层还是认证层的问题。
3.3 .git 目录和 remote 的注意事项
还有一个特别容易被忽略的坑:有人会把整个项目文件夹直接复制到另一台机器,然后发现git push报错。这是因为.git目录里保存着本地的 remote 配置、暂存区状态,甚至可能是绝对路径的残余信息。复制项目没问题,但复制之后要检查一下:
git remote -v如果 remote 还是老的 SSH 地址,而新机器的密钥没配对,就会认证失败。正确的做法是改 remote 地址或重新设置:
git remote set-url origin git@github.com:yourname/repo-name.git类似地,有些项目里有 Git 子模块。拉取的时候如果只运行git clone,子模块目录可能是空的。一键拉全:
git clone --recurse-submodules git@github.com:yourname/repo-name.git如果你已经 clone 了主仓库,可以手动补:
git submodule update --init --recursive这些细节平时用不到,但一旦遇到对应场景,就是卡你半天的主要原因。
4. VSCode 远程 SSH:从本地开发到服务器开发
4.1 环境准备与 config 写法
VSCode 的 Remote-SSH 插件是我现在写代码的主力方式。它解决的问题是:代码和运行环境都留在服务器上,本地只用一个编辑器界面,实时编辑、实时调试。这样就不会出现“本地能跑、服务器跑不起来”的尴尬。
使用前,先得有 SSH 客户端。Windows 10/11 自带 OpenSSH,不需要额外安装;macOS 和 Linux 则天然支持。然后在 VSCode 里安装Remote - SSH扩展,打开命令面板,选择Remote-SSH: Connect to Host,第一次会引导你配置~/.ssh/config。
一个标准配置:
Host dev-server HostName 192.168.1.100 User root Port 22 IdentityFile ~/.ssh/id_ed25519这里Host是一个简短别名,后续在 VSCode 里连接时只需输入dev-server。如果使用密钥登录,IdentityFile指向你的私钥路径;如果服务器只允许密码登录,也可以不写密钥,VSCode 会弹窗让你输密码。
4.2 连接失败常见原因
VSCode 远程连接失败的频率比想象中高,而且绝大多数问题出在服务器端,而不是插件本身。
第一种是服务器端 SSH 服务没启动。Ubuntu/Debian 上可以执行:
sudo systemctl status ssh sudo systemctl start ssh第二种是防火墙拦截了端口。在服务器上查看:
sudo ufw status如果没开放 22 端口,添加规则:
sudo ufw allow 22/tcp第三种与 VSCode 相关:它连接服务器后需要下载对应的vscode-server,如果服务器不能顺利访问微软的下载地址,可能会一直转圈。遇到这种情况,在配置里换个下载镜像源经常有效,但根据我的经验,更稳定的做法是先手动确认服务器能正常访问外网,再重试连接。
第四种是known_hosts冲突。如果你重装过服务器,VSCode 会提示 Host key 验证失败。解决办法是打开本地~/.ssh/known_hosts,删掉对应主机的旧记录,再重新连接。
4.3 远程开发配合 GitHub 使用的推荐工作流
服务器上开发有个天然优势:你可以在服务器上直接配置 SSH 密钥连 GitHub,本地 VSCode 不需要保存 GitHub 凭据。
我的习惯是:服务器上执行ssh-keygen生成专用密钥,再把公钥加入 GitHub 的 Deploy Keys 或账号 SSH Keys。然后把仓库 clone 到服务器某个目录,VSCode 远程打开这个目录,直接在服务器上操作 Git。
这样一来,你本地只需要一个 VSCode 窗口,其他所有计算都在服务器端完成。代码提交、拉取、部署都不需要再把文件拷来拷去,也避免了本地环境和服务器环境不一致带来的诡异 bug。
5. 服务器与交换机上的 SSH 细节:不止 GitHub
5.1 只允许特定用户组远程登录
GitHub 是 SSH 最广为人知的应用场景,但 SSH 本身更多的是用来远程登录服务器。很多热词里出现“设置只有 wheel 组的用户可以 ssh 远程登录”“取消 root 用户 SSH 登录”,这其实是 Linux 服务器安全基线的常见操作。
用 SSH 登录服务器之前,最好先确认是不是 root。root 权限太大,一旦密钥泄露,整台机器等于裸奔。常见做法是创建一个普通用户,把它加入wheel组,再修改 SSH 服务配置限制登录。
修改/etc/ssh/sshd_config:
# 只允许 wheel 组的用户登录 AllowGroups wheel # 禁止 root 直接登录 PermitRootLogin no修改后重启服务:
sudo systemctl restart sshd这里提醒一句:PermitRootLogin no是个“断后路”操作。如果你当前就是 root 且没配置普通用户,改完可能会把自己锁在门外。我吃过这种亏,所以每次修改sshd_config之前,都先开一个额外的 SSH 连接保持不断,确认新配置没问题后再退出旧的会话。
5.2 局域网交换机配置:H3C 设备
SSH 远程登录不只是 Linux 服务器的专利,企业里的 H3C 交换机也支持通过 SSH 管理。配置流程比 Linux 稍复杂一点,核心逻辑是先给交换机生成密钥对,再开启 SSH 服务。
H3C 上的典型配置思路如下:
system-view public-key local create rsa ssh user admin ssh user admin authentication-type password ssh user admin service-type stelnet local-user admin password simple your-password service-type ssh quit stelnet server enable要注意的是,不同 H3C 版本和型号的命令细节有差异。比如有的版本需要设置ssh server compatible-version,有的则要去指定ssh authentication-type default。最稳妥的做法是先用display ssh server status查看当前状态,再对照官方命令文档调整。
配置完成后,在本地用命令连接:
ssh admin@192.168.1.15.3 其他系统:AIX、欧拉、Windows
热词里还出现了一批特定系统,比如 AIX 光盘安装 SSH、欧拉离线安装 SSH、Windows SSH 认证失败,这些我都零星处理过,简单把经验列一下。
AIX 是老牌 UNIX 系统,默认可能不自带 OpenSSH,需要通过安装光盘安装。基本流程是挂载光盘、找到openssh.base相关文件、用installp或smitty安装,然后启动 SSH 子服务。AIX 的坑在于依赖包较多,如果缺了openssl或openssh.license,安装会中断,建议先把所有依赖包都装上。
欧拉(openEuler)离线安装 SSH 的场景也很多。离线环境必须先把 RPM 包准备好,然后执行:
rpm -ivh openssh-*.rpm如果提示依赖缺失,就按缺什么补什么的方式逐个装上。装完之后启动服务并设为开机自启:
systemctl start sshd systemctl enable sshdWindows 上的 SSH 认证失败就更有意思了,很多问题是服务没启动。Windows 10 以上版本自带 OpenSSH Server,但默认不是开启状态。可以在“设置”->“应用”->“可选功能”里安装 OpenSSH 服务器,然后确认服务状态:
Get-Service sshd Start-Service sshd如果服务已经启动仍然认证失败,常见原因包括:防火墙没放行 22 端口、用户密码策略限制、管理员组映射不对。在 Windows 上,最简单可靠的测试方式是本地先跑ssh localhost,验证服务是否正常,再转向远程连接。
6. 常见报错排查速查表与个人心得
6.1 排查速查
很多报错光看字面意思容易误导人。我把实际操作中遇到的典型问题整理成速查表,方便你对照。
| 报错信息 | 原因 | 解决办法 |
|---|---|---|
Permission denied (publickey) | 本地私钥与服务器公钥不匹配,或密钥未加载 | 检查公钥是否添加到 GitHub/服务器;运行ssh-add ~/.ssh/id_ed25519再试 |
Connection refused | 22 端口被限制,或远程 SSH 服务未启动 | 确认服务器sshd状态;尝试 SSH over 443 配置 |
Connection timed out | 网络链路不通或防火墙丢包 | 换网络测试;检查防火墙和安全组策略 |
Host key verification failed | 服务器重装过,本地known_hosts有旧指纹 | 编辑~/.ssh/known_hosts,删除旧记录后重连 |
git@github.com: Permission denied (publickey) | GitHub 公钥配置有误,或密钥已绑定其他账号 | 检查 GitHub 账号下公钥;使用独立密钥并配置~/.ssh/config |
Connection closed by remote host | SSH 服务异常或服务器主动断开 | 查看服务器/var/log/secure或journalctl日志 |
写到这里,我更想强调一个排查思路:不要被报错文本里的某个单词带走。Connection refused不等于 GitHub 挂了,Permission denied不一定是密码错了。先判断是网络层问题、服务层问题、还是密钥认证层问题,再逐层检查,效率最高。
6.2 几个我踩过坑的小习惯
最后分享几个我这些年养成的习惯,不算高深,但每一次都能帮我省下大量时间。
第一,修改服务器 SSH 配置前,永远先开一个备用连接。我之前在调sshd_config时把PermitRootLogin设成no,结果当前 root 会话还没退出,新配置一重启,我就再也连不上了。后来只能通过云控制台重置密码才救回来。自那以后,凡是动sshd_config、/etc/sudoers这类文件,我都会先开两个终端窗口,一个改配置一个待命。
第二,给每台设备、每个账号生成独立的 SSH 密钥。不要一把密钥走天下。密钥泄露的时候你只需要撤销一把,不会牵连其他系统。文件名上也最好带标识,比如~/.ssh/id_ed25519_github、~/.ssh/id_ed25519_devserver,配合 config 文件使用非常清晰。
第三,定期检查本地~/.ssh/known_hosts文件。这个文件会越积越大,里面很多旧记录已经没用了。我一般一个月清理一次,遇到“Host key verification failed”时也会第一时间想到它。
第四,多用ssh -v调试。很多人不知道 SSH 自带详细日志模式,连接出问题时直接加-v(甚至-vvv)运行,它会打印出密钥协商、端口连接、认证方式等全部过程。定位问题是神兵利器。比如你能清楚看到连接停在哪个环节,是卡在connect还是卡在authentications,就很好对症下药了。
SSH 和 GitHub 的组合看似基础,但牵扯到密钥、端口、网络、服务配置多个层面,任何一个环节出错,表现都可能是五花八门的报错。从我自己的经验看,花点时间把 SSH 原理和配置流程彻底搞明白,远比遇到问题临时搜索效率高得多。希望这篇文章能帮你把这套链路捋顺,少走一些我当年走过的弯路。