澳门新葡萄京官网注册 1

GitLab曝高危漏洞,可致private
token等敏感信息泄露。GitLab于昨日发布了8.17.4、8.16.8和8.15.8版本(社区版和企业版),修复多个高危漏洞,包含一个针对关键信息泄露漏洞的更新补丁,针对SSRF攻击的防护,以及针对可导致Atom源中私有邮件地址泄露漏洞的补丁,ElasticSearch中私有库数据泄露的补丁等。

GitLab搭建与维护(基于Docker镜像sameersbn/docker-gitlab)

漏洞概述

GitLab 是一个使用 Ruby on Rails 开发的开源应用程序,实现了一个 Git
仓库管理平台,可通过 Web
界面进行访问公开的或者私有的项目。在企业中得到的广泛的使用。近日研究者发现在其多个版本中存在用户多项敏感信息泄漏漏洞,攻击者可以通过这些漏洞来获取相应的用户权限,危害严重。

GitLab特别在其更新日志中提到,在内部代码审核过程中,在GitLab
Issue和Merge Request
tracker中发现一个Critical级别的高危漏洞,并因此强烈建议受影响用户尽快升级。

1. 阅读本文基础

  • 熟悉git使用
  • 熟悉docker

漏洞详情

当修改任务的分配者信息的时候,API
将返回该用户的个人信息详情,其中包括了该用户的authentication_tokenencrypted_otp_secretotp_backup_codes等敏感信息。

使用这些敏感信息结合 Gitlab API 可以实现使用该用户的身份和权限操作
GitLab。如果用户为管理员权限,可能会造成更大的危害。

澳门新葡萄京官网注册 2

2. GitLab简介

影响版本

GitLab 用户authentication_token等敏感信息泄露漏洞分析
以下版本都受该漏洞影响:

  • 8.7.0 ~ 8.15.7

  • 8.16.0 ~ 8.16.7

  • 8.17.0 ~ 8.17.3

漏洞编号

2.1. 概述

  • GitLab
    是一个用于仓库管理系统的开源项目。使用Git作为代码管理工具,并在此基础上搭建起来的web服务。Github是公共的git仓库,而Gitlab适合于搭建企业内部私有git仓库
  • 官网:

    https://about.gitlab.com/
    
    https://github.com/gitlabhq/gitlabhq
    

  • 截止本文创建时间,GitLab最新版本为v7.4.3

解决方案

更新 GitLab

升级 GitLab 至以下相应版本:

  • 8.15.8

  • 8.16.8

  • 8.17.4

升级完成后需要使用下述方式来重置所有用户的 private token 和 email token

对于以 Omnibus 方式安装的用户,新建文件/opt/gitlab/embedded/service/gitlab-rails/lib/tasks/reset_token.rake;对于以源码方式安装的用户,新建文件<gitlab_installation_dir>/lib/tasks/reset_token.rake,并写入如下内容:(如有复制代码需求,请点击“官方升级公告”查看)

澳门新葡萄京官网注册 3

澳门新葡萄京官网注册 4

Omnibus 方式安装的用户执行以下命令:

澳门新葡萄京官网注册 5

源码方式安装的用户执行以下命令:

澳门新葡萄京官网注册 6

CVE-2017-0882

2.2. 架构

  • 示意图:
  • 澳门新葡萄京官网注册 7

临时补丁

如果因为业务原因不能及时更新 GitLab,可使用下述方式进行临时修复:

将如下内容保存至 gitlab.patch

澳门新葡萄京官网注册 8

澳门新葡萄京官网注册 9

Omnibus 方式安装的用户执行以下命令:

澳门新葡萄京官网注册 10

源码方式安装的用户执行以下命令:

澳门新葡萄京官网注册 11

漏洞介绍

2.3. 组件

  • 前端:Nginx,用于页面及Git tool走http或https协议
  • 后端:Gitlab服务,采用Ruby on
    Rails框架,通过unicorn实现后台服务及多进程
  • SSHD:开启sshd服务,用于用户上传ssh
    key进行版本克隆及上传。注:用户上传的ssh key是保存到git账户中
  • 数据库:目前仅支持MySQL和PostgreSQL
  • Redis:用于存储用户session和任务,任务包括新建仓库、发送邮件等等
  • Sidekiq:Rails框架自带的,订阅redis中的任务并执行

