Loading [MathJax]/jax/output/CommonHTML/config.js
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >企业安全之内部代码管理平台Gitlab下载及权限审计

企业安全之内部代码管理平台Gitlab下载及权限审计

作者头像
FB客服
发布于 2019-05-17 03:51:58
发布于 2019-05-17 03:51:58
3.1K00
代码可运行
举报
文章被收录于专栏:FreeBufFreeBuf
运行总次数:0
代码可运行

企业通常会使用Gitlab作为内部代码管理平台,一来私有仓库更加安全,二来gitlab的功能十分完整。但仍不能保证私有仓库中的代码不被泄露到外部,于是对gitlab的权限审计以及下载审计就变得尤为重要。本文将基于gitlab-ee-11.10版本,详细叙述如何对gitlab的权限及代码下载进行审计。

0x00 快速部署gitlab

笔者使用了docker进行快速部署:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
docker pull gitlab/gitlab-eedocker run --detach --hostname gitlab.example.com --publish 443:443 --publish 80:80 --publish 22:22 --name gitlab --restart always --volume /srv/gitlab/config:/etc/gitlab --volume /srv/gitlab/logs:/var/log/gitlab --volume /srv/gitlab/data:/var/opt/gitlab gitlab/gitlab-ee:latest

gitlab可以与ldap绑定,使用AD域账号进行登录。如果想要修改gitlab.rb文件,可以登入到容器中修改:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
docker exec -it CONTAINER_ID /bin/bash

配置完成后,使用root访问http://ip:

官方网站: https://docs.gitlab.com/omnibus/docker/

0x01 Git的传输协议了解

Git主要以两种方式跨越两个仓库传输数据。

1.哑协议

Git基于HTTP之上传输通常被称为哑协议,这是因为它在服务端不需要有针对Git特有的代码。这个获取过程仅仅是一系列GET请求,客户端可以假定服务端的Git仓库中的布局。简单解读官方给出的举例,一次git clone过程:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
git clone http://github.com/schacon/simplegit-progit.git  //下载simplegit-progitInitialized empty Git repository in /private/tmp/simplegit-progit/.git/  //在/private/tmp/simplegit-progit/.git/目录中初始化一个空的git仓库got ca82a6dff817ec66f44342007202690a93763949  //获取info/refs文件,这个文件由服务端的update-server-info生成,用于给不进行动态包生成的哑服务器提供辅助信息文件,以帮助客户机发现服务器有哪些引用和包,哑服务器意味着通过http访问walk ca82a6dff817ec66f44342007202690a93763949  //获取commit对象got 085bb3bcb608e1e8451d4b2432f8ecbe6306e7e7 //查看commit对象的内容Getting alternates list for http://github.com/schacon/simplegit-progit.git  //获取替代仓库listGetting pack list for http://github.com/schacon/simplegit-progit.git //获取打包文件listGetting index for pack 816a9b2334da9953e530f27bcac22082a9f5b835 //获取这个打包文件的索引Getting pack 816a9b2334da9953e530f27bcac22082a9f5b835 which contains cfda3bf379e4f8dba8717dee55aab78aef7f4daf  //查看打包文件的索引是否包括要找的对象walk 085bb3bcb608e1e8451d4b2432f8ecbe6306e7e7  //获取commitwalk a11bef06a3f659402fe7563abf99ad00de2209e6  //下载对象
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
2.智能协议 

HTTP方法是很简单但效率不是很高。使用智能协议是传送数据的更常用的方法。这些协议在远端都有Git智能型进程在服务,它可以读出本地数据并计算出客户端所需要的合适的数据给它,这有两类传输数据的进程:一对用于上传数据和一对用于下载。此处只对下载展开描述:

当下载数据时,fetch-pack和upload-pack进程就起作用了。客户端启动fetch-pack进程,连接至远端的upload-pack进程,以协商后续数据传输过程。在远端仓库有不同的方式启动upload-pack进程。你可以使用与receive-pack(接收推送到存储库中的内容时所启用的进程)相同的透过SSH管道的方式,也可以通过Git后台来启动这个进程,它默认监听在9418号端口上。这里fetch-pack进程在连接后像这样向后发送数据:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
fgit-upload-pack schacon/simplegit-progit.git\0host=myserver.com\0

它也是以4字节指定后续字节长度的方式开始,然后是要运行的命令,和一个空字节,然后是服务端的主机名,再跟随一个最后的空字节。Git后台进程会检查这个命令是否可以运行,以及那个仓库是否存在,以及是否具有公开权限。如果所有检查都通过了,它会启动这个upload-pack进程并将客户端的请求移交给它。 如果你透过SSH使用获取功能,fetch-pack会像这样运行:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
ssh -x git@github.com "git-upload-pack 'schacon/simplegit-progit.git'"

