Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

dockerfile: support Dockerfile.pin for pinning sources #2816

Closed
wants to merge 1 commit into from

Conversation

AkihiroSuda
Copy link
Member

@AkihiroSuda AkihiroSuda commented Apr 20, 2022

Closes #2794

When Dockerfile.pin (initially proposed as Dockerfile.sum in #2794) exists in the context, the dockerfile.v0 builder does:

  • Pinning the digest of docker-image sources (FROM ...)
  • Pinning the digest of http sources (ADD https://...)
  • Recording the consumed Dockerfile.pin entries to the exporter response pin.consumed

The content of Dockerfile.pin is a subset of BuildInfo:

{
    "sources": [
      {
        "type": "docker-image",
        "ref": "docker.io/library/alpine:latest",
        "pin": "sha256:4edbd2beb5f78b1014028f4fbb99f3237d9561100b6881aabbf5acce2c4f9454"
      },
      {
        "type": "http",
        "ref": "https://raw.githubusercontent.com/moby/buildkit/v0.10.1/README.md",
        "pin": "sha256:6e4b94fc270e708e1068be28bd3551dc6917a4fc5a61293d51bb36e6b75c4b53"
      }
    ]
}

In the future, Dockerfile should also support ADD <gitref>. and pinning its commit hash.

Usage

$ cat Dockerfile
FROM alpine
ADD https://raw.githubusercontent.com/moby/buildkit/v0.10.1/README.md /README.md

$ cat Dockerfile.pin
{
    "sources": [
      {
        "type": "docker-image",
        "ref": "docker.io/library/alpine:latest",
        "pin": "sha256:4edbd2beb5f78b1014028f4fbb99f3237d9561100b6881aabbf5acce2c4f9454"
      },
      {
        "type": "http",
        "ref": "https://raw.githubusercontent.com/moby/buildkit/v0.10.1/README.md",
        "pin": "sha256:6e4b94fc270e708e1068be28bd3551dc6917a4fc5a61293d51bb36e6b75c4b53"
      }
    ]
}

$ sudo buildctl build --frontend dockerfile.v0  --local dockerfile=. --local context=.

When a docker-image pin is wrong:

$ sudo buildctl build --frontend dockerfile.v0  --local dockerfile=. --local context=.
[+] Building 1.6s (3/3) FINISHED                                                                                                           
 => [internal] load build definition from Dockerfile                                                                                  0.0s
 => => transferring dockerfile: 603B                                                                                                  0.0s
 => [internal] load .dockerignore                                                                                                     0.0s
 => => transferring context: 2B                                                                                                       0.0s
 => ERROR [internal] load metadata for docker.io/library/alpine:latest                                                                1.4s
------
 > [internal] load metadata for docker.io/library/alpine:latest:
------
Dockerfile:1
--------------------
   1 | >>> FROM alpine
   2 |     ADD https://raw.githubusercontent.com/moby/buildkit/v0.10.1/README.md /README.md
   3 |     
--------------------
error: failed to solve: alpine: docker.io/library/alpine:latest@sha256:fedbd2beb5f78b1014028f4fbb99f3237d9561100b6881aabbf5acce2c4f9454: not found

When an http pin is wrong:

$ sudo buildctl build --frontend dockerfile.v0  --local dockerfile=. --local context=.
[+] Building 0.6s (5/6)                                                                                                                    
 => [internal] load build definition from Dockerfile                                                                                  0.0s
 => => transferring dockerfile: 603B                                                                                                  0.0s
 => [internal] load .dockerignore                                                                                                     0.0s
 => => transferring context: 2B                                                                                                       0.0s
 => [internal] load metadata for docker.io/library/alpine:latest                                                                      0.0s
 => [1/2] FROM docker.io/library/alpine@sha256:4edbd2beb5f78b1014028f4fbb99f3237d9561100b6881aabbf5acce2c4f9454                       0.0s
 => => resolve docker.io/library/alpine@sha256:4edbd2beb5f78b1014028f4fbb99f3237d9561100b6881aabbf5acce2c4f9454                       0.0s
 => ERROR https://raw.githubusercontent.com/moby/buildkit/v0.10.1/README.md                                                           0.3s
------
 > https://raw.githubusercontent.com/moby/buildkit/v0.10.1/README.md:
------
error: failed to solve: digest mismatch sha256:6e4b94fc270e708e1068be28bd3551dc6917a4fc5a61293d51bb36e6b75c4b53: sha256:fe4b94fc270e708e1068be28bd3551dc6917a4fc5a61293d51bb36e6b75c4b53

Changes from pin-poc.20220411-0 in #2794

  • The pinning result was moved from the build info to a separate exporter response
  • The filename was changed from Dockerfile.sum to Dockerfile.pin

type Source = binfotypes.Source

// Pin provides pinning information to ensure determinism of sources.
type Pin struct {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@tonistiigi
Should this have a version field or mediaType field for extensibility?
#2794 (comment)

@imjasonh
Copy link

Dockerfile.sum is an equivalent of go.sum but s/go/Dockerfile/ .

This may be a confusing comparison. go.sum files are slightly different than what Dockerfile.sum seems to be here. AIUI, go.sum files list all the observed versions and sums of dependencies (including old versions you don't depend on anymore), so that if a dependency is seen with another sum, it can be reported as an error. As such, go.sum files are more or less safe to omit from repos if you want to, and in that case you end up relying on the Go module proxy not serving such mismatched sums, and on having other users who kept their go.sums to report mismatches.

This feels more like a lock file, which lists the one and only digest of the dependency as you depend on it at the time you build. We may want to call this Dockerfile.lock or even come up with some new suffix that doesn't collide with existing terminology (Dockerfile.info?)

@AkihiroSuda
Copy link
Member Author

AkihiroSuda commented Apr 21, 2022

“Dockerfile.pin” might be better?
“Dockerfile.lock” is taken by tern, and “Dockerfile.info” looks like a GNU Info page to me.

@AkihiroSuda AkihiroSuda changed the title dockerfile: support Dockerfile.sum for pinning sources dockerfile: support Dockerfile.pin for pinning sources Apr 21, 2022
@AkihiroSuda
Copy link
Member Author

Changed the filename to Dockerfile.pin

@cpuguy83
Copy link
Member

cpuguy83 commented May 2, 2022

This looks really good.
As a follow-on I'd like to add a Replace directive (which I've prototyped), and it should fit in well with this change.
The one thing for my change is I'd like to fail the build if a source is missing from the pin file. What are your thoughts on that?

Other than that, it would be nice to have a way to support this from the LLB level, but this doesn't seem possible today since resolution happens in the frontend.
I see there is a build-arg that lets one override the frontend, which helps.

@jedevc
Copy link
Member

jedevc commented May 20, 2022

I'm a big fan of the kind of functionality of pinning sources, think it would be a real help to enabling more reproducible builds 🎉

I think that there's some overlap between the pinning of docker-image sources, and the named contexts that are supported in bake - I'm not quite sure what the desired interaction might be between these, especially if they conflict?

I'm also not sure about introducing a new file (and file format) - there's already quite a mix of formats involved in standard image build pipelines. It feels like something like this might be better to see if we could handle almost entirely client side, since different build setups might want to have different ways of handling these kind of dependencies.

Could we possibly achieve the same level of functionality by writing the pin information to a bake.{hcl,json} file with information to override the contexts? With a flow of something like:

# build the image
$ docker buildx bake -f docker-bake.hcl myimagetarget -pin pin.json
# build the image again, but with pins
$ docker buildx bake -f docker-bake.hcl -f pin.json myimagetarget

Today bake doesn't support doing file checksums, so we'd need to add support for that, but don't think any other changes would be required in bake itself.

@cpuguy83
Copy link
Member

@jedevc Doing this client side means that the client needs to know how to parse/rewrite the Dockerfile ahead of time.
I guess there could be some API that asks the client to resolve a reference?

@AkihiroSuda
Copy link
Member Author

Rebased.

Could we possibly achieve the same level of functionality by writing the pin information to a bake.

This feature has to be available for non-buildx clients too , so this feature has to be implemented in the frontend part.

When Dockerfile.pin exists in the context, the dockerfile.v0 frontend does:
- Pinning the digest of `docker-image` sources (`FROM ...`)
- Pinning the digest of `http` sources (`ADD https://...`)
- Recording the consumed Dockerfile.pin entries to the exporter response `pin.consumed`

The content of Dockerfile.pin is a subset of BuildInfo:
```json
{
    "sources": [
      {
        "type": "docker-image",
        "ref": "docker.io/library/alpine:latest",
        "pin": "sha256:4edbd2beb5f78b1014028f4fbb99f3237d9561100b6881aabbf5acce2c4f9454"
      },
      {
        "type": "http",
        "ref": "https://raw.githubusercontent.com/moby/buildkit/v0.10.1/README.md",
        "pin": "sha256:6e4b94fc270e708e1068be28bd3551dc6917a4fc5a61293d51bb36e6b75c4b53"
      }
    ]
}
```

In the future, Dockerfile should also support `ADD git://...` and pinning its commit hash. (PR 2799)

Closes issue 2794

"Dockerfile.pin" was originally proposed as "Dockerfile.sum" in Issue 2794.

Signed-off-by: Akihiro Suda <akihiro.suda.cz@hco.ntt.co.jp>
@AkihiroSuda
Copy link
Member Author

(Ideally pinning should be implemented in the LLB rather than in the frontend but it is hard as the Dockerfile frontend calls ImageMetaResolver ahead of generating the LLB)

@tonistiigi
Copy link
Member

(Ideally pinning should be implemented in the LLB rather than in the frontend but it is hard as the Dockerfile frontend calls ImageMetaResolver ahead of generating the LLB)

That isn't really a problem. You would not put the pins inside of LLB anyway. When client sends the build requests it sends the pins/checksums (might be better to think of it more as a policy) that are associated with the job. Now when this job does LLB builds (or when it calls the config resolver that also goes through the LLB bride) all these calls go through the policy validation first (or if needed then the LLB is modified on the load phase). It is very similar to the current entitlements model where the client passes the allowed entitlements, and then anything the build does needs to be compliant with these rules.

@AkihiroSuda AkihiroSuda marked this pull request as draft June 16, 2022 13:43
@AkihiroSuda
Copy link
Member Author

AkihiroSuda commented Jun 16, 2022

To avoid potential TOCTOU attacks, (unlike entitlements) we should rewrite LLB ops to append source digests to their identifiers rather than just validating the LLB ops, but I'm not sure how we can mutate the LLB ops inside the LLB solver.

Are we allowed to add something like "MutatedIdentifier" string to solver.VertexOptions, and pass that to source.FromLLB?

Or can we just go through with the current frontend-oriented design?

@AkihiroSuda
Copy link
Member Author

@tonistiigi ↑WDYT? 🙏

@AkihiroSuda
Copy link
Member Author

@AkihiroSuda AkihiroSuda closed this Jul 5, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

RFC: dockerfile: support Dockerfile.sum for pinning sources
5 participants