Git 服务器搭建完全指南 / 第 2 章 - SSH 基础方案
第 2 章 - SSH 基础方案
使用裸仓库和 SSH 搭建最简单的 Git 服务器——零依赖、零额外服务,适合小团队快速上手。
2.1 架构概览
┌─────────────┐ SSH (22) ┌──────────────────────┐
│ 开发者 A │ ◄────────────────────► │ │
│ (ed25519) │ │ Git 服务器 │
├─────────────┤ │ Ubuntu 22.04 │
│ 开发者 B │ ◄────────────────────► │ │
│ (ed25519) │ │ /opt/git/ │
├─────────────┤ │ ├── project-a.git │
│ 开发者 C │ ◄────────────────────► │ ├── project-b.git │
│ (ed25519) │ │ └── project-c.git │
└─────────────┘ └──────────────────────┘
特点:
- 无额外软件依赖,仅使用系统自带的 OpenSSH
- 认证通过 SSH 密钥完成,安全可靠
- 权限控制通过 Linux 文件系统权限实现
- 无 Web 界面,纯命令行操作
2.2 服务端搭建
2.2.1 创建 Git 用户
# 创建专用的 git 用户
sudo adduser \
--system \
--shell /bin/bash \
--group \
--home /home/git \
--disabled-password \
git
# 确认用户创建成功
id git
# uid=1001(git) gid=1001(git) groups=1001(git)
2.2.2 初始化仓库目录
# 创建仓库根目录
sudo mkdir -p /opt/git
sudo chown git:git /opt/git
sudo chmod 755 /opt/git
# 切换到 git 用户创建裸仓库
sudo -u git bash
cd /opt/git
# 创建裸仓库
git init --bare project-a.git
git init --bare project-b.git
# 查看结构
ls -la project-a.git/
# total 24
# drwxr-xr-x 7 git git 4096 ... .
# drwxr-xr-x 3 git git 4096 ... ..
# drwxr-xr-x 2 git git 4096 ... branches
# -rw-r--r-- 1 git git 66 ... config
# -rw-r--r-- 1 git git 73 ... description
# -rw-r--r-- 1 git git 23 ... HEAD
# drwxr-xr-x 2 git git 4096 ... hooks
# drwxr-xr-x 2 git git 4096 ... info
# drwxr-xr-x 4 git git 4096 ... objects
# drwxr-xr-x 4 git git 4096 ... refs
exit
2.2.3 配置 SSH 公钥认证
# 确保 git 用户的 .ssh 目录存在
sudo mkdir -p /home/git/.ssh
sudo touch /home/git/.ssh/authorized_keys
sudo chown -R git:git /home/git/.ssh
sudo chmod 700 /home/git/.ssh
sudo chmod 600 /home/git/.ssh/authorized_keys
添加开发者的公钥:
# 假设有三个开发者,每人提供公钥
# 开发者 A
echo "ssh-ed25519 AAAAC3Nza...alice@work" | sudo tee -a /home/git/.ssh/authorized_keys
# 开发者 B
echo "ssh-ed25519 AAAAC3Nza...bob@work" | sudo tee -a /home/git/.ssh/authorized_keys
# 开发者 C
echo "ssh-ed25519 AAAAC3Nza...charlie@work" | sudo tee -a /home/git/.ssh/authorized_keys
# 确认
sudo cat /home/git/.ssh/authorized_keys
安全提醒: 每个开发者应使用独立的 SSH 密钥对,不要共享私钥。密钥注释中建议包含用户名和设备信息,如
alice@laptop。
2.2.4 限制 git 用户的 SSH 权限
为防止 git 用户获得完整的 Shell 访问权限,需要限制其只能执行 Git 操作。
# 编辑 /etc/passwd,将 git 用户的 shell 改为 git-shell
# 或使用 usermod
sudo usermod -s /usr/bin/git-shell git
现在 git 用户通过 SSH 只能执行 Git 操作:
# 正常 Git 操作(允许)
ssh git@server "git-upload-pack '/opt/git/project-a.git'"
# 尝试获得 Shell(被拒绝)
ssh git@server
# fatal: Interactive git shell is not enabled.
# hint: ~/git-shell-commands should exist and have read and execute access.
2.2.5 创建受限 Shell 的自定义命令(可选)
如果需要给 git-shell 添加一些管理命令:
# 创建命令目录
sudo mkdir -p /home/git/git-shell-commands
# 创建一个简单的帮助脚本
sudo tee /home/git/git-shell-commands/help << 'EOF'
#!/bin/bash
echo "Available commands:"
echo " help - Show this help"
echo " list - List all repositories"
echo " whoami - Show your SSH key fingerprint"
EOF
# 创建列出仓库的脚本
sudo tee /home/git/git-shell-commands/list << 'EOF'
#!/bin/bash
echo "=== Repositories ==="
ls -1 /opt/git/*.git 2>/dev/null || ls -1 /opt/git/*/*.git 2>/dev/null
EOF
sudo chmod +x /home/git/git-shell-commands/*
sudo chown -R git:git /home/git/git-shell-commands
2.3 多用户管理
2.3.1 共享 git 用户(推荐:简单场景)
所有开发者使用同一个 git 系统用户,通过 SSH 公钥区分身份。
/home/git/.ssh/authorized_keys
├── alice 的公钥
├── bob 的公钥
└── charlie 的公钥
优点:配置简单,用户管理只需操作 authorized_keys 文件。
缺点:无法区分 Git 提交身份(需要靠 Git 的 user.name / user.email 区分);权限控制粒度有限。
管理脚本:
#!/bin/bash
# manage-users.sh - 管理 Git SSH 用户
AUTHORIZED_KEYS="/home/git/.ssh/authorized_keys"
case "$1" in
add)
if [ -z "$2" ]; then
echo "Usage: $0 add <pubkey-file>"
exit 1
fi
cat "$2" >> "$AUTHORIZED_KEYS"
echo "Key added from: $2"
echo "Total keys: $(wc -l < "$AUTHORIZED_KEYS")"
;;
remove)
if [ -z "$2" ]; then
echo "Usage: $0 remove <comment>"
echo "Current keys:"
awk '{print NR, $3}' "$AUTHORIZED_KEYS"
exit 1
fi
grep -v "$2" "$AUTHORIZED_KEYS" > /tmp/ak_tmp
mv /tmp/ak_tmp "$AUTHORIZED_KEYS"
echo "Removed keys matching: $2"
;;
list)
echo "=== Authorized Keys ==="
awk '{printf "%3d %s\n", NR, $3}' "$AUTHORIZED_KEYS"
;;
*)
echo "Usage: $0 {add|remove|list} [args]"
exit 1
;;
esac
# 使用
sudo ./manage-users.sh list
sudo ./manage-users.sh add /tmp/alice-key.pub
sudo ./manage-users.sh remove alice@laptop
2.3.2 独立系统用户(推荐:需要严格隔离)
为每个开发者创建独立的系统用户,通过 git-shell 或自定义 SSH 命令限制访问。
# 创建用户
sudo adduser --disabled-password --shell /usr/bin/git-shell alice
sudo adduser --disabled-password --shell /usr/bin/git-shell bob
# 为每个用户添加 SSH 公钥
sudo mkdir -p /home/alice/.ssh
sudo cp alice-key.pub /home/alice/.ssh/authorized_keys
sudo chown -R alice:alice /home/alice/.ssh
sudo chmod 700 /home/alice/.ssh
sudo chmod 600 /home/alice/.ssh/authorized_keys
# bob 同理
sudo mkdir -p /home/bob/.ssh
sudo cp bob-key.pub /home/bob/.ssh/authorized_keys
sudo chown -R bob:bob /home/bob/.ssh
sudo chmod 700 /home/bob/.ssh
sudo chmod 600 /home/bob/.ssh/authorized_keys
2.3.3 使用 gitgroup 统一权限
# 创建 git 组
sudo groupadd gitgroup
# 将 git 用户和开发者加入组
sudo usermod -aG gitgroup git
sudo usermod -aG gitgroup alice
sudo usermod -aG gitgroup bob
# 设置仓库目录的组权限
sudo chown -R git:gitgroup /opt/git
sudo chmod -R 775 /opt/git
# 设置 SGID 位(新建文件自动继承组)
sudo find /opt/git -type d -exec chmod g+s {} \;
2.4 权限控制
2.4.1 基于文件系统权限
Linux 文件系统权限是最基础的权限控制方式:
# 只读仓库(所有人都只能读)
sudo chmod -R 755 /opt/git/public-repo.git
# 可读写仓库(组内成员可读写)
sudo chmod -R 775 /opt/git/team-repo.git
# 私有仓库(仅 owner 可读写)
sudo chmod -R 700 /opt/git/private-repo.git
2.4.2 权限矩阵
| 场景 | owner | group | others | 命令 |
|---|---|---|---|---|
| 公开只读 | rwx | r-x | r-x | chmod 755 |
| 团队读写 | rwx | rwx | r-x | chmod 775 |
| 私有仓库 | rwx | — | — | chmod 700 |
| 管理员读写/团队只读 | rwx | r-x | — | chmod 750 |
2.4.3 按团队组织仓库
/opt/git/
├── team-frontend/ # 前端团队
│ ├── web-app.git
│ └── shared-ui.git
├── team-backend/ # 后端团队
│ ├── api-server.git
│ └── auth-service.git
└── shared/ # 公共仓库
├── docs.git
└── scripts.git
# 创建团队目录
sudo mkdir -p /opt/git/{team-frontend,team-backend,shared}
sudo chown git:gitgroup /opt/git/{team-frontend,team-backend,shared}
# 创建前端团队组
sudo groupadd team-frontend
sudo usermod -aG team-frontend alice
sudo usermod -aG team-frontend bob
# 设置前端团队目录权限
sudo chown git:team-frontend /opt/git/team-frontend
sudo chmod 775 /opt/git/team-frontend
# 在前端团队目录下创建仓库
sudo -u git git init --bare /opt/git/team-frontend/web-app.git
# 公共仓库所有人都能读
sudo chown git:gitgroup /opt/git/shared
sudo chmod 775 /opt/git/shared
2.4.4 使用 ACL 进行细粒度权限控制
当标准的 Unix 权限不够用时,可以使用 ACL(Access Control Lists)。
# 安装 ACL 工具
sudo apt install acl -y
# 给 charlie 单独授予 team-frontend 目录的读取权限
sudo setfacl -R -m u:charlie:rx /opt/git/team-frontend
# 查看 ACL 设置
sudo getfacl /opt/git/team-frontend
# # file: opt/git/team-frontend
# # owner: git
# # group: team-frontend
# user::rwx
# user:charlie:r-x
# group::rwx
# mask::rwx
# other::r-x
# 设置默认 ACL(新建文件自动继承)
sudo setfacl -d -m u:charlie:rx /opt/git/team-frontend
2.5 客户端使用
2.5.1 克隆仓库
# 使用绝对路径
git clone git@server:/opt/git/project-a.git
# 如果配置了 git 用户的 home 目录,可用相对路径
git clone git@server:project-a.git
# 指定本地目录名
git clone git@server:/opt/git/project-a.git my-project
2.5.2 配置 SSH 简化连接
编辑 ~/.ssh/config:
# ~/.ssh/config
Host git-server
HostName 192.168.1.100
User git
Port 22
IdentityFile ~/.ssh/id_ed25519
# 如果使用自定义端口
Host git-server-alt
HostName git.example.com
User git
Port 2222
IdentityFile ~/.ssh/id_ed25519
现在可以简化命令:
# 之前
git clone [email protected]:/opt/git/project-a.git
# 现在
git clone git-server:/opt/git/project-a.git
2.5.3 设置远程仓库
# 为已有项目添加远程仓库
cd existing-project
git remote add origin git@server:/opt/git/project-a.git
git push -u origin main
# 查看远程仓库
git remote -v
# origin git@server:/opt/git/project-a.git (fetch)
# origin git@server:/opt/git/project-a.git (push)
2.6 仓库管理脚本
2.6.1 仓库创建脚本
#!/bin/bash
# create-repo.sh - 创建新的 Git 裸仓库
set -euo pipefail
GIT_ROOT="/opt/git"
GROUP="${1:?Usage: $0 <group> <repo-name> [description]}"
REPO="${2:?Usage: $0 <group> <repo-name> [description]}"
DESC="${3:-No description}"
REPO_PATH="${GIT_ROOT}/${GROUP}/${REPO}.git"
if [ -d "$REPO_PATH" ]; then
echo "错误: 仓库已存在 - $REPO_PATH"
exit 1
fi
# 创建目录
mkdir -p "${GIT_ROOT}/${GROUP}"
# 初始化裸仓库
git init --bare "$REPO_PATH" > /dev/null
# 设置描述
echo "$DESC" > "$REPO_PATH/description"
# 设置权限
chown -R git:git "${GIT_ROOT}/${GROUP}"
chmod -R 775 "${GIT_ROOT}/${GROUP}"
# 设置 SGID
find "$REPO_PATH" -type d -exec chmod g+s {} \;
echo "仓库创建成功: ${GROUP}/${REPO}.git"
echo "克隆命令: git clone git@server:${REPO_PATH}"
# 使用
sudo ./create-repo.sh team-frontend new-app "前端应用仓库"
2.6.2 仓库列表脚本
#!/bin/bash
# list-repos.sh - 列出所有仓库
GIT_ROOT="/opt/git"
echo "=== Git 仓库列表 ==="
echo ""
printf "%-30s %-20s %-15s %s\n" "仓库路径" "最后提交" "大小" "描述"
printf "%-30s %-20s %-15s %s\n" "--------" "--------" "----" "----"
find "$GIT_ROOT" -name "*.git" -type d | sort | while read repo; do
rel_path="${repo#$GIT_ROOT/}"
# 获取最后提交时间
last_commit=""
if [ -f "$repo/refs/heads/main" ] || [ -f "$repo/refs/heads/master" ]; then
branch="main"
[ -f "$repo/refs/heads/master" ] && branch="master"
last_commit=$(cd "$repo" && git log -1 --format="%ai" "$branch" 2>/dev/null | cut -d' ' -f1,2 | cut -c1-16)
fi
# 获取大小
size=$(du -sh "$repo" 2>/dev/null | cut -f1)
# 获取描述
desc=$(cat "$repo/description" 2>/dev/null || echo "")
printf "%-30s %-20s %-15s %s\n" "$rel_path" "${last_commit:-N/A}" "$size" "$desc"
done
2.6.3 备份脚本
#!/bin/bash
# backup-repos.sh - 备份所有 Git 仓库
set -euo pipefail
GIT_ROOT="/opt/git"
BACKUP_DIR="/var/backups/git"
DATE=$(date +%Y%m%d_%H%M%S)
BACKUP_FILE="${BACKUP_DIR}/git_backup_${DATE}.tar.gz"
mkdir -p "$BACKUP_DIR"
echo "开始备份 Git 仓库..."
echo "源目录: $GIT_ROOT"
echo "备份文件: $BACKUP_FILE"
# 创建压缩备份
tar czf "$BACKUP_FILE" -C "$(dirname $GIT_ROOT)" "$(basename $GIT_ROOT)"
# 显示备份信息
SIZE=$(du -sh "$BACKUP_FILE" | cut -f1)
echo "备份完成: $BACKUP_FILE ($SIZE)"
# 清理超过 30 天的旧备份
find "$BACKUP_DIR" -name "git_backup_*.tar.gz" -mtime +30 -delete
echo "已清理 30 天前的旧备份"
2.7 Git 客户端配置优化
2.7.1 凭据和 SSH 缓存
# 缓存 SSH 连接(避免反复握手)
# 在 ~/.ssh/config 中添加:
# Host *
# ControlMaster auto
# ControlPath ~/.ssh/sockets/%r@%h-%p
# ControlPersist 600
mkdir -p ~/.ssh/sockets
2.7.2 推送和拉取优化
# 全局配置
git config --global push.default simple
git config --global pull.rebase true
git config --global fetch.prune true
git config --global core.compression 6
# SSH 连接复用
git config --global core.sshCommand "ssh -o ControlMaster=auto -o ControlPersist=600 -o ControlPath=~/.ssh/sockets/%r@%h-%p"
2.8 业务场景示例
场景一:三人开发团队
需求:3 个开发者,2 个共享项目,1 个私有项目。
# 服务端操作
sudo ./create-repo.sh shared project-web "Web 应用"
sudo ./create-repo.sh shared project-api "API 服务"
sudo ./create-repo.sh alice private-notes "Alice 的私有笔记"
# 设置私有仓库权限(仅 Alice 可访问)
sudo chmod 700 /opt/git/alice/private-notes.git
场景二:开源项目托管
需求:代码对所有人可读,只有维护者可推送。
# 创建公开仓库
sudo ./create-repo.sh public awesome-project "开源项目"
# 设置权限:所有人可读,git 用户(维护者)可写
sudo chmod 755 /opt/git/public/awesome-project.git
2.9 扩展阅读
本章小结
| 学到了什么 | 关键要点 |
|---|---|
| 服务端搭建 | 创建 git 用户、裸仓库、配置 SSH 公钥 |
| 用户管理 | 共享 git 用户 vs 独立系统用户,各有适用场景 |
| 权限控制 | 文件系统权限、ACL、按团队组织目录 |
| 客户端使用 | SSH config 简化连接、仓库管理脚本 |
| 安全加固 | git-shell 限制、SSH 密钥管理、备份策略 |
局限性提醒: 纯 SSH + 裸仓库方案无法提供 Web 界面、Issue 管理、Code Review 等功能。如果需要这些能力,请阅读后续章节了解 Gitolite、Gitea、GitLab 等方案。
下一章:第 3 章 - Gitolite 权限管理 — 使用 Gitolite 实现更细粒度的仓库和分支权限控制。