参考

官方升级公告

来自:长亭科技订阅号

漏洞可导致拥有向其他用户发送issue或merge请求权限的攻击者获取到该用户的private
token, email token,
email地址和加密的OTP
secret。想要利用漏洞需要有Reporter级别的权限。通过Gitlab
API和这些敏感信息,就能以该用户权限进行操作,如果目标用户是管理员则可能产生更大危害。

3. GitLab安装部署

造成漏洞的原因是对用户对象序列化过程中的一个bug,这个问题自GitLab
8.7.0版本就开始出现。

3.1. 官方支持的方式

  • 包含一切的RPM包: (官方推荐)

    个人不推荐,因为RPM包容量太大,200多M,适合于Linux初学者,未尝试过
    
  • 手动安装:

    (深入了解)

    可以最大程度了解GitLab的组件之间架构,但对于入门并不适合
    
  • 第三方docker镜像:
    (笔者推荐)

    镜像可以快速实现部署并使用,适合于熟悉Docker的人使用,入门很快。而且使用Docker镜像就使用户不用过多了解内部细节,通过启动容器带上环境变量参数即可实现GitLab参数的配置
    

影响版本

3.2. 采用docker镜像安装GitLab

8.7.0至8.15.7

3.2.1. 简介

  • 官网:
  • 7.4.3之前版本,镜像里包含所有组件,7.4.3版本镜像里只包含核心组件:nginx、sshd、ruby
    on rails、sidekiq

8.16.0至8.16.7

3.2.2. 架构图

澳门新葡萄京官网注册 12

8.17.0至8.17.3

3.2.3. 下载镜像

  • docker pull sameersbn/gitlab:7.4.3 # 下载gitlab镜像
  • docker pull sameersbn/mysql:latest # 下载gitlab所用到的mysql镜像
  • docker pull sameersbn/redis:latest # 下载gitlab所用到的redis镜像

升级后注意事项

3.2.4. 安装

由于漏洞的性质,用户的token可能会缓存在代理或浏览器中。因此建议站长们重置所有用户的private
token和email token。

3.2.4.1. 启动redis

  • 命令:

    docker run 
    --name=gitlab_redis 
    -tid 
    sameersbn/redis:latest
    

一次性密码(OTP)的secret也有可能因漏洞而泄露。这些secret都经过加密,需要密钥才能解密,并且不能在没有用户密码的情况下使用。尽管如此,
还是建议先将一次性密码功能关闭然后再重新打开,从而重置OTP secret。

3.2.4.2. 启动mysql

  • 澳门新葡萄京官网注册,mkdir -p /opt/gitlab/mysql
  • 命令:

    docker run 
    --name=gitlab_mysql 
    -tid 
    -e 'DB_NAME=gitlabhq_production' 
    -e 'DB_USER=gitlab' 
    -e 'DB_PASS=password' 
    -v /opt/gitlab/mysql:/var/lib/mysql 
    sameersbn/mysql:latest
    

重置用户Tokens步骤

