验证 Git 仓库
Git 仓库经常作为源代码输入出现在 Docker 构建中。ADD 指令可以克隆仓库,构建上下文也可以引用 Git URL。验证这些输入可确保你是从受信任的源和经过验证的版本构建的。
本指南将教你编写验证 Git 输入的策略,从基本的版本固定到验证已签名的提交和标签。
先决条件
你应该已从 简介 了解了策略基础知识:创建策略文件、基本的 Rego 语法,以及策略在构建期间如何评估。
什么是 Git 输入?
Git 输入来自引用 Git 仓库的 ADD 指令:
# 克隆特定标签
ADD https://github.com/moby/buildkit.git#v0.26.1 /buildkit
# 克隆分支
ADD https://github.com/user/repo.git#main /src
# 克隆提交
ADD https://github.com/user/repo.git#abcde123 /src当你使用以下方式构建时,构建上下文也可以是 Git 仓库:
$ docker build https://github.com/user/repo.git#main
每个 Git 引用都会触发一次策略评估。你的策略可以检查仓库 URL、验证版本、检查提交元数据并验证签名。
匹配特定仓库
最简单的 Git 策略是限制可以使用哪些仓库:
package docker
default allow := false
allow if input.local
allow if {
input.git.host == "github.com"
input.git.remote == "https://github.com/moby/buildkit.git"
}
decision := {"allow": allow}此策略:
- 默认拒绝所有输入
- 允许本地构建上下文
- 仅允许来自 GitHub 的 BuildKit 仓库
host 字段包含 Git 服务器主机名,remote 包含完整的仓库 URL。测试它:
FROM scratch
ADD https://github.com/moby/buildkit.git#v0.26.1 /$ docker build .
构建成功。尝试一个不同的仓库,它会失败。
你可以使用额外的规则匹配多个仓库:
allow if {
input.git.host == "github.com"
input.git.remote == "https://github.com/moby/buildkit.git"
}
allow if {
input.git.host == "github.com"
input.git.remote == "https://github.com/docker/cli.git"
}
decision := {"allow": allow}固定到特定版本
标签和分支会随时间变化。固定到特定版本以确保可复现的构建:
package docker
default allow := false
allow if input.local
allow if {
input.git.remote == "https://github.com/moby/buildkit.git"
input.git.tagName == "v0.26.1"
}
decision := {"allow": allow}当 Git 引用指向标签时,tagName 字段包含标签名称。对分支使用 branch:
allow if {
input.git.remote == "https://github.com/user/repo.git"
input.git.branch == "main"
}或者对任何类型的引用(分支、标签或提交 SHA)使用 ref:
allow if {
input.git.ref == "v0.26.1"
}使用版本允许列表
对于你信任但想控制版本的仓库,维护一个允许列表:
package docker
default allow := false
allowed_versions = [
{"tag": "v0.26.1", "annotated": true, "sha": "abc123"},
]
is_buildkit if {
input.git.remote == "https://github.com/moby/buildkit.git"
}
allow if {
not is_buildkit
}
allow if {
is_buildkit
some version in allowed_versions
input.git.tagName == version.tag
input.git.isAnnotatedTag == version.annotated
startswith(input.git.commitChecksum, version.sha)
}
decision := {"allow": allow}此策略:
- 定义一个带有元数据的已批准版本允许列表
- 使用一个辅助规则(
is_buildkit)以提高可读性 - 允许所有非 BuildKit 的输入
- 对于 BuildKit,根据允许列表检查标签名称、它是否为带注释标签以及提交 SHA
辅助规则使复杂策略更易于维护。随着新版本被批准,你可以扩展允许列表:
allowed_versions = [
{"tag": "v0.26.1", "annotated": true, "sha": "abc123"},
{"tag": "v0.27.0", "annotated": true, "sha": "def456"},
{"tag": "v0.27.1", "annotated": true, "sha": "789abc"},
]使用正则表达式模式验证
使用模式匹配来实现语义化版本:
package docker
default allow := false
allow if input.local
allow if {
input.git.remote == "https://github.com/moby/buildkit.git"
regex.match(`^v[0-9]+\.[0-9]+\.[0-9]+$`, input.git.tagName)
}
decision := {"allow": allow}这允许任何匹配模式 vX.Y.Z 的 BuildKit 标签,其中 X、Y 和 Z 是数字。该正则确保你使用的是发布版本,而不是像 v0.26.0-rc1 这样的预发布标签。
匹配主版本:
# 仅允许 v0.x 发布版本
allow if {
input.git.remote == "https://github.com/moby/buildkit.git"
regex.match(`^v0\.[0-9]+\.[0-9]+$`, input.git.tagName)
}检查提交元数据
commit 对象提供有关提交的详细信息:
package docker
default allow := false
allow if input.local
# 检查提交作者
allow if {
input.git.remote == "https://github.com/user/repo.git"
input.git.commit.author.email == "[email protected]"
}
decision := {"allow": allow}commit 对象包括:
author.name:作者姓名author.email:作者邮箱author.when:提交创作时间committer.name:提交者姓名committer.email:提交者邮箱committer.when:提交时间message:提交信息
验证提交信息:
allow if {
input.git.commit
contains(input.git.commit.message, "Signed-off-by:")
}固定到特定提交 SHA:
allow if {
input.git.commitChecksum == "abc123def456..."
}要求已签名的提交
GPG 签名的提交可证明真实性。检查提交签名:
package docker
default allow := false
allow if input.local
allow if {
input.git.remote == "https://github.com/moby/buildkit.git"
input.git.commit.pgpSignature != null
}
decision := {"allow": allow}对于未签名的提交,pgpSignature 字段为 null。对于已签名的提交,它包含签名详情。
SSH 签名的工作方式类似:
allow if {
input.git.commit.sshSignature != null
}要求已签名的标签
带注释的标签可以被签名,从而提供对发布的密码学保证:
package docker
default allow := false
allow if input.local
allow if {
input.git.remote == "https://github.com/moby/buildkit.git"
input.git.tag.pgpSignature != null
}
decision := {"allow": allow}tag 对象仅对带注释的标签可用。它包括:
tagger.name:谁创建了该标签tagger.email:打标签者的邮箱tagger.when:标签创建时间message:标签信息pgpSignature:GPG 签名(如果已签名)sshSignature:SSH 签名(如果已签名)
轻量级标签没有 tag 对象,因此此策略实际上要求带注释的、已签名的标签。
使用公钥验证签名
使用 verify_git_signature() 函数针对受信任的公钥对 Git 签名进行密码学验证:
package docker
default allow := false
allow if input.local
allow if {
input.git.remote == "https://github.com/moby/buildkit.git"
input.git.tagName != ""
verify_git_signature(input.git.tag, "keys.asc")
}
decision := {"allow": allow}这会验证 Git 标签是否由 keys.asc 公钥文件中的密钥签名。要设置它:
- 导出维护者公钥:
$ curl https://github.com/user.gpg > keys.asc - 将
keys.asc放在你的策略文件旁边
该函数验证提交或标签上的 PGP 签名。有关更多详细信息,请参阅 内置函数。
应用条件规则
对不同上下文使用不同的规则。在开发期间允许未签名的引用,但对生产要求签名:
package docker
default allow := false
allow if input.local
is_buildkit if {
input.git.remote == "https://github.com/moby/buildkit.git"
}
is_version_tag if {
is_buildkit
regex.match(`^v[0-9]+\.[0-9]+\.[0-9]+$`, input.git.tagName)
}
# 版本标签必须已签名
allow if {
is_version_tag
input.git.tagName != ""
verify_git_signature(input.git.tag, "keys.asc")
}
# 在开发中允许未签名的引用
allow if {
is_buildkit
not is_version_tag
input.env.target != "release"
}
decision := {"allow": allow}此策略:
- 定义辅助规则以提高可读性
- 要求来自维护者的已签名版本标签
- 允许未签名的引用(分支、提交),除非构建 release 目标
- 使用
input.env.target检测构建目标
构建一个没有签名开发目标:
$ docker buildx build --target=dev .
构建 release 目标,签名将被强制执行:
$ docker buildx build --target=release .
下一步
你现在理解了如何在构建策略中验证 Git 仓库。要继续学习: