Skip to content

Commit 859bf9e

Browse files
authored
Merge pull request #1153 from ckyrouac/reinstall-followup
Reinstall ssh followup cleanup
2 parents d957678 + 8a5f5e2 commit 859bf9e

File tree

4 files changed

+77
-32
lines changed

4 files changed

+77
-32
lines changed

Cargo.lock

Lines changed: 32 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

system-reinstall-bootc/Cargo.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,14 @@ anyhow = { workspace = true }
1919
bootc-utils = { path = "../utils" }
2020
clap = { workspace = true, features = ["derive"] }
2121
dialoguer = "0.11.0"
22+
indoc = { workspace = true }
2223
log = "0.4.21"
24+
openssh-keys = "0.6.4"
2325
rustix = { workspace = true }
2426
serde = { workspace = true, features = ["derive"] }
2527
serde_json = { workspace = true }
2628
serde_yaml = "0.9.22"
27-
tempfile = "3.10.1"
29+
tempfile = { workspace = true }
2830
tracing = { workspace = true }
2931
uzers = "0.12.1"
3032
which = "7.0.2"

system-reinstall-bootc/src/prompt.rs

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,13 @@ const NO_SSH_PROMPT: &str = "None of the users on this system found have authori
66
you may not be able to log in after reinstalling. Do you want to continue?";
77