3.2.4.3. 启动gitlab

  • mkdir -p /opt/gitlab/data /opt/gitlab/log
  • 命令:

    docker run 
    --name='gitlab' 
    -itd 
    --link gitlab_mysql:mysql 
    --link gitlab_redis:redisio 
    -e 'GITLAB_PORT=80' 
    -e 'GITLAB_SSH_PORT=22' 
    -e 'GITLAB_HOST=gitlab.example.com' 
    -v /var/run/docker.sock:/run/docker.sock 
    -v $(which docker):/bin/docker 
    -v /opt/gitlab/data:/home/git/data 
    -v /opt/gitlab/log:/var/log/gitlab 
    sameersbn/gitlab:7.4.3
    
    上述是开启一个基本gitlab。
    完整(包含LDAP、EMAIL):
    docker run 
    --name='gitlab' 
    -itd 
    --link gitlab_mysql:mysql 
    --link gitlab_redis:redisio 
    -e 'GITLAB_PORT=80' 
    -e 'GITLAB_SSH_PORT=22' 
    -e 'LDAP_ENABLED=true' 
    -e 'LDAP_HOST=192.168.1.1' 
    -e 'LDAP_PORT=389' 
    -e 'LDAP_UID=sAMAccountName' 
    -e 'LDAP_METHOD=plain' 
    -e '[email protected]' 
    -e 'LDAP_PASS=passwd' 
    -e 'LDAP_BASE=OU=example_users,DC=example-family,DC=com' 
    -e 'LDAP_ACTIVE_DIRECTORY=true' 
    -e 'LDAP_ALLOW_USERNAME_OR_EMAIL_LOGIN=false' 
    -e 'GITLAB_HOST=gitlab.example.com' 
    -e 'SMTP_ENABLED=true' 
    -e 'SMTP_DOMAIN=example.com' 
    -e 'SMTP_HOST=192.168.1.2' 
    -e 'SMTP_PORT=25' 
    -e 'SMTP_STARTTLS=false' 
    -v /var/run/docker.sock:/run/docker.sock 
    -v $(which docker):/bin/docker 
    -v /opt/gitlab/data:/home/git/data 
    -v /opt/gitlab/log:/var/log/gitlab 
    sameersbn/gitlab:7.4.3

 

这一步骤会耗时几分钟,因为这一步会做一些初始化操作,例如导入数据表结构等。可以通过docker
logs gitlab来查看安装过程。同理,mysql、redis容器也可以通过docker logs
gitlab_mysql和docker logs gitlab_redis来查看启动信息。

 

注意:上面创建的3个容器必须位于同一台宿主上,因为–link
gitlab_mysql:mysql –link
gitlab_redis:redisio就是将同个宿主上的容器做链接

  • 当然,redis和mysql也支持使用ip+端口,不用–link,带上环境变量即可,方法是:

    完整:
    docker run 
    --name='gitlab' 
    -itd 
    --net=none 
    --hostname='gitlab.example.com' 
    -e 'DB_TYPE=mysql' 
    -e 'DB_HOST=192.168.3.1' 
    -e 'DB_PORT=3356' 
    -e 'DB_NAME=gitlabhq_production' 
    -e 'DB_USER=gitlab' 
    -e 'DB_PASS=passwd' 
    -e 'REDIS_HOST=192.168.3.2' 
    -e 'REDIS_PORT=6402' 
    -e 'UNICORN_WORKERS=20' 
    -e 'GITLAB_PORT=80' 
    -e 'GITLAB_SSH_PORT=22' 
    -e 'LDAP_ENABLED=true' 
    -e 'LDAP_HOST=192.168.3.3' 
    -e 'LDAP_PORT=389' 
    -e 'LDAP_UID=sAMAccountName' 
    -e 'LDAP_METHOD=plain' 
    -e '[email protected]' 
    -e 'LDAP_PASS=passwd' 
    -e 'LDAP_BASE=OU=example_users,DC=example-family,DC=com' 
    -e 'LDAP_ACTIVE_DIRECTORY=true' 
    -e 'LDAP_ALLOW_USERNAME_OR_EMAIL_LOGIN=false' 
    -e 'GITLAB_HOST=gitlab.example.com' 
    -e 'SMTP_ENABLED=true' 
    -e 'SMTP_DOMAIN=example.com' 
    -e 'SMTP_HOST=192.168.3.4' 
    -e 'SMTP_PORT=25' 
    -e 'SMTP_STARTTLS=false' 
    -v /var/run/docker.sock:/run/docker.sock 
    -v $(which docker):/bin/docker 
    -v /opt/gitlab/data:/home/git/data 
    -v /opt/gitlab/log:/var/log/gitlab 
    sameersbn/gitlab:7.4.3
    
    sameersbn/gitlab:7.4.3不支持redis的任何验证,只能无密码使用
    

 

目前发现sameersbn/gitlab:7.4.3有一个非常坑的地方:容器启动可能连不上数据库,是因为容器启动时会探测数据库是否可用,而探测的方法是mysqladmin连接DB_HOST的3306端口,而不是DB_PORT指定的端口,已提交issue给作者。