0x02 gitlab数据库结构了解

docker镜像使用的是postgresql数据库,一共有236张数据表,我们知道gitlab采用了ueba的用户权限管理模型,因此想要获得用户、项目、项目组、key之间的关系,我们首先要关心这几张表:

identities存储ldap的信息,其中extern_uid存储ldap的部门等信息 :

Column

Type

id

integer

extern_uid

character varying

provider

character varying

user_id

integer

created_at

timestamp without time zone

updated_at

timestamp without time zone

secondaryexternuid

character varying

samlproviderid

integer

keys存储key,以及与user_id的对应关系:

Column

Type

id

integer

user_id

integer

created_at

timestamp without time zone

updated_at

timestamp without time zone

key

text

title

character varying

type

character varying

fingerprint

character varying

public

boolean

lastusedat

timestamp without time zone

namespaces存储用户及项目组的路径,主要用于获取项目组(type='Group')的信息(description)

Column

Type

id

integer

name

character varying

path

character varying

owner_id

integer

created_at

timestamp without time zone

updated_at

timestamp without time zone

type

character varying

description

character varying

avatar

character varying

membership_lock

boolean

sharewithgroup_lock

boolean

visibility_level

integer

requestaccessenabled

boolean

ldapsyncstatus

character varying

ldapsyncerror

character varying

ldapsynclastupdateat

timestamp without time zone

ldapsynclastsuccessfulupdate_at

timestamp without time zone

ldapsynclastsyncat

timestamp without time zone

description_html

text

lfs_enabled

boolean

parent_id

integer

sharedrunnersminutes_limit

integer

repositorysizelimit

bigint

requiretwofactor_authentication

boolean

twofactorgrace_period

integer

cachedmarkdownversion

integer

plan_id

integer

projectcreationlevel

integer

runners_token

character varying

trialendson

timestamp with time zone

filetemplateproject_id

integer

samldiscoverytoken

character varying

runnerstokenencrypted

character varying

customprojecttemplatesgroupid

integer

autodevopsenabled

boolean

extrasharedrunnersminuteslimit

integer

project_authorizations存储用户、项目以及访问权限的关系

Column

Type

user_id

integer

project_id

integer

access_level

integer

其中,access_level的含义为:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
10 => Guest access    20 => Reporter access    30 => Developer access    40 => Maintainer access    50 => Owner access # Only valid for groups

projects存储项目信息:

Column

Type

id

integer

name

character varying(510)

path

character varying(510)

description

text

created_at

timestamp with time zone

updated_at

timestamp with time zone

creator_id

integer

namespace_id

integer

lastactivityat

timestamp with time zone

import_url

character varying(510)

visibility_level

integer

archived

boolean

avatar

character varying(510)

import_status

character varying(510)

star_count

integer

import_type

character varying(510)

import_source

character varying(510)

import_error

text

ci_id

integer

sharedrunnersenabled

boolean

runners_token

character varying

buildcoverageregex

character varying

buildallowgit_fetch

boolean

build_timeout

integer

pending_delete

boolean

public_builds

boolean

lastrepositorycheck_failed

boolean

lastrepositorycheck_at

timestamp without time zone

containerregistryenabled

boolean

onlyallowmergeifbuild_succeeds

boolean

hasexternalissue_tracker

boolean

repository_storage

character varying

requestaccessenabled

boolean

hasexternalwiki

boolean

lfs_enabled

boolean

description_html

text

onlyallowmergeifalldiscussionsare_resolved

boolean

0x03 Gitlab日志了解

Gitlab初始化配置中,会在/var/log/gitlab目录下保存近1个月的日志,每天凌晨1点左右将目录下的*.log文件压缩为gz格式,比如将gitlab-shell.log压缩为gitlab-shell.log.1.gz,这个数字从1-30依次增加和轮换。

基于传输协议我们知道,当git执行一次git clone/git pull/git fetch的下载操作时,会在服务端启用协议,gitlab本身没有提供直观的下载日志,因此我们需要通过这个协议的启动来进行gitlab的下载审计。