88
fn prompt_single_user(user: &crate::users::UserKeys) -> Result<Vec<&crate::users::UserKeys>> {
9-
let prompt = format!(
10-
"Found only one user ({}) with {} SSH authorized keys.\n\
11-
Would you like to import its SSH authorized keys\n\
12-
into the root user on the new bootc system?",
13-
user.user,
14-
user.num_keys(),
15-
);
9+
let prompt = indoc::formatdoc! {
10+
"Found only one user ({user}) with {num_keys} SSH authorized keys.
11+
Would you like to import its SSH authorized keys
12+
into the root user on the new bootc system?",
13+
user = user.user,
14+
num_keys = user.num_keys(),
15+
};
1616
let answer = ask_yes_no(&prompt, true)?;
1717
Ok(if answer { vec![&user] } else { vec![] })
1818
}
@@ -24,10 +24,10 @@ fn prompt_user_selection(
2424

2525
// TODO: Handle https://github.com/console-rs/dialoguer/issues/77
2626
let selected_user_indices: Vec<usize> = dialoguer::MultiSelect::new()
27-
.with_prompt(
28-
"Select which user's SSH authorized keys you want to\n\
27+
.with_prompt(indoc::indoc! {
28+
"Select which user's SSH authorized keys you want to
2929
import into the root user of the new bootc system",
30-
)
30+
})
3131
.items(&keys)
3232
.interact()?;
3333

@@ -90,9 +90,18 @@ pub(crate) fn get_ssh_keys(temp_key_file_path: &str) -> Result<()> {
9090

9191
let keys = selected_users
9292
.into_iter()
93-
.map(|user_key| user_key.authorized_keys.as_str())
94-
.collect::<Vec<&str>>()
95-
.join("\n");
93+
.flat_map(|user| &user.authorized_keys)
94+
.map(|key| {
95+
let mut key_copy = key.clone();
96+
97+
// These options could contain a command which will
98+
// cause the new bootc system to be inaccessible.
99+
key_copy.options = None;
100+
key_copy.to_key_format() + "\n"
101+
})
102+
.collect::<String>();
103+
104+
tracing::trace!("keys: {:?}", keys);
96105

97106
std::fs::write(temp_key_file_path, keys.as_bytes())?;
98107

system-reinstall-bootc/src/users.rs

Lines changed: 20 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use anyhow::{Context, Result};
22
use bootc_utils::CommandRunExt;
33
use bootc_utils::PathQuotedDisplay;
4+
use openssh_keys::PublicKey;
45
use rustix::fs::Uid;
56
use rustix::process::geteuid;
67
use rustix::process::getuid;
@@ -10,6 +11,8 @@ use std::collections::BTreeMap;
1011
use std::collections::BTreeSet;
1112
use std::fmt::Display;
1213
use std::fmt::Formatter;
14+
use std::fs::File;
15+
use std::io::BufReader;
1316
use std::os::unix::process::CommandExt;
1417
use std::process::Command;
1518
use uzers::os::unix::UserExt;
@@ -84,12 +87,12 @@ impl Drop for UidChange {
8487
#[derive(Clone, Debug)]
8588
pub(crate) struct UserKeys {
8689
pub(crate) user: String,
87-
pub(crate) authorized_keys: String,
90+
pub(crate) authorized_keys: Vec<PublicKey>,
8891
}
8992

9093
impl UserKeys {
9194
pub(crate) fn num_keys(&self) -> usize {
92-
self.authorized_keys.lines().count()
95+
self.authorized_keys.len()
9396
}
9497
}
9598

@@ -135,9 +138,9 @@ impl<'a> SshdConfig<'a> {
135138
}
136139
}
137140

138-
fn get_keys_from_files(user: &uzers::User, keyfiles: &Vec<&str>) -> Result<String> {
141+
fn get_keys_from_files(user: &uzers::User, keyfiles: &Vec<&str>) -> Result<Vec<PublicKey>> {
139142
let home_dir = user.home_dir();
140-
let mut user_authorized_keys = String::new();
143+
let mut user_authorized_keys: Vec<PublicKey> = Vec::new();
141144

142145
for keyfile in keyfiles {
143146
let user_authorized_keys_path = home_dir.join(keyfile);
@@ -159,16 +162,16 @@ fn get_keys_from_files(user: &uzers::User, keyfiles: &Vec<&str>) -> Result<Strin
159162
// shouldn't through symlinks
160163
let _uid_change = UidChange::new(user_uid)?;
161164

162-
let key = std::fs::read_to_string(&user_authorized_keys_path)
165+
let file = File::open(user_authorized_keys_path)
163166
.context("Failed to read user's authorized keys")?;
164-
user_authorized_keys.push_str(key.as_str());
165-
user_authorized_keys.push('\n');
167+
let mut keys = PublicKey::read_keys(BufReader::new(file))?;
168+
user_authorized_keys.append(&mut keys);
166169
}
167170

168171
Ok(user_authorized_keys)
169172
}
170173

171-
fn get_keys_from_command(command: &str, command_user: &str) -> Result<String> {
174+
fn get_keys_from_command(command: &str, command_user: &str) -> Result<Vec<PublicKey>> {
172175
let user_config = uzers::get_user_by_name(command_user).context(format!(
173176
"authorized_keys_command_user {} not found",
174177
command_user
@@ -177,9 +180,10 @@ fn get_keys_from_command(command: &str, command_user: &str) -> Result<String> {
177180
let mut cmd = Command::new(command);
178181
cmd.uid(user_config.uid());
179182
let output = cmd
180-
.run_get_string()
183+
.run_get_output()
181184
.context(format!("running authorized_keys_command {}", command))?;
182-
Ok(output)
185+
let keys = PublicKey::read_keys(output)?;
186+
Ok(keys)
183187
}
184188

185189
pub(crate) fn get_all_users_keys() -> Result<Vec<UserKeys>> {
@@ -200,26 +204,26 @@ pub(crate) fn get_all_users_keys() -> Result<Vec<UserKeys>> {
200204
let user_info = uzers::get_user_by_name(user_name.as_str())
201205
.context(format!("user {} not found", user_name))?;
202206

203-
let mut user_authorized_keys = String::new();
207+
let mut user_authorized_keys: Vec<PublicKey> = Vec::new();
204208
if !sshd_config.authorized_keys_files.is_empty() {
205-
let keys = get_keys_from_files(&user_info, &sshd_config.authorized_keys_files)?;
206-
user_authorized_keys.push_str(keys.as_str());
209+
let mut keys = get_keys_from_files(&user_info, &sshd_config.authorized_keys_files)?;
210+
user_authorized_keys.append(&mut keys);
207211
}
208212

209213
if sshd_config.authorized_keys_command != "none" {
210-
let keys = get_keys_from_command(
214+
let mut keys = get_keys_from_command(
211215
&sshd_config.authorized_keys_command,
212216
&sshd_config.authorized_keys_command_user,
213217
)?;
214-
user_authorized_keys.push_str(keys.as_str());
218+
user_authorized_keys.append(&mut keys);
215219
};
216220

217221
let user_name = user_info
218222
.name()
219223
.to_str()
220224
.context("user name is not valid utf-8")?;
221225

222-
if user_authorized_keys.trim().is_empty() {
226+
if user_authorized_keys.is_empty() {
223227
tracing::debug!(
224228
"Skipping user {} because it has no SSH authorized_keys",
225229
user_name
@@ -232,7 +236,6 @@ pub(crate) fn get_all_users_keys() -> Result<Vec<UserKeys>> {
232236
authorized_keys: user_authorized_keys,
233237
};
234238

235-
tracing::trace!("Found user keys: {:?}", user_keys);
236239
tracing::debug!(
237240
"Found user {} with {} SSH authorized_keys",
238241
user_keys.user,

0 commit comments

Comments
 (0)