使用Omnibus的用户将下面的源码保存到:
/opt/gitlab/embedded/service/gitlab-rails/lib/tasks/reset_token.rake

3.2.4.4. 给gitlab容器配置IP

  • pipework br1 gitlab
    192.168.1.1/[email protected]

 

在gitlab容器启动(docker
run)时可以加上-p参数来将容器里的端口映射到宿主上,但个人比较倾向给容器配置一个独立IP,因此上述命令没有采用-p来做端口映射,但是-e
‘GITLAB_PORT=80′ -e ‘GITLAB_SSH_PORT=22′ -e
‘GITLAB_HOST=gitlab.example.com’依然要指定,否则gitlab无法使用

使用源码安装的用户保存到: /lib/tasks/reset_token.rake

3.2.4.5. 安装已完成,可以打开页面

  • url: gitlab.example.com

    账户:root
    密码:5iveL!fe
    
  • 效果:
    澳门新葡萄京官网注册 13

# lib/tasks/reset_token.rake

3.2.4.6. 加入开机启动

  • 加入/etc/rc.local

    echo 'docker start gitlab_redis' >> /etc/rc.local
    echo 'docker start gitlab_mysql' >> /etc/rc.local
    echo 'docker start gitlab' >> /etc/rc.local
    echo 'pipework br1 gitlab 192.168.1.1/[email protected]' >> /etc/rc.local
    

require_relative ‘../../app/models/concerns/token_authenticatable.rb’

4. GitLab API wrappers

  • 官方推荐:
  • python:pyapi-gitlab

    基本使用:
    pip install pyapi-gitlab
    import gitlab
    git = gitlab.Gitlab("http://gitlab.example.com", token="EHBLkwhr_WYzn-sXNnNs")  # token即在页面上Profile settings->Account里能看到自动生成好的Private token
    git.getusers()
    
  • 顺便推荐个Git本身的python api模块:

 

其他很多git库的实现都太底层了,和linux的git命令完全不一样,一点也不友好。但目前发现这个模块上传下载代码只支持SSH,不支持HTTP,因此我还是更喜欢使用linux
git命令

STDOUT.sync = true

5. GitLab使用的FAQ

namespace :tokens do

5.1. git代码发布支持2种方法:ssh和http(s)

  • ssh:必须添加ssh key才能发布
  • http:使用用户名、密码,若接入了LDAP,就是LDAP中的账户密码
  • https:暂未测试过

desc “Reset all GitLab user auth tokens”

5.2. 限制git上传的单个文件大小

  • 若gitlab是通过sameersbn/gitlab:7.4.3镜像创建的,那么可以通过环境变量NGINX_MAX_UPLOAD_SIZE进行限制

5.3.
通过API进行一些操作例如创建Project,得到成功的响应,但却没有立即生效,过了几秒才生效

  • 由于GitLab为异步架构,Ruby on
    Rails收到创建Project请求后将该任务推送到Redis中,但是没有等待执行完毕,而是直接返回成功。后台Sidekiq从Redis订阅任务并实时执行,但由于执行需要时间,而API返回很快,因此会造成这种情况发生。目前没有其他解决办法,只能在自己程序逻辑里sleep几秒进行重试

5.4. 页面上”Profile settings->SSH Keys”与”Project Settings里Deploy
Keys”的区别

  • Profile settings->SSH
    Keys:是用户全局的Key,具有对该用户所有项目仓库进行上传下载(push、clone)的权限
  • Project Settings里Deploy
    Keys:针对某个项目,具有下载(clone)的权限,而不具备上传(push)的权限

task reset_all: :environment do

5.5. gitlab默认时区是UTC

  • sameersbn/gitlab:7.4.3镜像无法对时区进行修改,若是手动安装的gitlab,可以通过修改一个.rb文件来生效,具体没有测试过,有需要的可以自行百度搜下。

 

