From 847b4732e45569f1eaf7541b34566a13d43b9ded Mon Sep 17 00:00:00 2001 From: WxNzEMof <143541718+WxNzEMof@users.noreply.github.com> Date: Tue, 16 Jul 2024 06:56:50 +0000 Subject: [PATCH 1/2] dockerTools: Allow separately specifying metadata and filesystem timestamps Setting the image creation timestamp in the image metadata to a constant date can cause problems with self-hosted container registries, that need to e.g. prune old images. This timestamp is also useful for debugging. However, it is almost never useful to set the filesystem timestamp to a constant value. Doing so not only causes the image to possibly no longer be reproducible, but also removes any possibility of deduplicating layers with other images, causing unnecessary storage space usage. Therefore, this commit introduces "mtime", a new parameter to streamLayeredImage, which allows specifying the filesystem timestamps separately from "created". For backwards compatibility, "mtime" defaults to the value of "created". --- doc/build-helpers/images/dockertools.section.md | 15 ++++++++++++++- pkgs/build-support/docker/default.nix | 10 ++++++++-- .../docker/stream_layered_image.py | 17 +++++++++++------ 3 files changed, 33 insertions(+), 9 deletions(-) diff --git a/doc/build-helpers/images/dockertools.section.md b/doc/build-helpers/images/dockertools.section.md index 6732f790733d..c1a9526466cd 100644 --- a/doc/build-helpers/images/dockertools.section.md +++ b/doc/build-helpers/images/dockertools.section.md @@ -453,7 +453,7 @@ See [](#ex-dockerTools-streamLayeredImage-exploringlayers) to understand how the `streamLayeredImage` allows scripts to be run when creating the additional layer with symlinks, allowing custom behaviour to affect the final results of the image (see the documentation of the `extraCommands` and `fakeRootCommands` attributes). The resulting repository tarball will list a single image as specified by the `name` and `tag` attributes. -By default, that image will use a static creation date (see documentation for the `created` attribute). +By default, that image will use a static creation date (see documentation for the `created` and `mtime` attributes). This allows the function to produce reproducible images. ### Inputs {#ssec-pkgs-dockerTools-streamLayeredImage-inputs} @@ -516,6 +516,7 @@ This allows the function to produce reproducible images. `created` (String; _optional_) : Specifies the time of creation of the generated image. + This date will be used for the image metadata, and as the default value for `mtime`. This should be either a date and time formatted according to [ISO-8601](https://en.wikipedia.org/wiki/ISO_8601) or `"now"`, in which case the current date will be used. :::{.caution} @@ -524,6 +525,18 @@ This allows the function to produce reproducible images. _Default value:_ `"1970-01-01T00:00:01Z"`. +`mtime` (String; _optional_) + +: Specifies the time used for the modification timestamp of files within the layers of the generated image. + This should be either a date and time formatted according to [ISO-8601](https://en.wikipedia.org/wiki/ISO_8601) or `"now"`, in which case the current date will be used. + + :::{.caution} + Using a non-constant date will cause built layers to have a different hash each time, preventing deduplication. + Using `"now"` also means that the generated image will not be reproducible anymore (because the date will always change whenever it's built). + ::: + + _Default value:_ the same value as `created`. + `uid` (Number; _optional_) []{#dockerTools-buildLayeredImage-arg-uid} `gid` (Number; _optional_) []{#dockerTools-buildLayeredImage-arg-gid} `uname` (String; _optional_) []{#dockerTools-buildLayeredImage-arg-uname} diff --git a/pkgs/build-support/docker/default.nix b/pkgs/build-support/docker/default.nix index 033bce988ae9..264cfbf82eed 100644 --- a/pkgs/build-support/docker/default.nix +++ b/pkgs/build-support/docker/default.nix @@ -907,6 +907,7 @@ rec { , config ? { } , architecture ? defaultArchitecture , created ? "1970-01-01T00:00:01Z" + , mtime ? created , uid ? 0 , gid ? 0 , uname ? "root" @@ -1009,7 +1010,7 @@ rec { conf = runCommand "${baseName}-conf.json" { - inherit fromImage maxLayers created uid gid uname gname; + inherit fromImage maxLayers created mtime uid gid uname gname; imageName = lib.toLower name; preferLocalBuild = true; passthru.imageTag = @@ -1029,10 +1030,13 @@ rec { imageTag="${tag}" ''} - # convert "created" to iso format + # convert "created" and "mtime" to iso format if [[ "$created" != "now" ]]; then created="$(date -Iseconds -d "$created")" fi + if [[ "$mtime" != "now" ]]; then + mtime="$(date -Iseconds -d "$mtime")" + fi paths() { cat $paths ${lib.concatMapStringsSep " " @@ -1089,6 +1093,7 @@ rec { "customisation_layer", $customisation_layer, "repo_tag": $repo_tag, "created": $created, + "mtime": $mtime, "uid": $uid, "gid": $gid, "uname": $uname, @@ -1100,6 +1105,7 @@ rec { --arg customisation_layer ${customisationLayer} \ --arg repo_tag "$imageName:$imageTag" \ --arg created "$created" \ + --arg mtime "$mtime" \ --arg uid "$uid" \ --arg gid "$gid" \ --arg uname "$uname" \ diff --git a/pkgs/build-support/docker/stream_layered_image.py b/pkgs/build-support/docker/stream_layered_image.py index d3778625ccdf..0078c1cb764e 100644 --- a/pkgs/build-support/docker/stream_layered_image.py +++ b/pkgs/build-support/docker/stream_layered_image.py @@ -307,6 +307,15 @@ def add_bytes(tar, path, content, mtime): tar.addfile(ti, io.BytesIO(content)) +now = datetime.now(tz=timezone.utc) + + +def parse_time(s): + if s == "now": + return now + return datetime.fromisoformat(s) + + def main(): arg_parser = argparse.ArgumentParser( description=""" @@ -342,12 +351,8 @@ Docker Image Specification v1.2 as reference [1]. with open(args.conf, "r") as f: conf = json.load(f) - created = ( - datetime.now(tz=timezone.utc) - if conf["created"] == "now" - else datetime.fromisoformat(conf["created"]) - ) - mtime = int(created.timestamp()) + created = parse_time(conf["created"]) + mtime = int(parse_time(conf["mtime"]).timestamp()) uid = int(conf["uid"]) gid = int(conf["gid"]) uname = conf["uname"] From d0b33648223427e635df7336097f8732764d3419 Mon Sep 17 00:00:00 2001 From: Tom Bereknyei Date: Wed, 25 Sep 2024 00:25:47 -0400 Subject: [PATCH 2/2] dockerTools: set mtime to epoch by default --- doc/build-helpers/images/dockertools.section.md | 4 ++-- pkgs/build-support/docker/default.nix | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/build-helpers/images/dockertools.section.md b/doc/build-helpers/images/dockertools.section.md index c1a9526466cd..26c1d9c14a26 100644 --- a/doc/build-helpers/images/dockertools.section.md +++ b/doc/build-helpers/images/dockertools.section.md @@ -516,7 +516,7 @@ This allows the function to produce reproducible images. `created` (String; _optional_) : Specifies the time of creation of the generated image. - This date will be used for the image metadata, and as the default value for `mtime`. + This date will be used for the image metadata. This should be either a date and time formatted according to [ISO-8601](https://en.wikipedia.org/wiki/ISO_8601) or `"now"`, in which case the current date will be used. :::{.caution} @@ -535,7 +535,7 @@ This allows the function to produce reproducible images. Using `"now"` also means that the generated image will not be reproducible anymore (because the date will always change whenever it's built). ::: - _Default value:_ the same value as `created`. + _Default value:_ `"1970-01-01T00:00:01Z"`. `uid` (Number; _optional_) []{#dockerTools-buildLayeredImage-arg-uid} `gid` (Number; _optional_) []{#dockerTools-buildLayeredImage-arg-gid} diff --git a/pkgs/build-support/docker/default.nix b/pkgs/build-support/docker/default.nix index 264cfbf82eed..c19df2937539 100644 --- a/pkgs/build-support/docker/default.nix +++ b/pkgs/build-support/docker/default.nix @@ -907,7 +907,7 @@ rec { , config ? { } , architecture ? defaultArchitecture , created ? "1970-01-01T00:00:01Z" - , mtime ? created + , mtime ? "1970-01-01T00:00:01Z" , uid ? 0 , gid ? 0 , uname ? "root"