转自:https://wayjam.me/post/how-gitlab-shell-works-with-ssh.md

GitLab访问Git仓库

首先回顾GitLab的Git仓库四种访问方式:

  1. git pull over http -> gitlab-rails (Authorization) -> accept or decline -> execute git command
  2. git push over http -> gitlab-rails (git command is not executed yet) -> execute git command -> gitlab-shell pre-receive hook -> API call to gitlab-rails (authorization) -> accept or decline push
  3. git pull over ssh -> gitlab-shell -> API call to gitlab-rails (Authorization) -> accept or decline -> execute git command
  4. git push over ssh -> gitlab-shell (git command is not executed yet) -> execute git command -> gitlab-shell pre-receive hook -> API call to gitlab-rails (authorization) -> accept or decline push

四种方式都有GitLab Shell的参与,但不同过程GitLab Shell发挥了不同的作用,并且它并不是一个整体的服务,而是由一些子命令组合而成。HTTP方式的Git操作,经gitlab workhorse直接交由Rails应用处理,然后通过HTTP协议交换数据,对于git的操作有三条路径:Gem包Rugged、Raw Git命令或者Gitaly,push/pull一般只跟后两种有关,GitLab Shell充当的作用仅仅是git hook的作用。

SSH方式的push/pull是GitLab Shell的主场景,而Rails在这其中充当了权限再验证的角色。

Git SSH 传输协议

首先,简要说明Git是如何通过SSH协议与服务端的Git交互数据。

ssh git@example.com "the-command"

在客户端执行这样的命令时候,服务端SSHD验证身份通过后,默认将启动一个Shell解析执行the-command的命令。普通使用中,大多都不加自定义命令,这将启动一个Shell交互式命令解析器。

要了解GitLab Shell的原理,不能不说它的“前任”——Gitolite。Gitolite是一个Git的授权层前端,同样提供了HTTP(httpd)和SSH的方式访问Git仓库。而Gitlab在v5.0完全用GitLab Shell替代了Gitolite,前者完全依赖Rails层的权限认证(项目、分支、用户等的权限),后者则由于需要完全保持一份冗余数据在自身的配置文件,主要由于速度和数据不同步被Gitlab官方放弃。但是,二者仍然采用了authorized_keyscommand magic方案。

GitLab CE 10.4之后加入了`AuthorizedKeysCommand`的使用(_require_ OpenSSH 6.9+),使用自定义程序匹配Key而不是文件文本匹配。

SSH command magic

SSHD服务端收到客户端的连接请求后,会在authorized_keys进行匹配,认证失败则拒绝连接。查看~/.ssh/authorized_keys文件内容如下:

# Gitolite
command="[path]/gitolite-shell user-one",[more options] ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEArXtCT...
# GitLab Shell
command="/home/git/gitlab-shell/bin/gitlab-shell key-1",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEArXtCT...

可以发现保存的每条公钥前面都有command=,Gitolite和GitLab Shell后面的参数代表着用户的识别信息,Gitolite保存在配置文件,而gitlab保存在数据库。当用户的服务端校验成功后,则会执行command=后的命令,而不是shell access,同时将SSH命令所要执行的命令赋值给SSH_ORIGINAL_COMMAND。所以与客户端交互的真正命令是(以pull代码为例):

SSH_ORIGINAL_COMMAND=git-upload-pack git-pack/home/git/gitlab-shell/bin/gitlab-shell key-1

"Shell"

GitLab Shell由ruby脚本和go程序组成,首先看GitLab Shell入口代码:bin/gitlab-shell

#!/usr/bin/env ruby
# ...
key_id = /key-[0-9]+/.match(ARGV.join).to_s
original_cmd = ENV.delete('SSH_ORIGINAL_COMMAND')
# ...
require File.join(ROOT_PATH, 'lib', 'gitlab_shell')
if GitlabShell.new(key_id).exec(original_cmd)
# ...

这段代码里面的两个变量:

  • key_id -> sshd调用GitLab Shell时传入的参数。
  • original_cmd -> 即上文提到的SSH_ORIGINAL_COMMAND环境变量,并且获取完即移除。

举个例子,如果用户通过git客户端调用git clone git@server的时候,实际上git客户端启动的是receive-pack(git由许许多多子命令组成)并且在内部执行的是ssh git@git@server git-upload-pack git@server,那么此时,服务端给GitLab Shell设定的环境变量则是git-upload-pack git@server,。

然后看lib/gitlab-shell.rb代码,初始化一个GitlabShell,并且执行exec,而exec里面有几个关键步骤:

def exec(origin_cmd)
unless origin_cmd
puts "Welcome to GitLab, #{username}!"
return true
end args = Shellwords.shellwords(origin_cmd)
args = parse_cmd(args) if GIT_COMMANDS.include?(args.first)
GitlabMetrics.measure('verify-access') { verify_access }
end process_cmd(args) true
rescue something #异常
end
1. 解析命令 parse_cmd

主要是处理Windows/Linux命令差异、屏蔽非法命令、LFS命令。

2. 验证权限 verify_access
/api/v4/internal/allowed

通过Rails的HTTP API :/api/v4/internal/allowed发送查询参数到Rails,接口返回此用户对这个仓库是否有此操作的权限。

参数:

{
command => git命令
repo => 仓库信息
key_id => SSH key id(在数据库能找查找到对应的用户)
protocol => ssh/env
}
3. 处理命令 process_cmd

处理命令,检测gitaly此特性是否有开启,如果开启则调用gitaly处理,否则则调用git原生命令。

设定环境变量,然后使用Kernel.exec调用目标进程替代当前进程。

env = {
'HOME' => ENV['HOME'],
'PATH' => ENV['PATH'],
'LD_LIBRARY_PATH' => ENV['LD_LIBRARY_PATH'],
'LANG' => ENV['LANG'],
'GL_ID' => @key_id,
'GL_PROTOCOL' => GL_PROTOCOL,
'GL_REPOSITORY' => @gl_repository,
'GL_USERNAME' => @username
}
# ...
Kernel.exec(env, *args, unsetenv_others: true, chdir: ROOT_PATH)

处理Git命令

以Pull,且调用Gitaly为例

