mirror of
https://github.com/moby/buildkit.git
synced 2026-11-06 05:16:45 +00:00
provenance: add dockerfile frontend version
Record the builtin Dockerfile frontend version in provenance attestations for both SLSA v0.2 and v1 formats. The version is derived from a new builder.Version constant, normalized with the BuildKit release version suffix. The external frontend Dockerfile validates that the builtin version constant matches the git tag at release time. Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
This commit is contained in:
8 files changed
+159
-4
No files matched your search
@@ -0,0 +1,3 @@
|
||||
package builder
|
||||
|
||||
const Version = "1.24"
|
||||
@@ -28,6 +28,25 @@ RUN --mount=target=. <<EOT
|
||||
else
|
||||
VERSION=$(git describe --always --tags --match "dockerfile/[0-9]*-$CHANNEL")
|
||||
fi
|
||||
BUILTIN_VERSION=$(sed -n 's/^const Version = "\\(.*\\)"$/\\1/p' ./frontend/dockerfile/builder/version.go)
|
||||
case "$BUILTIN_VERSION" in
|
||||
*.*.*) EXPECTED_VERSION="$BUILTIN_VERSION" ;;
|
||||
*.*) EXPECTED_VERSION="$BUILTIN_VERSION.0" ;;
|
||||
*)
|
||||
echo "invalid frontend/dockerfile/builder/version.go version: $BUILTIN_VERSION"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
case "$VERSION" in
|
||||
dockerfile/*)
|
||||
ACTUAL_VERSION=${VERSION#dockerfile/}
|
||||
ACTUAL_VERSION=${ACTUAL_VERSION%%-*}
|
||||
if [ "$ACTUAL_VERSION" != "$EXPECTED_VERSION" ]; then
|
||||
echo "frontend/dockerfile/builder/version.go version $BUILTIN_VERSION does not match external frontend version $VERSION"
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
PKG=github.com/moby/buildkit/frontend/dockerfile/cmd/dockerfile-frontend
|
||||
REVISION=$(git rev-parse HEAD)$(if ! git diff --no-ext-diff --quiet --exit-code; then echo .m; fi)
|
||||
echo "-X main.Version=${VERSION} -X main.Revision=${REVISION} -X main.Package=${PKG}" | tee /tmp/.ldflags
|
||||
|
||||
@@ -27,6 +27,7 @@ import (
|
||||
"github.com/moby/buildkit/client"
|
||||
"github.com/moby/buildkit/client/llb"
|
||||
"github.com/moby/buildkit/exporter/containerimage/exptypes"
|
||||
dockerfilebuilder "github.com/moby/buildkit/frontend/dockerfile/builder"
|
||||
"github.com/moby/buildkit/frontend/dockerui"
|
||||
gateway "github.com/moby/buildkit/frontend/gateway/client"
|
||||
"github.com/moby/buildkit/identity"
|
||||
@@ -190,6 +191,10 @@ RUN echo ok> /foo
|
||||
if isDockerd {
|
||||
expCustom = provenancetypes.ProvenanceCustomEnv{}
|
||||
}
|
||||
expectedDockerfileVersionPrefix := dockerfilebuilder.Version
|
||||
if strings.Count(expectedDockerfileVersionPrefix, ".") == 1 {
|
||||
expectedDockerfileVersionPrefix += ".0"
|
||||
}
|
||||
|
||||
if slsaVersion == "" || slsaVersion == "v1" {
|
||||
type stmtT struct {
|
||||
@@ -277,6 +282,11 @@ RUN echo ok> /foo
|
||||
require.True(t, pred.RunDetails.Metadata.StartedOn.Before(*pred.RunDetails.Metadata.FinishedOn))
|
||||
|
||||
require.Equal(t, platforms.Format(platforms.Normalize(platforms.DefaultSpec())), pred.BuildDefinition.InternalParameters.BuilderPlatform)
|
||||
if isClient || isGateway {
|
||||
require.Empty(t, pred.BuildDefinition.InternalParameters.DockerfileVersion)
|
||||
} else {
|
||||
require.True(t, strings.HasPrefix(pred.BuildDefinition.InternalParameters.DockerfileVersion, expectedDockerfileVersionPrefix))
|
||||
}
|
||||
|
||||
require.False(t, pred.RunDetails.Metadata.Completeness.ResolvedDependencies)
|
||||
require.False(t, pred.RunDetails.Metadata.Reproducible)
|
||||
@@ -346,6 +356,11 @@ RUN echo ok> /foo
|
||||
}
|
||||
require.Equal(t, "https://xxxxx:xxxxx@example.invalid/foo.html", args["context:foo"])
|
||||
}
|
||||
if isClient || isGateway {
|
||||
require.Empty(t, pred.Invocation.Environment.DockerfileVersion)
|
||||
} else {
|
||||
require.True(t, strings.HasPrefix(pred.Invocation.Environment.DockerfileVersion, expectedDockerfileVersionPrefix))
|
||||
}
|
||||
|
||||
expectedBaseImage := integration.UnixOrWindows("busybox", "nanoserver")
|
||||
escapedPlatform := url.PathEscape(platforms.Format(platforms.Normalize(platforms.DefaultSpec())))
|
||||
@@ -516,6 +531,10 @@ COPY myapp.Dockerfile /
|
||||
|
||||
_, isClient := f.(*clientFrontend)
|
||||
_, isGateway := f.(*gatewayFrontend)
|
||||
expectedDockerfileVersionPrefix := dockerfilebuilder.Version
|
||||
if strings.Count(expectedDockerfileVersionPrefix, ".") == 1 {
|
||||
expectedDockerfileVersionPrefix += ".0"
|
||||
}
|
||||
|
||||
if slsaVersion == "" || slsaVersion == "v1" {
|
||||
require.Equal(t, "https://slsa.dev/provenance/v1", attest.PredicateType) // intentionally not const
|
||||
@@ -531,10 +550,16 @@ COPY myapp.Dockerfile /
|
||||
require.Empty(t, pred.BuildDefinition.ExternalParameters.Request.Frontend)
|
||||
require.Equal(t, "", pred.BuildDefinition.ExternalParameters.ConfigSource.URI)
|
||||
require.Equal(t, "", pred.BuildDefinition.ExternalParameters.ConfigSource.Path)
|
||||
require.Empty(t, pred.BuildDefinition.InternalParameters.DockerfileVersion)
|
||||
} else {
|
||||
require.NotEmpty(t, pred.BuildDefinition.ExternalParameters.Request.Frontend)
|
||||
require.Equal(t, expectedURL+"/.git#v1:src", pred.BuildDefinition.ExternalParameters.ConfigSource.URI)
|
||||
require.Equal(t, "myapp.Dockerfile", pred.BuildDefinition.ExternalParameters.ConfigSource.Path)
|
||||
if isGateway {
|
||||
require.Empty(t, pred.BuildDefinition.InternalParameters.DockerfileVersion)
|
||||
} else {
|
||||
require.True(t, strings.HasPrefix(pred.BuildDefinition.InternalParameters.DockerfileVersion, expectedDockerfileVersionPrefix))
|
||||
}
|
||||
}
|
||||
|
||||
expBase := "pkg:docker/busybox@latest?platform=" + url.PathEscape(platforms.Format(platforms.Normalize(platforms.DefaultSpec())))
|
||||
@@ -586,10 +611,16 @@ COPY myapp.Dockerfile /
|
||||
require.Empty(t, pred.Invocation.Parameters.Frontend)
|
||||
require.Equal(t, "", pred.Invocation.ConfigSource.URI)
|
||||
require.Equal(t, "", pred.Invocation.ConfigSource.EntryPoint)
|
||||
require.Empty(t, pred.Invocation.Environment.DockerfileVersion)
|
||||
} else {
|
||||
require.NotEmpty(t, pred.Invocation.Parameters.Frontend)
|
||||
require.Equal(t, expectedURL+"/.git#v1:src", pred.Invocation.ConfigSource.URI)
|
||||
require.Equal(t, "myapp.Dockerfile", pred.Invocation.ConfigSource.EntryPoint)
|
||||
if isGateway {
|
||||
require.Empty(t, pred.Invocation.Environment.DockerfileVersion)
|
||||
} else {
|
||||
require.True(t, strings.HasPrefix(pred.Invocation.Environment.DockerfileVersion, expectedDockerfileVersionPrefix))
|
||||
}
|
||||
}
|
||||
|
||||
expBase := "pkg:docker/busybox@latest?platform=" + url.PathEscape(platforms.Format(platforms.Normalize(platforms.DefaultSpec())))
|
||||
@@ -1040,6 +1071,11 @@ COPY --from=base C:\out C:\Files
|
||||
pred := stmt.Predicate
|
||||
|
||||
require.Equal(t, "dockerfile.v0", pred.BuildDefinition.ExternalParameters.Request.Frontend)
|
||||
expectedDockerfileVersionPrefix := dockerfilebuilder.Version
|
||||
if strings.Count(expectedDockerfileVersionPrefix, ".") == 1 {
|
||||
expectedDockerfileVersionPrefix += ".0"
|
||||
}
|
||||
require.True(t, strings.HasPrefix(pred.BuildDefinition.InternalParameters.DockerfileVersion, expectedDockerfileVersionPrefix))
|
||||
require.NotContains(t, pred.BuildDefinition.ExternalParameters.Request.Args, "source")
|
||||
require.Equal(t, 2, len(pred.BuildDefinition.ExternalParameters.Request.Locals), "%+v", pred.BuildDefinition.ExternalParameters.Request.Locals)
|
||||
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
package llbsolver
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
dockerfilebuilder "github.com/moby/buildkit/frontend/dockerfile/builder"
|
||||
"github.com/moby/buildkit/version"
|
||||
"github.com/pkg/errors"
|
||||
"golang.org/x/mod/semver"
|
||||
)
|
||||
|
||||
func builtinDockerfileVersion() (string, error) {
|
||||
return normalizeBuiltinDockerfileVersion(dockerfilebuilder.Version, version.Version)
|
||||
}
|
||||
|
||||
func normalizeBuiltinDockerfileVersion(v, buildkitVersion string) (string, error) {
|
||||
v = strings.TrimSpace(v)
|
||||
parts := strings.Split(v, ".")
|
||||
if len(parts) != 2 && len(parts) != 3 {
|
||||
return "", errors.Errorf("invalid dockerfile frontend version %q", v)
|
||||
}
|
||||
for _, part := range parts {
|
||||
if part == "" {
|
||||
return "", errors.Errorf("invalid dockerfile frontend version %q", v)
|
||||
}
|
||||
for _, ch := range part {
|
||||
if ch < '0' || ch > '9' {
|
||||
return "", errors.Errorf("invalid dockerfile frontend version %q", v)
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(parts) == 2 {
|
||||
v += ".0"
|
||||
}
|
||||
if !semver.IsValid(buildkitVersion) {
|
||||
return v + "-dev", nil
|
||||
}
|
||||
if prerelease := semver.Prerelease(buildkitVersion); prerelease != "" {
|
||||
return v + prerelease, nil
|
||||
}
|
||||
if semver.Build(buildkitVersion) != "" {
|
||||
return v + "-dev", nil
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package llbsolver
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestBuiltinDockerfileVersion(t *testing.T) {
|
||||
for _, tc := range []struct {
|
||||
name string
|
||||
dockerfileVer string
|
||||
buildkitVersion string
|
||||
want string
|
||||
wantErr bool
|
||||
}{
|
||||
{name: "stable short", dockerfileVer: "1.24", buildkitVersion: "v0.29.0", want: "1.24.0"},
|
||||
{name: "stable patch", dockerfileVer: "1.24", buildkitVersion: "v0.29.1", want: "1.24.0"},
|
||||
{name: "prerelease", dockerfileVer: "1.24", buildkitVersion: "v0.29.0-rc1", want: "1.24.0-rc1"},
|
||||
{name: "describe", dockerfileVer: "1.24", buildkitVersion: "v0.29.0-45-g95735c4ef", want: "1.24.0-45-g95735c4ef"},
|
||||
{name: "metadata", dockerfileVer: "1.24", buildkitVersion: "v0.29.0+unknown", want: "1.24.0-dev"},
|
||||
{name: "invalid buildkit", dockerfileVer: "1.24", buildkitVersion: "not-semver", want: "1.24.0-dev"},
|
||||
{name: "dockerfile with patch", dockerfileVer: "1.24.3", buildkitVersion: "v0.29.0", want: "1.24.3"},
|
||||
{name: "invalid dockerfile major only", dockerfileVer: "1", buildkitVersion: "v0.29.0", wantErr: true},
|
||||
{name: "invalid dockerfile suffix", dockerfileVer: "1.24.x", buildkitVersion: "v0.29.0", wantErr: true},
|
||||
} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
got, err := normalizeBuiltinDockerfileVersion(tc.dockerfileVer, tc.buildkitVersion)
|
||||
if tc.wantErr {
|
||||
require.Error(t, err)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, tc.want, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -465,6 +465,13 @@ func NewProvenanceCreator(ctx context.Context, slsaVersion provenancetypes.Prove
|
||||
}
|
||||
|
||||
pr.BuildDefinition.InternalParameters.ProvenanceCustomEnv = customEnv
|
||||
if pr.BuildDefinition.ExternalParameters.Request.Frontend == "dockerfile.v0" {
|
||||
dockerfileVersion, err := builtinDockerfileVersion()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pr.BuildDefinition.InternalParameters.DockerfileVersion = dockerfileVersion
|
||||
}
|
||||
|
||||
pc := &ProvenanceCreator{
|
||||
pr: pr,
|
||||
|
||||
@@ -171,8 +171,9 @@ type ProvenanceConfigSourceSLSA1 struct {
|
||||
}
|
||||
|
||||
type ProvenanceInternalParametersSLSA1 struct {
|
||||
BuildConfig *BuildConfig `json:"buildConfig,omitempty"`
|
||||
BuilderPlatform string `json:"builderPlatform"`
|
||||
BuildConfig *BuildConfig `json:"buildConfig,omitempty"`
|
||||
BuilderPlatform string `json:"builderPlatform"`
|
||||
DockerfileVersion string `json:"dockerfileVersion,omitempty"`
|
||||
ProvenanceCustomEnv
|
||||
}
|
||||
|
||||
@@ -195,7 +196,8 @@ type Parameters struct {
|
||||
}
|
||||
|
||||
type Environment struct {
|
||||
Platform string `json:"platform"`
|
||||
Platform string `json:"platform"`
|
||||
DockerfileVersion string `json:"dockerfileVersion,omitempty"`
|
||||
ProvenanceCustomEnv
|
||||
}
|
||||
|
||||
@@ -259,6 +261,7 @@ func (p *ProvenancePredicateSLSA1) ConvertToSLSA02() *ProvenancePredicateSLSA02
|
||||
Parameters: p.BuildDefinition.ExternalParameters.Request,
|
||||
Environment: Environment{
|
||||
Platform: p.BuildDefinition.InternalParameters.BuilderPlatform,
|
||||
DockerfileVersion: p.BuildDefinition.InternalParameters.DockerfileVersion,
|
||||
ProvenanceCustomEnv: p.BuildDefinition.InternalParameters.ProvenanceCustomEnv,
|
||||
},
|
||||
},
|
||||
@@ -293,6 +296,7 @@ func (p *ProvenancePredicateSLSA02) ConvertToSLSA1() *ProvenancePredicateSLSA1 {
|
||||
InternalParameters: ProvenanceInternalParametersSLSA1{
|
||||
BuildConfig: p.BuildConfig,
|
||||
BuilderPlatform: p.Invocation.Environment.Platform,
|
||||
DockerfileVersion: p.Invocation.Environment.DockerfileVersion,
|
||||
ProvenanceCustomEnv: p.Invocation.Environment.ProvenanceCustomEnv,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -15,7 +15,8 @@ func TestMarsalBuildDefinitionSLSA1(t *testing.T) {
|
||||
"request": {}
|
||||
},
|
||||
"internalParameters": {
|
||||
"builderPlatform": "linux/amd64",
|
||||
"builderPlatform": "linux/amd64",
|
||||
"dockerfileVersion": "1.24.0-dev",
|
||||
"foo": "bar",
|
||||
"abc": 123,
|
||||
"def": {"one": 1}
|
||||
@@ -28,6 +29,7 @@ func TestMarsalBuildDefinitionSLSA1(t *testing.T) {
|
||||
|
||||
require.Equal(t, "btype1", def.BuildType)
|
||||
require.Equal(t, "linux/amd64", def.InternalParameters.BuilderPlatform)
|
||||
require.Equal(t, "1.24.0-dev", def.InternalParameters.DockerfileVersion)
|
||||
require.Equal(t, "bar", def.InternalParameters.ProvenanceCustomEnv["foo"])
|
||||
require.InEpsilon(t, float64(123), def.InternalParameters.ProvenanceCustomEnv["abc"], 0.001)
|
||||
require.Equal(t, map[string]any{"one": float64(1)}, def.InternalParameters.ProvenanceCustomEnv["def"])
|
||||
@@ -48,6 +50,7 @@ func TestMarshalInvocation(t *testing.T) {
|
||||
},
|
||||
"environment": {
|
||||
"platform": "linux/amd64",
|
||||
"dockerfileVersion": "1.24.0-dev",
|
||||
"buildkit": "v0.10.3",
|
||||
"custom": {
|
||||
"foo": "bar"
|
||||
@@ -63,6 +66,7 @@ func TestMarshalInvocation(t *testing.T) {
|
||||
require.Equal(t, "git+https://github.com/example/repo.git", inv.ConfigSource.URI)
|
||||
require.Equal(t, "dockerfile.v0", inv.Parameters.Frontend)
|
||||
require.Equal(t, "linux/amd64", inv.Environment.Platform)
|
||||
require.Equal(t, "1.24.0-dev", inv.Environment.DockerfileVersion)
|
||||
require.Equal(t, "v0.10.3", inv.Environment.ProvenanceCustomEnv["buildkit"])
|
||||
require.Equal(t, "bar", inv.Environment.ProvenanceCustomEnv["custom"].(map[string]any)["foo"])
|
||||
require.Equal(t, []any{float64(1), float64(2), float64(3)}, inv.Environment.ProvenanceCustomEnv["bar"])
|
||||
|
||||
Reference in new issue
Block a user