repart: add EncryptAddToken= to register custom LUKS2 tokens at provisioning time
Adds EncryptAddToken=<type>[:<source>:<value>] to repart.d partition files, registering one or more LUKS2 token objects in the partition header after the keyslot is created. This allows custom token plugins (e.g. a TDX KBS plugin) to be wired up at provisioning time without a separate cryptsetup token import step. The type name is always explicit and extra token fields can be supplied as a JSON file (:file:/path) or base64-encoded JSON (:data:<base64>). The type and keyslots fields are always injected by repart. The option is repeatable; an empty value clears all tokens for the partition. Supported with Encrypt=key-file, tpm2, and key-file+tpm2.
This commit is contained in:
committed by
Luca Boccassi
parent
06c2dfa73c
commit
c99af7968e
@@ -773,6 +773,59 @@
|
||||
<xi:include href="version-info.xml" xpointer="v261"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>EncryptAddToken=</varname></term>
|
||||
|
||||
<listitem><para>Registers a custom LUKS2 token in the partition's LUKS2 header at provisioning
|
||||
time. This option may be specified more than once to register multiple tokens. Each invocation adds
|
||||
one token entry. Assigning the empty string clears all previously assigned tokens.</para>
|
||||
|
||||
<para>The syntax is:</para>
|
||||
<programlisting>EncryptAddToken=<replaceable>type</replaceable>[:<replaceable>source</replaceable>:<replaceable>value</replaceable>]</programlisting>
|
||||
|
||||
<para>The <replaceable>type</replaceable> field is mandatory and specifies the LUKS2 token type
|
||||
name (e.g. <literal>tdx-kbs</literal>). It must be a valid filename component (no
|
||||
<literal>/</literal>, no <literal>.</literal> or <literal>..</literal>) and corresponds to the
|
||||
name of the token handler plugin installed as
|
||||
<filename>libcryptsetup-token-<replaceable>type</replaceable>.so</filename>.</para>
|
||||
|
||||
<para>The optional <replaceable>source</replaceable> and <replaceable>value</replaceable> fields
|
||||
supply token-plugin-specific metadata as a JSON object. Two sources are supported:</para>
|
||||
|
||||
<variablelist>
|
||||
<varlistentry>
|
||||
<term><literal>file</literal></term>
|
||||
<listitem><para>The <replaceable>value</replaceable> is a path to a file containing a JSON
|
||||
object with the extra token metadata. Example:</para>
|
||||
<programlisting>EncryptAddToken=tdx-kbs:file:/etc/repart/tdx-kbs-token.json</programlisting>
|
||||
<xi:include href="version-info.xml" xpointer="v262"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><literal>data</literal></term>
|
||||
<listitem><para>The <replaceable>value</replaceable> is a Base64-encoded JSON object with the
|
||||
extra token metadata, suitable for embedding directly in the configuration file. Example:</para>
|
||||
<programlisting>EncryptAddToken=tdx-kbs:data:eyJ0cnVzdGVlLmticy51cmwiOiJodHRwOi8va2JzLXNlcnZpY2U6ODA4MCJ9</programlisting>
|
||||
<xi:include href="version-info.xml" xpointer="v262"/></listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
|
||||
<para>The extra metadata object must not contain <literal>type</literal> or
|
||||
<literal>keyslots</literal> fields — these are always set by
|
||||
<command>systemd-repart</command>: <literal>type</literal> is taken from the
|
||||
<replaceable>type</replaceable> argument, and <literal>keyslots</literal> is set to the keyslot
|
||||
number created for this partition.</para>
|
||||
|
||||
<para>This option is supported with all <varname>Encrypt=</varname> modes except
|
||||
<literal>off</literal>. When used with <literal>key-file+tpm2</literal>, the token is bound to the
|
||||
key-file keyslot. When used with <literal>tpm2</literal>, the token is bound to the TPM2
|
||||
keyslot.</para>
|
||||
|
||||
<para>This option has no effect if the partition already exists.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v262"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>Verity=</varname></term>
|
||||
|
||||
|
||||
+210
-1
@@ -350,6 +350,11 @@ typedef struct PartitionEncryptedVolume {
|
||||
bool fixate_volume_key;
|
||||
} PartitionEncryptedVolume;
|
||||
|
||||
typedef struct PartitionEncryptAddToken {
|
||||
char *type; /* token type name, always set */
|
||||
sd_json_variant *data; /* extra fields (excluding type/keyslots), may be NULL */
|
||||
} PartitionEncryptAddToken;
|
||||
|
||||
static PartitionEncryptedVolume* partition_encrypted_volume_free(PartitionEncryptedVolume *c) {
|
||||
if (!c)
|
||||
return NULL;
|
||||
@@ -361,6 +366,19 @@ static PartitionEncryptedVolume* partition_encrypted_volume_free(PartitionEncryp
|
||||
return mfree(c);
|
||||
}
|
||||
|
||||
static void partition_encrypt_add_token_done(PartitionEncryptAddToken *t) {
|
||||
assert(t);
|
||||
free(t->type);
|
||||
sd_json_variant_unref(t->data);
|
||||
}
|
||||
|
||||
static void partition_encrypt_add_token_free_many(PartitionEncryptAddToken *tokens, size_t n) {
|
||||
assert(tokens || n == 0);
|
||||
FOREACH_ARRAY(t, tokens, n)
|
||||
partition_encrypt_add_token_done(t);
|
||||
free(tokens);
|
||||
}
|
||||
|
||||
typedef struct CopyFiles {
|
||||
char *source;
|
||||
char *target;
|
||||
@@ -545,6 +563,8 @@ typedef struct Partition {
|
||||
size_t n_mountpoints;
|
||||
|
||||
PartitionEncryptedVolume *encrypted_volume;
|
||||
PartitionEncryptAddToken *encrypt_add_tokens;
|
||||
size_t n_encrypt_add_tokens;
|
||||
|
||||
unsigned last_percent;
|
||||
RateLimit progress_ratelimit;
|
||||
@@ -873,6 +893,7 @@ static Partition* partition_free(Partition *p) {
|
||||
p->n_mountpoints = 0;
|
||||
|
||||
partition_encrypted_volume_free(p->encrypted_volume);
|
||||
partition_encrypt_add_token_free_many(p->encrypt_add_tokens, p->n_encrypt_add_tokens);
|
||||
|
||||
partition_unlink_supplement(p);
|
||||
|
||||
@@ -933,6 +954,9 @@ static void partition_foreignize(Partition *p) {
|
||||
p->n_mountpoints = 0;
|
||||
|
||||
p->encrypted_volume = partition_encrypted_volume_free(p->encrypted_volume);
|
||||
partition_encrypt_add_token_free_many(p->encrypt_add_tokens, p->n_encrypt_add_tokens);
|
||||
p->encrypt_add_tokens = NULL;
|
||||
p->n_encrypt_add_tokens = 0;
|
||||
|
||||
partition_unlink_supplement(p);
|
||||
}
|
||||
@@ -2694,7 +2718,7 @@ static int config_parse_encrypted_volume(
|
||||
int r;
|
||||
|
||||
if (isempty(rvalue)) {
|
||||
p->encrypted_volume = mfree(p->encrypted_volume);
|
||||
p->encrypted_volume = partition_encrypted_volume_free(p->encrypted_volume);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -2761,6 +2785,139 @@ static int config_parse_encrypted_volume(
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int config_parse_encrypt_add_token(
|
||||
const char *unit,
|
||||
const char *filename,
|
||||
unsigned line,
|
||||
const char *section,
|
||||
unsigned section_line,
|
||||
const char *lvalue,
|
||||
int ltype,
|
||||
const char *rvalue,
|
||||
void *data,
|
||||
void *userdata) {
|
||||
|
||||
Partition *p = ASSERT_PTR(data);
|
||||
int r;
|
||||
|
||||
assert(rvalue);
|
||||
|
||||
if (isempty(rvalue)) {
|
||||
partition_encrypt_add_token_free_many(p->encrypt_add_tokens, p->n_encrypt_add_tokens);
|
||||
p->encrypt_add_tokens = NULL;
|
||||
p->n_encrypt_add_tokens = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Syntax: EncryptAddToken=<type>[:<source>:<value>]
|
||||
* where source is "file" (path) or "data" (base64-encoded JSON).
|
||||
* The token type name is always supplied explicitly and stored separately.
|
||||
* Extra metadata may be supplied as:
|
||||
* :file:/path/to/metadata.json — read JSON from file
|
||||
* :data:<base64> — inline base64-encoded JSON
|
||||
*/
|
||||
|
||||
_cleanup_free_ char *type = NULL, *source = NULL;
|
||||
const char *q = rvalue;
|
||||
|
||||
/* Extract only type and source; leave the rest of q as the value verbatim.
|
||||
* This allows ':' in the value (e.g. file paths containing colons). */
|
||||
r = extract_many_words(&q, ":", EXTRACT_DONT_COALESCE_SEPARATORS, &type, &source);
|
||||
if (r == -ENOMEM)
|
||||
return log_oom();
|
||||
if (r < 0)
|
||||
return log_syntax(unit, LOG_ERR, filename, line, r,
|
||||
"Failed to parse %s=: %s", lvalue, rvalue);
|
||||
if (r < 1)
|
||||
return log_syntax(unit, LOG_ERR, filename, line, SYNTHETIC_ERRNO(EINVAL),
|
||||
"%s= requires at least a token type name.", lvalue);
|
||||
|
||||
/* Validate the type name: it becomes the suffix of libcryptsetup-token-<type>.so, so it must
|
||||
* be a valid filename component — no slashes, no '.' or '..' path traversal. */
|
||||
if (isempty(type) || dot_or_dot_dot(type) || !filename_part_is_valid(type))
|
||||
return log_syntax(unit, LOG_ERR, filename, line, SYNTHETIC_ERRNO(EINVAL),
|
||||
"Invalid token type name in %s= (must be a valid filename component): %s", lvalue, type);
|
||||
|
||||
/* Refuse the token type names systemd own cryptsetup token plugins use */
|
||||
if (STR_IN_SET(type, "systemd-tpm2", "systemd-fido2", "systemd-pkcs11", "systemd-recovery"))
|
||||
return log_syntax(unit, LOG_ERR, filename, line, SYNTHETIC_ERRNO(EINVAL),
|
||||
"Token type name in %s= is reserved for systemd's own use: %s", lvalue, type);
|
||||
|
||||
_cleanup_(sd_json_variant_unrefp) sd_json_variant *extra = NULL;
|
||||
|
||||
if (source) {
|
||||
/* EXTRACT_DONT_COALESCE_SEPARATORS yields an empty-string source for inputs like
|
||||
* 'type:' (trailing colon) or 'type::value' (double colon) — reject explicitly. */
|
||||
if (isempty(source))
|
||||
return log_syntax(unit, LOG_ERR, filename, line, SYNTHETIC_ERRNO(EINVAL),
|
||||
"Empty source in %s=, expected 'file' or 'data': %s", lvalue, rvalue);
|
||||
/* Value is the remainder of the input after source, so ':' in it is preserved. */
|
||||
if (isempty(q))
|
||||
return log_syntax(unit, LOG_ERR, filename, line, SYNTHETIC_ERRNO(EINVAL),
|
||||
"%s= source '%s' requires a value.", lvalue, source);
|
||||
|
||||
_cleanup_free_ char *json_text = NULL;
|
||||
|
||||
if (streq(source, "file")) {
|
||||
if (!path_is_absolute(q))
|
||||
return log_syntax(unit, LOG_ERR, filename, line, SYNTHETIC_ERRNO(EINVAL),
|
||||
"Path in %s= is not absolute: %s", lvalue, q);
|
||||
|
||||
/* Load JSON from a file; cap at 64K — token metadata is tiny */
|
||||
r = read_full_file_full(
|
||||
AT_FDCWD, q,
|
||||
/* offset= */ UINT64_MAX,
|
||||
/* size= */ 64 * 1024,
|
||||
READ_FULL_FILE_FAIL_WHEN_LARGER | READ_FULL_FILE_VERIFY_REGULAR,
|
||||
/* bind_name= */ NULL,
|
||||
&json_text, /* ret_size= */ NULL);
|
||||
if (r < 0)
|
||||
return log_syntax(unit, LOG_ERR, filename, line, r,
|
||||
"Failed to read token data file '%s': %m", q);
|
||||
} else if (streq(source, "data")) {
|
||||
/* Decode base64-encoded JSON */
|
||||
_cleanup_free_ void *decoded = NULL;
|
||||
size_t decoded_size = 0;
|
||||
r = unbase64mem(q, &decoded, &decoded_size);
|
||||
if (r < 0)
|
||||
return log_syntax(unit, LOG_ERR, filename, line, r,
|
||||
"Failed to decode base64 token data in %s=: %m", lvalue);
|
||||
/* JSON cannot contain raw NUL bytes; reject early rather than silently truncating */
|
||||
if (memchr(decoded, '\0', decoded_size))
|
||||
return log_syntax(unit, LOG_ERR, filename, line, SYNTHETIC_ERRNO(EINVAL),
|
||||
"Base64-decoded token data in %s= contains embedded NUL bytes.", lvalue);
|
||||
json_text = strndup(decoded, decoded_size);
|
||||
if (!json_text)
|
||||
return log_oom();
|
||||
} else
|
||||
return log_syntax(unit, LOG_ERR, filename, line, SYNTHETIC_ERRNO(EINVAL),
|
||||
"Unknown source '%s' in %s=, expected 'file' or 'data'.", source, lvalue);
|
||||
|
||||
r = sd_json_parse(json_text, SD_JSON_PARSE_MUST_BE_OBJECT, &extra, NULL, NULL);
|
||||
if (r < 0)
|
||||
return log_syntax(unit, LOG_ERR, filename, line, r,
|
||||
"Token data in %s= is not a valid JSON object: %m", lvalue);
|
||||
|
||||
/* Reject any pre-set "type" or "keyslots" — we manage those */
|
||||
if (sd_json_variant_by_key(extra, "type"))
|
||||
return log_syntax(unit, LOG_ERR, filename, line, SYNTHETIC_ERRNO(EINVAL),
|
||||
"Token data in %s= must not contain a \"type\" field (set by the type name argument).", lvalue);
|
||||
if (sd_json_variant_by_key(extra, "keyslots"))
|
||||
return log_syntax(unit, LOG_ERR, filename, line, SYNTHETIC_ERRNO(EINVAL),
|
||||
"Token data in %s= must not contain a \"keyslots\" field (set automatically).", lvalue);
|
||||
}
|
||||
|
||||
if (!GREEDY_REALLOC(p->encrypt_add_tokens, p->n_encrypt_add_tokens + 1))
|
||||
return log_oom();
|
||||
|
||||
p->encrypt_add_tokens[p->n_encrypt_add_tokens++] = (PartitionEncryptAddToken) {
|
||||
.type = TAKE_PTR(type),
|
||||
.data = TAKE_PTR(extra),
|
||||
};
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int config_parse_tpm2_pcrs(
|
||||
const char *unit,
|
||||
const char *filename,
|
||||
@@ -2995,6 +3152,7 @@ static int partition_read_definition(
|
||||
{ "Partition", "VerityHashBlockSizeBytes", config_parse_block_size, 0, &p->verity_hash_block_size },
|
||||
{ "Partition", "MountPoint", config_parse_mountpoint, 0, p },
|
||||
{ "Partition", "EncryptedVolume", config_parse_encrypted_volume, 0, p },
|
||||
{ "Partition", "EncryptAddToken", config_parse_encrypt_add_token, 0, p },
|
||||
{ "Partition", "TPM2PCRs", config_parse_tpm2_pcrs, 0, p },
|
||||
{ "Partition", "KeyFile", config_parse_key_file, 0, p },
|
||||
{ "Partition", "Integrity", config_parse_integrity, 0, &p->integrity },
|
||||
@@ -3208,6 +3366,10 @@ static int partition_read_definition(
|
||||
log_syntax(NULL, LOG_WARNING, path, 1, 0,
|
||||
"EncryptKDF= has no effect with Encrypt=off.");
|
||||
|
||||
if (p->n_encrypt_add_tokens > 0 && p->encrypt == ENCRYPT_OFF)
|
||||
log_syntax(NULL, LOG_WARNING, path, 1, 0,
|
||||
"EncryptAddToken= has no effect with Encrypt=off.");
|
||||
|
||||
if (p->encrypt != ENCRYPT_OFF && p->integrity == INTEGRITY_INLINE && p->discard > 0)
|
||||
return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL),
|
||||
"Integrity=inline is incompatible with Discard=yes.");
|
||||
@@ -5475,6 +5637,39 @@ static size_t dmcrypt_proper_key_size(Partition *p) {
|
||||
}
|
||||
}
|
||||
|
||||
#if HAVE_LIBCRYPTSETUP
|
||||
static int partition_add_tokens_for_keyslot(Partition *p, struct crypt_device *cd, int keyslot) {
|
||||
int r;
|
||||
|
||||
assert(p);
|
||||
assert(cd);
|
||||
|
||||
char keyslot_str[DECIMAL_STR_MAX(int)];
|
||||
xsprintf(keyslot_str, "%i", keyslot);
|
||||
|
||||
FOREACH_ARRAY(t, p->encrypt_add_tokens, p->n_encrypt_add_tokens) {
|
||||
_cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
|
||||
|
||||
/* Start with extra data if any (NULL-safe), then inject type and keyslots.
|
||||
* merge_objectbo handles a blank/NULL *v by replacing it with the merged object,
|
||||
* so this works whether or not t->data is set. */
|
||||
v = sd_json_variant_ref(t->data);
|
||||
r = sd_json_variant_merge_objectbo(&v,
|
||||
SD_JSON_BUILD_PAIR_STRING("type", t->type),
|
||||
SD_JSON_BUILD_PAIR("keyslots",
|
||||
SD_JSON_BUILD_ARRAY(SD_JSON_BUILD_STRING(keyslot_str))));
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to build LUKS2 token JSON for type '%s': %m", t->type);
|
||||
|
||||
r = cryptsetup_add_token_json(cd, v);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to add LUKS2 token '%s' to header: %m", t->type);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int partition_encrypt(Context *context, Partition *p, PartitionTarget *target, bool offline, bool temporary) {
|
||||
#if HAVE_LIBCRYPTSETUP
|
||||
#if HAVE_TPM2
|
||||
@@ -5679,8 +5874,14 @@ static int partition_encrypt(Context *context, Partition *p, PartitionTarget *ta
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to add LUKS2 key: %m");
|
||||
|
||||
int key_file_keyslot = r;
|
||||
|
||||
passphrase = strempty(iovec_key->iov_base);
|
||||
passphrase_size = iovec_key->iov_len;
|
||||
|
||||
r = partition_add_tokens_for_keyslot(p, cd, key_file_keyslot);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
if (IN_SET(p->encrypt, ENCRYPT_TPM2, ENCRYPT_KEY_FILE_TPM2)) {
|
||||
@@ -5886,6 +6087,14 @@ static int partition_encrypt(Context *context, Partition *p, PartitionTarget *ta
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to add TPM2 JSON token to LUKS2 header: %m");
|
||||
|
||||
/* For key-file+tpm2 the custom tokens were already bound to the key-file keyslot
|
||||
* above; only bind them to the TPM2 keyslot in pure TPM2 mode. */
|
||||
if (p->encrypt == ENCRYPT_TPM2) {
|
||||
r = partition_add_tokens_for_keyslot(p, cd, keyslot);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
passphrase = base64_encoded;
|
||||
passphrase_size = strlen(base64_encoded);
|
||||
#else
|
||||
|
||||
@@ -2940,6 +2940,192 @@ EOF
|
||||
losetup -d "$loop"
|
||||
}
|
||||
|
||||
testcase_encrypt_add_token() {
|
||||
local defs imgs loop token_json metadata_json metadata_b64
|
||||
|
||||
defs="$(mktemp --directory "/tmp/test-repart.defs.XXXXXXXXXX")"
|
||||
imgs="$(mktemp --directory "/var/tmp/test-repart.imgs.XXXXXXXXXX")"
|
||||
# shellcheck disable=SC2064
|
||||
trap "rm -rf '$defs' '$imgs'" RETURN
|
||||
chmod 0755 "$defs"
|
||||
|
||||
echo "*** testcase for EncryptAddToken= (plain type name, no extra data) ***"
|
||||
|
||||
tee "$defs/root.conf" <<EOF
|
||||
[Partition]
|
||||
Type=linux-generic
|
||||
Format=ext4
|
||||
Encrypt=key-file
|
||||
EncryptAddToken=test-dummy-token
|
||||
EOF
|
||||
|
||||
systemd-repart --pretty=yes \
|
||||
--definitions "$defs" \
|
||||
--empty=create \
|
||||
--size=100M \
|
||||
--seed="$seed" \
|
||||
--dry-run=no \
|
||||
--offline=no \
|
||||
"$imgs/enctoken-plain.img"
|
||||
|
||||
loop="$(losetup -P --show --find "$imgs/enctoken-plain.img")"
|
||||
# shellcheck disable=SC2064
|
||||
trap "rm -rf '$defs' '$imgs'; losetup -d '$loop'" RETURN
|
||||
udevadm wait --timeout="$UDEVADM_WAIT_TIMEOUT" --settle "${loop:?}p1"
|
||||
|
||||
cryptsetup luksDump "${loop}p1" | grep "test-dummy-token" >/dev/null
|
||||
|
||||
token_json="$(cryptsetup token export --token-id 0 "${loop}p1")"
|
||||
echo "$token_json" | python3 -c "
|
||||
import json, sys
|
||||
d = json.load(sys.stdin)
|
||||
assert d['type'] == 'test-dummy-token', 'Wrong token type: {}'.format(d['type'])
|
||||
assert '0' in d.get('keyslots', []), 'keyslot 0 not referenced by token: {}'.format(d)
|
||||
"
|
||||
|
||||
losetup -d "$loop"
|
||||
# shellcheck disable=SC2064
|
||||
trap "rm -rf '$defs' '$imgs'" RETURN
|
||||
|
||||
echo "*** testcase for EncryptAddToken= (type + extra data via :data: base64) ***"
|
||||
|
||||
rm -f "$defs/root.conf"
|
||||
|
||||
# Build base64-encoded JSON for the extra metadata (no type/keyslots)
|
||||
metadata_json='{"trustee.kbs.url":"http://kbs-service:8080"}'
|
||||
metadata_b64="$(echo -n "$metadata_json" | base64 -w0)"
|
||||
|
||||
tee "$defs/root.conf" <<EOF
|
||||
[Partition]
|
||||
Type=linux-generic
|
||||
Format=ext4
|
||||
Encrypt=key-file
|
||||
EncryptAddToken=test-dummy-token:data:${metadata_b64}
|
||||
EOF
|
||||
|
||||
systemd-repart --pretty=yes \
|
||||
--definitions "$defs" \
|
||||
--empty=create \
|
||||
--size=100M \
|
||||
--seed="$seed" \
|
||||
--dry-run=no \
|
||||
--offline=no \
|
||||
"$imgs/enctoken-data.img"
|
||||
|
||||
loop="$(losetup -P --show --find "$imgs/enctoken-data.img")"
|
||||
# shellcheck disable=SC2064
|
||||
trap "rm -rf '$defs' '$imgs'; losetup -d '$loop'" RETURN
|
||||
udevadm wait --timeout="$UDEVADM_WAIT_TIMEOUT" --settle "${loop:?}p1"
|
||||
|
||||
cryptsetup luksDump "${loop}p1" | grep "test-dummy-token" >/dev/null
|
||||
|
||||
token_json="$(cryptsetup token export --token-id 0 "${loop}p1")"
|
||||
echo "$token_json" | python3 -c "
|
||||
import json, sys
|
||||
d = json.load(sys.stdin)
|
||||
assert d['type'] == 'test-dummy-token', 'Wrong token type: {}'.format(d['type'])
|
||||
assert d.get('trustee.kbs.url') == 'http://kbs-service:8080', 'Extra field not preserved: {}'.format(d)
|
||||
assert '0' in d.get('keyslots', []), 'keyslot 0 not referenced by token: {}'.format(d)
|
||||
"
|
||||
|
||||
losetup -d "$loop"
|
||||
# shellcheck disable=SC2064
|
||||
trap "rm -rf '$defs' '$imgs'" RETURN
|
||||
|
||||
echo "*** testcase for EncryptAddToken= (type + extra data via :file:) ***"
|
||||
|
||||
rm -f "$defs/root.conf"
|
||||
|
||||
echo -n '{"trustee.kbs.url":"http://kbs-service:9090"}' > "$imgs/token-meta.json"
|
||||
|
||||
tee "$defs/root.conf" <<EOF
|
||||
[Partition]
|
||||
Type=linux-generic
|
||||
Format=ext4
|
||||
Encrypt=key-file
|
||||
EncryptAddToken=test-dummy-token:file:${imgs}/token-meta.json
|
||||
EOF
|
||||
|
||||
systemd-repart --pretty=yes \
|
||||
--definitions "$defs" \
|
||||
--empty=create \
|
||||
--size=100M \
|
||||
--seed="$seed" \
|
||||
--dry-run=no \
|
||||
--offline=no \
|
||||
"$imgs/enctoken-file.img"
|
||||
|
||||
loop="$(losetup -P --show --find "$imgs/enctoken-file.img")"
|
||||
# shellcheck disable=SC2064
|
||||
trap "rm -rf '$defs' '$imgs'; losetup -d '$loop'" RETURN
|
||||
udevadm wait --timeout="$UDEVADM_WAIT_TIMEOUT" --settle "${loop:?}p1"
|
||||
|
||||
token_json="$(cryptsetup token export --token-id 0 "${loop}p1")"
|
||||
echo "$token_json" | python3 -c "
|
||||
import json, sys
|
||||
d = json.load(sys.stdin)
|
||||
assert d['type'] == 'test-dummy-token', 'Wrong token type: {}'.format(d['type'])
|
||||
assert d.get('trustee.kbs.url') == 'http://kbs-service:9090', 'Extra field from file not preserved: {}'.format(d)
|
||||
assert '0' in d.get('keyslots', []), 'keyslot 0 not referenced by token: {}'.format(d)
|
||||
"
|
||||
|
||||
losetup -d "$loop"
|
||||
# shellcheck disable=SC2064
|
||||
trap "rm -rf '$defs' '$imgs'" RETURN
|
||||
|
||||
echo "*** testcase for EncryptAddToken= (multiple tokens on one partition) ***"
|
||||
|
||||
rm -f "$defs/root.conf"
|
||||
|
||||
metadata_b64="$(echo -n '{"extra-key":"extra-val"}' | base64 -w0)"
|
||||
|
||||
tee "$defs/root.conf" <<EOF
|
||||
[Partition]
|
||||
Type=linux-generic
|
||||
Format=ext4
|
||||
Encrypt=key-file
|
||||
EncryptAddToken=first-token
|
||||
EncryptAddToken=second-token:data:${metadata_b64}
|
||||
EOF
|
||||
|
||||
systemd-repart --pretty=yes \
|
||||
--definitions "$defs" \
|
||||
--empty=create \
|
||||
--size=100M \
|
||||
--seed="$seed" \
|
||||
--dry-run=no \
|
||||
--offline=no \
|
||||
"$imgs/enctoken-multi.img"
|
||||
|
||||
loop="$(losetup -P --show --find "$imgs/enctoken-multi.img")"
|
||||
# shellcheck disable=SC2064
|
||||
trap "rm -rf '$defs' '$imgs'; losetup -d '$loop'" RETURN
|
||||
udevadm wait --timeout="$UDEVADM_WAIT_TIMEOUT" --settle "${loop:?}p1"
|
||||
|
||||
# Both tokens should appear in luksDump
|
||||
cryptsetup luksDump "${loop}p1" | grep "first-token" >/dev/null
|
||||
cryptsetup luksDump "${loop}p1" | grep "second-token" >/dev/null
|
||||
|
||||
# Verify each token individually
|
||||
cryptsetup token export --token-id 0 "${loop}p1" | python3 -c "
|
||||
import json, sys
|
||||
d = json.load(sys.stdin)
|
||||
assert d['type'] == 'first-token', 'Wrong first token type: {}'.format(d['type'])
|
||||
assert '0' in d.get('keyslots', []), 'keyslot not in first token: {}'.format(d)
|
||||
"
|
||||
cryptsetup token export --token-id 1 "${loop}p1" | python3 -c "
|
||||
import json, sys
|
||||
d = json.load(sys.stdin)
|
||||
assert d['type'] == 'second-token', 'Wrong second token type: {}'.format(d['type'])
|
||||
assert d.get('extra-key') == 'extra-val', 'Extra field missing in second token: {}'.format(d)
|
||||
assert '0' in d.get('keyslots', []), 'keyslot not in second token: {}'.format(d)
|
||||
"
|
||||
|
||||
losetup -d "$loop"
|
||||
# shellcheck disable=SC2064
|
||||
trap "rm -rf '$defs' '$imgs'" RETURN
|
||||
}
|
||||
|
||||
testcase_fstab_crypttab_in_repart() {
|
||||
local defs imgs root volume
|
||||
|
||||
|
||||
Reference in New Issue
Block a user