// bin/gitaly-upload-pack
code, err := handler.UploadPack(os.Args[1], &request)
// go/internal/handler/upload_pack.go
func UploadPack(gitalyAddress string, request *pb.SSHUploadPackRequest) (int32, error) {
# ... conn, err := client.Dial(gitalyAddress, dialOpts())
if err != nil {
return 0, err
}
defer conn.Close() ctx, cancel := context.WithCancel(context.Background())
defer cancel()
return client.UploadPack(ctx, conn, os.Stdin, os.Stdout, os.Stderr, request)
}
// go/vendor/gitlab.com/gitlab-org/gitaly/client/upload_pack.go
func UploadPack(ctx context.Context, conn *grpc.ClientConn, stdin io.Reader, stdout, stderr io.Writer, req *pb.SSHUploadPackRequest) (int32, error) {
ctx2, cancel := context.WithCancel(ctx)
defer cancel() ssh := pb.NewSSHServiceClient(conn)
stream, err := ssh.SSHUploadPack(ctx2)
if err != nil {
return 0, err
} if err = stream.Send(req); err != nil {
return 0, err
} inWriter := streamio.NewWriter(func(p []byte) error {
return stream.Send(&pb.SSHUploadPackRequest{Stdin: p})
}) return streamHandler(func() (stdoutStderrResponse, error) {
return smHandler(func() (stdoutStderrResponse, error) {
return stream.Recv()
}, func(errC chan error) {
_, errRecv := io.Copy(inWriter, stdin)
stream.CloseSend()
errC <- errRecv
}, stdout, stderr)
}

可以看到通过grpc跟gitaly server通信,获得响应之后:

# go/vendor/gitlab.com/gitlab-org/gitaly/client/std_stream.go
exited code => resp.GetExitStatus().GetValue()
stderr => stderr.Write(resp.GetStderr())
stdout => stdout.Write(resp.GetStdout())

git客户端将标准错误打印到控制台,解析标准输出作为git数据:通过远程ssh调用命令将数据打印到标准输出传输到客户端解析

Cloning into 'gitlab-shell'...
remote: Counting objects: 5558, done.
remote: Compressing objects: 100% (2548/2548), done.
remote: Total 5558 (delta 3051), reused 5117 (delta 2754)
Receiving objects: 100% (5558/5558), 2.83 MiB | 2.72 MiB/s, done.
Resolving deltas: 100% (3051/3051), done.

Refs

 
 
 
 

GitLab Shell如何通过SSH工作的更多相关文章

  1. github/gitlab 管理多个ssh key

    github/gitlab 管理多个ssh key 以前只使用一个 ssh key 在github上提交代码,由于工作原因,需要再添加一个ssh key在公司的 gitlab上提交代码,下面记录下配置 ...

  2. SSH工作过程简介和SSH协议算法集简介

    SSH简介 SSH是Secure Shell(安全外壳)的简称,是一种在不安全的网络环境中,通过加密机制和认证机制,实现安全的远程访问以及文件传输等业务的网络安全协议. SSH协议采用了典型的客户端/ ...

  3. 全新 Mac 安装指南(编程篇)(环境变量、Shell 终端、SSH 远程连接)

    注:本文专门用于指导对计算机编程与设计(尤其是互联网产品开发与设计)感兴趣的 Mac 新用户,如何在 Mac OS X 系统上配置开发与上网环境,另有<全新 Mac 安装指南(通用篇)>作 ...

  4. gitlab+TortoiseGit中使用SSH

    1.在文件夹空白位置右键打开"Git Bash" 2.按 https://gitlab.yourhost.com/help/ssh/ssh.md 中的说明,输入命令   ssh-k ...

  5. centos shell编程6一些工作中实践脚本 nagios监控脚本 自定义zabbix脚本 mysql备份脚本 zabbix错误日志 直接送给bc做计算 gzip innobackupex/Xtrabackup 第四十节课

    centos   shell编程6一些工作中实践脚本   nagios监控脚本 自定义zabbix脚本 mysql备份脚本 zabbix错误日志  直接送给bc做计算  gzip  innobacku ...

  6. J2EE进阶(十八)基于留言板分析SSH工作流程

    J2EE进阶(十八)基于留言板分析SSH工作流程   留言板采用SSH(Struts1.2 + Spring3.0 + Hibernate3.0)架构.   工作流程(以用户登录为例):   首先是用 ...

  7. shell脚本批量ssh登陆主机并执行命令

    shell脚本批量ssh登陆主机并执行命令 今天在客户现场遇到了这个问题,客户没有管理工具,无法批量登陆主机下发命令,几个个C段啊,让我一个一个登陆,.................. 所以写了个s ...

  8. 为GitLab帐号添加SSH keys并连接GitLab

    https://blog.csdn.net/xyzchenxiaolin/article/details/51852333 为github帐号添加SSH keys使用git clone命令从GitLa ...

  9. [转+自]SSH工作原理

    SSH工作原理 熟悉Linux的人肯定都知道SSH.SSH是一种用于安全访问远程服务器的网络协议.它将客户端与服务端之间的消息通过加密保护起来,这样就无法被窃取或篡改了.那么它安全性是如何实现的呢? ...

随机推荐

  1. 四:FAQ附录(容器交互,镜像交互,镜像导出)

    1.交互式运行容器的方法: 1>-it进入到操作系统中: 2>另开一个cmd验证: 3>这是在image之上多了一个可写的从:可以运行centos的命令做一些事(touch .yum ...

  2. VSTO:使用C#开发Excel、Word【4】

    <Visual Studio Tools for Office: Using C# with Excel, Word, Outlook, and InfoPath >——By Eric C ...

  3. Windows server 2016 安装 TFS

    一:准备: 1.1下载TFS https://visualstudio.microsoft.com/zh-hans/tfs/ 1.2 下载SQL2017 http://msdn.itellyou.cn ...

  4. js如何调试,使用debug模式

    js的代码断点调试非常简单,不需要借助代码编辑器,只要浏览器就行了(注意:html代码打断点是没有用的,只有js的才行,下图第二步打开开发者模式使用F12才对):

  5. 强化学习9-Deep Q Learning

    之前讲到Sarsa和Q Learning都不太适合解决大规模问题,为什么呢? 因为传统的强化学习都有一张Q表,这张Q表记录了每个状态下,每个动作的q值,但是现实问题往往极其复杂,其状态非常多,甚至是连 ...

  6. apache rewrite 规则

    啥是虚拟主机呢?就是说把你自己的本地的开发的机子变成一个虚拟域名,比如:你在开发pptv下面的一个项目 127.0.0.1/pptv_trunk,你想把自己的机器域名变成www.pptv.com.那么 ...

  7. web 架构 /http协议,状态码,django中常用命令

    什么是web应用? web应用 架构 :B/S架构 | C/S架构 网站:BS架构其实就是应用程序: B是浏览器 S是sever(实现了wsgi协议,实现了socket的服务端) + applicat ...

  8. Python学习笔记第十五周

    目录: 一.CSS补充 1.position 2.overflow 3.hover 4.background 二.JavaScript 三.DOM 主要内容: 一.CSS补充 1.position 可 ...

  9. Ubuntu 12.04不能在華碩F81se系列電腦上安裝解决辦法

    本人華碩F81se系列的電腦,剛開始是裝的ubuntu 10.04的系統,周末閑的無聊,就想把系統換成ubuntu 12.04的,從ubuntu官網上下載了12.04的安裝包,下了個usb insta ...

  10. 2017-2018-2 20165228 实验四《Android程序设计》实验报告

    一.实验报告封面 课程:Java程序设计 班级:1652班 姓名:苏祚堃 学号:20165228 指导教师:娄嘉鹏 实验日期:2018年5月14日 实验时间:13:45 - 3:25 实验序号:实验四 ...