Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,12 @@ This is a set of options that can be put in any of the above definitions, with t
# This defaults to `false`
interactiveSudo = false;

# Whether to enable reading the sudo password from the environment (password based sudo). Useful when using non-root sshUsers.
# It reads the passwords on a per-node basis with the environment variable prefixed by DEPLOY_RS_SUDO_PASSWORD_ and followed by the node name in capital letters, dashes get replaced with underscores.
# For example: DEPLOY_RS_SUDO_PASSWORD_MY_SYSTEM
# This defaults to `false`
environmentSudo = false;

# This is an optional list of arguments that will be passed to SSH.
sshOpts = [ "-p" "2121" ];

Expand Down
3 changes: 3 additions & 0 deletions interface.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@
},
"interactiveSudo": {
"type": "boolean"
},
"environmentSudo": {
"type": "boolean"
}
}
},
Expand Down
22 changes: 21 additions & 1 deletion src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,9 @@ pub struct Opts {
/// Prompt for sudo password during activation.
#[arg(long)]
interactive_sudo: Option<bool>,
/// Read sudo password from environment during activation.
#[arg(long)]
environment_sudo: Option<bool>,
}

/// Returns if the available Nix installation supports flakes
Expand Down Expand Up @@ -563,6 +566,22 @@ async fn run_deploy(
info!("You will now be prompted for the sudo password for {}.", node.node_settings.hostname);
let sudo_password = rpassword::prompt_password(format!("(sudo for {}) Password: ", node.node_settings.hostname)).unwrap_or("".to_string());

deploy_defs.sudo_password = Some(sudo_password);
} else if deploy_data.merged_settings.environment_sudo.unwrap_or(false) {
if deploy_data.merged_settings.sudo.is_some() {
warn!("Custom sudo commands should be configured to accept password input from stdin when using the 'interactive sudo' option. Deployment may fail if the custom command ignores stdin.");
} else {
// this configures sudo to hide the password prompt and accept input from stdin
// at the time of writing, deploy_defs.sudo defaults to 'sudo -u root' when using user=root and sshUser as non-root
let original = deploy_defs.sudo.unwrap_or("sudo".to_string());
deploy_defs.sudo = Some(format!("{} -S -p \"\"", original));
}

let node_name_as_env = deploy_data.node_name.to_uppercase().replace("-", "_");

info!("Reading sudo password from environment variable DEPLOY_RS_SUDO_PASSWORD_{} for {}.", node_name_as_env, node.node_settings.hostname);
let sudo_password = std::env::var(format!("DEPLOY_RS_SUDO_PASSWORD_{}", node_name_as_env)).unwrap_or("".to_string());

deploy_defs.sudo_password = Some(sudo_password);
}

Expand Down Expand Up @@ -710,7 +729,8 @@ pub async fn run(args: Option<&ArgMatches>) -> Result<(), RunError> {
dry_activate: opts.dry_activate,
remote_build: opts.remote_build,
sudo: opts.sudo,
interactive_sudo: opts.interactive_sudo
interactive_sudo: opts.interactive_sudo,
environment_sudo: opts.environment_sudo
};

let supports_flakes = test_flake_support().await.map_err(RunError::FlakeTest)?;
Expand Down
2 changes: 2 additions & 0 deletions src/data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ pub struct GenericSettings {
pub remote_build: Option<bool>,
#[serde(rename(deserialize = "interactiveSudo"))]
pub interactive_sudo: Option<bool>,
#[serde(rename(deserialize = "environmentSudo"))]
pub environment_sudo: Option<bool>,
}

#[derive(Deserialize, Debug, Clone)]
Expand Down
10 changes: 5 additions & 5 deletions src/deploy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -301,7 +301,7 @@ pub async fn confirm_profile(
.spawn()
.map_err(ConfirmProfileError::SSHConfirm)?;

if deploy_data.merged_settings.interactive_sudo.unwrap_or(false) {
if deploy_data.merged_settings.interactive_sudo.unwrap_or(false) || deploy_data.merged_settings.environment_sudo.unwrap_or(false) {
trace!("[confirm] Piping in sudo password");
handle_sudo_stdin(&mut ssh_confirm_child, deploy_defs)
.await
Expand Down Expand Up @@ -413,7 +413,7 @@ pub async fn deploy_profile(
.spawn()
.map_err(DeployProfileError::SSHSpawnActivate)?;

if deploy_data.merged_settings.interactive_sudo.unwrap_or(false) {
if deploy_data.merged_settings.interactive_sudo.unwrap_or(false) || deploy_data.merged_settings.environment_sudo.unwrap_or(false) {
trace!("[activate] Piping in sudo password");
handle_sudo_stdin(&mut ssh_activate_child, deploy_defs)
.await
Expand Down Expand Up @@ -454,7 +454,7 @@ pub async fn deploy_profile(
.spawn()
.map_err(DeployProfileError::SSHSpawnActivate)?;

if deploy_data.merged_settings.interactive_sudo.unwrap_or(false) {
if deploy_data.merged_settings.interactive_sudo.unwrap_or(false) || deploy_data.merged_settings.environment_sudo.unwrap_or(false) {
trace!("[activate] Piping in sudo password");
handle_sudo_stdin(&mut ssh_activate_child, deploy_defs)
.await
Expand Down Expand Up @@ -498,7 +498,7 @@ pub async fn deploy_profile(
.spawn()
.map_err(DeployProfileError::SSHWait)?;

if deploy_data.merged_settings.interactive_sudo.unwrap_or(false) {
if deploy_data.merged_settings.interactive_sudo.unwrap_or(false) || deploy_data.merged_settings.environment_sudo.unwrap_or(false) {
trace!("[wait] Piping in sudo password");
handle_sudo_stdin(&mut ssh_wait_child, deploy_defs)
.await
Expand Down Expand Up @@ -581,7 +581,7 @@ pub async fn revoke(
.spawn()
.map_err(RevokeProfileError::SSHSpawnRevoke)?;

if deploy_data.merged_settings.interactive_sudo.unwrap_or(false) {
if deploy_data.merged_settings.interactive_sudo.unwrap_or(false) || deploy_data.merged_settings.environment_sudo.unwrap_or(false) {
trace!("[revoke] Piping in sudo password");
handle_sudo_stdin(&mut ssh_revoke_child, deploy_defs)
.await
Expand Down
4 changes: 4 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,7 @@ pub struct CmdOverrides {
pub activation_timeout: Option<u16>,
pub sudo: Option<String>,
pub interactive_sudo: Option<bool>,
pub environment_sudo: Option<bool>,
pub dry_activate: bool,
pub remote_build: bool,
}
Expand Down Expand Up @@ -468,6 +469,9 @@ pub fn make_deploy_data<'a, 's>(
if let Some(interactive_sudo) = cmd_overrides.interactive_sudo {
merged_settings.interactive_sudo = Some(interactive_sudo);
}
if let Some(environment_sudo) = cmd_overrides.environment_sudo {
merged_settings.environment_sudo = Some(environment_sudo);
}

DeployData {
node_name,
Expand Down