ARTICLE DETAIL

资讯详情

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

GitHub Classic Token 服务器部署:拉取私有仓库代码全流程指南

GitHub Classic Token 服务器部署:拉取私有仓库代码全流程指南 在日常的服务器部署和自动化流程里GitHub 应该是最常用的代码托管平台了。但很多人第一次在服务器上用 HTTPS 方式拉取私有仓库代码时都会碰到同一个尴尬场面明明本地电脑上能正常 clone服务器上输入账号密码却一直报Authentication failed。其实从 2021 年 8 月起GitHub 就已经正式取消了密码用于 Git 操作的认证方式现在唯一推荐的 HTTPS 认证手段就是 Personal Access Token。这篇文章就是围绕“用 classic token 拉取 GitHub 代码进行部署”这个场景把从创建 token、配置权限、到在服务器上安全拉取代码、部署上线的整个链路结合我自己的实际经验完整走一遍。内容会比较细适合正在做 CI/CD、服务器环境初始化、或者被 Git 认证坑过的同学参考。我会尽量把每一步的“为什么这么做”也讲清楚而不是只丢给你几条命令。1. 为什么要用 Token 而不是密码拉代码1.1 GitHub 认证机制的变化GitHub 在 2020 年 7 月就开始要求所有在 GitHub 上的操作使用基于令牌的认证并在 2021 年 8 月 13 日正式移除了对账户密码进行 Git 操作的支持。也就是说你再也不能在命令行里用用户名 密码的形式去git clone一个私有仓库。如果你硬要这么干GitHub 会直接拒绝你的请求返回一段很长的提示信息告诉你需要使用 PAT 或者 SSH key。对于服务器部署场景来说这个变化影响很大。以前很多部署脚本里会写死密码或者通过交互提示输入密码现在这条路彻底堵死了。而 classic token 就是替代密码的一种访问凭据它本质上是一串随机生成的字符串通过 HTTP Header 或者 URL 参数的方式传给 GitHub 服务端让它知道你是一个合法用户。1.2 Classic Token 和 Fine-grained Token 的取舍GitHub 现在同时支持两种 PATClassic Token 和 Fine-grained Token。Classic Token 是早期的模式它可以访问你账号下所有有权限的仓库权限范围通过repo、workflow之类的 scope 来控制。Fine-grained Token 是后来推出的精细化令牌可以指定某一个仓库或某几个仓库权限控制也更细致。我用的最多的是 Classic Token原因很直接配置简单在命令行里用起来方便很多老项目、旧版 CI 工具和部署脚本只支持 classic tokenFine-grained Token 虽然更安全但在部分 GitHub API 或第三方工具里还没完全覆盖容易遇到兼容性问题对于服务器拉代码这种单一用途classic token 足够用了所以在本文的场景里我默认使用 classic token。1.3 Token 为什么适合部署场景Token 相比密码有几个很实际的好处可以有独立的过期时间比如 90 天、180 天即使泄露风险窗口可控可以只授权需要的权限比如只给repo不会把你的账号完全暴露可以随时吊销不需要改密码可以在 CI/CD 系统里作为 secret 变量存在不直接暴露在代码里尤其是在无人值守的服务器上用 token 配合 Git 的凭据管理机制可以做到一次性配置之后git pull不再需要人工干预。2. 创建 Classic Token 的完整流程与权限勾选2.1 进入创建页面登录 GitHub 后点击右上角头像选择Settings然后在左侧菜单最底部找到Developer settings。接着进入Personal access tokens再选择Tokens (classic)最后点击Generate new token选择Generate new token (classic)。这里注意很多人会误以为Fine-grained tokens是默认推荐选项但对于经典场景还是选Tokens (classic)最稳妥。2.2 必填信息和权限设置创建 classic token 时有几个东西需要填Note给这个 token 起一个容易识别的名字比如deploy-server-2025方便日后管理Expiration过期时间。我建议不要选No expiration哪怕麻烦一点也要定期更换。一般选 90 天比较合理既能减少维护频率又不至于让 token 长期有效Select scopes这里是最关键的一步。如果你只是要拉取仓库代码勾选repo就足够了repo权限是整个 token 的核心权限它包含对公共仓库和私有仓库的完整读写能力。如果你需要拉取代码后触发 GitHub Actions还要额外勾选workflow。如果你要删除仓库或者修改仓库设置才需要delete_repo等高级权限。我见过很多人图省事直接全选所有 scope这其实非常危险。Token 一旦泄露攻击者就能用你的账号做任何事。做部署用途的话权限越小越好。2.3 Token 显示与保存点击Generate token之后页面会显示一次完整的 token 字符串。注意这个字符串只显示这一次离开页面后就再也看不到完整内容了。GitHub 只会保留 token 的哈希值用于后续校验无法找回原文。所以生成后要立刻复制保存到一个安全的地方比如密码管理器。千万不要直接放到代码仓库或公共文档里。我自己会先把 token 存到服务器的环境变量文件里同时保存一份到本地的密码管理器双重备份。2.4 实际操作中的一个细节如果你在创建 token 时服务器环境已经存在几个旧 token建议顺手在Tokens (classic)列表里检查一下有没有已经不用了的直接Delete掉。避免积累过多缺乏管理的 token这也是安全习惯。3. 在服务器上使用 Token 拉取代码的几种姿势3.1 最直接的方法URL 内嵌 token拿到 token 后最直接的 clone 方式是这种git clone https://用户名:TOKENgithub.com/用户名/仓库名.git比如git clone https://myaccount:ghp_xxxx123456github.com/myaccount/myproject.git这里的用户名是你的 GitHub 登录名TOKEN是刚刚生成的 classic token。这种方式适合临时手动操作跑完命令后就能把仓库拉到本地。但它的缺点也很明显Token 会出现在 shell 历史记录里如果你敲命令时有人在看屏幕token 可能被看到如果后续git remote -v查看远程地址也会暴露出 token不小心输出到日志里就是事故所以我一般只在调试的时候用这种方式正式的部署脚本绝不这么写。3.2 更安全的方法使用环境变量注入在部署脚本里推荐用环境变量来传 token。比如export GITHUB_TOKENghp_xxxx123456 git clone https://x-access-token:${GITHUB_TOKEN}github.com/myaccount/myproject.git这里用x-access-token作为用户名并不是必须的但 GitHub 官方文档里推荐过这种写法可以避免某些场景下 url 解析错误。在 CI/CD 系统里你可以把GITHUB_TOKEN配置成 secret 变量运行时动态注入到环境里这样就不会把 token 写死在脚本文件里。3.3 一劳永逸的方法Git 凭据管理器对于自己长期维护的服务器我更推荐用 Git 自带的凭据存储功能让git clone过程只出现一次认证之后所有git pull都自动携带 token不需要每次手动注入。先做一次带 token 的 clone 或者远程更新然后配置 credential helpergit config --global credential.helper store然后执行一次git clone https://x-access-token:${GITHUB_TOKEN}github.com/myaccount/myproject.git之后 Git 会把 URL 中的用户名和 token 写入~/.git-credentials文件。后续同一主机下的git pull就会自动复用这个凭据。不过请注意store模式的凭据是明文存储在用户目录下的。如果服务器上还有其他用户或者你觉得这台机器的安全性不够高建议改用cache模式让 token 只在内存中保留一段时间git config --global credential.helper cache --timeout3600这样 token 会在 3600 秒内缓存过期后重新认证一次即可。如果用的是 Linux 服务器做单用户部署store模式其实也能接受只要管理好文件权限就行。3.4 不要在命令行直接敲 token无论用哪种方式我都强烈建议不要直接在交互式 shell 里把 token 作为明文参数打一遍。因为 shell 的历史记录文件比如~/.bash_history会保留你敲过的命令。哪怕你后面删了文件也可能已经被日志系统采集走。正确做法是把 token 写入一个只有当前用户可读的环境变量文件。比如cat ~/.deploy_env EOF export GITHUB_TOKENghp_xxxx123456 EOF chmod 600 ~/.deploy_env source ~/.deploy_env执行完再启动部署脚本就不需要再在命令行里显式写出 token。4. 部署场景的完整实践从拉取到上线4.1 部署前的服务器环境准备假设我们有一台全新的 Ubuntu 服务器想要把 GitHub 上的一个 Node.js 项目部署上去。首先确认服务器已经安装好 Git 和 Node.jssudo apt update sudo apt install -y git nodejs npm然后创建一个专门的部署用户避免直接用 root 部署sudo adduser deploy sudo usermod -aG sudo deploy su - deploy部署用户的意义在于即使 token 或者某个项目出错也不会对服务器的系统文件造成致命影响。很多部署事故都是从 root 权限执行了错误的rm -rf开始的。4.2 拉取代码的完整步骤登录部署用户后创建目录结构mkdir -p /home/deploy/apps cd /home/deploy/apps然后设置环境变量并 cloneexport GITHUB_TOKENghp_xxxx123456 git clone https://x-access-token:${GITHUB_TOKEN}github.com/myaccount/myproject.git cd myproject如果项目本身是私有仓库这一步就能正常拉下来了。如果是公共仓库其实不需要任何认证这也是很多人容易搞混的地方公共仓库用git clone https://github.com/...即可私有仓库才需要 token。4.3 部署脚本示例拉下来之后通常还要做依赖安装、构建、启动三个环节。我写一个简单的部署脚本逻辑很清晰#!/bin/bash set -e export GITHUB_TOKENghp_xxxx123456 APP_NAMEmyproject APP_DIR/home/deploy/apps/${APP_NAME} REPO_URLhttps://x-access-token:${GITHUB_TOKEN}github.com/myaccount/${APP_NAME}.git cd ${APP_DIR} echo [1/4] 拉取最新代码 if [ ! -d ${APP_DIR}/.git ]; then git clone ${REPO_URL} ${APP_DIR} else git pull origin main fi echo [2/4] 安装依赖 npm install --production echo [3/4] 构建项目 npm run build echo [4/4] 重启服务 if systemctl list-units --typeservice | grep -q ${APP_NAME}; then sudo systemctl restart ${APP_NAME} else echo 服务不存在请手动创建 systemd unit fi这个脚本里有一个比较关键的细节在git pull之前需要确保 Git 能拿到 token。如果在脚本里通过环境变量 export 了 token那么git pull时 Git 会解析远程 URL自动把环境变量替换进去。但如果你使用的是 credential store那这一步可以省略 token 的环境变量。4.4 配置 systemd 服务实现持续部署为了让服务能在后台稳定运行我通常会配合 systemd 写一个 unit 文件。在/etc/systemd/system/myproject.service中[Unit] DescriptionMy Project Service Afternetwork.target [Service] Userdeploy WorkingDirectory/home/deploy/apps/myproject ExecStart/usr/bin/node /home/deploy/apps/myproject/server.js Restartalways EnvironmentNODE_ENVproduction [Install] WantedBymulti-user.target这样写的好处是服务崩溃后会自动拉起服务器重启后服务也会自动启动。配合前面的部署脚本整个流程就是更新代码 - 安装依赖 - 构建 - 重启服务一气呵成。4.5 用 Webhook 或定时任务做自动化如果你不想每次手动去服务器上执行脚本可以设置一个定时任务每隔几分钟检查一次版本更新crontab -e */5 * * * * cd /home/deploy/apps/myproject git pull origin main /dev/null 21 npm run build sudo systemctl restart myproject但这种方式有个问题Git pull 也需要凭据。在坑里爬过几次后我总结出一个更建议的方案在部署脚本里统一管理 token 环境变量然后用 CI/CD 平台比如 GitHub Actions在代码 push 后自动触发服务器上的部署脚本。这样 token 不需要放在服务器上而是放在 CI 平台的 secret 里安全性和便利性都好很多。如果只有一台服务器并且没有 CI 平台那退回定时任务 credential helper 也完全够用。5. 常见问题与排查技巧实录5.1 fatal: Authentication failed这是最常见的问题。当你执行git clone遇到fatal: Authentication failed for https://github.com/...时可能的原因有Token 复制错误多了空格或者少了字符Token 已经过期当前用户对该仓库没有访问权限账号启用了两步验证但用的不是 PAT而是密码仓库本身不存在或者 URL 拼写有误我的排查顺序是先确认 URL 语法再看 token 是否能访问仓库git ls-remote https://x-access-token:${GITHUB_TOKEN}github.com/myaccount/myproject.git如果上面命令能正常输出远端分支列表说明认证和网络都没问题问题可能出在本地仓库的状态上。如果输出还是有 error那就能确认是 token 的问题。5.2 remote: Repository not found这种报错很容易让人以为是网站访问不了但其实绝大多数情况是仓库名打错或者当前 token 没有该仓库的访问权限。注意 GitHub 对不存在或没权限的仓库都统一返回Repository not found这是出于安全考虑避免泄露仓库是否存在。这时候先检查仓库地址是否正确再检查 token 权限里是否包含repo。5.3 提示 “Support for password authentication was removed”如果你用的 URL 还是https://用户名:密码github.com/...GitHub 会直接拒绝。解决办法就是换成 token或者改用基于 token 的 URL。这个提示现在基本出现在老部署脚本或者某些教学文档里注意看报错信息就能明白。5.4 Token 明文泄露很多人会把 token 写在部署脚本后不小心提交到了公共仓库或者打印到了 CI 日志里。一旦泄露最好的办法是立即到 GitHub 后台删除该 token然后创建一个新的替换掉。不要想着“反正还有别人也这样”token 泄露就是事故处理要果断。可以在部署脚本里加一段日志脱敏的逻辑避免打印 URL 时把 token 带出来echo 克隆中仓库地址${REPO_URL//${GITHUB_TOKEN}/***}Shell 的${REPO_URL//${GITHUB_TOKEN}/***}会把 URL 中 token 部分替换成星号这样日志里就不会出现敏感信息。5.5 Token 过期导致定时任务失败定时任务在凌晨执行部署时如果 token 过期了就会出现间歇性的拉取失败。因为 cron 环境通常不会加载你手动source的环境变量文件所以你可能发现手动执行脚本没问题但 cron 里就不行。解决办法是把环境变量文件写到部署用户下并在脚本开头显式 sourcesource /home/deploy/.deploy_env或者在 systemd unit 里通过EnvironmentFile引入变量[Service] EnvironmentFile/home/deploy/.deploy_env这样即使在 cron 或 systemd 环境下token 也能正常加载。5.6 常见问题速查表报错信息可能原因解决办法fatal: Authentication failedtoken 错误/过期/无权限检查 token确认 repo 权限remote: Repository not found仓库地址错误或权限不足检查仓库 URL 和 token 权限Support for password authentication was removed使用了密码认证改用 classic tokenRPC failed; curl 56 OpenSSL SSL_read网络问题或文件过大尝试浅克隆--depth1或分批次拉取could not read Username未配置 token 且仓库需要认证在 URL 中注入 token 或配置 credential helper5.7 用 SSH 替代 Token 的场景Token 并不是唯一的选择。如果你觉得自己长期维护同一台服务器SSH key 方式可能更简单把公钥添加到 GitHub 账号的 SSH keys 里然后 clone 用gitgithub.com:用户名/仓库名.git认证过程完全透明不需要在 URL 中附带任何敏感信息。但 SSH key 也有它的局限私钥一旦泄露和 token 泄露一样危险多台服务器就需要管理多个 key 对在 CI 容器环境里每次构建都要重新注入私钥反而不如 token 方便所以我现在的基本策略是个人本地开发用 SSH服务器和 CI 环境用 token。两者不冲突按场景选择即可。最后再分享两个小技巧像 token 轮换这种操作建议做一个固定流程。我一般会在手机日历上设置提醒在 token 还有 15 天过期时就更新服务器上的环境变量文件避免临时抱佛脚。如果你用的 CI 平台记得把所有使用该 token 的项目都同步更新漏掉一个可能就会出现构建失败。还有一个容易被忽略的点GitHub 生成的 token 只显示一次但很多人会在浏览器里刷新页面以为还能再看到。其实刷新之后页面就变成了 token 列表无法查看具体值。如果忘了保存只能重新生成一个然后把之前用到的地方全部替换掉。因此生成 token 后的第一个动作永远是找一个安全的地方存下来。
返回列表