Skip to content

Conversation

@peytonr18
Copy link
Contributor

@peytonr18 peytonr18 commented Oct 22, 2025

Problem

Currently, SSH configuration updates (PasswordAuthentication in sshd_config) are tightly coupled with password provisioning. When set_user_password() or lock_user() is called during provisioning, SSH configuration is automatically modified based on password provisioner presence. This prevents consumers from independently controlling SSH settings without triggering password logic.

Solution

This PR decouples SSH configuration from password provisioning by:

  1. Adding an explicit config knob: ssh.configure_sshd_password_authentication (default: true)
  2. Making password operations side-effect free: set_user_password() and lock_user() never modify SSH configuration
  3. Keeping SSH configuration management internal: SSH config functions remain private to the provisioning system
  4. Adding a no-SSH provisioning path: Provision::provision_without_ssh_config() skips SSH config updates entirely

Changes

Configuration

  • Added ssh.configure_sshd_password_authentication: bool field (defaults to true for backward compatibility)
  • Updated all config tests to validate the new flag

Public API

  • Added Provision::provision_without_ssh_config() method for skipping SSH configuration updates
  • SSH configuration functions (update_sshd_config(), get_sshd_config_path()) remain internal (pub(crate))

Refactoring

  • Split Provision::provision() into logical steps:
    • provision_core() - hostname, user, password (no SSH impact)
    • update_ssh_config() - SSH configuration (separate, gated step)
    • provision_ssh_keys() - authorized_keys provisioning
  • SSH config updates now respect ssh.update_sshd_config instead of password backend type

Documentation

  • Updated rustdocs to explain decoupling and available options
  • Clarified that password operations have zero SSH side effects

Performance Improvements

  • Eliminated unnecessary clones by accepting &Path instead of PathBuf in SSH functions
  • Changed provision_ssh_keys() to consume self, avoiding string clones

Usage Examples

For consumers wanting to skip SSH configuration:

Option 1: Disable via config

[ssh]
configure_sshd_password_authentication = false

Option 2: Use provision_without_ssh_config()

Provision::new(hostname, user, config, disable_password_authentication)
   .provision_without_ssh_config()?;

Backward Compatibility

No changes required. Default behavior is unchanged - Provision::provision() still updates SSH configuration when ssh.update_sshd_config = true (default).

Resolves #265

Password provisioning operations (set_user_password, lock_user) now have
zero SSH side effects. SSH configuration updates are controlled by a new
config flag and can be managed independently.
@peytonr18 peytonr18 marked this pull request as ready for review October 22, 2025 21:17
@peytonr18 peytonr18 requested a review from Copilot October 22, 2025 22:22
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This PR decouples SSH configuration management from password provisioning, allowing independent control of SSH settings without triggering password logic. Previously, SSH configuration updates were automatically performed when password operations occurred.

Key Changes:

  • Added ssh.update_sshd_config configuration flag (defaults to true) to control SSH configuration updates
  • Exposed update_sshd_config() and get_sshd_config_path() as public APIs for explicit SSH management
  • Refactored Provision::provision() into separate logical steps and added provision_without_ssh_config() method

Reviewed Changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.

File Description
libazureinit/src/provision/ssh.rs Made SSH functions public and updated documentation to reflect decoupling
libazureinit/src/provision/mod.rs Split provisioning into provision_core(), update_ssh_config(), and provision_ssh_keys() methods; changed SSH update logic from password backend detection to config flag
libazureinit/src/lib.rs Re-exported public SSH functions for external use
libazureinit/src/config.rs Added update_sshd_config field to Ssh config struct with default value true

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

// If the "/etc/ssh/sshd_config.d" directory exists, it returns the path for a drop-in configuration file.
// Otherwise, it defaults to the main SSH configuration file at "/etc/ssh/sshd_config".
pub(crate) fn get_sshd_config_path() -> &'static str {
pub fn get_sshd_config_path() -> &'static str {
Copy link
Member

Choose a reason for hiding this comment

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

In your example this only gets passed as input to update_sshd_config(), and I don't think we necessarily need users to know or care about this detail. What's the use-case for users to need to get ahold of the path outside of the provision API?

Is the overall desire to let users provision without touching the sshd config, or is there a use case where they don't want to provision at all and only touch sshd config? My guess is it's the first, in which case it might be nicer to keep both these private.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Changed these back to private and removed any ability to only touch sshd config without provisioning!

…assword_authentication; making all SSH methods internal and removing ability to alter sshd_config file outside of provisioning
/// Performs hostname, user, password, and SSH key provisioning, but skips
/// SSH configuration updates.
#[instrument(skip_all)]
pub fn provision_without_ssh_config(self) -> Result<(), Error> {
Copy link
Contributor

Choose a reason for hiding this comment

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

this seems like something that could/should be handled by configuration or custom calls, would suggest removing it unless needed

@peytonr18 peytonr18 merged commit 0b2034f into Azure:main Nov 13, 2025
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[RFE] Decouple SSH Configuration from Password Provisioning

3 participants