From 4c6b441753d9fa21ca21428719d31306a00dd302 Mon Sep 17 00:00:00 2001 From: "xiuting.xu" Date: Tue, 12 May 2026 15:23:09 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E9=83=A8=E7=BD=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Cargo.toml | 4 + deploy/client/.env | 17 +++ deploy/client/docker-compose.clients.yml | 10 +- deploy/client/docker-compose.ssh.password.yml | 36 +++++ deploy/client/docker-compose.ssh.yml | 10 +- deploy/client/docker-compose.tcp.yml | 2 +- deploy/client/docker-compose.tls.yml | 4 +- deploy/client/docker-compose.yml | 2 +- deploy/server/Dockerfile | 4 +- deploy/server/docker-compose.ssh.yml | 15 +- deploy/server/entrypoint.sh | 2 +- src/bin/rtr_debug_client/README.md | 21 +++ src/bin/rtr_debug_client/main.rs | 137 +++++++++++++----- src/{main.rs => main_rtr.rs} | 14 ++ src/rtr/cache/core.rs | 10 +- src/rtr/server/service.rs | 8 +- src/rtr/server/ssh.rs | 42 +++++- tests/test_server_transports.rs | 4 + 18 files changed, 273 insertions(+), 69 deletions(-) create mode 100644 deploy/client/.env create mode 100644 deploy/client/docker-compose.ssh.password.yml rename src/{main.rs => main_rtr.rs} (97%) diff --git a/Cargo.toml b/Cargo.toml index b487b65..d5dfe88 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,6 +3,10 @@ name = "rpki" version = "0.1.0" edition = "2024" +[[bin]] +name = "rpki_rtr" +path = "src/main_rtr.rs" + [dependencies] der-parser = "10.0.0" hex = "0.4.3" diff --git a/deploy/client/.env b/deploy/client/.env new file mode 100644 index 0000000..a333784 --- /dev/null +++ b/deploy/client/.env @@ -0,0 +1,17 @@ +# Target RTR server address for client compose files +# TCP example: 10.0.0.12:323 +# TLS example: rpki.example.com:324 +# SSH example: 10.0.0.12:22 +RPKI_RTR_SERVER_ADDR=rpki-rtr-tcp:323 + +# TLS server name used by --server-name in TLS mode +# Must match server certificate SAN dNSName. +RPKI_RTR_TLS_SERVER_NAME=localhost + +# SSH mode examples: +# RPKI_RTR_SERVER_ADDR=10.0.0.12:2222 +# RPKI_RTR_CLIENT_KEYS_VOLUME=../../certs:/app/certs:ro +# RPKI_RTR_CLIENT_KEY_PATH=/app/certs/rtr-client.key +# RPKI_RTR_SSH_SERVER_PUBKEY_PATH=/app/certs/ssh_host_rsa_key.pub +# RPKI_RTR_SSH_USERNAME=rpki-rtr +# RPKI_RTR_SSH_PASSWORD=your-password diff --git a/deploy/client/docker-compose.clients.yml b/deploy/client/docker-compose.clients.yml index efde91c..8c4b1ff 100644 --- a/deploy/client/docker-compose.clients.yml +++ b/deploy/client/docker-compose.clients.yml @@ -3,7 +3,7 @@ version: "3.9" services: rtr-client-1: image: rpki-rtr-debug-client:latest - command: ["rpki-rtr-tcp:323", "2", "reset", "--keep-after-error", "--summary-only"] + command: ["${RPKI_RTR_SERVER_ADDR:-rpki-rtr-tcp:323}", "2", "reset", "--keep-after-error", "--summary-only"] volumes: - ../../logs/client:/app/logs restart: no @@ -12,7 +12,7 @@ services: rtr-client-2: image: rpki-rtr-debug-client:latest - command: ["rpki-rtr-tcp:323", "2", "reset", "--keep-after-error", "--summary-only"] + command: ["${RPKI_RTR_SERVER_ADDR:-rpki-rtr-tcp:323}", "2", "reset", "--keep-after-error", "--summary-only"] volumes: - ../../logs/client:/app/logs restart: no @@ -21,7 +21,7 @@ services: rtr-client-3: image: rpki-rtr-debug-client:latest - command: ["rpki-rtr-tcp:323", "2", "reset", "--keep-after-error", "--summary-only"] + command: ["${RPKI_RTR_SERVER_ADDR:-rpki-rtr-tcp:323}", "2", "reset", "--keep-after-error", "--summary-only"] volumes: - ../../logs/client:/app/logs restart: no @@ -30,7 +30,7 @@ services: rtr-client-4: image: rpki-rtr-debug-client:latest - command: ["rpki-rtr-tcp:323", "2", "reset", "--keep-after-error", "--summary-only"] + command: ["${RPKI_RTR_SERVER_ADDR:-rpki-rtr-tcp:323}", "2", "reset", "--keep-after-error", "--summary-only"] volumes: - ../../logs/client:/app/logs restart: no @@ -39,7 +39,7 @@ services: rtr-client-5: image: rpki-rtr-debug-client:latest - command: ["rpki-rtr-tcp:323", "2", "reset", "--keep-after-error", "--summary-only"] + command: ["${RPKI_RTR_SERVER_ADDR:-rpki-rtr-tcp:323}", "2", "reset", "--keep-after-error", "--summary-only"] volumes: - ../../logs/client:/app/logs restart: no diff --git a/deploy/client/docker-compose.ssh.password.yml b/deploy/client/docker-compose.ssh.password.yml new file mode 100644 index 0000000..e7d0786 --- /dev/null +++ b/deploy/client/docker-compose.ssh.password.yml @@ -0,0 +1,36 @@ +version: "3.9" + +services: + rtr-debug-client: + build: + context: ../.. + dockerfile: deploy/client/Dockerfile + image: rpki-rtr-debug-client:latest + command: + [ + "${RPKI_RTR_SERVER_ADDR:-rpki-rtr-ssh:22}", + "2", + "reset", + "--ssh", + "--ssh-user", + "${RPKI_RTR_SSH_USERNAME:-rpki-rtr}", + "--ssh-password", + "${RPKI_RTR_SSH_PASSWORD}", + "--ssh-server-key", + "${RPKI_RTR_SSH_SERVER_PUBKEY_PATH:-/app/certs/ssh_host_rsa_key.pub}", + "--keep-after-error", + "--summary-only" + ] + volumes: + - ${RPKI_RTR_CLIENT_KEYS_VOLUME:-../../certs:/app/certs:ro} + - ../../logs/client:/app/logs + restart: no + stdin_open: true + tty: true + networks: + - rpki_net + +networks: + rpki_net: + name: rpki_net + driver: bridge diff --git a/deploy/client/docker-compose.ssh.yml b/deploy/client/docker-compose.ssh.yml index a09fdf0..8755ca1 100644 --- a/deploy/client/docker-compose.ssh.yml +++ b/deploy/client/docker-compose.ssh.yml @@ -8,21 +8,21 @@ services: image: rpki-rtr-debug-client:latest command: [ - "rpki-rtr-ssh:${RPKI_RTR_SSH_PORT:-22}", + "${RPKI_RTR_SERVER_ADDR:-rpki-rtr-ssh:22}", "2", "reset", "--ssh", "--ssh-user", - "rpki-rtr", + "${RPKI_RTR_SSH_USERNAME:-rpki-rtr}", "--ssh-key", - "/app/certs/rtr-client.key", + "${RPKI_RTR_CLIENT_KEY_PATH:-/app/certs/rtr-client.key}", "--ssh-server-key", - "/app/certs/ssh_host_rsa_key.pub", + "${RPKI_RTR_SSH_SERVER_PUBKEY_PATH:-/app/certs/ssh_host_rsa_key.pub}", "--keep-after-error", "--summary-only" ] volumes: - - ../../certs:/app/certs:ro + - ${RPKI_RTR_CLIENT_KEYS_VOLUME:-../../certs:/app/certs:ro} - ../../logs/client:/app/logs restart: no stdin_open: true diff --git a/deploy/client/docker-compose.tcp.yml b/deploy/client/docker-compose.tcp.yml index 99e2479..fe712ba 100644 --- a/deploy/client/docker-compose.tcp.yml +++ b/deploy/client/docker-compose.tcp.yml @@ -4,7 +4,7 @@ services: context: ../.. dockerfile: deploy/client/Dockerfile image: rpki-rtr-debug-client:latest - command: ["rpki-rtr-tcp:323", "2", "reset", "--keep-after-error", "--summary-only"] + command: ["${RPKI_RTR_SERVER_ADDR:-rpki-rtr-tcp:323}", "2", "reset", "--keep-after-error", "--summary-only"] volumes: - ../../logs/client:/app/logs restart: no diff --git a/deploy/client/docker-compose.tls.yml b/deploy/client/docker-compose.tls.yml index 34c69d0..a0ca781 100644 --- a/deploy/client/docker-compose.tls.yml +++ b/deploy/client/docker-compose.tls.yml @@ -8,14 +8,14 @@ services: image: rpki-rtr-debug-client:latest command: [ - "rpki-rtr-tls:324", + "${RPKI_RTR_SERVER_ADDR:-rpki-rtr-tls:324}", "2", "reset", "--tls", "--ca-cert", "/app/certs/client-ca.crt", "--server-name", - "localhost", + "${RPKI_RTR_TLS_SERVER_NAME:-localhost}", "--client-cert", "/app/certs/client-good.crt", "--client-key", diff --git a/deploy/client/docker-compose.yml b/deploy/client/docker-compose.yml index 99e2479..fe712ba 100644 --- a/deploy/client/docker-compose.yml +++ b/deploy/client/docker-compose.yml @@ -4,7 +4,7 @@ services: context: ../.. dockerfile: deploy/client/Dockerfile image: rpki-rtr-debug-client:latest - command: ["rpki-rtr-tcp:323", "2", "reset", "--keep-after-error", "--summary-only"] + command: ["${RPKI_RTR_SERVER_ADDR:-rpki-rtr-tcp:323}", "2", "reset", "--keep-after-error", "--summary-only"] volumes: - ../../logs/client:/app/logs restart: no diff --git a/deploy/server/Dockerfile b/deploy/server/Dockerfile index 0067d7c..0c10435 100644 --- a/deploy/server/Dockerfile +++ b/deploy/server/Dockerfile @@ -32,7 +32,7 @@ RUN apt-get update \ COPY Cargo.toml Cargo.lock ./ COPY src ./src -RUN cargo build --release --bin rpki +RUN cargo build --release --bin rpki_rtr FROM debian:bookworm-slim AS runtime @@ -60,7 +60,7 @@ RUN apt-get update \ WORKDIR /app -COPY --from=builder /build/target/release/rpki /usr/local/bin/rpki +COPY --from=builder /build/target/release/rpki_rtr /usr/local/bin/rpki_rtr COPY --chmod=755 deploy/server/entrypoint.sh /usr/local/bin/rpki-rtr-entrypoint.sh RUN mkdir -p /app/data /app/rtr-db /app/certs /app/slurm /app/logs diff --git a/deploy/server/docker-compose.ssh.yml b/deploy/server/docker-compose.ssh.yml index ccba9ed..708609f 100644 --- a/deploy/server/docker-compose.ssh.yml +++ b/deploy/server/docker-compose.ssh.yml @@ -10,16 +10,18 @@ services: restart: no ports: - "323:323" - - "${RPKI_RTR_SSH_HOST_PORT:-2222}:22" + - "${RPKI_RTR_SSH_HOST_PORT:-2222}:${RPKI_RTR_SSH_CONTAINER_PORT:-22}" environment: RPKI_RTR_ENABLE_TLS: "false" RPKI_RTR_ENABLE_SSH: "true" RPKI_RTR_TCP_ADDR: "0.0.0.0:323" - RPKI_RTR_SSH_ADDR: "0.0.0.0:22" - RPKI_RTR_SSH_HOST_KEY_PATH: "/app/certs/ssh_host_rsa_key" - RPKI_RTR_SSH_AUTHORIZED_KEYS_PATH: "/app/certs/rtr-authorized_keys" - RPKI_RTR_SSH_USERNAME: "rpki-rtr" - RPKI_RTR_SSH_SUBSYSTEM_NAME: "rpki-rtr" + RPKI_RTR_SSH_ADDR: "0.0.0.0:${RPKI_RTR_SSH_CONTAINER_PORT:-22}" + RPKI_RTR_SSH_HOST_KEY_PATH: "${RPKI_RTR_SSH_HOST_KEY_PATH:-/host-ssh/ssh_host_ed25519_key}" + RPKI_RTR_SSH_AUTHORIZED_KEYS_PATH: "${RPKI_RTR_SSH_AUTHORIZED_KEYS_PATH:-/app/certs/rtr-authorized_keys}" + RPKI_RTR_SSH_USERNAME: "${RPKI_RTR_SSH_USERNAME:-rpki-rtr}" + RPKI_RTR_SSH_SUBSYSTEM_NAME: "${RPKI_RTR_SSH_SUBSYSTEM_NAME:-rpki-rtr}" + # SSH auth mode: key | password | both + RPKI_RTR_SSH_AUTH_MODE: "${RPKI_RTR_SSH_AUTH_MODE:-key}" # Optional: enable password authentication in addition to publickey # RPKI_RTR_SSH_PASSWORD: "test-password" RPKI_RTR_DB_PATH: "/app/rtr-db" @@ -33,6 +35,7 @@ services: - ../../data:/app/data:ro - ../../rtr-db:/app/rtr-db - ../../data:/app/slurm:ro + - ${RPKI_RTR_SSH_KEYS_VOLUME:-/etc/ssh:/host-ssh:ro} - ../../certs:/app/certs:ro - ../../logs/server:/app/logs networks: diff --git a/deploy/server/entrypoint.sh b/deploy/server/entrypoint.sh index a747ea5..5df4831 100644 --- a/deploy/server/entrypoint.sh +++ b/deploy/server/entrypoint.sh @@ -7,4 +7,4 @@ log_name="${HOSTNAME:-rpki-rtr}" stdout_log="/app/logs/${log_name}.stdout.log" stderr_log="/app/logs/${log_name}.stderr.log" -exec /usr/local/bin/rpki "$@" >>"$stdout_log" 2>>"$stderr_log" +exec /usr/local/bin/rpki_rtr "$@" >>"$stdout_log" 2>>"$stderr_log" diff --git a/src/bin/rtr_debug_client/README.md b/src/bin/rtr_debug_client/README.md index b536cf2..0bc9926 100644 --- a/src/bin/rtr_debug_client/README.md +++ b/src/bin/rtr_debug_client/README.md @@ -148,3 +148,24 @@ SSH 参数: - `keep-after-error` - `output` / `output verbose` / `output summary` - `quit` + +## SSH Password Auth (Added) + +`rtr_debug_client` now supports password auth in SSH mode. + +- Use exactly one auth option in SSH mode: + - `--ssh-key ` + - `--ssh-password ` +- Host key verification is still required: + - `--ssh-known-hosts ` or `--ssh-server-key ` + +Example: + +```sh +cargo run --bin rtr_debug_client -- \ + 127.0.0.1:22 1 reset \ + --ssh \ + --ssh-user rpki-rtr \ + --ssh-password 'your-password' \ + --ssh-server-key certs/ssh_host_rsa_key.pub +``` diff --git a/src/bin/rtr_debug_client/main.rs b/src/bin/rtr_debug_client/main.rs index 7b451c6..917a9e6 100644 --- a/src/bin/rtr_debug_client/main.rs +++ b/src/bin/rtr_debug_client/main.rs @@ -1001,6 +1001,15 @@ impl Config { })?; ensure_ssh_config(&mut transport)?.private_key = Some(PathBuf::from(path)); } + "--ssh-password" => { + let password = args.next().ok_or_else(|| { + io::Error::new( + io::ErrorKind::InvalidInput, + "--ssh-password requires a value", + ) + })?; + ensure_ssh_config(&mut transport)?.password = Some(password); + } "--ssh-subsystem" => { let subsystem = args.next().ok_or_else(|| { io::Error::new( @@ -1181,8 +1190,15 @@ impl TransportConfig { .unwrap_or_else(|| "".to_string()) ), Self::Ssh(cfg) => format!( - "ssh (user={}, subsystem={}, host_key_check={})", + "ssh (user={}, auth={}, subsystem={}, host_key_check={})", cfg.user.as_deref().unwrap_or(""), + if cfg.private_key.is_some() { + "publickey" + } else if cfg.password.is_some() { + "password" + } else { + "" + }, cfg.subsystem .as_deref() .unwrap_or(DEFAULT_SSH_SUBSYSTEM_NAME), @@ -1222,6 +1238,7 @@ impl HostKeyVerification { struct SshConfig { user: Option, private_key: Option, + password: Option, subsystem: Option, known_hosts: Option, server_key: Option, @@ -1315,12 +1332,28 @@ fn finalize_transport(transport: TransportConfig, addr: &str) -> io::Result", - ) - })?; + "SSH mode authentication must choose one: --ssh-key or --ssh-password ", + )); + } + if private_key.is_none() && password.is_none() { + return Err(io::Error::new( + io::ErrorKind::InvalidInput, + "SSH mode requires authentication: --ssh-key or --ssh-password ", + )); + } if cfg.known_hosts.is_some() && cfg.server_key.is_some() { return Err(io::Error::new( @@ -1356,7 +1389,8 @@ fn finalize_transport(transport: TransportConfig, addr: &str) -> io::Result io::Result io::Result, max_delta: u8, @@ -63,6 +65,7 @@ impl Default for AppConfig { ssh_authorized_keys_path: "./certs/rtr-authorized_keys".to_string(), ssh_username: "rpki-rtr".to_string(), ssh_subsystem_name: "rpki-rtr".to_string(), + ssh_auth_mode: SshAuthMode::Key, ssh_password: None, max_delta: 100, @@ -153,6 +156,14 @@ impl AppConfig { if let Some(value) = env_var("RPKI_RTR_SSH_SUBSYSTEM_NAME")? { config.ssh_subsystem_name = value; } + if let Some(value) = env_var("RPKI_RTR_SSH_AUTH_MODE")? { + config.ssh_auth_mode = SshAuthMode::parse(&value).ok_or_else(|| { + anyhow!( + "invalid RPKI_RTR_SSH_AUTH_MODE '{}': expected key|password|both", + value + ) + })?; + } if let Some(value) = env_var("RPKI_RTR_SSH_PASSWORD")? { let value = value.trim().to_string(); config.ssh_password = if value.is_empty() { None } else { Some(value) }; @@ -368,6 +379,7 @@ fn start_servers(config: &AppConfig, service: &RtrService) -> RunningRtrService &config.ssh_authorized_keys_path, &config.ssh_username, &config.ssh_subsystem_name, + config.ssh_auth_mode, config.ssh_password.as_deref(), ) } else if config.enable_tls { @@ -388,6 +400,7 @@ fn start_servers(config: &AppConfig, service: &RtrService) -> RunningRtrService &config.ssh_authorized_keys_path, &config.ssh_username, &config.ssh_subsystem_name, + config.ssh_auth_mode, config.ssh_password.as_deref(), ) } else { @@ -505,6 +518,7 @@ fn log_startup_config(config: &AppConfig) { ); info!("ssh_username={}", config.ssh_username); info!("ssh_subsystem_name={}", config.ssh_subsystem_name); + info!("ssh_auth_mode={}", config.ssh_auth_mode.as_str()); info!("ssh_password_enabled={}", config.ssh_password.is_some()); } diff --git a/src/rtr/cache/core.rs b/src/rtr/cache/core.rs index c705c31..fa14aff 100644 --- a/src/rtr/cache/core.rs +++ b/src/rtr/cache/core.rs @@ -65,7 +65,7 @@ pub struct VersionState { impl VersionState { fn new(session_id: u16, serial: u32, snapshot: Snapshot, max_delta: u8) -> Self { - let rtr_payloads = Arc::new(snapshot.payloads_for_rtr()); + let rtr_payloads = snapshot.rtr_payloads_for_rtr_arc(); Self { session_id, serial, @@ -199,7 +199,7 @@ impl RtrCacheBuilder { VersionState { session_id: session_ids.as_array()[idx], serial: serials[idx], - rtr_payloads: Arc::new(snapshot.payloads_for_rtr()), + rtr_payloads: snapshot.rtr_payloads_for_rtr_arc(), snapshot: Arc::new(snapshot), deltas: deltas[idx].clone(), } @@ -230,7 +230,7 @@ impl RtrCache { self.availability = CacheAvailability::NoDataAvailable; for version_state in &mut self.versions { version_state.snapshot = Arc::new(Snapshot::empty()); - version_state.rtr_payloads = Arc::new(Vec::new()); + version_state.rtr_payloads = version_state.snapshot.rtr_payloads_for_rtr_arc(); version_state.deltas.clear(); } } @@ -247,7 +247,7 @@ impl RtrCache { state.session_id = new_session_ids.get(v); state.serial = 1; state.snapshot = Arc::new(project_snapshot_for_version(source_snapshot, v)); - state.rtr_payloads = Arc::new(state.snapshot.as_ref().payloads_for_rtr()); + state.rtr_payloads = state.snapshot.rtr_payloads_for_rtr_arc(); state.deltas.clear(); } self.last_update_end = DualTime::now(); @@ -362,7 +362,7 @@ impl RtrCache { } state.snapshot = Arc::new(projected); - state.rtr_payloads = Arc::new(state.snapshot.as_ref().payloads_for_rtr()); + state.rtr_payloads = state.snapshot.rtr_payloads_for_rtr_arc(); Self::push_delta( state, self.max_delta, diff --git a/src/rtr/server/service.rs b/src/rtr/server/service.rs index 53a6a85..7a9f366 100644 --- a/src/rtr/server/service.rs +++ b/src/rtr/server/service.rs @@ -13,7 +13,7 @@ use crate::rtr::cache::SharedRtrCache; use crate::rtr::server::config::RtrServiceConfig; use crate::rtr::server::listener::RtrServer; use crate::rtr::server::notifier::RtrNotifier; -use crate::rtr::server::ssh::load_rtr_ssh_runtime_config; +use crate::rtr::server::ssh::{SshAuthMode, load_rtr_ssh_runtime_config}; pub struct RtrService { cache: SharedRtrCache, @@ -167,6 +167,7 @@ impl RtrService { authorized_keys_path: impl AsRef, username: &str, subsystem_name: &str, + auth_mode: SshAuthMode, password: Option<&str>, ) -> JoinHandle<()> { let host_key_path = host_key_path.as_ref().to_path_buf(); @@ -184,6 +185,7 @@ impl RtrService { &authorized_keys_path, &username, &subsystem_name, + auth_mode, password.as_deref(), inactivity_timeout, keepalive_interval, @@ -213,6 +215,7 @@ impl RtrService { authorized_keys_path: impl AsRef, username: &str, subsystem_name: &str, + auth_mode: SshAuthMode, password: Option<&str>, ) -> RunningRtrService { let tcp_handle = self.spawn_tcp(tcp_bind_addr); @@ -222,6 +225,7 @@ impl RtrService { authorized_keys_path, username, subsystem_name, + auth_mode, password, ); @@ -244,6 +248,7 @@ impl RtrService { authorized_keys_path: impl AsRef, username: &str, subsystem_name: &str, + auth_mode: SshAuthMode, password: Option<&str>, ) -> RunningRtrService { let tcp_handle = self.spawn_tcp(tcp_bind_addr); @@ -255,6 +260,7 @@ impl RtrService { authorized_keys_path, username, subsystem_name, + auth_mode, password, ); diff --git a/src/rtr/server/ssh.rs b/src/rtr/server/ssh.rs index feecfc0..97cef64 100644 --- a/src/rtr/server/ssh.rs +++ b/src/rtr/server/ssh.rs @@ -9,6 +9,32 @@ use russh::keys::ssh_key::{self, AuthorizedKeys}; use russh::server::Config as RusshServerConfig; use russh::{MethodKind, MethodSet}; +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum SshAuthMode { + Key, + Password, + Both, +} + +impl SshAuthMode { + pub fn parse(value: &str) -> Option { + match value.trim().to_ascii_lowercase().as_str() { + "key" | "publickey" => Some(Self::Key), + "password" => Some(Self::Password), + "both" => Some(Self::Both), + _ => None, + } + } + + pub fn as_str(self) -> &'static str { + match self { + Self::Key => "key", + Self::Password => "password", + Self::Both => "both", + } + } +} + #[derive(Debug, Clone)] pub struct RtrSshRuntimeConfig { pub server_config: Arc, @@ -23,6 +49,7 @@ pub fn load_rtr_ssh_runtime_config( authorized_keys_path: impl AsRef, username: &str, subsystem_name: &str, + auth_mode: SshAuthMode, password: Option<&str>, inactivity_timeout: Option, keepalive_interval: Option, @@ -35,12 +62,21 @@ pub fn load_rtr_ssh_runtime_config( } let host_key = load_host_key(host_key_path.as_ref())?; - let authorized_keys = load_authorized_keys(authorized_keys_path.as_ref())?; + let authorized_keys = if matches!(auth_mode, SshAuthMode::Key | SshAuthMode::Both) { + load_authorized_keys(authorized_keys_path.as_ref())? + } else { + Vec::new() + }; let password = password.map(str::trim).filter(|value| !value.is_empty()); + if matches!(auth_mode, SshAuthMode::Password | SshAuthMode::Both) && password.is_none() { + bail!("SSH auth mode '{}' requires non-empty password", auth_mode.as_str()); + } let mut methods = MethodSet::empty(); - methods.push(MethodKind::PublicKey); - if password.is_some() { + if matches!(auth_mode, SshAuthMode::Key | SshAuthMode::Both) { + methods.push(MethodKind::PublicKey); + } + if matches!(auth_mode, SshAuthMode::Password | SshAuthMode::Both) { methods.push(MethodKind::Password); } diff --git a/tests/test_server_transports.rs b/tests/test_server_transports.rs index 5934e0c..a7e1544 100644 --- a/tests/test_server_transports.rs +++ b/tests/test_server_transports.rs @@ -16,6 +16,7 @@ use tokio_rustls::TlsConnector; use rpki::rtr::cache::{RtrCacheBuilder, SessionIds, SharedRtrCache}; use rpki::rtr::payload::Timing; use rpki::rtr::pdu::{CacheResponse, EndOfDataV1, ResetQuery}; +use rpki::rtr::server::ssh::SshAuthMode; use rpki::rtr::server::RtrService; use russh::client; use russh::keys; @@ -201,6 +202,7 @@ async fn unified_server_ssh_opens_listener_and_emits_banner() { &authorized_keys_path, "rpki-rtr", "rpki-rtr", + SshAuthMode::Key, None, ); wait_for_port(ssh_addr).await; @@ -249,6 +251,7 @@ async fn unified_server_ssh_accepts_password_when_configured() { &authorized_keys_path, "rpki-rtr", "rpki-rtr", + SshAuthMode::Both, Some("test-password"), ); wait_for_port(ssh_addr).await; @@ -323,6 +326,7 @@ async fn unified_server_ssh_rejects_password_when_not_configured() { &authorized_keys_path, "rpki-rtr", "rpki-rtr", + SshAuthMode::Key, None, ); wait_for_port(ssh_addr).await;