gitlab-shell.log:日志文件位于/var/log/gitlab/gitlab-shell中,该日志文件的作用是记录执行gitlab命令以及为项目添加ssh权限的日志文件:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
time="2019-05-06T08:27:37+00:00" level=info msg="executing git command" command="gitaly-upload-pack unix:/var/opt/gitlab/gitaly/gitaly.socket {\"repository\":{\"storage_name\":\"default\",\"relative_path\":\"root/mytest.git\",\"git_object_directory\":\"\",\"git_alternate_object_directories\":[],\"gl_repository\":\"project-1\",\"gl_project_path\":\"root/mytest\"},\"gl_repository\":\"project-1\",\"gl_project_path\":\"root/mytest\",\"gl_id\":\"key-2\",\"gl_username\":\"root\",\"git_config_options\":[],\"git_protocol\":null}" pid=29421 user="user with id key-2" 

更多其他日志的介绍可以浏览:https://docs.gitlab.com/ee/administration/logs.html

0x04 Gitlab代码下载以及权限审计

在了解了以上Gitlab的基础知识后,开始着手于对Gitlab的审计。首先我们讨论如何对代码下载进行审计。 gitlab-shell日志记录了upload pack 的操作,但是日志中并不直观,因此需要对日志处理,希望获取到json日志格式:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
{    "time": "2019-05-06T08:27:37+00:00",     "gitcommand": "git-upload-pack",     "username": "test",     "name": "测试账号",     “user_department”: "运维部"    "project_description": "用于gitlab测试",    "gitpath": "/data/gitlab/git-data/repositories/root/mytest.git"    ”key_id": "233",     } 

为了得到上方的日志信息,我们先从gitlab-shell.log日志中提取time、git command、glprojectpath、gl_id:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
time="2019-05-06T08:27:37+00:00"    git command="gitaly-upload-pack    gl_project_path:"root/mytest"    gl_id:"key-233" 
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
由于日志中没有直接显示user的信息,因此需要从Gitlab的数据库users、keys、identities、namespaces表中查询:

通过key-id 获取user_id:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
SELECT user_id FROM keys WHERE id=key-id 

通过userid获取name、username以及部门信息userdepartment:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
SELECT name,username FROM users WHERE id=user_idSELECT extern_uid FROM identities WHERE user_id=usr_id 

再从namespaces中查询path='mytest',以获取项目的描述信息projectdescription,用于与userdepartment进行比对。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
SELECT description FROM projects where path=path

至此我们需要的信息就可以存为一条日志了,通过这种方法将gitlab-shell.log处理为可读性的json日志,再接入到ELK中用于审计。

通过Python可以再写一些告警规则,比如当用户所属部门与项目的描述不一致时,则发出邮件告警。也可以从数据库中获取其他的信息来补充日志。

在Gitlab数据库中,project_authorizations表记录用户、项目以及项目访问权限的关系:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
user_id | project_id | access_level     ---------+------------+--------------           1 |          1 |           40 
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
我们可以结合users和projects表,统计出可读的用户-项目访问权限表,以此来审计gitlab项目的访问权限,也可将这些用户对项目的访问权限加入到gitlab代码下载日志中去。

由于gitlab的数据库中包含了密钥这类重要敏感信息,因此gitlab的数据库建议绑定本地IP,笔者的做法是每天将仅需要使用到的数据推送到MySQL服务器中,再从MySQL服务器中获取对应信息。

推送users以及keys表:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#!/bin/bashpsql -h /var/opt/gitlab/postgresql -d gitlabhq_production <<EOF        COPY (SELECT id,user_id FROM keys) TO '/var/opt/gitlab/postgresql/key.csv' with csv header;        COPY (SELECT id,name,username FROM users) TO '/var/opt/gitlab/postgresql/user.csv' with csv header;        COPY (SELECT user_id,extern_uid FROM users) TO '/var/opt/gitlab/postgresql/department.csv' with csv header;EOFscp user.csv root@远程主机IP:/rootscp key.csv root@ 远程主机IP :/rootscp department.csv root@ 远程主机IP :/root

推送最新打包好的gitlab-shell.log.x.gz日志:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#!/bin/bashls | find /var/log/gitlab/gitlab-shell/ -mtime -1 |grep gz |xargs -i cp -f {} /var/log/gitlab/gitlab-shell/gitlab-shell.log.gzscp -i /var/log/gitlab/gitlab-shell/.ssh/id_rsa gitlab-shell.log.gz root@远程主机IP:远程目录sleep 10rm -f gitlab-shell.log.gz
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Python处理日志格式(部分代码):
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import jsonimport pymysqlfrom datetime import datetimedef get_info(key_id):        conn = pymysql.connect(                host = 'x.x.x.x',                port = 3306,                user = 'gitlab',                password = 'password',                database = 'gitlab'                )        cursor = conn.cursor()        #get user_id        sql = "SELECT user_id FROM keys WHERE id=" + str(key_id)        cursor.execute(sql)        row_1 = cursor.fetchone()        if row_1 != None:                user_id = row_1[0]                #get name_username                sql2 = "SELECT name,username FROM users WHERE id=" + str(user_id)                cursor.execute(sql2)                row_2 = cursor.fetchone()                name_username = row_2                #get user_department                sql3 = "SELECT  extern_uid  FROM identities WHERE id=" + str(user_id)                cursor.execute(sql3)                row_3 = cursor.fetchone()                if row_3 != None:                        userdepartment = row_3[0].split(",")[1]                else:                        userdepartment = 'null'                user_department = userdepartment        else:                name_username = {'null','null'        return (name_username,user_department)def logtojson():        with open(r'gitlab-shell.log') as myfile:                logs = myfile.readlines()                array = []                for log in logs:                        array.append(log)        logdict = []        for i in range(len(array)):                info = array[i]                info1 = info.split()                if info1[0] == "I," and info1[6] == "gitlab-shell:":                        time1 = info1[1].split("[")[1]                        time = time1.split(".")[0]                        gitcommand = info1[10].split("<")[1]                        gitpath = info1[11].split(">")[0]                        key = info1[16].split("-")[1]                        key_id = key.split(".")[0]                        group = gitpath.split("/")[5]                        info = get_info(key_id)                        name = info[0][0]                        username = info[0][1]                        user_department = info[1]                        newlog = {                                "logDate": time,                                "gitcommand":gitcommand,                                "gitpath":gitpath,                                "name":name,                                "username":username,                                "user_department":user_department,                                "key_id":key_id                                }                        logdict.append(newlog)        with open(datetime.now().date().isoformat()+'.log',"w") as f:                for i in logdict:                        json.dump(i,f,ensure_ascii=False)                        f.write('\n')if __name__ == '__main__':        logtojson()
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-05-15,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 FreeBuf 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
xshell使用xftp传输文件,使用pure-ftpd搭建ftp服务
xshell使用xftp传输文件: 使用xshell登录一个在线的主机,按下ctrl+alt+f,会自动弹出一个xftp的提示下载界面 进入下载界面 选择下载以后,会进入一个填写资料的页面,购买密钥可
叶瑾
2018/06/14
1.1K0
Xshell使用xftp传输文件,使用pure-ftpd搭建ftp服务
笔记内容: 15.4 xshell使用xftp传输文件 15.5 使用pure-ftpd搭建ftp服务 笔记日期:2017-11-03 15.4 xshell使用xftp传输文件 在官网下载xftp,
端碗吹水
2020/09/23
1.2K0
Xshell使用xftp传输文件,使用pure-ftpd搭建ftp服务
xftp、使用pure-ftpd搭建FTP服务
在xshell中退出当前会话使用组合键:Ctrl+Alt+f,出现如下所示内容:
阿dai学长
2019/04/03
1.7K0
Linux基础(day58)
15.4 xshell使用xftp传输文件 在xshell按快捷键 ctrl+alt+f ,会自动弹出一个xftp的提示下载界面 进入到下载界面 选择下载以后,会进入一个填写资料的页面,上面的是免费的
运维小白
2018/02/06
1.2K0
Linux基础(day58)
使用pure-ftpd搭建ftp服务
一、使用pure-ftpd搭建ftp服务 一款非常适合个人使用的轻量ftp软件,配置简单,容易上手,后期如果朋友或者客户需要搭建一个ftp服务器的时候,我认为这是首选。 1.1 安装: [root@zhdy-03 ~]# yum install -y epel-release [root@zhdy-03 ~]# yum install -y pure-ftpd 1.2 更改配置文件: [root@zhdy-03 ~]# vim /etc/pure-ftpd/pure-ftpd.conf //找到pu
老七Linux
2018/05/09
1.8K0
学习笔记0525----NFS文件共享和FTP服务器
用两台机器做实验,一台作为服务端(linux-001 192.168.141.128),一台作为客户端(linux-02 192.168.141.129)
嘻哈记
2020/11/24
3.3K0
Debian Linux下安装配置 Pure-ftpd
PureFTPd 是一款专注于程序健壮和软件安全的免费FTP服务器软件(基于BSD License)。其可以在多种类Unix操作系统中编译运行,包括Linux、OpenBSD、FreeBSD、Solaris、Darwin等。PureFTPd 还有Android移植版本。
Debian中国
2018/12/20
2.3K0
CentOS 7安装和配置Pure-ftpd
PureFTPd是一款专注于程序健壮和软件安全的免费FTP服务器软件(基于BSD License),目前常见的一键安装包,如Oneinstack、lnmp.org、宝塔等服务都已集成PureFTPd服务,可见PureFTPd是一款非常流行的FTP服务软件。
星哥玩云
2022/08/13
2.3K0
CentOS 7安装和配置Pure-ftpd
Pure-ftpd服务器搭建
[root@mail pureftp]# tar -zxvf pure-ftpd-1.0.36.tar.gz -C /usr/local/src/
星哥玩云
2022/06/11
9330
Pure-ftpd服务器搭建
【玩转服务器】CentOS安装Pure-FTPd
Pure-FTPd是一款免费FTP服务器软件,优势在于其安全性与灵活性,可自定义权限、创建虚拟用户、配置主目录等操作。
参谋带个长
2023/12/14
2K0
CentOS pure-ftpd配置及错误解决
Pure-FTPd是Linux上的一个开源的FTP服务程序,在易用性、配置性上比vsftp较方便,下面我们使用CentOS 6演示安装和配置pure-ftpd。
星哥玩云
2022/07/26
1.8K0
FTP & VBScript & Powershell & Debug 远程传输
之前我们说过FTP在非交互shell 下会出现没有办法输入密码,导致身份认证过程没有办法完整完成,这里我们就来介绍一下如何变通。
意大利的猫
2020/08/19
2K0
exportfs命令,NFS客户端问题,FTP介绍,使用vsftpd搭建ftp
/tmp/ 192.168.209.129/24(rw,sync,no_root_squash)
叶瑾
2018/05/10
4K2
exportfs命令,NFS客户端问题,FTP介绍,使用vsftpd搭建ftp
Linux安装FTP
2.FTP连接刚刚装了FTP服务的另一台服务器(下面有服务端教程)。(ip自己改成自己的)
shaun
2023/10/26
3.7K0
Linux安装FTP
linux学习第五十二篇: exportfs命令,NFS客户端问题,FTP介绍,使用vsftpd搭建ftp服务
exportfs命令 常用选项 -a 全部挂载或者全部卸载 -r 重新挂载 -u 卸载某一个目录 -v 显示共享目录 以下操作在服务端上 vim /etc/exports 增加 /tm
用户1215343
2018/02/05
2.9K0
linux学习第五十二篇: exportfs命令,NFS客户端问题,FTP介绍,使用vsftpd搭建ftp服务
FTP服务搭建与配置 原
说明: vsftpd可以使用系统级别的用户进行登录,但是这样做不安全,所以需要使用vsftpd创建一个虚拟用户。在此创建一个系统普通用户的作用是使vsftpd的虚拟用户来进行映射,然后完成数据的传输。
阿dai学长
2019/04/03
1.2K0
3 FTP文件传输服务
用户控制列表文件: /etc/vsftpd/ftpusers(黑名单)、/etc/vsftpd/user_list
py3study
2020/01/14
1.9K0
exportfs命令,NFS客户端问题,FTP介绍,使用vsftp搭建ftp
这个exportfs命令会在安装nfs-util包时一起安装,当服务端需要停止NFS服务或者进行一些变动并重启时,其他挂载了共享目录的机器需要将这个挂载的目录卸载掉,不然的话服务进程就会出问题,单台机器我们可以使用unmnt命令去卸载,但是如果有几十台机器的话总不能一个个去unmnt吧,所以这个exportfs命令就是用来进行多台机器的卸载、重新挂载之类的操作的。
端碗吹水
2020/09/23
5.6K0
exportfs命令,NFS客户端问题,FTP介绍,使用vsftp搭建ftp
FTP远程文件传输服务安装与配置
描述: FTP只通过TCP连接,没有用于FTP的UDP组件.FTP不同于其他服务的是它使用了两个端口, 一个数据端口和一个命令端口(或称为控制端口)。通常21端口是命令端口,20端口是数据端口。当混入主动(Active)/被动模式(Passive)的概念时,数据端口就有可能不是20了。
全栈工程师修炼指南
2022/09/29
2.1K0
FTP远程文件传输服务安装与配置
玩转企业常见应用与服务系列(二):文件共享服务 FTP 原理与实践
前面介绍了企业常用服务NFS网络文件共享存储相关的知识点,今天我将详细的为大家介绍文件共享服务FTP原理与实践相关知识,希望大家能够从中收获多多!如有帮助,请点在看、转发朋友圈支持一波!!!
民工哥
2023/11/16
5920
玩转企业常见应用与服务系列(二):文件共享服务 FTP 原理与实践
推荐阅读
相关推荐
xshell使用xftp传输文件,使用pure-ftpd搭建ftp服务
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验