Skip to content

install: Read --root-ssh-authorized-keys before changing mounts - #2476

Open
ericcurtin wants to merge 2 commits into
bootc-dev:mainfrom
ericcurtin:install-read-ssh-keys-before-mounts
Open

ericcurtin wants to merge 2 commits into
bootc-dev:mainfrom
ericcurtin:install-read-ssh-keys-before-mounts

Conversation

@ericcurtin

@ericcurtin ericcurtin commented Sep 19, 2026

Copy link
Copy Markdown
Contributor

Problem

prepare_install() mounts a tmpfs over /tmp and mirrors the host's /var/tmp, and only afterwards reads the --root-ssh-authorized-keys file. A file bind mounted into the install container under /tmp is hidden by then:

podman run ... -v ./authorized_keys:/tmp/authorized_keys:ro <image> \
    bootc install to-existing-root --root-ssh-authorized-keys /tmp/authorized_keys
error: Installing to filesystem: Reading /tmp/authorized_keys: No such file or directory (os error 2)

The file is visible via podman run ... cat /tmp/authorized_keys, so this is confusing.

Change

  • Read the file before any mount changes.
  • prepare_install() may re-exec (unshare, SELinux install_t) and run again with the tmpfs in place, so carry the content to the child via the environment with a small bootc_utils::reexec helper used by both re-exec sites. This avoids mutating our own environment, which is not thread safe.
  • Integration test now mounts the key file under /tmp to cover this.
  • Preparatory commit: just package pulls the base image with retries (fedora-46 job failed on a quay.io EOF).

Testing

Fedora 44 aarch64, SELinux enforcing (exercises the install_t re-exec), key file bind mounted at /tmp/authorized_keys:

  • bootc 1.16.10: install to-disk --via-loopback ... --root-ssh-authorized-keys /tmp/authorized_keys fails as above.
  • With this branch: install completes and /etc/tmpfiles.d/bootc-root-ssh.conf contains the keys.
  • cargo test -p bootc-lib --lib, cargo test -p bootc-internal-utils --lib, cargo fmt --check pass.

Generated-by: AI
I hit this on real hardware, reviewed the change and tested it end to end as above.

@github-actions github-actions Bot added the area/install Issues related to `bootc install` label Sep 19, 2026
@bootc-bot
bootc-bot Bot requested a review from cgwalters September 19, 2026 21:55

@Johan-Liebert1 Johan-Liebert1 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Logic looks good to me. Could you please sign off your commit as we require a Signed-off-by: <email> line at the end of commit messages

Comment thread crates/lib/src/install.rs Outdated
});
}
let contents = std::fs::read_to_string(p).with_context(|| format!("Reading {p}"))?;
bootc_utils::reexec::set_reexec_env(ROOT_SSH_AUTHORIZED_KEYS_ENV, &contents);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should make sure it's UTF-8 compatible here itself

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Switched to std::env::var(), so the value is a String (UTF-8 enforced by the type) and a non-UTF-8 value is a proper error. The value we write is also a String from read_to_string, so it is UTF-8 by construction.


/// Additional environment variables to pass along when we re-execute ourself;
/// see [`set_reexec_env`].
static REEXEC_ENV: Mutex<Vec<(OsString, OsString)>> = Mutex::new(Vec::new());

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We only ever call this from one thread so I'm sure if the mutex is required. Also, not a fan of this being a global var. We should only ever re-exec when we are installing, so putting this in prepare_install would make sense and passing in the vector of env vars to reexec_with_guardenv as an extra_args param?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need a mutex or equiv around any static, no problem with that from my PoV.

Also, not a fan of this being a global var.

Yes, but doing it differently would require threading this state from the install code into the lsm code...doable but ugly in a different way.

BTW I would generalize this slightly and e.g.:

  • Define a struct we can serialize to JSON of stuff we need to save between re-exec
  • In the install path, gather that state before we re-exec
  • Serialize it to a memfd
  • Set an env var _BOOTC_INSTALL_REEXEC_STATE
  • Deserialize it early in the install path

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Kept the global per Colin (threading it from install into lsm was the alternative), but folded the env application together with the argv/argv0 setup into a single prepare_reexec() used by both re-exec sites, so the duplication there is gone too, plus a unit test.

On the memfd/JSON generalization: set_reexec_env is already generic over key/value, and the only payload today is a small text file, so I left the env approach for now. Happy to move to a memfd-backed struct if we grow more state to carry across re-exec (or if key files near the 128KiB per-env-string limit turn out to be a real concern).

`podman build` in `just package` pulls the base image itself, and a
transient registry error (e.g. an EOF from quay.io mid-blob) fails the
whole CI package job. Pull it up front with the same retry settings we
already use for LBI images.

Assisted-by: AI
prepare_install() mounts a tmpfs over /tmp and mirrors the host's
/var/tmp before reading the --root-ssh-authorized-keys file, so a file
bind mounted into the install container under /tmp was hidden and the
install failed with "No such file or directory" even though the file
was visible in the container.

Read the file before touching mounts. Since we may re-exec afterwards
(unshare, SELinux install_t) and run prepare_install() again with the
mounts in place, carry the content to the child via the environment
using a small bootc_utils::reexec helper shared by both re-exec sites.

Mount the key file under /tmp in the integration test to cover this.

Generated-by: AI
@ericcurtin
ericcurtin force-pushed the install-read-ssh-keys-before-mounts branch from 06d92ae to ede527b Compare September 22, 2026 15:53

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/install Issues related to `bootc install`

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants