1. 项目概述:为什么我们需要一个公网可访问的私有Git仓库?
最近在带团队做一个小型项目,代码管理成了个不大不小的麻烦。用GitHub、Gitee这些公有平台吧,代码放别人服务器上总有点不放心,尤其是涉及一些内部业务逻辑的代码。用公司内网的GitLab服务器吧,一旦需要在家办公或者和外部协作者沟通,访问就成了大问题。相信不少独立开发者、小团队或者学生党都遇到过类似的困境:既想要私有仓库的安全性,又希望它能像公有仓库一样随时随地可访问。
这个需求其实非常普遍。你可能正在开发一个商业项目原型,代码暂时不想公开;或者是一个课程设计,需要和同学协作;又或者像我一样,管理着多个个人项目的代码,需要一个统一的、私有的、自己完全掌控的托管中心。公有云服务要么有隐私顾虑,要么有容量或协作人数限制。而自建一个能在公网访问的私有Git仓库,就成了一个极具吸引力的解决方案。它不仅能让你完全掌控数据,还能根据团队习惯定制工作流,更重要的是,它打通了内网与公网的壁垒,让分布式协作变得和用GitHub一样方便。
听起来好像很复杂,涉及到服务器、网络、安全等一系列问题。但别担心,这篇教程的目标就是把它拆解成一个个清晰的步骤,即使你之前没有太多服务器运维经验,只要跟着操作,也能一步步搭建起来。整个过程的核心可以概括为:在一台具有公网IP的服务器(或通过内网穿透技术暴露到公网的本地机器)上,部署Git服务,并配置SSH访问,最终实现通过类似git@your-domain.com:project.git这样的地址进行克隆、推送和拉取。
2. 核心方案选型与前期准备
在动手之前,我们需要明确几个关键选择,这直接决定了后续的搭建路径和复杂程度。主要分为两大方向:拥有公网IP的云服务器和无公网IP的本地机器(依赖内网穿透)。
2.1 服务器环境选择:云服务器 vs. 本地穿透
方案一:使用云服务器(推荐给大多数用户)这是最直接、最稳定的方案。你需要在阿里云、腾讯云、华为云等厂商购买一台最低配置的云服务器(通常1核1G或1核2G就足够,新用户首年成本极低)。它的优势非常明显:
- 拥有独立的公网IP:这是实现公网访问的基石,无需依赖第三方穿透服务的中转,速度更快,连接更稳定。
- 控制权完整:你可以完全掌控服务器的防火墙、安全组策略,定制化程度高。
- 24小时在线:云服务器默认持续运行,无需担心家里断电或网络波动导致服务中断。
对于长期使用或小型团队协作,我强烈建议选择此方案。初期投入不大,但换来的是省心和高可靠性。本教程也将主要围绕云服务器方案展开。
方案二:本地机器 + 内网穿透(适合临时或学习用途)如果你的代码仓库只是临时用用,或者想先零成本体验一下,可以使用此方案。它利用内网穿透工具(如 frp、ngrok、花生壳等),将你本地电脑的某个端口(如SSH的22端口或Git服务的端口)映射到一个公网地址上。
- 优点:零硬件成本,利用现有电脑即可。
- 缺点:
- 依赖第三方服务:大多数免费的穿透服务有带宽、流量、连接数或域名稳定性的限制。
- 稳定性差:你的本地电脑必须一直开机并保持网络连接,一旦关机或重启,服务就中断了。
- 安全性挑战:将本地端口直接暴露到公网,如果配置不当,风险比云服务器更高。
- 性能瓶颈:数据传输需要经过穿透服务商的中转服务器,速度受其影响。
注意:对于生产环境或重要项目,请务必使用方案一。方案二仅建议用于演示、测试或个人临时同步。
2.2 操作系统与基础工具准备
无论选择哪种方案,我们都需要一个Linux环境。绝大多数云服务器镜像和本地虚拟化都首选Ubuntu Server LTS或CentOS/Rocky Linux。本教程以Ubuntu 22.04 LTS为例,因为其软件源较新,社区支持好,命令也相对通用。
在开始之前,请确保你拥有服务器的root权限或可通过sudo执行管理员命令。你需要通过SSH工具(如系统自带的终端、PuTTY、Xshell、VS Code Remote-SSH插件等)连接到你的服务器。
首先,我们进行系统更新和安装最基础的Git软件包:
# 更新软件包列表 sudo apt update # 升级已安装的软件包(可选,但建议执行) sudo apt upgrade -y # 安装 Git 和用于管理用户的工具 sudo apt install git -y安装完成后,可以通过git --version验证。
3. 搭建Git服务核心:配置SSH访问与仓库目录
Git本身是一个分布式版本控制系统,它可以通过多种协议传输数据,其中最常用、最安全高效的就是SSH协议。我们自建私有仓库,本质上就是在服务器上创建一个可以通过SSH访问的Git用户,并将仓库目录配置为该用户的授权访问区域。
3.1 创建专用的Git系统用户
为了安全和管理方便,我们不建议直接使用root用户来管理Git仓库。创建一个专用的、权限受限的用户是更佳实践。
# 创建一个名为‘git’的系统用户,并指定其家目录为 /home/git sudo adduser --system --shell /bin/bash --gecos 'Git Version Control' --group --disabled-password --home /home/git git命令参数解释:
--system: 创建系统用户,无登录密码。--shell /bin/bash: 为其指定bash shell,方便后续操作。--home /home/git: 指定家目录。--disabled-password: 禁用密码登录,强制使用更安全的SSH密钥认证。
3.2 配置SSH密钥认证(免密登录的核心)
SSH密钥认证是安全访问的黄金标准。它使用一对加密密钥(公钥和私钥)来代替传统的密码。公钥放在服务器上,私钥留在你的本地电脑。连接时,服务器用公钥挑战,本地用私钥应答,验证通过即可登录。
第一步:在本地生成SSH密钥对(如果你还没有的话)在你的本地电脑(Windows/Mac/Linux)上打开终端或Git Bash,执行:
ssh-keygen -t ed25519 -C "your_email@example.com"-t ed25519: 指定使用Ed25519算法,它比传统的RSA更安全、更快、密钥更短。如果你的旧系统不支持,可以用-t rsa -b 4096替代。-C: 添加注释,通常用你的邮箱,便于识别密钥所有者。 执行命令后,会询问你密钥保存路径(直接回车使用默认路径~/.ssh/id_ed25519)和设置密钥密码(可选,为私钥再加一层保护,直接回车则不留密码)。
完成后,你会在~/.ssh/目录下得到两个文件:
id_ed25519:私钥文件,绝对不要分享给任何人!相当于你的家门钥匙。id_ed25519.pub:公钥文件,需要上传到服务器。相当于一把能开你家门锁的“锁芯”,交给服务器保管。
第二步:将本地公钥上传到服务器我们需要将本地的公钥内容,添加到服务器上git用户的授权密钥列表中。
查看并复制你的公钥内容:
cat ~/.ssh/id_ed25519.pub全选复制输出的那一长串以
ssh-ed25519 AAA...开头,注释结尾的文本。在服务器上,切换到
git用户,并创建SSH授权目录和文件:# 切换到git用户 sudo su - git # 创建.ssh目录并设置严格权限(700表示只有所有者可读、写、执行) mkdir -p ~/.ssh chmod 700 ~/.ssh # 创建authorized_keys文件并设置权限(600表示只有所有者可读、写) touch ~/.ssh/authorized_keys chmod 600 ~/.ssh/authorized_keys # 编辑该文件,将你复制的公钥粘贴进去 nano ~/.ssh/authorized_keys在nano编辑器中,粘贴你的公钥,然后按
Ctrl+X,再按Y,最后回车保存退出。
第三步:测试SSH密钥登录在本地终端,尝试用git用户SSH连接到你的服务器(将your-server-ip替换为你的服务器公网IP或域名):
ssh git@your-server-ip如果配置正确,你应该不需要输入密码就能直接登录(或只输入你之前为私钥设置的密码),并看到类似git@hostname:~$的提示符。输入exit退出。
实操心得:权限 (
chmod 700和600) 是SSH密钥认证能否成功的关键。权限过松(如755),SSH出于安全考虑会直接拒绝连接,并可能在服务器的/var/log/auth.log中看到Authentication refused: bad ownership or modes的错误。这一步务必检查仔细。
3.3 初始化第一个裸仓库(Bare Repository)
Git仓库分为两种:工作仓库(Working Repository)和裸仓库(Bare Repository)。工作仓库包含工作区(你看到的项目文件)和.git目录。而裸仓库没有工作区,只有.git目录里的内容,专门用于作为中央仓库进行推送和拉取。我们的服务器上应该存放裸仓库。
在服务器上,我们以git用户身份操作:
# 确保在git用户的家目录或你规划的目录下 cd /home/git # 创建一个用于存放所有仓库的目录,例如叫‘repositories’ mkdir repositories cd repositories # 初始化一个裸仓库,名字叫‘my-project.git’ git init --bare my-project.git--bare参数是关键。初始化后,你会看到my-project.git目录下直接就是branches、hooks、objects等Git内部目录,而没有项目源文件。这个目录就是你的远程仓库地址。
4. 从本地连接与使用自建仓库
服务端配置好后,现在你可以在任何能通过SSH访问到这台服务器的机器上,像使用GitHub一样使用这个私有仓库了。
4.1 本地克隆远程仓库
假设你的服务器IP是123.123.123.123,仓库路径是/home/git/repositories/my-project.git。那么远程仓库的地址就是:
git@123.123.123.123:/home/git/repositories/my-project.git通常,我们会为git用户设置一个简化的访问路径。这可以通过修改服务器上git用户的shell来实现,但更简单通用的方法是直接使用上述绝对路径。
在你的本地电脑上,找一个合适的目录,执行克隆命令:
git clone git@123.123.123.123:/home/git/repositories/my-project.git因为我们已经配置了SSH密钥,这里不会要求输入密码。克隆完成后,你会得到一个空的my-project目录(因为仓库是刚初始化的)。
4.2 进行常规Git操作
进入克隆下来的本地仓库目录,进行常规开发:
cd my-project echo "# My Private Project" >> README.md git add README.md git commit -m "Initial commit" git push origin main如果你的本地默认分支是master,则将最后一句改为git push origin master。push命令会将本地的提交推送到你的自建远程仓库。
4.3 为仓库添加更多协作者
团队协作时,你需要将其他成员的公钥也添加到服务器git用户的~/.ssh/authorized_keys文件中。每个成员一行,一个公钥占一行。
重要安全与管理建议:对于稍大的团队,将所有成员的公钥都放在同一个authorized_keys文件里会难以管理。更专业的做法是使用Gitolite或Gitolite3。它是一个用Perl写的Git仓库管理工具,能基于公钥实现精细到分支的读写权限控制。你可以指定用户A只能读develop分支,用户B可以读写main和develop分支等。对于超过3人的团队,我强烈建议在完成基础搭建后,研究部署Gitolite,它能极大提升管理效率和安全性。
5. 安全加固与高级配置
将服务器暴露在公网上,安全是头等大事。以下是一些必须做的基础加固措施。
5.1 修改SSH服务端口
默认的SSH端口22是黑客扫描的重灾区。修改为一个非标准的高位端口(如2222)能过滤掉绝大部分自动化攻击脚本。 在服务器上编辑SSH配置文件:
sudo nano /etc/ssh/sshd_config找到#Port 22这一行,去掉注释#,并将22改为你想要的端口,例如:
Port 2222务必注意:修改前,请确保你有其他方式能访问服务器(如云控制台的VNC),并且修改后不要立即重启SSH服务,先测试新端口是否可用。
5.2 测试新端口并禁用密码登录
在本地新开一个终端窗口,测试新端口连接:
ssh -p 2222 git@your-server-ip确认能够成功登录。
禁用SSH密码认证,强制使用密钥: 再次编辑
/etc/ssh/sshd_config,确保以下配置:PasswordAuthentication no PubkeyAuthentication yes PermitRootLogin no # 禁止root用户直接SSH登录,增加安全性重启SSH服务使配置生效:
sudo systemctl restart sshd重启后,立即用已经测试成功的新端口终端窗口再次连接,确保一切正常。现在,任何尝试用密码登录的行为都会被拒绝,只有拥有有效私钥的用户才能连接。
5.3 配置服务器防火墙
使用ufw(Uncomplicated Firewall) 可以轻松管理防火墙规则。
# 安装ufw(如果未安装) sudo apt install ufw -y # 设置默认策略:拒绝所有入站,允许所有出站 sudo ufw default deny incoming sudo ufw default allow outgoing # 允许新的SSH端口 sudo ufw allow 2222/tcp # 如果你后续需要通过HTTP/HTTPS访问GitWeb之类的界面,可以开放80/443端口 # sudo ufw allow 80/tcp # sudo ufw allow 443/tcp # 启用防火墙 sudo ufw enable # 查看规则状态 sudo ufw status verbose现在,只有端口2222是对外开放的,其他所有端口都被屏蔽,大大减少了攻击面。
5.4 为仓库配置域名(可选但推荐)
总是用IP地址访问不够优雅,也不利于记忆。你可以购买一个域名,并为其添加一条A记录,指向你的服务器IP。例如,将git.yourdomain.com解析到123.123.123.123。
之后,你的仓库克隆地址就可以变得更专业:
git clone git@git.yourdomain.com:/home/git/repositories/my-project.git你还可以在本地SSH配置文件~/.ssh/config中为这个连接创建别名,进一步简化:
Host mygit HostName git.yourdomain.com Port 2222 User git IdentityFile ~/.ssh/id_ed25519配置后,克隆命令简化为:git clone mygit:/home/git/repositories/my-project.git
6. 常见问题排查与维护技巧
即使按照步骤操作,也可能会遇到一些问题。这里记录一些我踩过的坑和解决方案。
6.1 SSH连接失败问题排查表
| 问题现象 | 可能原因 | 排查命令/步骤 |
|---|---|---|
Connection refused | 1. SSH服务未运行 2. 防火墙阻止 3. 端口错误 | 1.sudo systemctl status sshd2. sudo ufw status3. 确认连接命令中的IP和端口 |
Permission denied (publickey) | 1. 公钥未正确上传 2. authorized_keys文件权限错误3. 私钥未加载或路径不对 | 1. 检查服务器~/.ssh/authorized_keys内容2. 确认权限为 600,.ssh目录为7003. 本地执行 ssh-add -l查看加载的密钥,或用-i指定私钥路径 |
| 连接超时 | 1. 服务器IP错误 2. 安全组/网络ACL未放行端口(云服务器) | 1. 核对服务器公网IP 2. 登录云控制台,检查安全组规则是否允许你的IP访问指定端口 |
克隆时提示not a git repository | 远程路径错误或仓库不是裸仓库 | 1. 确认服务器上仓库路径是否正确 2. 确认是用 git init --bare创建的 |
一个关键的调试技巧:在本地连接时添加-v参数(如ssh -v -p 2222 git@your-server-ip),会输出详细的连接过程日志,能精准定位到在哪一步失败了。
6.2 Git操作相关问题
推送时提示
[remote rejected] (branch is currently checked out): 这是因为你推送到了服务器上一个非裸仓库(即有工作区的仓库)。服务器上的仓库必须是--bare的。解决方法是备份服务器上该仓库的文件,删除原目录,重新用git init --bare初始化,或者将本地仓库推送到一个新的裸仓库地址。如何备份服务器上的所有仓库?最简单的方式是定期打包整个
/home/git/repositories/目录,并传输到其他存储位置。sudo tar -czf /backup/git-repos-$(date +%Y%m%d).tar.gz -C /home/git repositories/也可以使用
git bundle命令对单个仓库进行增量备份。仓库越来越大,如何清理?长期项目会产生很多松散对象和过期引用。可以在服务器仓库目录下执行
git gc --aggressive --prune=now来进行垃圾回收和压缩。对于本地克隆的仓库,定期执行git gc也有好处。
6.3 性能与监控建议
对于小规模使用,上述配置性能完全足够。如果仓库数量增多或团队变大,可以考虑:
- 启用Git的打包功能:在服务器仓库的
hooks/post-update文件中加入git gc命令,让每次推送后自动优化仓库。 - 监控磁盘空间:确保
/home分区有足够空间,可以用df -h查看。 - 查看连接日志:关注
/var/log/auth.log,可以及时发现异常登录尝试。
7. 内网穿透方案补充说明
如果你因为条件所限,只能采用本地机器+内网穿透的方案,其核心步骤与上述云服务器方案在“创建Git用户”、“配置SSH密钥”、“初始化仓库”部分是完全一致的。区别仅在于网络层:
- 选择穿透工具:以frp为例,你需要在公网有一台具有固定IP的服务器(可以是另一台云服务器,也可以是朋友提供的)作为frp 服务端,在你的本地机器上运行frp 客户端。
- 配置端口映射:在 frp 客户端配置文件中,将本地
22端口(或你为Git服务指定的其他端口)映射到服务端的某个高位端口(如6000)。 - 连接地址变化:此时,你的远程仓库地址将变为
git@frp-server-ip:6000:/path/to/repo.git。你需要通过-p参数指定映射后的端口。
踩坑提醒:使用内网穿透时,务必在本地防火墙和路由器中做好端口转发设置。同时,由于SSH连接经过中转,首次克隆或推送大型仓库时可能会比较慢,且稳定性高度依赖于穿透服务商的质量。因此,再次强调,此方案仅作临时用途。
走到这里,你已经拥有了一个完全受自己控制、通过公网可访问的私有Git仓库。它可能没有GitLab、Gitea那些Web界面和项目管理功能,但它核心的版本控制、分布式协作能力是完整且高效的。对于个人或小团队来说,这种极简的SSH+Git方案往往是最轻量、最直接的选择。后续如果你需要Issue跟踪、Pull Request、Web代码浏览等功能,可以考虑在同样的服务器上部署Gitea或GitLab CE,它们提供了更完善的开源解决方案,但部署和维护的复杂度也会相应增加。无论如何,今天搭建的这个基础服务,已经为你打开了自建代码托管的大门。