时区是UTC主要影响的是数据库里关于时间字段的值(created_at、updated_at等),对用户git仓库是不影响的,因为git仓库的创建、代码更新都是用户通过git工具自行操作,因此是用户自己的时区。

reset_all_users_token(:reset_authentication_token!)

6. GitLab维护

end

6.1. 数据路径、日志路径

  • 由于gitlab容器在启动时已将宿主/opt/gitlab/data、/opt/gitlab/log目录挂载到容器里,因此可以在宿主上进入这2个目录中查看,另外可以通过docker
    logs查看。

desc “Reset all GitLab email tokens”

6.2. 高可用

  • 官方有篇关于GitLab高可用的文章:

    有1/3都在讲述高可用的利弊以及不同程度的高可用带来的收益及可能引发的更多问题,说的挺有道理的。由于笔者也才刚开始使用GitLab,因此暂不测试多机load balance或failover。
    
  • 2种备份恢复方式

    1. 备份配置、仓库、数据库。
    2. 给文件系统做快照。
    官方认为第二种方法比起第一种更快,因为可以省去人为介入restore的麻烦
    XFS文件系统支持快照
    
    有2篇文章可以拜读下:
    http://www.icicletech.com/blog/gitlab-backup-made-easy 数据库数据备份到本地
    http://doc.gitlab.com/ce/raketasks/backup_restore.html 官方的,和上述其实一样
    
  • 我的备份方案:

    一. 简介:每天固定时间备份数据(仅备份MySQL和GitLab仓库重要文件,其他不备份)。若数据丢失,需要恢复,则将最近备份的数据用来恢复,会将所有数据,包括本地文件(仓库等文件)、数据库一同还原回备份时刻。
    
    二. 具体操作:
    1. 备份:
    docker run 
    --name='gitlab_backup' 
    -it 
    --rm 
    --link gitlab_mysql:mysql 
    --link gitlab_redis:redisio 
    -v /var/run/docker.sock:/run/docker.sock 
    -v $(which docker):/bin/docker 
    -v /opt/gitlab/data:/home/git/data 
    -v /opt/gitlab/log:/var/log/gitlab 
    sameersbn/gitlab:7.4.3 app:rake gitlab:backup:create
    
       过程能在屏幕上看到,备份会自动打成tar包放入/opt/gitlab/data/backups里。可以自行对该tar包做压缩,例如gzip 时间戳_gitlab_backup.tar。
       若$?=0表示备份成功,然后将生成的文件使用gzip压缩为.tar.gz,然后rsync推到存储上
    通过rsync命令传到一台备份存储上
    2. 恢复:
    docker run 
    --name='gitlab_restore' 
    -it 
    --rm 
    --link gitlab_mysql:mysql 
    --link gitlab_redis:redisio 
    -v /var/run/docker.sock:/run/docker.sock 
    -v $(which docker):/bin/docker 
    -v /opt/gitlab/data:/home/git/data 
    -v /opt/gitlab/log:/var/log/gitlab 
    sameersbn/gitlab:7.4.3 app:rake gitlab:backup:restore
    
    屏幕上会将/opt/gitlab/data/backups中的所有文件和目录列出来,复制粘贴要恢复的tar包文件名,敲入回车即开始恢复。
       注意:恢复时会将当前数据库中的所有表先删掉再导入备份tar包的里sql文件,因此此步要小心。
    
       若redis、mysql是使用环境变量带入gitlab容器的,备份和恢复命令也类似,将启动gitlab的命令复制过来,修改下--name,添加一个--rm,CMD改为gitlab:backup:create或gitlab:backup:restore即可
    
    三. 注意:
        1. 用户配置的key(即data/.ssh)是保存在数据库里,在gitlab服务启动时候会从数据库里加载并导入到.ssh里,被gitlab-shell管理
        2. 由于ssh目录(保存着服务器sshd服务启动时自己生成的ssh_key)不备份,因此还原时候若这个目录里有文件,则不去重新生成ssh_key,若不存在,则重新生成。若重新生成,用户使用ssh协议进行git push,会报错告知不合法的认证
        3. 备份只会备份3个目录:repositories、db、uploads,然后会额外生成一个backup_information.yml文件
        4. gitlab-satellites和tmp都是临时目录,因此不参与备份
        5. docker run --rm不会影响容器退出状态的输出
        6. 还原时候会将数据库里的表先DROP表再创建,因为sql文件在INSERT之前会drop table if exists
        7. 还原时候,若存在要恢复的目录,则会将原来的目录mv成.old.时间戳
    

