doc/development/gitlab_shell/_index.md
GitLab Shell handles Git SSH sessions for GitLab and modifies the list of authorized keys. GitLab Shell is not a Unix shell nor a replacement for Bash or Zsh.
GitLab supports Git LFS authentication through SSH.
GitLab Shell is written in Go, and needs a Go compiler to build. It still requires Ruby to build and test, but not to run.
GitLab Shell runs on port 22 on a Linux package installation. To use a regular SSH
service, configure it on an alternative port.
Download and install the current version of Go. We follow the Go Release Policy and support:
The two version files relevant to GitLab Shell:
GitLab team members can also monitor the #announcements internal Slack channel.
When you access the GitLab server over SSH, GitLab Shell then:
git push, git pull, git fetch).If you access a GitLab server over HTTP(S) you end up in gitlab-workhorse.
git pull over SSH%%{init: { "fontFamily": "GitLab Sans" }}%%
graph LR
A[Git pull] --> |via SSH| B[gitlab-shell]
B -->|API call| C[gitlab-rails
authorization]
C -->|accept or decline| D[Gitaly session]
git push over SSHThe git push command is not performed until after gitlab-rails accepts the 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_keysGitLab Shell modifies the authorized_keys file on the client machine.
To contribute to GitLab Shell:
make checkgitlab-shell binaries, placing them into bin/: make compilemake install to build the gitlab-shell binaries and install them onto the file system.
The default location is /usr/local. To change it, set the PREFIX and DESTDIR environment variables.make setup.
It compiles the GitLab Shell binaries, and ensures that various paths on the file system
exist with the correct permissions. Do not run this command unless your installation method
documentation instructs you to.For more information, see CONTRIBUTING.md.
When contributing, run tests:
Run tests with bundle install and make test.
Run Gofmt: make verify
Run both test and verify (the default Makefile target):
bundle install
make validate
If needed, configure Gitaly.
Some tests need a Gitaly server. The
docker-compose.yml file runs Gitaly on port 8075.
To tell the tests where Gitaly is, set GITALY_CONNECTION_INFO:
export GITALY_CONNECTION_INFO='{"address": "tcp://localhost:8075", "storage": "default"}'
make test
If no GITALY_CONNECTION_INFO is set, the test suite still runs, but any
tests requiring Gitaly are skipped. The tests always run in the CI environment.
GitLab Shell performs rate-limiting by user account and project for Git operations.
GitLab Shell accepts Git operation requests and then makes a call to the Rails
rate-limiter, backed by Redis. If the user + project exceeds the rate limit,
then GitLab Shell then drop further connection requests for that user + project.
The rate-limiter is applied at the Git command (plumbing) level. Each command has
a rate limit of 600 per minute. For example, git push has 600 per minute, and
git pull has another 600 per minute.
Because they are using the same plumbing command, git-upload-pack, git pull,
and git clone are in effect the same command for the purposes of rate-limiting.
Gitaly also has a rate-limiter in place, but calls are never made to Gitaly if the rate limit is exceeded in GitLab Shell (Rails).
In general, you can determine the structure, but not content, of a GitLab Shell
or gitlab-sshd session by inspecting the logs. Some guidelines:
gitlab.com/gitlab-org/labkit/log
for logging.log.WithField, log.WithFields, or log.WithError.A diagram of the flow of gitlab-shell on 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
load balancer]
e1[Git]
subgraph HAProxy Fleet
b2[HAProxy]
b3[HAProxy]
b4[HAProxy]
end
subgraph GKE
c1[Internal TCP
load balancer
port 2222] --> c2[GitLab-shell
pods]
end
subgraph Gitaly
d1[Gitaly]
d2[Gitaly]
d3[Gitaly]
end
%%{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