Git:使用Gitleaks扫描存储库中的秘密

2,384 阅读7分钟

诸如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 Hoggitleaks都值得一试,但我不喜欢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通知插件。请看这里的文档>>>。

创建一个Slack Bot

转到Slack应用程序,创建一个新的应用程序。

转到 "权限"

添加以下内容。

  • files:write
  • chat: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"
        }
...

目前就这些了。

类似的帖子

The postGit: scan repositories for secrets using Gitleaksfirst appeared onRTFM: Linux, DevOps, and system administration.