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
29 changes: 10 additions & 19 deletions src/connectors/ssh.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ pub struct SshConnectorConfig {
pub port: u16,
pub username: String,
pub auth: SshAuth,
#[serde(default)]
pub server_key_verification: ServerKeyVerification,
#[serde(default = "default_connector_timeout")]
pub inactivity_timeout_secs: u64,
Expand All @@ -46,31 +45,13 @@ pub enum SshAuth {
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]
#[serde(tag = "type")]
#[derive(Default)]
pub enum ServerKeyVerification {
#[serde(rename = "fingerprint")]
Fingerprint { fingerprint: String },
#[serde(rename = "insecureAcceptAny")]
#[default]
InsecureAcceptAny,
}

impl Default for SshConnectorConfig {
fn default() -> Self {
Self {
name: String::new(),
server: String::new(),
port: 22,
username: String::new(),
auth: SshAuth::Password {
password: String::new(),
},
server_key_verification: ServerKeyVerification::default(),
inactivity_timeout_secs: default_connector_timeout(),
}
}
}

#[derive(Debug, Clone, Serialize)]
pub struct SshConnector {
#[serde(flatten)]
Expand Down Expand Up @@ -249,6 +230,8 @@ username: testuser
auth:
type: password
password: testpass
serverKeyVerification:
type: insecureAcceptAny
"#;
let value: Value = serde_yaml_ng::from_str(yaml).expect("Failed to parse test YAML");
let connector = from_value(&value).expect("Failed to deserialize SSH connector config");
Expand All @@ -267,6 +250,8 @@ auth:
type: privateKey
path: /path/to/key
passphrase: keypass
serverKeyVerification:
type: insecureAcceptAny
"#;
let value: Value = serde_yaml_ng::from_str(yaml).expect("Failed to parse test YAML");
let connector = from_value(&value).expect("Failed to deserialize SSH connector config");
Expand All @@ -284,6 +269,8 @@ username: keyuser
auth:
type: privateKey
path: /path/to/key
serverKeyVerification:
type: insecureAcceptAny
"#;
let value: Value = serde_yaml_ng::from_str(yaml).expect("Failed to parse test YAML");
let _connector = from_value(&value).expect("Failed to deserialize SSH connector config");
Expand Down Expand Up @@ -317,6 +304,8 @@ username: testuser
auth:
type: password
password: testpass
serverKeyVerification:
type: insecureAcceptAny
"#;
let value: Value = serde_yaml_ng::from_str(yaml).expect("Failed to parse test YAML");
let result = from_value(&value);
Expand All @@ -334,6 +323,8 @@ username: testuser
auth:
type: password
password: testpass
serverKeyVerification:
type: insecureAcceptAny
"#;
let value: Value = serde_yaml_ng::from_str(yaml).expect("Failed to parse test YAML");
let _connector = from_value(&value).expect("Failed to deserialize SSH connector config");
Expand Down
9 changes: 4 additions & 5 deletions src/rules/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,16 +120,15 @@ impl Rule {
rules_metrics().execute_time.start_timer()
};
let t = Instant::now();
let ret = if self.filter.is_none() {
true
} else {
match self.filter.as_ref().unwrap().evaluate(request).await {
let ret = match &self.filter {
None => true,
Some(f) => match f.evaluate(request).await {
Ok(b) => b,
Err(e) => {
trace!("error evaluating filter: {:?}", e);
false
}
}
},
};
let t = t.elapsed().as_nanos() as u64;
#[cfg(feature = "metrics")]
Expand Down
3 changes: 3 additions & 0 deletions tests/comprehensive/scripts/generate_matrix_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,9 @@ def _define_connectors(self) -> List[ConnectorConfig]:
"auth": {
"type": "password",
"password": "proxy123"
},
"serverKeyVerification": {
"type": "insecureAcceptAny"
}
}
),
Expand Down
73 changes: 73 additions & 0 deletions tests/ssh_security.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
use redproxy_rs::connectors::ssh::{ServerKeyVerification, SshConnectorConfig};
use serde_yaml_ng::Value;

#[test]
fn test_missing_verification_fails() {
let yaml = r#"
name: test-ssh
type: ssh
server: ssh.example.com
port: 22
username: testuser
auth:
type: password
password: testpass
"#;
let result: Result<SshConnectorConfig, _> = serde_yaml_ng::from_str(yaml);
assert!(
result.is_err(),
"Deserialization should fail when serverKeyVerification is missing"
);
let err = result.err().unwrap().to_string();
assert!(
err.contains("missing field `serverKeyVerification`"),
"Error message should mention missing field: {}",
err
);
}

#[test]
fn test_explicit_insecure_verification_succeeds() {
let yaml = r#"
name: test-ssh
type: ssh
server: ssh.example.com
port: 22
username: testuser
auth:
type: password
password: testpass
serverKeyVerification:
type: insecureAcceptAny
"#;
let config: SshConnectorConfig = serde_yaml_ng::from_str(yaml)
.expect("Deserialization should succeed with explicit insecureAcceptAny");
assert!(matches!(
config.server_key_verification,
ServerKeyVerification::InsecureAcceptAny
));
}

#[test]
fn test_explicit_fingerprint_verification_succeeds() {
let yaml = r#"
name: test-ssh
type: ssh
server: ssh.example.com
port: 22
username: testuser
auth:
type: password
password: testpass
serverKeyVerification:
type: fingerprint
fingerprint: "SHA256:abcd"
"#;
let config: SshConnectorConfig = serde_yaml_ng::from_str(yaml)
.expect("Deserialization should succeed with explicit fingerprint");
if let ServerKeyVerification::Fingerprint { fingerprint } = config.server_key_verification {
assert_eq!(fingerprint, "SHA256:abcd");
} else {
panic!("Expected Fingerprint variant");
}
}
Loading