GitLab Shell 开发指南
GitLab Shell 处理 GitLab 的 Git SSH 会话,并修改授权密钥列表。 GitLab Shell 不是 Unix shell,也不是 Bash 或 Zsh 的替代品。
GitLab 通过 SSH 支持 Git LFS 认证。
要求
GitLab Shell 使用 Go 语言编写,需要 Go 编译器来构建。它仍然需要 Ruby 来构建和测试,但运行时不需要。
在 Linux 包安装中,GitLab Shell 运行在 端口 22。要使用常规 SSH
服务,请在其他端口上配置它。
下载并安装 Go 的当前版本。 我们遵循 Go 发布策略 并支持:
- 当前稳定版本。
- 前两个主要版本。
版本
与 GitLab Shell 相关的两个版本文件:
GitLab 团队成员也可以监控 #announcements 内部 Slack 频道。
GitLab Shell 工作原理
当您通过 SSH 访问 GitLab 服务器时,GitLab Shell 会:
- 将您限制在预定义的 Git 命令(
git push、git pull、git fetch)。 - 调用 GitLab Rails API 检查您是否被授权,以及您的仓库在哪个 Gitaly 服务器上。
- 在 SSH 客户端和 Gitaly 服务器之间来回复制数据。
如果您通过 HTTP(S) 访问 GitLab 服务器,您最终会进入 gitlab-workhorse。
通过 SSH 执行 git pull
%%{init: { "fontFamily": "GitLab Sans" }}%%
graph LR
A[Git pull] --> |via SSH| B[gitlab-shell]
B -->|API call| C[gitlab-rails<br>authorization]
C -->|accept or decline| D[Gitaly session]
通过 SSH 执行 git push
在 gitlab-rails 接受推送之前,不会执行 git push 命令:
%%{init: { "fontFamily": "GitLab Sans" }}%%
graph LR
subgraph User initiates
A[Git push] -->|via SSH| B[gitlab-shell]
end
subgraph Gitaly
B -->|establish Gitaly session| C[gitlab-shell pre-receive hook]
C -->|API auth call| D[Gitlab-rails]
D --> E[accept or decline push]
end
修改 authorized_keys
GitLab Shell 修改客户端机器上的 authorized_keys 文件。
为 GitLab Shell 做出贡献
要为 GitLab Shell 做出贡献:
- 检查是否可以访问 GitLab API 以及带有内部 API 的 Redis:
make check - 编译
gitlab-shell二进制文件,并将它们放入bin/:make compile - 运行
make install来构建gitlab-shell二进制文件并将它们安装到文件系统。 默认位置是/usr/local。要更改它,请设置PREFIX和DESTDIR环境变量。 - 要在单台机器上从源代码安装 GitLab,请运行
make setup。 它编译 GitLab Shell 二进制文件,并确保文件系统上的各种路径 具有正确的权限。除非您的安装方法 文档指示您,否则不要运行此命令。
有关更多信息,请参阅 CONTRIBUTING.md。
运行测试
在贡献时,运行测试:
-
使用
bundle install和make test运行测试。 -
运行 Gofmt:
make verify -
运行测试和验证(默认 Makefile 目标):
bundle install make validate -
如果需要,配置 Gitaly。
为本地测试配置 Gitaly
某些测试需要 Gitaly 服务器。该
docker-compose.yml 文件在端口 8075 上运行 Gitaly。
要告诉测试 Gitaly 的位置,请设置 GITALY_CONNECTION_INFO:
export GITALY_CONNECTION_INFO='{"address": "tcp://localhost:8075", "storage": "default"}'
make test如果没有设置 GITALY_CONNECTION_INFO,测试套件仍然会运行,但任何
需要 Gitaly 的测试都会被跳过。测试总是在 CI 环境中运行。
速率限制
GitLab Shell 对 Git 操作按用户账户和项目进行速率限制。
GitLab Shell 接受 Git 操作请求,然后调用 Rails
速率限制器,该限制器由 Redis 支持。如果 用户 + 项目 超过速率限制,
那么 GitLab Shell 将丢弃该 用户 + 项目 的后续连接请求。
速率限制器应用于 Git 命令(plumbing)级别。每个命令每分钟
有 600 次的速率限制。例如,git push 每分钟 600 次,
git pull 每分钟另外 600 次。
因为它们使用相同的 plumbing 命令,git-upload-pack、git pull
和 git clone 在速率限制方面实际上是同一个命令。
Gitaly 也有速率限制器,但如果 GitLab Shell(Rails)中的速率限制被超过,则永远不会调用 Gitaly。
GitLab Shell 中的日志
通常,通过检查日志,您可以确定 GitLab Shell
或 gitlab-sshd 会话的结构,但不能确定其内容。一些指导原则:
- 我们使用
gitlab.com/gitlab-org/labkit/log进行日志记录。 - 始终包含关联 ID。
- 日志消息应该是不可变且唯一的。使用
log.WithField、log.WithFields或log.WithError将辅助信息包含在字段中。 - 同时记录成功和错误情况。
- 记录过多比记录不足更好。如果消息看起来过于 冗长,请考虑在删除消息之前降低日志级别。
GitLab SaaS
gitlab-shell 在 GitLab.com 上的流程图:
%%{init: { "fontFamily": "GitLab Sans" }}%%
graph LR
a2 --> b2
a2 --> b3
a2 --> b4
b2 --> c1
b3 --> c1
b4 --> c1
c2 --> d1
c2 --> d2
c2 --> d3
d1 --> e1
d2 --> e1
d3 --> e1
a1[Cloudflare] --> a2[TCP<br/> load balancer]
e1[Git]
subgraph HAProxy Fleet
b2[HAProxy]
b3[HAProxy]
b4[HAProxy]
end
subgraph GKE
c1[Internal TCP<br/> load balancer<br/>port 2222] --> c2[GitLab-shell<br/> pods]
end
subgraph Gitaly
d1[Gitaly]
d2[Gitaly]
d3[Gitaly]
end
GitLab Shell 架构
%%{init: { "fontFamily": "GitLab Sans" }}%%
sequenceDiagram
participant Git on client
participant SSH server
participant AuthorizedKeysCommand
participant GitLab Shell
participant Rails
participant Gitaly
participant Git on server
Note left of Git on client: git fetch
Git on client->>+SSH server: ssh git fetch-pack request
SSH server->>+AuthorizedKeysCommand: gitlab-shell-authorized-keys-check git AAAA...
AuthorizedKeysCommand->>+Rails: GET /internal/api/authorized_keys?key=AAAA...
Note right of Rails: Lookup key ID
Rails-->>-AuthorizedKeysCommand: 200 OK, command="gitlab-shell upload-pack key_id=1"
AuthorizedKeysCommand-->>-SSH server: command="gitlab-shell upload-pack key_id=1"
SSH server->>+GitLab Shell: gitlab-shell upload-pack key_id=1
GitLab Shell->>+Rails: GET /internal/api/allowed?action=upload_pack&key_id=1
Note right of Rails: Auth check
Rails-->>-GitLab Shell: 200 OK, { gitaly: ... }
GitLab Shell->>+Gitaly: SSHService.SSHUploadPack request
Gitaly->>+Git on server: git upload-pack request
Note over Git on client,Git on server: Bidirectional communication between Git client and server
Git on server-->>-Gitaly: git upload-pack response
Gitaly -->>-GitLab Shell: SSHService.SSHUploadPack response
GitLab Shell-->>-SSH server: gitlab-shell upload-pack response
SSH server-->>-Git on client: ssh git fetch-pack response