streamLayeredImage: Allow customizing ownership

This opens the way towards building images where Nix can be used as an
unprivileged user (in single-user mode).
This commit is contained in:
WxNzEMof 2024-01-22 11:48:36 +00:00
parent fcea2b6260
commit 0ec13cdb90
2 changed files with 32 additions and 12 deletions

View File

@ -907,6 +907,11 @@ rec {
, # Time of creation of the image. Passing "now" will make the , # Time of creation of the image. Passing "now" will make the
# created date be the time of building. # created date be the time of building.
created ? "1970-01-01T00:00:01Z" created ? "1970-01-01T00:00:01Z"
, # Credentials for file ownership.
uid ? 0
, gid ? 0
, uname ? "root"
, gname ? "root"
, # Optional bash script to run on the files prior to fixturizing the layer. , # Optional bash script to run on the files prior to fixturizing the layer.
extraCommands ? "" extraCommands ? ""
, # Optional bash script to run inside fakeroot environment. , # Optional bash script to run inside fakeroot environment.
@ -1007,7 +1012,7 @@ rec {
conf = runCommand "${baseName}-conf.json" conf = runCommand "${baseName}-conf.json"
{ {
inherit fromImage maxLayers created; inherit fromImage maxLayers created uid gid uname gname;
imageName = lib.toLower name; imageName = lib.toLower name;
preferLocalBuild = true; preferLocalBuild = true;
passthru.imageTag = passthru.imageTag =
@ -1086,14 +1091,22 @@ rec {
"store_layers": $store_layers[0], "store_layers": $store_layers[0],
"customisation_layer", $customisation_layer, "customisation_layer", $customisation_layer,
"repo_tag": $repo_tag, "repo_tag": $repo_tag,
"created": $created "created": $created,
"uid": $uid,
"gid": $gid,
"uname": $uname,
"gname": $gname
} }
' --arg store_dir "${storeDir}" \ ' --arg store_dir "${storeDir}" \
--argjson from_image ${if fromImage == null then "null" else "'\"${fromImage}\"'"} \ --argjson from_image ${if fromImage == null then "null" else "'\"${fromImage}\"'"} \
--slurpfile store_layers store_layers.json \ --slurpfile store_layers store_layers.json \
--arg customisation_layer ${customisationLayer} \ --arg customisation_layer ${customisationLayer} \
--arg repo_tag "$imageName:$imageTag" \ --arg repo_tag "$imageName:$imageTag" \
--arg created "$created" | --arg created "$created" \
--arg uid "$uid" \
--arg gid "$gid" \
--arg uname "$uname" \
--arg gname "$gname" |
tee $out tee $out
''; '';

View File

@ -9,6 +9,8 @@ image as an uncompressed tarball to stdout:
the fields with the same name on the image spec [2]. the fields with the same name on the image spec [2].
* "created" can be "now". * "created" can be "now".
* "created" is also used as mtime for files added to the image. * "created" is also used as mtime for files added to the image.
* "uid", "gid", "uname", "gname" is the file ownership, for example,
0, 0, "root", "root".
* "store_layers" is a list of layers in ascending order, where each * "store_layers" is a list of layers in ascending order, where each
layer is the list of store paths to include in that layer. layer is the list of store paths to include in that layer.
@ -45,7 +47,7 @@ from datetime import datetime, timezone
from collections import namedtuple from collections import namedtuple
def archive_paths_to(obj, paths, mtime): def archive_paths_to(obj, paths, mtime, uid, gid, uname, gname):
""" """
Writes the given store paths as a tar file to the given stream. Writes the given store paths as a tar file to the given stream.
@ -61,10 +63,10 @@ def archive_paths_to(obj, paths, mtime):
def apply_filters(ti): def apply_filters(ti):
ti.mtime = mtime ti.mtime = mtime
ti.uid = 0 ti.uid = uid
ti.gid = 0 ti.gid = gid
ti.uname = "root" ti.uname = uname
ti.gname = "root" ti.gname = gname
return ti return ti
def nix_root(ti): def nix_root(ti):
@ -208,7 +210,7 @@ def overlay_base_config(from_image, final_config):
return final_config return final_config
def add_layer_dir(tar, paths, store_dir, mtime): def add_layer_dir(tar, paths, store_dir, mtime, uid, gid, uname, gname):
""" """
Appends given store paths to a TarFile object as a new layer. Appends given store paths to a TarFile object as a new layer.
@ -231,7 +233,7 @@ def add_layer_dir(tar, paths, store_dir, mtime):
archive_paths_to( archive_paths_to(
extract_checksum, extract_checksum,
paths, paths,
mtime=mtime, mtime, uid, gid, uname, gname
) )
(checksum, size) = extract_checksum.extract() (checksum, size) = extract_checksum.extract()
@ -247,7 +249,7 @@ def add_layer_dir(tar, paths, store_dir, mtime):
archive_paths_to( archive_paths_to(
write, write,
paths, paths,
mtime=mtime, mtime, uid, gid, uname, gname
) )
write.close() write.close()
@ -324,6 +326,10 @@ def main():
else datetime.fromisoformat(conf["created"]) else datetime.fromisoformat(conf["created"])
) )
mtime = int(created.timestamp()) mtime = int(created.timestamp())
uid = int(conf["uid"])
gid = int(conf["gid"])
uname = conf["uname"]
gname = conf["gname"]
store_dir = conf["store_dir"] store_dir = conf["store_dir"]
from_image = load_from_image(conf["from_image"]) from_image = load_from_image(conf["from_image"])
@ -336,7 +342,8 @@ def main():
for num, store_layer in enumerate(conf["store_layers"], start=start): for num, store_layer in enumerate(conf["store_layers"], start=start):
print("Creating layer", num, "from paths:", store_layer, print("Creating layer", num, "from paths:", store_layer,
file=sys.stderr) file=sys.stderr)
info = add_layer_dir(tar, store_layer, store_dir, mtime=mtime) info = add_layer_dir(tar, store_layer, store_dir,
mtime, uid, gid, uname, gname)
layers.append(info) layers.append(info)
print("Creating layer", len(layers) + 1, "with customisation...", print("Creating layer", len(layers) + 1, "with customisation...",