stages: add new luks2 stage

New stage to initialize LUKS2 container on a given device, usually a
loopback device bound to a partition. The passphrase and uuid of the
container need to be specified. Optionally the cipher, label, sector
size and sub-label can be specified. Requires the cryptsetup binary
to be install in the build root.
This commit is contained in:
Christian Kellner 2021-11-07 23:42:53 +01:00 committed by Tom Gundersen
parent 0261b96e55
commit 2801c17730

175
stages/org.osbuild.luks2.format Executable file
View file

@ -0,0 +1,175 @@
#!/usr/bin/python3
"""
Create an LUKS2 container via `cryptsetup`.
This stage formats the given `device` to be a Linux Unified Key Setup,
LUKS version 2, container and set the key to be `passphrase`. The uuid
of the container must be specified via the coressponding option.
Use the corresponding `org.osbuild.luks2` device to open the container
during build.
Buildhost commands used: `cryptsetup`.
"""
import os
import subprocess
import sys
import osbuild.api
SCHEMA_2 = r"""
"definitions": {
"pbkdf2": {
"description": "pbkdf2 PBKDF parameters",
"type": "object",
"additionalProperties": false,
"required": ["method", "iterations"],
"properties": {
"method": {
"enum": ["pbkdf2"]
},
"iterations": {
"description": "Iterations cost",
"type": "integer",
"minimum": 1000,
"maximum": 4294967295
}
}
},
"argon2i_d": {
"description": "argon2i and argon2id PBKDF parameters",
"type": "object",
"additionalProperties": false,
"required": ["method", "iterations"],
"properties": {
"method": {
"enum": ["argon2i", "argon2id"]
},
"memory": {
"description": "Memory cost in kilobytes",
"type": "integer",
"minimum": 32,
"maximum": 4194304
},
"iterations": {
"description": "Iterations cost",
"type": "integer",
"minimum": 4,
"maximum": 4294967295
},
"parallelism": {
"description": "Parallel cost",
"type": "integer",
"minimum": 1,
"maximum": 4
}
}
}
},
"devices": {
"type": "object",
"additionalProperties": true,
"required": ["device"],
"properties": {
"device": {
"type": "object",
"additionalProperties": true
}
}
},
"options": {
"additionalProperties": false,
"required": ["passphrase", "uuid", "pbkdf"],
"properties": {
"passphrase": {
"description": "Passphrase to use",
"type": "string"
},
"uuid": {
"description": "UUID for the LUKS device to use",
"type": "string"
},
"cipher": {
"description": "Cipher to use",
"type": "string"
},
"pbkdf": {
"description": "Password-Based Key Derivation Function parameters",
"oneOf": [
{"$ref": "#/definitions/pbkdf2"},
{"$ref": "#/definitions/argon2i_d"}
]
},
"label": {
"description": "Label to use",
"type": "string"
},
"subsystem": {
"description": "Additional label",
"type": "string"
},
"sector-size": {
"description": "Sector size to use",
"type": "integer"
}
}
}
"""
def main(devices, options):
device = devices["device"]
passphrase = options["passphrase"]
device_uuid = options["uuid"]
pbkdf = options["pbkdf"]
cipher = options.get("cipher")
label = options.get("label")
subsystem = options.get("subsystem", "")
sector_size = options.get("sector-size")
path = os.path.join("/dev", device["path"])
command = [
"cryptsetup",
"-q", # batch mode
"--uuid", device_uuid,
"luksFormat",
"--type", "luks2",
"--force-password"
]
if cipher:
command += ["--cipher", cipher]
if label:
command += ["--label", label, "--subsystem", subsystem]
if sector_size:
command += ["--sector-size", str(sector_size)]
# password base key derivation function parameters
command += [
"--pbkdf", pbkdf["method"],
"--pbkdf-force-iterations", str(pbkdf["iterations"])
]
memory = pbkdf.get("memory", 32)
if memory:
command += ["--pbkdf-memory", str(memory)]
parallelism = pbkdf.get("parallelism", 1)
if parallelism:
command += ["--pbkdf-parallel", str(parallelism)]
subprocess.run(command + [path],
encoding='utf-8', check=True,
input=passphrase)
if __name__ == '__main__':
args = osbuild.api.arguments()
ret = main(args["devices"], args["options"])
sys.exit(ret)