更多GitLab相关教程见以下内容:

Ubuntu 14.04下安装GitLab指南 

如何在Ubuntu Server 14.04下安装Gitlab中文版 

CentOS源码安装GitLab汉化版 

在 Ubuntu 12.04 上安装 GitLab

GitLab 5.3 升级注意事项

在 CentOS 上部署 GitLab (自托管的Git项目仓库)

在RHEL6/CentOS6/ScientificLinux6上安装GitLab 6.0.2

CentOS 6.5安装GitLab教程及相关问题解决

GitLab 的详细介绍:请点这里
GitLab 的下载地址:请点这里

本文永久更新链接地址:

  1. 阅读本文基础 熟悉git使用 熟悉docker 2. GitLab简介 2.1. 概述 GitLab
    是一个用于仓库管理…

task reset_all_email: :environment do

reset_all_users_token(:reset_incoming_email_token!)

end

def reset_all_users_token(token)

TmpUser.find_in_batches do |batch|

puts “Processing batch starting with user ID: #{batch.first.id}”

batch.each(&token)

end

end

end

class TmpUser

include TokenAuthenticatable

self.table_name = ‘users’

def reset_authentication_token!

write_new_token(:authentication_token)

save!(validate: false)

end

def reset_incoming_email_token!

write_new_token(:incoming_email_token)

save!(validate: false)

end

end

Omnibus用户运行

sudo gitlab-rake tokens:reset_all

sudo gitlab-rake tokens:reset_all_email

源码用户运行

sudo -u git -H bundle exec rake tokens:reset_all RAILS_ENV=production

sudo -u git -H bundle exec rake tokens:reset_all_email
RAILS_ENV=production

执行完所有操作后可以把rake文件删除。

临时补丁

对于某些无法升级的用户可以使用以下的补丁:

Omnibus用户:

$ cd /opt/gitlab/embedded/service/gitlab-rails/

$ git apply

$ sudo gitlab-ctl restart unicorn

源码用户:

$ cd

diff –git a/app/controllers/projects/issues_controller.rb
b/app/controllers/projects/issues_controller.rb

index 1151555..857d907 100644

— a/app/controllers/projects/issues_controller.rb

+++ b/app/controllers/projects/issues_controller.rb

@@ -129,7 +129,7 @@ class Projects::IssuesController

end

format.json do

– render json: @issue.to_json(include: { milestone: {}, assignee: {
methods: :avatar_url }, labels: { methods: :text_color } }, methods:
[:task_status, :task_status_short])

+ render json: @issue.to_json(include: { milestone: {}, assignee: {
only: [:name, :username], methods: [:avatar_url] }, labels: {
methods: :text_color } }, methods: [:task_status,
:task_status_short])

end

end

diff –git a/app/controllers/projects/merge_requests_controller.rb
b/app/controllers/projects/merge_requests_controller.rb

index 82f9b6e..677a8a1 100644

— a/app/controllers/projects/merge_requests_controller.rb

+++ b/app/controllers/projects/merge_requests_controller.rb

@@ -308,7 +308,7 @@ class Projects::MergeRequestsController

end

format.json do

– render json: @merge_request.to_json(include: { milestone: {},
assignee: { methods: :avatar_url }, labels: { methods: :text_color }
}, methods: [:task_status, :task_status_short])

+ render json: @merge_request.to_json(include: { milestone: {},
assignee: { only: [:name, :username], methods: [:avatar_url] },
labels: { methods: :text_color } }, methods: [:task_status,
:task_status_short])

end

end

rescue ActiveRecord::StaleObjectError

检验漏洞是否修复

打开项目;

打开项目的issue跟踪器;

创建一个issue,将issue拥有权限分配给另一个用户;

查看返回的JSON,检查其中是否有敏感信息。