來源證明

來源證明包含了關於建置過程的事實,其中包括如下詳細資訊:

  • 建置時間戳記
  • 建置參數與環境
  • 版本控制元資料
  • 原始程式碼詳細資訊
  • 建置期間所使用的材料(檔案、指令碼)

來源證明遵循 SLSA 來源結構描述,版本 0.2

有關 BuildKit 如何填充這些來源屬性的詳細資訊,請參閱 SLSA 定義

建立來源證明

若要建立來源證明,請將 --attest type=provenance 選項傳遞給 docker buildx build 指令。

$ docker buildx build --tag <namespace>/<image>:<version> \
    --attest type=provenance,mode=[min,max] .

或者,您可以使用 --provenance=true 縮寫選項來代替 --attest type=provenance。若要使用縮寫選項指定 mode 參數,請使用:--provenance=mode=max

有關如何透過 GitHub Actions 加入來源證明的範例,請參閱 使用 GitHub Actions 加入證明

模式

您可以使用 mode 參數來定義來源證明中包含的詳細程度。支援的值為 mode=min(預設值)和 mode=max

最小 (Min)

min 模式下,來源證明僅包含最少量的資訊,例如:

  • 建置時間戳記
  • 所使用的前端 (Frontend)
  • 建置材料
  • 原始程式碼儲存庫與修訂版
  • 建置平台
  • 可重現性

建置引數 (build arguments) 的值、密鑰 (secrets) 的識別資訊以及豐富的層級元資料不會包含在 mode=min 中。min 等級的證明可以安全地用於所有建置,因為它不會洩漏建置環境任何部分的資訊。

以下 JSON 範例顯示了使用 min 模式建立的來源證明中所包含的資訊:

{
  "_type": "https://in-toto.io/Statement/v0.1",
  "predicateType": "https://slsa.dev/provenance/v0.2",
  "subject": [
    {
      "name": "pkg:docker/<registry>/<image>@<tag/digest>?platform=<platform>",
      "digest": {
        "sha256": "e8275b2b76280af67e26f068e5d585eb905f8dfd2f1918b3229db98133cb4862"
      }
    }
  ],
  "predicate": {
    "builder": { "id": "" },
    "buildType": "https://mobyproject.org/buildkit@v1",
    "materials": [
      {
        "uri": "pkg:docker/docker/dockerfile@1",
        "digest": {
          "sha256": "9ba7531bd80fb0a858632727cf7a112fbfd19b17e94c4e84ced81e24ef1a0dbc"
        }
      },
      {
        "uri": "pkg:docker/golang@1.19.4-alpine?platform=linux%2Farm64",
        "digest": {
          "sha256": "a9b24b67dc83b3383d22a14941c2b2b2ca6a103d805cac6820fd1355943beaf1"
        }
      }
    ],
    "invocation": {
      "configSource": { "entryPoint": "Dockerfile" },
      "parameters": {
        "frontend": "gateway.v0",
        "args": {
          "cmdline": "docker/dockerfile:1",
          "source": "docker/dockerfile:1",
          "target": "binaries"
        },
        "locals": [{ "name": "context" }, { "name": "dockerfile" }]
      },
      "environment": { "platform": "linux/arm64" }
    },
    "metadata": {
      "buildInvocationID": "c4a87v0sxhliuewig10gnsb6v",
      "buildStartedOn": "2022-12-16T08:26:28.651359794Z",
      "buildFinishedOn": "2022-12-16T08:26:29.625483253Z",
      "reproducible": false,
      "completeness": {
        "parameters": true,
        "environment": true,
        "materials": false
      },
      "https://mobyproject.org/buildkit@v1#metadata": {
        "vcs": {
          "revision": "a9ba846486420e07d30db1107411ac3697ecab68",
          "source": "git@github.com:<org>/<repo>.git"
        }
      }
    }
  }
}

最大 (Max)

max 模式包含了 min 模式中的所有資訊,以及:

  • 建置的 LLB 定義。這些定義顯示了用於產生映像檔的確切步驟。
  • 關於 Dockerfile 的資訊,包括該檔案完整的 base64 編碼版本。
  • 描述建置步驟與映像檔層級之間關係的來源對應 (Source maps)。

儘可能優先使用 mode=max,因為它包含了用於分析的更詳盡資訊。

警告

請注意,mode=max 會公開建置引數 (build arguments) 的值。

如果您濫用建置引數來傳遞憑證、驗證權杖或其他密鑰,您應該重新規劃您的建置過程,改用密鑰掛載 (secret mounts)。密鑰掛載不會洩漏到建置環境之外,也永遠不會包含在來源證明中。

檢查來源證明

若要探索透過 image 匯出工具匯出的來源證明,您可以使用 imagetools inspect

使用 --format 選項,您可以指定輸出的樣板。所有與來源相關的資料都可在 .Provenance 屬性下取得。例如,若要取得 SLSA 格式的原始來源內容:

$ docker buildx imagetools inspect <namespace>/<image>:<version> \
    --format "{{ json .Provenance.SLSA }}"
{
  "buildType": "https://mobyproject.org/buildkit@v1",
  ...
}

您也可以利用 Go 樣板的全部功能來建構更複雜的表達式。例如,對於以 mode=max 產生的證明,您可以提取用於建置該映像檔的 Dockerfile 之完整原始碼:

