Skip to content

Commit 0267ea8

Browse files
authored
Updating sshd config for PasswordAuthentication (#136)
Update /etc/ssh/sshd_config for PasswordAuthentication to yes and restart sshd. Fixes #67 Signed-off-by: Bala Konda Reddy M <bala12352@gmail.com>
1 parent 99296a6 commit 0267ea8

File tree

4 files changed

+136
-7
lines changed

4 files changed

+136
-7
lines changed

libazureinit/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ block-utils = "0.11.1"
2020
tracing = "0.1.40"
2121
strum = { version = "0.26.3", features = ["derive"] }
2222
fstab = "0.4.0"
23+
regex = "1"
24+
lazy_static = "1.4"
25+
tempfile = "3.3.0"
2326

2427
[dev-dependencies]
2528
tracing-test = { version = "0.2", features = ["no-env-filter"] }

libazureinit/src/error.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,8 @@ pub enum Error {
7070
NoPasswordProvisioner,
7171
#[error("A timeout error occurred")]
7272
Timeout,
73+
#[error("Failed to update the sshd configuration")]
74+
UpdateSshdConfig,
7375
}
7476

7577
impl From<tokio::time::error::Elapsed> for Error {

libazureinit/src/provision/password.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ use tracing::instrument;
77

88
use crate::{error::Error, User};
99

10+
use super::ssh::update_sshd_config;
11+
1012
/// Available tools to set the user's password (if a password is provided).
1113
#[derive(strum::EnumIter, Debug, Clone)]
1214
#[non_exhaustive]
@@ -29,6 +31,12 @@ impl Provisioner {
2931

3032
#[instrument(skip_all)]
3133
fn passwd(user: &User) -> Result<(), Error> {
34+
// Update the sshd configuration to allow password authentication.
35+
let sshd_config_path = "/etc/ssh/sshd_config.d/50-azure-init.conf";
36+
let ret = update_sshd_config(sshd_config_path);
37+
if ret.is_err() {
38+
return Err(Error::UpdateSshdConfig);
39+
}
3240
let path_passwd = env!("PATH_PASSWD");
3341

3442
if user.password.is_none() {

libazureinit/src/provision/ssh.rs

Lines changed: 123 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,28 @@
22
// Licensed under the MIT License.
33

44
use std::{
5-
fs::Permissions,
6-
io::{self, Write},
5+
fs::{self, OpenOptions, Permissions},
6+
io::{self, Read, Write},
77
os::unix::fs::{DirBuilderExt, PermissionsExt},
8+
path::PathBuf,
89
process::{Command, Output},
910
};
1011

11-
use tracing::{error, info, instrument};
12-
1312
use crate::error::Error;
1413
use crate::imds::PublicKeys;
14+
use lazy_static::lazy_static;
15+
use regex::Regex;
16+
use tempfile::NamedTempFile;
17+
use tracing::{error, info, instrument};
18+
19+
lazy_static! {
20+
static ref PASSWORD_REGEX: Regex = Regex::new(
21+
r"(?m)^\s*#?\s*PasswordAuthentication\s+(yes|no)\s*$"
22+
)
23+
.expect(
24+
"The regular expression is invalid or exceeds the default regex size"
25+
);
26+
}
1527

1628
#[instrument(skip_all, name = "ssh")]
1729
pub(crate) fn provision_ssh(
@@ -112,20 +124,64 @@ fn extract_authorized_keys_file_path(stdout: &[u8]) -> Option<String> {
112124
None
113125
}
114126

127+
pub(crate) fn update_sshd_config(
128+
sshd_config_path: &str,
129+
) -> Result<(), io::Error> {
130+
// Check if the path exists otherwise create it
131+
let sshd_config_path = PathBuf::from(sshd_config_path);
132+
if !sshd_config_path.exists() {
133+
let mut file = std::fs::File::create(&sshd_config_path)?;
134+
file.set_permissions(Permissions::from_mode(0o644))?;
135+
file.write_all(b"PasswordAuthentication yes\n")?;
136+
return Ok(());
137+
}
138+
139+
let mut file_content = String::new();
140+
{
141+
let mut file = OpenOptions::new().read(true).open(&sshd_config_path)?;
142+
file.read_to_string(&mut file_content)?;
143+
}
144+
145+
let re = &PASSWORD_REGEX;
146+
if re.is_match(&file_content) {
147+
let modified_content =
148+
re.replace_all(&file_content, "PasswordAuthentication yes\n");
149+
150+
let temp_sshd_config = NamedTempFile::new()?;
151+
let temp_sshd_config_path = temp_sshd_config.path();
152+
let mut temp_file = OpenOptions::new()
153+
.write(true)
154+
.create(true)
155+
.truncate(true)
156+
.open(temp_sshd_config_path)?;
157+
temp_file.write_all(modified_content.as_bytes())?;
158+
temp_file.set_permissions(fs::Permissions::from_mode(0o644))?;
159+
160+
fs::rename(temp_sshd_config_path, &sshd_config_path)?;
161+
} else {
162+
let mut file =
163+
OpenOptions::new().append(true).open(&sshd_config_path)?;
164+
file.write_all(b"PasswordAuthentication yes\n")?;
165+
}
166+
167+
Ok(())
168+
}
169+
115170
#[cfg(test)]
116171
mod tests {
117172
use crate::imds::PublicKeys;
118173
use crate::provision::ssh::{
119174
extract_authorized_keys_file_path, get_authorized_keys_path_from_sshd,
120-
provision_ssh, run_sshd_command,
175+
provision_ssh, run_sshd_command, update_sshd_config,
121176
};
122177
use std::{
123-
fs::Permissions,
124-
io::{self, Read},
178+
fs::{File, Permissions},
179+
io::{self, Read, Write},
125180
os::unix::fs::{DirBuilderExt, PermissionsExt},
126181
os::unix::process::ExitStatusExt,
127182
process::{ExitStatus, Output},
128183
};
184+
use tempfile::TempDir;
129185

130186
fn create_output(status_code: i32, stdout: &str, stderr: &str) -> Output {
131187
Output {
@@ -361,4 +417,64 @@ mod tests {
361417

362418
assert_eq!("not-a-real-key xyz987\n", buf);
363419
}
420+
421+
#[test]
422+
fn test_update_sshd_config_create_new() -> io::Result<()> {
423+
let temp_dir = TempDir::new().unwrap();
424+
let sshd_config_path = temp_dir.path().join("sshd_config");
425+
let ret: Result<(), io::Error> =
426+
update_sshd_config(sshd_config_path.to_str().unwrap());
427+
assert!(ret.is_ok());
428+
429+
let mut updated_content = String::new();
430+
let mut file = File::open(&sshd_config_path).unwrap();
431+
file.read_to_string(&mut updated_content).unwrap();
432+
assert!(updated_content.contains("PasswordAuthentication yes"));
433+
Ok(())
434+
}
435+
436+
#[test]
437+
fn test_update_sshd_config_change() -> io::Result<()> {
438+
let temp_dir = TempDir::new()?;
439+
let sshd_config_path = temp_dir.path().join("sshd_config");
440+
{
441+
let mut file = File::create(&sshd_config_path)?;
442+
writeln!(file, "PasswordAuthentication no")?;
443+
}
444+
445+
let ret: Result<(), io::Error> =
446+
update_sshd_config(sshd_config_path.to_str().unwrap());
447+
assert!(ret.is_ok());
448+
let mut updated_content = String::new();
449+
{
450+
let mut file = File::open(&sshd_config_path)?;
451+
file.read_to_string(&mut updated_content)?;
452+
}
453+
assert!(updated_content.contains("PasswordAuthentication yes"));
454+
assert!(!updated_content.contains("PasswordAuthentication no"));
455+
456+
Ok(())
457+
}
458+
459+
#[test]
460+
fn test_update_sshd_config_no_change() -> io::Result<()> {
461+
let temp_dir = TempDir::new()?;
462+
let sshd_config_path = temp_dir.path().join("sshd_config");
463+
{
464+
let mut file = File::create(&sshd_config_path)?;
465+
writeln!(file, "PasswordAuthentication yes")?;
466+
}
467+
let ret: Result<(), io::Error> =
468+
update_sshd_config(sshd_config_path.to_str().unwrap());
469+
assert!(ret.is_ok());
470+
let mut updated_content = String::new();
471+
{
472+
let mut file = File::open(&sshd_config_path)?;
473+
file.read_to_string(&mut updated_content)?;
474+
}
475+
assert!(updated_content.contains("PasswordAuthentication yes"));
476+
assert!(!updated_content.contains("PasswordAuthentication no"));
477+
478+
Ok(())
479+
}
364480
}

0 commit comments

Comments
 (0)