诸如RDS密钥或密码等机密数据泄露到Git仓库,即使是私有的Github仓库,也是一件非常糟糕的事情,最好是检查你的仓库,了解是否有开发者推送了含有此类数据的提交。
扫描工具
要检查 Git 仓库是否有泄漏,乍一看有很多实用工具,但实际上,这些实用工具都有一个共同点,那就是它们都有一个共同的特点。
- Gittyleaks- 看起来很有趣,但最后一次更新是在2年前
- Repo Supervisor- 有一个 WebUI,使用 AWS Lambda,与 Github 完全集成,可以稍后检查
- Truffle Hog- 只有CLI,看起来不坏
- Git Hound-
git的一个插件,只能在提交前进行扫描,但不能扫描远程仓库 - Gitrob- 最后一次更新是在三年前
- Watchtower- 看起来很有趣,甚至有一个WebUI,但他们甚至没有在网站上公布他们的价格,所以被淘汰了
- GitGuardian- 一个非常好的解决方案,但价格过高
- gitleaks- 只有CLI,我们将在这篇文章中使用它。
所以,从上面的列表来看,Truffle Hog和gitleaks都值得一试,但我不喜欢Truffle Hog的文档。
Repo Supervisor看起来也很有希望,我们将在下一篇文章中检查它。
从这两个方面来看。
- Gitleaks:只是一个扫描器--给了一个版本库的URL,它就会生成一个JSON报告,其中包含发现。
- Repo Supervisor:可以通过两种方式使用。
- 只是扫描一个本地目录
- 在PullRequest/push/etc上扫描一个远程版本库
因此,对于Gitleaks,我们可以在Jenkins或Kubernetes中创建一个cronjob,接收要检查的版本库列表,然后将报告发送到Slack频道。
另外,Gitleaks可以和Github Actions一起使用,更多信息请看这里>>,但不是所有的开发团队都使用Action。另一种方法是预提交钩子。
计划
所以,现在,让我们试试用Jenkins的解决方案,尽管有各种方法来运行它。
- 用GitHub Pull Request Builder触发一个作业
- 使用 через GitHub 钩子触发工作 触发 GITScm 轮询илиPoll SCM
- 仅仅作为一个crontask运行
首先,我们将创建一个简单的作业,按时间表运行,然后检查其他解决方案。
我们的项目里有什么。
- 约200个Github仓库
- 大约10个开发团队--后端、前端、分析、iOS和Android移动应用、游戏、devops。
我们能用Gitleaks做什么?
- 为每个团队创建一个Jenkins作业
- 该作业将接受一个参数,包含该团队的存储库列表
- 将为每个团队创建一个专门的Slack频道
- 每天运行一次扫描,并将报告发送到相应的Slack频道。
首先,让我们手动运行Gitleaks,看看它是如何工作的,然后将做一个自动化的工作。
Gitleaks - 手动运行
安装它。在Arch Linux上,可以从AUR安装。
yay -S gitleaks
Github令牌
接下来,需要创建一个令牌来访问Github组织的存储库。
进入你的Github用户的设置,创建一个令牌。
给它repo 的权限。
然后带着令牌和一个仓库的URL运行Gitleaks,添加--verbose ,将结果保存到文件中。
gitleaks --access-token=ghp_C6h***3z5 --repo-urlhttps://github.com/example/BetterBI --verbose --report=analytics-repo.json
...
INFO[0036] 扫描时间:32秒 756毫秒 672微秒
INFO[0036] 扫描到的提交:1893个
WARN[0036] 发现的泄漏:111个
检查报告。
less analytics-repo.json
还有一个发现中的例子。
...
{
"line": " \"private_key\": \"-----BEGIN PRIVATE KEY-----\\nMIIEvQIBADA***CCaM=\\n-----END PRIVATE KEY-----\\n\",",
"lineNumber": 5,
"offender": "-----BEGIN PRIVATE KEY-----",
"offenderEntropy": -1,
"commit": "0f047f0cca3994b3465821ef133dbd3c8b55ee7a",
"repo": "BetterBI",
"repoURL": "https://github.com/example/BetterBI",
"leakURL": "https://github.com/example/BetterBI/blob/0f047f0cca3994b3465821ef133dbd3c8b55ee7a/adslib/roas_automation/example-service-account.json#L5",
"rule": "Asymmetric Private Key",
"commitMessage": "DT-657 update script for subs and add test\n\nDT-657 create test for check new json (add new subs)",
"author": "username",
"email": "example@users.noreply.github.com",
"file": "adslib/roas_automation/example-service-account.json",
"date": "2021-05-11T19:46:46+03:00",
"tags": "key, AsymmetricPrivateKey"
},
...
这里。
line:到底发现了什么offender:一个规则,为这个发现而触发的commit: 一个带有秘密的提交ID
用regex表达式进行查找,描述在 default.go.
另外,你可以创建你自己的配置文件并将其传递给Gitleaks。
例如,上面的RSA私钥是由非对称私钥规则找到的。
[[rules]]
description = "Asymmetric Private Key"
regex = '''-----BEGIN ((EC|PGP|DSA|RSA|OPENSSH) )?PRIVATE KEY( BLOCK)?-----'''
tags = ["key", "AsymmetricPrivateKey"]
所以,我们可以为每个团队或仓库创建一个专门的配置文件,并通过Kubernetes ConfigMap或作为Jenkins作业中的文件传递。
Jenkins作业
现在,当我们看到Gitleaks是如何启动的,让我们添加一个Jenkins作业来定期运行它。
管道脚本
因此,对于每个团队,我们将创建一个专门的Jenkins作业,该作业将有一个参数,包含该团队的仓库列表。
Groovy中的循环
前段时间我用Golang做了一个类似的解决方案,请看Go:检查Github中的公共仓库列表。详细情况请点击对比帖子,在那里对列表进行循环操作是比较简单的。用Groovy,必须要用google一下。
创建一个新的Jenkins作业,将其类型设置为管道。
在作业的设置中,创建一个字符串参数,列出团队的仓库,这里只用了两个。
接下来,到Jenkins脚本中去。
设置一个名为$repos_list 的变量,它将接受一个环境变量$TEAM_REPOS ,然后通过使用split() 的方法划分列表的对象。
然后,通过使用for 循环对它们进行整合。
node('master') {
def repos_list = "${env.TEAM_REPOS}".split(',')
for (repo in repos_list) {
println repo
}
}
运行作业。
Jenkins Docker插件
我们默认运行Jenkins构建的方法是使用Docker容器来保持主机系统的清洁。
添加另一个密码类型的参数,在这里保存Github token。
通过使用Jenkins Docker Plugin创建一个带有Gitleaks的Docker容器,传递令牌、URL和报告文件。注意,报告的文件将包含一个版本库的名称。
node('master') {
def repos_list = "${env.TEAM_REPOS}".split(',')
for (repo in repos_list) {
stage("Repository ${repo}") {
docker.image('zricethezav/gitleaks').inside('--entrypoint=""') {
sh "gitleaks --access-token=${GITHUB_TOKEN} --repo-url=https://github.com/example/${repo} --verbose --report=analytics-${repo}-repo.json"
}
}
}
}
在这里,对于repos_list 列表中的每一个版本库名称,我们将创建一个专门的Jenkins管道阶段,也将使用版本库的名称。
运行并检查。
嗯......这里有一个问题:扫描将在第一个被扫描的版本库的第一个发现后立即停止,因为Geatleaks发现了一个漏洞,返回退出1代码,工作立即被停止。
忽略Jenkins阶段中的错误{}。
为了解决这个问题,我们可以使用try/catch 方案:每个阶段都会在其try ,万一出现错误,我们会用catch ,并继续进行构建。
node('master') {
def repos_list = "${env.TEAM_REPOS}".split(',')
def build_ok = true
for (repo in repos_list) {
try {
stage("Repository ${repo}") {
docker.image('zricethezav/gitleaks').inside('--entrypoint=""') {
sh "gitleaks --access-token=${GITHUB_TOKEN} --repo-url=https://github.com/example/${repo} --verbose --report=analytics-${repo}-repo.json"
}
}
} catch(e) {
currentBuild.result = 'FAILURE'
}
}
}
运行它。
很好--现在所有阶段都在运行,尽管前一个阶段出现了结果。
来自Jenkins的Slack通知
我们的下一步是配置发送警报到Slack工作区。
创建一个Slack Bot
转到Slack应用程序,创建一个新的应用程序。
转到 "权限"。
添加以下内容。
files:writechat:write
转到OAuth & Permissions,将机器人安装到Slack工作区。
保存令牌。
Jenkins凭证
将令牌添加到Jenkins中 - 去管理Jenkins > 管理凭证。
添加一个新的。
将其类型设置为秘密文件。
在Slack工作区,创建一个新的频道。
邀请机器人进入该频道。
在Jenkins脚本中添加一个新函数--notifySlack() ,并从catch{} ,如果在扫描过程中发现任何秘密,就发送警报。
def notifySlack(String buildStatus = 'STARTED') {
// Build status of null means success.
buildStatus = buildStatus ?: 'SUCCESS'
def color
//change for another slack chanel
def token = 'gitleaks-slack-bot'
if (buildStatus == 'STARTED') {
color = '#D4DADF'
} else if (buildStatus == 'SUCCESS') {
color = '#BDFFC3'
} else if (buildStatus == 'UNSTABLE') {
color = '#FFFE89'
} else {
color = '#FF9FA1'
}
def msg = "${buildStatus}: `${env.JOB_NAME}` #${env.BUILD_NUMBER}:\n${env.BUILD_URL}"
slackSend(color: color, message: msg, tokenCredentialId: token, channel: "#devops-alarms-gitleaks-analytics")
}
node('master') {
def repos_list = "${env.TEAM_REPOS}".split(',')
for (repo in repos_list) {
try {
stage("Repository ${repo}") {
docker.image('zricethezav/gitleaks').inside('--entrypoint=""') {
sh "gitleaks --access-token=${GITHUB_TOKEN} --repo-url=https://github.com/example/${repo} --verbose --report=analytics-${repo}-repo.json"
}
}
} catch(e) {
currentBuild.result = 'FAILURE'
notifySlack(currentBuild.result)
}
}
}
jenkins.plugins.slack.StandardSlackService postToSlack 响应代码。404
运行构建,得到以下错误。
12:42:39 ERROR: Slack通知失败。详见Jenkins日志。
在https://<JENKINS_URL>/log/all上查看Jenkin的日志。
进入Manage Jenkins > Configure System,找到Slack插件的选项,设置Custom slack app bot user。
这里的凭证是默认的,我们要从管道中覆盖它们。
在 "高级"中删除 "覆盖URL ",如果它被设置了。
再次运行,现在一切正常了。
文件上传到Slack
现在,让我们通过使用slackUploadFile() 功能,将报告文件上传与调查结果添加到Slack频道的消息中。
def notifySlack(String buildStatus = 'STARTED', reportFile) {
// Build status of null means success.
buildStatus = buildStatus ?: 'SUCCESS'
def color
//change for another slack chanel
def token = 'gitleaks-slack-bot'
if (buildStatus == 'STARTED') {
color = '#D4DADF'
} else if (buildStatus == 'SUCCESS') {
color = '#BDFFC3'
} else if (buildStatus == 'UNSTABLE') {
color = '#FFFE89'
} else {
color = '#FF9FA1'
}
def msg = "${buildStatus}: `${env.JOB_NAME}` #${env.BUILD_NUMBER}:\n${env.BUILD_URL}"
slackSend(color: color, message: msg, tokenCredentialId: token, channel: "#devops-alarms-gitleaks-analytics")
slackUploadFile(credentialId: token, channel: "#devops-alarms-gitleaks-analytics", filePath: "${reportFile}")
}
node('master') {
def repos_list = "${env.TEAM_REPOS}".split(',')
for (repo in repos_list) {
try {
stage("Repository ${repo}") {
docker.image('zricethezav/gitleaks').inside('--entrypoint=""') {
sh "gitleaks --access-token=${GITHUB_TOKEN} --repo-url=https://github.com/example/${repo} --verbose --report=analytics-${repo}-repo.json"
}
}
} catch(e) {
currentBuild.result = 'FAILURE'
notifySlack(currentBuild.result, "analytics-${repo}-repo.json")
}
}
}
这里的频道可以稍后移到工作的参数中。
在这里,在notifySlack() ,我们已经添加了另一个参数--reportFile ,然后在notifySlack() 函数调用期间,我们将报告的文件作为第二个参数传递给函数。
运行作业,检查Slack频道。
最后就是设置一个时间表来运行这个工作。
Gitleaks配置
要检查的承诺
此刻,Gitleasks将对版本库进行全面扫描--所有提交、所有历史。
如果我们每天都运行它,那么每天我们都会收到关于一些有问题的旧提交的信息。
作为一种缓解方法,我们可以创建两个作业:在第一个作业中,我们将进行全面扫描,在第二个作业中,将对过去24小时内的修改进行增量扫描。
也就是说,"增量 "作业将在每天中午12:00运行,此时所有的开发人员都在办公室,该作业将只检查最后一天的提交。
要做到这一点,Gitleaks有--commit-since 选项。让我们添加一个名为yesterday 的新变量,其中包含由Date() 类的previous() 方法获取的昨天的日期,然后这个日期将被传递给--commit-since 。
...
node('master') {
def repos_list = "${env.TEAM_REPOS}".split(',')
def yesterday = new Date().format( 'yyyy-MM-dd' ).previous()
println yesterday
for (repo in repos_list) {
try {
stage("Repository ${repo}") {
docker.image('zricethezav/gitleaks').inside('--entrypoint=""') {
sh "gitleaks --access-token=${GITHUB_TOKEN} --repo-url=https://github.com/example/${repo} --verbose --report=analytics-${repo}-repo.json --commit-since=${yesterday}"
}
}
} catch(e) {
currentBuild.result = 'FAILURE'
notifySlack(currentBuild.result, "analytics-${repo}-repo.json")
}
}
}
Gitleaks 配置文件
另一件事是为Gitleaks创建一个专门的规则文件。
这可以通过--repo-config-path ,在每个资源库中,我们可以添加自己的配置文件。
在那里添加一些默认规则,另外我想检查以明文形式传递给提交的密码。
...
[[rules]]
description = "Plaintext password"
regex = '''(?i)pass*[a-z]{5}[:|=]? +["|'](.*)["|']'''
tags = ["password", "PlainTextPassword"]
[allowlist]
description = "Allowlisted files"
files = ['''^\.?gitleaks.config$''']
使用(?i)pass*[a-z]{5}[:|=]? +["|'](.*)["|'] 正则表达式,我们要寻找一个以pass 开头的字符串,然后是": "或"="符号,然后可以包含或不包含空格,然后是一个引号,然后是任何文本,最后是一个引号。
看起来一定是在工作。
把它保存到资源库中,作为.github/gitleaks.config ,并在工作中通过使用这个文件添加另一个参数。
...
docker.image('zricethezav/gitleaks').inside('--entrypoint=""') {
sh "gitleaks --access-token=${GITHUB_TOKEN} --repo-url=https://github.com/example/${repo} --verbose --report=analytics-${repo}-repo.json --commit-since=${yesterday} --repo-config-path=.github/gitleaks.config"
}
...
目前就这些了。
-
[
](twitter.com/share?text=…: Linux, DevOps, and system administration "Tweet This Post")
-
[
](www.linkedin.com/shareArticl…: Linux, DevOps, and system administration)
类似的帖子
-
04/16/2019 Jenkins:检查Github组织的公共仓库列表的作业 (0)
-
04/25/2019 Linux。GPG-keys,Pass-密码管理器,以及从KeePass数据库导入密码 (0)
-
05/19/2021 ArgoCD:声明式项目、应用程序和ArgoCD从Jenkins部署 (0)
-
11/22/2020 ArgoCD:概述,SSL配置,以及应用程序的部署 (0)
-
10/23/2020 Git: git clone - fatal: unable to fork and RSA key fingerprint (0)
The postGit: scan repositories for secrets using Gitleaksfirst appeared onRTFM: Linux, DevOps, and system administration.