$ docker buildx imagetools inspect <namespace>/<image>:<version> \
    --format '{{ range (index .Provenance.SLSA.metadata "https://mobyproject.org/buildkit@v1#metadata").source.infos }}{{ if eq .filename "Dockerfile" }}{{ .data }}{{ end }}{{ end }}' | base64 -d
FROM ubuntu:24.04
RUN apt-get update
...

來源證明範例

以下範例顯示了 mode=max 的來源證明的 JSON 表示形式看起來像什麼:

{
  "_type": "https://in-toto.io/Statement/v0.1",
  "predicateType": "https://slsa.dev/provenance/v0.2",
  "subject": [
    {
      "name": "pkg:docker/<registry>/<image>@<tag/digest>?platform=<platform>",
      "digest": {
        "sha256": "e8275b2b76280af67e26f068e5d585eb905f8dfd2f1918b3229db98133cb4862"
      }
    }
  ],
  "predicate": {
    "builder": { "id": "" },
    "buildType": "https://mobyproject.org/buildkit@v1",
    "materials": [
      {
        "uri": "pkg:docker/docker/dockerfile@1",
        "digest": {
          "sha256": "9ba7531bd80fb0a858632727cf7a112fbfd19b17e94c4e84ced81e24ef1a0dbc"
        }
      },
      {
        "uri": "pkg:docker/golang@1.19.4-alpine?platform=linux%2Farm64",
        "digest": {
          "sha256": "a9b24b67dc83b3383d22a14941c2b2b2ca6a103d805cac6820fd1355943beaf1"
        }
      }
    ],
    "buildConfig": {
      "llbDefinition": [
        {
          "id": "step4",
          "op": {
            "Op": {
              "exec": {
                "meta": {
                  "args": ["/bin/sh", "-c", "go mod download -x"],
                  "env": [
                    "PATH=/go/bin:/usr/local/go/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
                    "GOLANG_VERSION=1.19.4",
                    "GOPATH=/go",
                    "CGO_ENABLED=0"
                  ],
                  "cwd": "/src"
                },
                "mounts": [
                  { "input": 0, "dest": "/", "output": 0 },
                  {
                    "input": -1,
                    "dest": "/go/pkg/mod",
                    "output": -1,
                    "mountType": 3,
                    "cacheOpt": { "ID": "//go/pkg/mod" }
                  },
                  {
                    "input": 1,
                    "selector": "/go.mod",
                    "dest": "/src/go.mod",
                    "output": -1,
                    "readonly": true
                  },
                  {
                    "input": 1,
                    "selector": "/go.sum",
                    "dest": "/src/go.sum",
                    "output": -1,
                    "readonly": true
                  }
                ]
              }
            },
            "platform": { "Architecture": "arm64", "OS": "linux" },
            "constraints": {}
          },
          "inputs": ["step3:0", "step1:0"]
        }
      ]
    },
    "metadata": {
      "buildInvocationID": "edf52vxjyf9b6o5qd7vgx0gru",
      "buildStartedOn": "2022-12-15T15:38:13.391980297Z",
      "buildFinishedOn": "2022-12-15T15:38:14.274565297Z",
      "reproducible": false,
      "completeness": {
        "parameters": true,
        "environment": true,
        "materials": false
      },
      "https://mobyproject.org/buildkit@v1#metadata": {
        "vcs": {
          "revision": "a9ba846486420e07d30db1107411ac3697ecab68-dirty",
          "source": "git@github.com:<org>/<repo>.git"
        },
        "source": {
          "locations": {
            "step4": {
              "locations": [
                {
                  "ranges": [
                    { "start": { "line": 5 }, "end": { "line": 5 } },
                    { "start": { "line": 6 }, "end": { "line": 6 } },
                    { "start": { "line": 7 }, "end": { "line": 7 } },
                    { "start": { "line": 8 }, "end": { "line": 8 } }
                  ]
                }
              ]
            }
          },
          "infos": [
            {
              "filename": "Dockerfile",
              "data": "RlJPTSBhbHBpbmU6bGF0ZXN0Cg==",
              "llbDefinition": [
                {
                  "id": "step0",
                  "op": {
                    "Op": {
                      "source": {
                        "identifier": "local://dockerfile",
                        "attrs": {
                          "local.differ": "none",
                          "local.followpaths": "[\"Dockerfile\",\"Dockerfile.dockerignore\",\"dockerfile\"]",
                          "local.session": "s4j58ngehdal1b5hn7msiqaqe",
                          "local.sharedkeyhint": "dockerfile"
                        }
                      }
                    },
                    "constraints": {}
                  }
                },
                { "id": "step1", "op": { "Op": null }, "inputs": ["step0:0"] }
              ]
            }
          ]
        },
        "layers": {
          "step2:0": [
            [
              {
                "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
                "digest": "sha256:261da4162673b93e5c0e7700a3718d40bcc086dbf24b1ec9b54bca0b82300626",
                "size": 3259190
              },
              {
                "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
                "digest": "sha256:bc729abf26b5aade3c4426d388b5ea6907fe357dec915ac323bb2fa592d6288f",
                "size": 286218
              },
              {
                "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
                "digest": "sha256:7f1d6579712341e8062db43195deb2d84f63b0f2d1ed7c3d2074891085ea1b56",
                "size": 116878653
              },
              {
                "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
                "digest": "sha256:652874aefa1343799c619d092ab9280b25f96d97939d5d796437e7288f5599c9",
                "size": 156
              }
            ]
          ]
        }
      }
    }
  }
}
© . This site is unofficial and not affiliated with Kubernetes or Docker Inc.