When a partition is deleted from a table containing multiple partitions
of the same type, filling the resulting partition-number gap can place
a new partition before an existing partition matched to an earlier
definition. This changes definition matching on the next invocation and
breaks idempotency.
Keep new partitions after existing partitions of the same type. If no
partition table slot is available after them, fall back to the first
unused slot.
Which existing partition is matched to which definition is still
determined by partition type and partition number order, as documented,
and is unchanged by this.
Fixes#43449
When an administrator installed a custom keyring under /etc,
signature verification selected it instead of the vendor keyring
under /usr. That could make vendor-signed system updates stop
verifying after adding local keys.
Collect all readable default keyrings, including the current and
legacy /etc paths, and pass temporary copies to GnuPG with
separate --keyring options. Keep SYSTEMD_OPENPGP_KEYRING as an
explicit override, add regression coverage for keyring
coexistence, override behavior, and current-path signing, and
silence the expected shellcheck false positive in the test
cleanup helper.
Closes: #40702
Image builds create a key separate from the SecureBoot one for verity
signing. Its certificate ships inside the image as the sysext trust
anchor.
`genkey` certs had a hardcoded "SecureBoot signing key on host <fqdn of
the build machine>" CN, which describes neither that verity role nor a
meaningful hostname.
Make that name configurable with `SecureBootCertificateCommonName=`,
like mkosi's `--genkey-common-name`. That allows image build tools to
re-use `ukify genkey` for non-SB keys.
Unlike the implicit truncation for the default name, a specified one
which is too long is an error. Add a citation where that limit comes
frm.
This adds FONT_SCALE= setting in vconsole.conf,
vconsole.font_scale= kernel command line option and credential support.
When 2 or 200% is specified, the console font size will be doubled.
Closes#43273.
Create the file with open_tmpfile_linkable_at().
Write the recovery key and trailing newline to the temporary fd.
Set the final mode to 0600 before publishing it.
Link it into place with link_tmpfile_at(..., LINK_TMPFILE_SYNC).
`STOPPING=1` is final, explain in the sd_notify man page that this is
the case and log an error if a service attempts to get back out of
STOPPING with another notification.
Fixes#43465 / 98734ac74d
nsresourced hands out transient, recyclable UID/GID ranges to
unprivileged user namespaces, and a BPF-LSM policy is what keeps a
client from leaving inodes owned by its transient range on a file system
that outlives the namespace, so that a later client which gets the same
range can't inherit them.
The old policy decided based on the mount a write went through, which is
forgeable: unshare(CLONE_NEWNS) gives you clones of every mount with
fresh mount IDs but the same superblock, and the mount namespace's owner
is yours to pick. overlayfs sidesteps it entirely. So this reworks the
check to look at where the write actually lands on disk.
The first commit (Christian Brauner's) replaces the per-mount allowlist
with two questions answered from fields the client can't forge and that
survive mount cloning: does the file system die together with the user
namespace (sb->s_user_ns at or below the managed one), and does the
transient ID actually reach the disk, or does an idmapped mount
translate it into something else first. It walks the userns chain
upwards so a nested namespace can't escape it. The map layout changed,
so the pins move to userns-restrict.v2 and the old ones are removed on
install.
The second commit handles overlayfs. A write through an overlay creates
the file on the upper layer via vfs_create(), which only trips the inode
LSM hooks, not the path hooks we attach to, and the one path hook that
does fire sees the overlay mount, which belongs to the managed namespace
and so looks like it dies with it even though the upper may be
persistent. So we judge the upper at mount time in sb_kern_mount and
refuse a writable overlay unless its upper dies with the namespace too.
Reading overlayfs' private ovl_fs/ovl_layer uses CO-RE type flavors
guarded by bpf_core_type_exists(), and the service now orders after
modprobe@overlay.service.
The third commit deals with restarts. The managed set only lives in the
pinned maps, so an upgrade that changes the map layout comes up with
empty maps and every already-running namespace silently loses its
restriction. manager_startup() now re-adds the live registry entries to
the maps in the same pass that reaps the dead ones. The setgroups()
denial is restored from a flag we now store in the registry.
One consequence: a managed namespace can only mount a writable overlay
whose upper dies with it, e.g. a tmpfs it set up itself. If you need a
writable persistent upperdir (mkosi does), use a plain unprivileged user
namespace for that, not a transient nsresourced range.
UnsetEnvironment= was applied to the environment block before the
command line was expanded. Since the expansion looks the variables up
in that same block, any variable listed in UnsetEnvironment= was
already gone when $VAR in ExecStart= and friends was expanded, and a
whole-word $VAR reference was dropped from the command line.
Let the expansion see the full environment block, and apply the
unsetting only to the environment block passed to the executed
process. This way UnsetEnvironment= can hide variables from the
executed process without breaking command lines that reference them.
Extend the exec-unsetenvironment test accordingly: the whole-word
$VAR6 argument must be expanded into a positional parameter for the
shell, while ${VAR6-X} must evaluate to X in the executed process.
Also document in both the UnsetEnvironment= description and the
"Environment Variables in Spawned Processes" section that the
unsetting happens after command line variable expansion.
Fixes: #43343
This adds an initial TPM2 implementation of the io.systemd.Report.Signer
interface, which returns a set of signed TPM attestations that also
includes the supplied report digest.
The returned report consists of multiple components:
- A signed PCR quote generated with TPM2_Quote.
- Signed NvPCR attestations generated with TPM2_NV_Certify.
- A signed audit session attestation generated with
TPM2_GetSessionAuditDigest.
Each signed report component includes the JSON encoded TPMS_ATTEST
attestation structure and corresponding signature, in both PEM encoded
form and as a JSON encoded TPMT_SIGNATURE structure.
NvPCR report components also include the readable name of the NvPCR, the
NV index public area as the JSON encoded TPMS_NV_PUBLIC structure, and some
additional authenticated data that is digested and supplied as the
qualifying data to the TPM2_NV_Certify command. This authenticated data
includes the NvPCR name and priority.
The report digest that is supplied via varlink is supplied as the
qualifying data to the audit session attestation.
The returned report also includes the event log which is retrieved from
pcrlock's varlink interface, and the public area of the signing key in
both PEM encoded form and as a JSON encoded TPMT_PUBLIC structure.
The audit session attestation is always the last component. The
preceding components are all executed with the audit session, and the
signer requires the audit session to remain exclusive whilst generating
the report. What this does is provide evidence to a verifier that the
overall report is a single, consistent snapshot of the system (ie, no
other TPM commands executed whilst generating the report) because the
audit session attestation contains a field that indicates that the
session was exclusive at the time it was attested.
The set of attested PCRs and NvPCRs are currently hardcoded. It includes
all firmware PCRs with the exception of PCR6, and all other PCRs with
the exception of the IMA one (which isn't useful on its own without the
IMA log). All defined NvPCRs are included.
A verifier can verify the signed attestation components individually by
verifying their signatures against the supplied attestation data, using
the supplied public key.
For the PCR report component, a verifier can replay the measurements in
the log in order to reconstruct the PCR values, and then use this and
the PCR selection from the attestation in order to calculate the PCR
digest, and then check that this matches the one in the attestation.
For the NvPCR report components, a verifier checks that the NV index
name in the attestation matches one that is anchored in PCR9 to be sure
that the attestation is for a valid NvPCR. A verifier can then replay
the measurements in the log to ensure that the resulting NvPCR value
matches the one supplied in the attestation. The authenticated data is a
JSON blob containing the NvPCR name and priority. A verifier can digest
this and ensure it matches the qualifying data in the attestation.
The audit session attestation includes the audit session digest and
whether the audit session was still exclusive when the attestation was
generated. A verifier should confirm exclusivity. The digest includes a
record of all TPM commands executed with the audit session, as each
command results in it being extended with the command parameter hash and
the result parameter hash. A verifier should replay the preceding report
components to compute an expected audit digest and make sure that this
digest matches the one in the attestation in order to be sure that the
overall report represents a single, consistent snapshot. The other
report components provide sufficient information for a verifier to
reconstruct the cpHash and rpHash.
When reconstructing the cpHash for the PCR and NvPCR components, the
signing scheme is the NULL scheme (they use the attestation key's
signing scheme). The required signing key public area is supplied in
the report. The supplied qualifying data is in the extra data field of
the attestation.
The PCR selection is also required to reconstruct the cpHash for the
PCR component. The PCR selection is available in the attestation.
The name of the NV index is also required to reconstruct the cpHash for
the NvPCR components. The name is available in the attestation.
The rpHash for each component can be reconstructed from the supplied
attestation blob and signature.
In the future, we might want to add more report component types. Eg, we
could add report components for ordinary NV indexes. Using the audit
session means that we could also add additional components that the TPM
can't attest directly, such as the values of interesting TPM properties
(as the TPM2_GetCapability calls will appear in the audit session digest).
The attestation signing key is stored as a JSON file in the report
signer's state directory. Different types of keys are supported:
- Ordinary keys that are protected by an existing persistent TPM key
such as the SRK or EK.
- Persistent TPM keys.
- Primary keys, which are recreated using a template on use.
When an ordinary or primary key is used, the report signer saves the
object context in its runtime directory. This context is used on future
invocations. This has the benefit that where a primary key is used, it
doesn't need to be recreated on each use. When an ordinary key is used,
we don't need to run TPM2_Load, which requires authorization with the
parent key, on each This authorization currently requires a policy
session for low-range EKs.
Regardless of the key type, it must be a restricted signing key. If no
key exists, an ordinary key is generated automatically.
The attestation key can also have an associated credential which is
attached to the signed report. The credential can be issued by a CA or
some other party during onboarding, and is presented to a verifier as
proof that the attestation key is authentic. It is considered an opaque
blob in order to not require it to be a specific format, such as a X.509
certificate. I've called this a "voucher" though because "cred" or
"credential" has other meanings in systemd.
As a follow-up to this, I plan to add a varlink interface for creating
attestation keys that can be used for report signing.
Since run0 --help now goes through the pager, provide a way to turn that
off from the command line, like all other commands that page --help. The
option is documented the same way as for systemd-run.
parse_argv() requires at least one DEVICE unless --all is given, and
refuses DEVICE arguments when --all is given, so "[DEVICE…]" described
neither mode correctly. Spell out the three modes as separate synopsis
lines, like pcrextend and inhibit do, and update the man page to match.
The custom help() is replaced by a COMMAND() description, and
--introspect-cli is added. The positional argument is spelled MOUNTPOINT
in the synopsis, like the other metavars, instead of /path/to/mountpoint,
and the man page synopsis, which said "[DEVICE]", is fixed to match: the
argument is required and it is a mount point.
The custom help() is replaced by a COMMAND() description, and
--introspect-cli is added. The positional argument is spelled MOUNTPOINT
in the synopsis, like the other metavars, instead of /path/to/mountpoint.
The man page is adjusted to match.
The custom help() is replaced by a COMMAND() description, and
--introspect-cli is added. The synopsis is updated to be more truthful
and consistent between the prog and man page.
The custom help() is replaced by a COMMAND() description, and
--introspect-cli is added. --help now goes through the pager, like other
commands with --no-pager.
Synopses are adjusted. --user-unit/--unit without any unit names is
technically allowed, but not very meaningful, since it's the same as
the default mode. So [] is dropped. And the prog help string is then
adjusted to match the man page.
The securebits parsing code now recognizes 'no-cap-ambient-raise'
(added in Linux 4.3), 'exec-restrict-file', and 'exec-deny-interactive'
(both added in Linux 6.14). The '-locked' variants were added too.
The resolvconf compatibility parser ignored errors from
nameserver, domain, and search lines. If one of those lines was
malformed, resolvectl could still send the partially parsed
configuration to resolved and overwrite the previous link state.
Reproducer:
IF=enp1s0
TMP="$(mktemp -d)"
ln -s "$(command -v resolvectl)" "$TMP/resolvconf"
sudo resolvectl dns "$IF" 1.1.1.1
sudo resolvectl domain "$IF" keep.example
resolvectl domain "$IF"
printf 'nameserver 1.1.1.1\nsearch "unterminated\n' |
sudo "$TMP/resolvconf" -a "$IF" || true
resolvectl domain "$IF"
Before:
The malformed search line was ignored and the previous domain
was cleared:
Link 2 (enp1s0): keep.example
Link 2 (enp1s0):
After:
The malformed input fails before updating resolved, so
keep.example remains configured.
Follow-up for: 097f130731
--raw only affects ResolveRecord output. Without --type= or an
RFC4501 dns: URI, query takes the ResolveHostname/ResolveAddress
paths, where --raw was silently ignored.
Reproducer:
build/resolvectl --raw query localhost
Before:
The command exited successfully and printed regular text output,
such as:
localhost: 127.0.0.1
After:
The command fails before issuing a lookup:
--raw may only be combined with --type= or dns: URIs.
Follow-up for: 05ea7e87b6
A write through an overlay mount does not create the object on the overlay
itself: overlayfs creates it on the upper layer via vfs_create(), which
reaches only the *inode* LSM hooks, not the *path* hooks the transient
range policy attaches to. The one path hook that does fire sees the overlay
mount, whose superblock belongs to the (managed) mounting namespace and so
looks like it dies with it -- while the upper may be a persistent file
system on which the transient IDs then persist.
Regular file system -- caught by the path hook:
openat(dir_on_persistent_fs, "f", O_CREAT)
path_openat -> ... -> security_path_mknod(path{mnt = persistent mount}) << path hook (attached)
userns_restrict_path_mknod -> validate_mount()
sb->s_user_ns = init -> not below managed (outlives the ns)
mnt_idmap does not remap the transient id (reaches disk)
=> -EPERM BLOCKED
Overlay write -- slips past the path hook (this is the hole):
openat(overlay_mnt/"f", O_CREAT)
path_openat -> ... -> security_path_mknod(path{mnt = OVERLAY mount}) << path hook (attached)
validate_mount(): overlay sb->s_user_ns == managed
=> "dies with the namespace" ALLOW
ovl_create_or_link -> vfs_create(UPPER dentry)
security_inode_create(UPPER inode) << inode hook (NOT attached)
=> no check
inode created on the UPPER fs, owned by the transient uid PERSISTS
Judge the upper at mount time instead, in lsm/sb_kern_mount:
mount("overlay", ..., upperdir=<X>, workdir=<Y>)
do_new_mount -> vfs_get_tree
ovl_fill_super
stamps work/index onto the upper via vfs_mkdir, owned by the
transient mounter -> security_inode_mkdir only (not a path hook)
security_sb_kern_mount(sb) << hook (attached)
userns_restrict_sb_kern_mount:
sb->s_magic != OVERLAYFS_SUPER_MAGIC -> return 0 (not an overlay)
find_managed_userns(sb->s_user_ns) == none -> return 0 (unmanaged mounter)
upper = ((struct ovl_fs*)sb->s_fs_info)->layers[0].mnt
upper == NULL -> return 0 (read-only, no upper)
fs_userns = upper->mnt_sb->s_user_ns
upper dies with the managed ns (e.g. a tmpfs) => return 0 ALLOW
upper persistent, idmap records no transient id => return 0 ALLOW
upper persistent, an idmap extent records a transient => -EPERM REFUSE
The upper has to be judged at mount time because overlayfs performs the
actual upper creations (both the mount-time work/index dirs and every later
copy-up) with internal vfs_create()/vfs_mkdir() calls that reach only the
inode hooks, on the upper's own superblock, where neither the overlay's
ovl_fs nor the mount idmapping is available. Refusing the mount stops every
such write. It cannot undo the work/index dirs ovl_fill_super() already
stamped onto the upper before this hook runs. Reaching those takes an
upperdir and a workdir that already exist on the same persistent,
non-idmapped file system and that the transient IDs may write to: the client
cannot create them itself, since that is what the path hooks deny.
The idmap check walks the upper mount's uid_map and gid_map and permits the
overlay only when no extent records a transient id on disk (ids the idmap
does not map fail the write, so they are fine too). This is what lets a
client back a writable overlay with a persistent but id-mapped upper, e.g.
one handed out by mountfsd, while a plain persistent upper stays refused.
The check keys on whether a writable upper exists (layers[0].mnt is
non-NULL), which is fixed for the superblock's lifetime, not on the mutable
SB_RDONLY flag: an overlay mounted read-only but with an upper can be
remounted read-write later without re-entering sb_kern_mount (remount uses a
different LSM hook), so it must be judged now. A genuinely read-only overlay
has no upper and is left alone. Reading the upper layer needs overlayfs'
private struct ovl_fs/ovl_layer, which are absent from vmlinux.h when
overlayfs is built as a module; they are declared as CO-RE type flavors and
guarded by bpf_core_type_exists(), and the service now orders itself after
modprobe@overlay.service so the module's BTF is present by the time the
program is loaded.
A managed (nsresourced-provisioned) user namespace can therefore only mount
a writable overlay whose upper dies with it (e.g. a tmpfs it set up
itself). Callers that need a writable, persistent (non-tmpfs) upperdir --
mkosi, for one -- should use a regular unprivileged user namespace for that
rather than a transient nsresourced range, since such an overlay is refused
for a managed namespace by design.
test-userns-restrict now mounts real overlays: a read-only overlay, a
writable overlay with a tmpfs upper, and a writable overlay whose upper is
an init-owned but idmapped mount are all allowed, while a writable overlay
with a plain init-owned upper is refused with EPERM. The kernel permits that
last mount without the LSM loaded, so the EPERM is the policy's doing.
nsresourced hands out transient, recyclable UID/GID ranges to
unprivileged user namespaces. So that a client cannot gain persistence by
leaving inodes owned by its transient range on a file system that outlives
the namespace -- where a later, unrelated client that recycles the range
would inherit them -- a BPF-LSM policy vetoes inode creation and chown()
that would end up owned by a transient ID.
The policy this replaces keyed its decision on properties of the mount a
write targeted. Those can be forged: a single unshare(CLONE_NEWNS) hands
the namespace clones of every mount it can see, each backed by the very
same superblock but no longer recognisable by the original check, and the
mount namespace's owning user namespace is attacker-chosen as well.
Decide instead from two questions about the object the write would actually
create, both answered from fields the kernel stamps itself and that survive
mount cloning and cannot be forged with unshare():
a) Does the file system die with the user namespace? A superblock records
the user namespace it was mounted in (sb->s_user_ns); if that sits at
or below the managed namespace, nothing it carries can outlive the
transient range.
b) Will the transient ID actually reach the disk? An idmapped mount
(vfsmount->mnt_idmap) may translate it into something else first, in
which case nothing recyclable is recorded. This mirrors the kernel's
own from_vfsuid()/from_vfsgid().
Walking the user-namespace chain upwards, rather than looking only at the
exact namespace, is what keeps a nested namespace from shaking off the
policy.
The map schema changed incompatibly (the old mount-id allowlist became the
flat userns_managed inode set), so the BPF objects are pinned under a fresh
directory, /sys/fs/bpf/systemd/userns-restrict-v2, and the obsolete
unsuffixed tree from previous versions is removed on install. Removing the
old link pins detaches the previous, bypassable programs, so they do not
linger attached next to the new ones.
Replace the paragraph that claims overlayfs is always blocked. Mention
that only the file data is checked, keep the btrfs note and mention the
new behavior in NEWS.
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
When --json is requested, resolution errors returned by resolved still went
through text logging. That made scripts parse stderr even though the
varlink error already carried structured fields such as rcode and EDE.
Add a small JSON error dumper for resolvectl query errors. It outputs the
queried name and varlink error id alongside the original varlink parameters
so DNSError and DNSSECValidationFailed expose rcode, extendedDNSErrorCode,
extendedDNSErrorMessage, queryString, and result without duplicating the
schema in resolvectl.
Keep non-JSON text output and silent warn_missing NXDOMAIN handling
unchanged. Document the JSON resolution error object in resolvectl(1),
including the distinction between resolved errors and local invocation or
connection errors.
Closes#42904
For some of us building image based operating systems with
`systemd-sysupdate` it's useful to have UKI filenames that encode
`IMAGE_VERSION` (and architecture). For example
`SimonOS_45.20260805.0_x86-64.efi`.
Currently to do this I have to manually post-process around the `.efi`
produced by `60-ukify` because it is named
`$ENTRY_TOKEN-$KERNEL_VERSION.efi`. It'd be very useful if I could skip
that step and instead have the image built directly without this
post-processing step; then extract the UKI and /usr partitions and plop
them onto my update server. It makes the build much more "install RPMs
into a tree and you're done" (barring other things `60-ukify` currently
can't do but I have follow-ups for those in mind).
This PR adds a knob to `install.conf` to let users specify a custom
format for the output filename for both UKI's and BLS entries produced
by `kernel-install`.
It accepts the following specifiers:
- `%e`: entry token
- `%v`: kernel version being installed
- Several common specifiers
The default is %e-%v, which is the current behavior. With
`entry_name_format=%M_%A_%a`, an OS with `IMAGE_ID=SimonOS` and
`IMAGE_VERSION=1.0` would get moved to `SimonOS_1.0_x86-64.efi`.
The resolved name is exported as `$KERNEL_INSTALL_ENTRY_NAME` for shell
plugins. Both `90-uki-copy.install` and `90-loaderentry.install` are
updated to use it, with a fallback to `$ENTRY_TOKEN-$KERNEL_VERSION` for
compatibility with older kernel-install versions.
RestrictSUIDSGID uses seccomp to block setting SUID/SGID bits.
openat2() passes these mode bits indirectly via struct open_how.
Document that such calls are blocked regardless of requested mode.
Closes#43314
We currently blindly send the configured reload signal (e.g. SIGHUP or
SIGUSR1) to the main service PID for Type=notify-reload units, even if
the service hasn't installed a userspace handler. This can lead to
invoking unintended default behavior (typically process termination) in
daemons which later deprecate and remove their reload handler.
This is a real problem we have seen in production on multiple occasions.
In one particularly egregious instance, a production distributed storage
service had a large percentage of its nodes all terminate at once when
sent a reload signal. In this case a signal handler had been removed,
but another place still sending the signal was missed.
To mitigate this, introduce two new checks for Type=notify-reload
services:
1. On READY=1: When the service first sends READY=1 during initial
startup, we check for the handler. If it's missing, the service startup
is failed with SERVICE_FAILURE_PROTOCOL. This enforces the contract that
services *must* install their handler before signalling readiness.
2. On reload: Immediately before sending the reload signal, we check
again. If the handler is now missing, we log a warning but still send
the signal. This preserves the requested operator action while making
the missing handler visible.
The startup check catches definite static misconfiguration before the
service is accepted as ready. The reload check is advisory because the
service has already started successfully, but warns if its handler later
disappears. Together these provide a best-effort safety net while
preserving the agreed reload semantics.
Document the new entry_name_format= setting in install.conf and the
KERNEL_INSTALL_ENTRY_NAME / KERNEL_INSTALL_ENTRY_NAME_FORMAT environment
variables set for plugins.
List the available specifiers: %e (entry token), %v (kernel version),
%a (architecture), %A (IMAGE_VERSION), %M (IMAGE_ID), and all common
system specifiers.
Each of these citerefentry targets exists, but is referenced with a
section it is not installed in, so the generated link does not resolve
and 'man <page>' sends the reader to the wrong section.
systemd-confext 1 -> 8
systemd-cryptsetup@.service 5 -> 8
systemd-firstboot.service 8 -> 1 (three places)
systemd-nspawn 8 -> 1
systemd-userdbd.service 7 -> 8
The section for each was taken from the refmeta of the page that
defines it. In systemd.special.xml the neighbouring reference to
systemd-veritysetup@.service already uses 8, which is what the
cryptsetup one should have been.
Document that needs-restart/needs-reload markers follow try-restart /
try-reload behavior for enqueue-marked, and drop the duplicated note
from the deprecated --marked alias entry.
Reword the note for --fido2-with-user-presence= to make it clear that the
setting has no effect if the token does not support the up feature, or does
not allow the user presence requirement to be enabled or disabled for the
requested operation. Drop the surrounding parentheses and the spurious
"in other contexts" qualifier.
Apply the same rewording to the sibling --fido2-with-client-pin= and
--fido2-with-user-verification= options so the three parallel FIDO2
entries stay consistent.
Fixes#23632.
- --strict=/--full moved to the OPTION_LONG() parser framework that
replaced getopt in the meantime
- the is-supported commit is dropped, main grew that verb independently.
That also gets rid of the early tpm2_support() gate that broke CI back
then, and TEST-70-TPM2 runs with a software TPM these days anyway
- special_glyph() → glyph() renames
- the log table PCR filter needs an EVENT_LOG_RECORD_IS_PCR() guard now
that the event log has NV-index records
We currently blindly send the configured reload signal (e.g. SIGHUP or
SIGUSR1) to the main service PID for Type=notify-reload units, even if
the service hasn't installed a userspace handler. This can lead to
invoking unintended default behavior (typically process termination) in
daemons which later deprecate and remove their reload handler.
This is a real problem we have seen in production on multiple occasions.
In one particularly egregious instance, a production distributed storage
service had a large percentage of its nodes all terminate at once when
sent a reload signal. In this case a signal handler had been removed, but
another place still sending the signal was missed.
To mitigate this, introduce signal handler validation for
Type=notify-reload services. We check both SigCgt (traditional handlers
via sigaction) and SigBlk (blocked signals for signalfd) to detect valid
handler configurations:
1. On first READY=1: When the service first sends READY=1 during initial
startup, we check for the handler. If it's missing, the service
startup is aborted with Result=protocol.
2. On reload: Immediately before sending the reload signal, we check
again. If the handler is missing, we log a warning but still send
the signal.
The rationale is that a missing handler at first startup represents a
definite misconfiguration that should be caught early. A missing handler
at reload time is warned about, but we defer to the operator's judgement
since the service already started successfully.
These checks are a best-effort, pragmatic safety net. They are naturally
racy, but in practice they prevent the common class of bugs from static
misconfiguration or software updates.
Add a --strict option that makes sure all PCRs for which components were
found are indeed included in the set of predicted PCRs. Instead throw an
error. Avoids creating a policy less secure than requested.
Co-authored-by: Akarithos <277667353+Akarithos@users.noreply.github.com>
Shorten number of bytes show of sha256 hashes. Similar to git, makes
output less wide and more readable.
Co-authored-by: Akarithos <277667353+Akarithos@users.noreply.github.com>
Combining --ephemeral with --grow-image= truncated the file passed to
--image= to the requested size before the VM was started, so a mode whose
entire purpose is to leave the original image untouched modified it on
disk.
Skip growing the original image when running ephemeral, and instead create
the qcow2 overlay with the requested virtual size, so the guest sees the
larger disk while the base image stays read-only. As only the overlay is
sized in that case, the base image format no longer matters, so allow
--grow-image= together with qcow2 images when running ephemeral. The round
up to a multiple of 4096 moves to option parsing so both paths apply the
same value.
The vmspawn drive test grows an ephemeral VM and checks that the image
passed to --image= keeps its size, and that the overlay QEMU runs off is
created at the requested one.
systemd-repart implicitly excludes the contents of APIVFS and temporary
directories (/proc, /sys, /dev, /tmp, /run, /var/tmp) when copying files
via CopyFiles=. When populating a root partition, the contents below the
mount points of other partitions in the image (e.g. /boot, /efi for an
esp partition) are excluded as well, so that data belonging to a separate
partition is not duplicated into the root file system.
Neither of these implied denylists was documented, which surprised users
(see #32651). Document them, and explain how to work around the
mount-point exclusion by adding an explicit CopyFiles= entry for the
directory in question.
Fixes#32651
The openpgp and tlsa verbs rejected --json= even though they
already determine the RR type they query. Allow JSON output for
these shortcut commands.
Accept matching explicit --type= values as well, but reject
mismatched types so openpgp only queries OPENPGPKEY records and
tlsa only queries TLSA records.
Refuse unauthenticated key records in JSON format so scripts do
not accidentally consume cryptographic DNS data that would only
produce a warning in human-readable output.
Fixes#43245