Skip to content

Commit a034e23

Browse files
authored
Merge pull request #6 from veeso/develop
2 parents 2cabc17 + 41857f5 commit a034e23

File tree

16 files changed

+195
-321
lines changed

16 files changed

+195
-321
lines changed

CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# Changelog
22

33
- [Changelog](#changelog)
4+
- [0.3.0](#030)
45
- [0.2.1](#021)
56
- [0.2.0](#020)
67
- [0.1.6](#016)
@@ -12,6 +13,15 @@
1213

1314
---
1415

16+
## 0.3.0
17+
18+
Released on 09/07/2024
19+
20+
- Fix: resolved_host from configuration wasn't used to connect
21+
- `SshOpts::method` now requires `KeyMethod` and `MethodType` to setup key method
22+
- Feat: Implemented `SshAgentIdentity` to specify the ssh agent configuration to be used to authenticate.
23+
- use `SshOpts.ssh_agent_identity()` to set the option
24+
1525
## 0.2.1
1626

1727
Released on 06/07/2023

Cargo.toml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ license = "MIT"
1111
name = "remotefs-ssh"
1212
readme = "README.md"
1313
repository = "https://github.com/veeso/remotefs-rs-ssh"
14-
version = "0.2.1"
14+
version = "0.3.0"
1515

1616
[dependencies]
1717
chrono = "^0.4"
@@ -20,13 +20,13 @@ log = "^0.4"
2020
regex = "^1.7"
2121
remotefs = "^0.2"
2222
ssh2-config = "^0.2"
23-
ssh2 = "^0.9.0"
23+
ssh2 = "^0.9"
2424

2525
[dev-dependencies]
26-
env_logger = "^0.10"
26+
env_logger = "^0.11"
2727
pretty_assertions = "^1.0.0"
2828
rand = "^0.8.4"
29-
serial_test = "^2.0"
29+
serial_test = "^3"
3030
tempfile = "^3.5"
3131

3232
[features]

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
<p align="center">~ Remotefs SSH client ~</p>
1212

1313
<p align="center">Developed by <a href="https://veeso.github.io/" target="_blank">@veeso</a></p>
14-
<p align="center">Current version: 0.2.1 (06/07/2023)</p>
14+
<p align="center">Current version: 0.3.0 (09/07/2024)</p>
1515

1616
<p align="center">
1717
<a href="https://opensource.org/licenses/MIT"
@@ -81,7 +81,7 @@ remotefs-ssh is a client implementation for [remotefs](https://github.com/veeso/
8181
First of all, add `remotefs-ssh` to your project dependencies:
8282

8383
```toml
84-
remotefs-ssh = "^0.2"
84+
remotefs-ssh = "^0.3"
8585
```
8686

8787
these features are supported:

src/lib.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
//!
1212
//! ```toml
1313
//! remotefs = "^0.2"
14-
//! remotefs-ssh = "^0.2"
14+
//! remotefs-ssh = "^0.3"
1515
//! ```
1616
//!
1717
//! these features are supported:
@@ -58,7 +58,10 @@ extern crate lazy_static;
5858
extern crate log;
5959

6060
mod ssh;
61-
pub use ssh::{ParseRule as SshConfigParseRule, ScpFs, SftpFs, SshKeyStorage, SshOpts};
61+
pub use ssh::{
62+
KeyMethod, MethodType, ParseRule as SshConfigParseRule, ScpFs, SftpFs, SshAgentIdentity,
63+
SshKeyStorage, SshOpts,
64+
};
6265

6366
// -- utils
6467
pub(crate) mod utils;

src/mock/mod.rs

Lines changed: 0 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2,29 +2,6 @@
22
//!
33
//! Contains mock for test units
44
5-
/**
6-
* MIT License
7-
*
8-
* remotefs - Copyright (c) 2021 Christian Visintin
9-
*
10-
* Permission is hereby granted, free of charge, to any person obtaining a copy
11-
* of this software and associated documentation files (the "Software"), to deal
12-
* in the Software without restriction, including without limitation the rights
13-
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14-
* copies of the Software, and to permit persons to whom the Software is
15-
* furnished to do so, subject to the following conditions:
16-
*
17-
* The above copyright notice and this permission notice shall be included in all
18-
* copies or substantial portions of the Software.
19-
*
20-
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21-
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22-
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23-
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24-
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25-
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26-
* SOFTWARE.
27-
*/
285
pub mod ssh;
296
// -- logger
307

src/mock/ssh.rs

Lines changed: 0 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -6,29 +6,6 @@ use std::io::Write;
66

77
use tempfile::NamedTempFile;
88

9-
/**
10-
* MIT License
11-
*
12-
* remotefs - Copyright (c) 2021 Christian Visintin
13-
*
14-
* Permission is hereby granted, free of charge, to any person obtaining a copy
15-
* of this software and associated documentation files (the "Software"), to deal
16-
* in the Software without restriction, including without limitation the rights
17-
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
18-
* copies of the Software, and to permit persons to whom the Software is
19-
* furnished to do so, subject to the following conditions:
20-
*
21-
* The above copyright notice and this permission notice shall be included in all
22-
* copies or substantial portions of the Software.
23-
*
24-
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
25-
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
26-
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
27-
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
28-
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
29-
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
30-
* SOFTWARE.
31-
*/
329
use crate::SshKeyStorage;
3310

3411
/// Mock ssh key storage

src/ssh/commons.rs

Lines changed: 104 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -11,30 +11,9 @@ use std::time::Duration;
1111
use remotefs::{RemoteError, RemoteErrorType, RemoteResult};
1212
use ssh2::{MethodType as SshMethodType, Session};
1313

14-
/**
15-
* MIT License
16-
*
17-
* remotefs - Copyright (c) 2021 Christian Visintin
18-
*
19-
* Permission is hereby granted, free of charge, to any person obtaining a copy
20-
* of this software and associated documentation files (the "Software"), to deal
21-
* in the Software without restriction, including without limitation the rights
22-
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
23-
* copies of the Software, and to permit persons to whom the Software is
24-
* furnished to do so, subject to the following conditions:
25-
*
26-
* The above copyright notice and this permission notice shall be included in all
27-
* copies or substantial portions of the Software.
28-
*
29-
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
30-
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
31-
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
32-
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
33-
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
34-
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
35-
* SOFTWARE.
36-
*/
37-
use super::{config::Config, SshOpts};
14+
use super::config::Config;
15+
use super::SshOpts;
16+
use crate::SshAgentIdentity;
3817

3918
// -- connect
4019

@@ -101,27 +80,45 @@ pub fn connect(opts: &SshOpts) -> RemoteResult<Session> {
10180
error!("SSH handshake failed: {}", err);
10281
return Err(RemoteError::new_ex(RemoteErrorType::ProtocolError, err));
10382
}
104-
// Authenticate with password or key
105-
match opts
106-
.key_storage
107-
.as_ref()
108-
.and_then(|x| x.resolve(ssh_config.host.as_str(), ssh_config.username.as_str()))
109-
{
110-
Some(rsa_key) => {
111-
session_auth_with_rsakey(
112-
&mut session,
113-
&ssh_config.username,
114-
rsa_key.as_path(),
115-
opts.password.as_deref(),
116-
ssh_config.params.identity_file.as_deref(),
117-
)?;
83+
84+
// if use_ssh_agent is enabled, try to authenticate with ssh agent
85+
if let Some(ssh_agent_config) = &opts.ssh_agent_identity {
86+
match session_auth_with_agent(&mut session, &ssh_config.username, ssh_agent_config) {
87+
Ok(_) => {
88+
info!("Authenticated with ssh agent");
89+
return Ok(session);
90+
}
91+
Err(err) => {
92+
error!("Could not authenticate with ssh agent: {}", err);
93+
}
11894
}
119-
None => {
120-
session_auth_with_password(
121-
&mut session,
122-
&ssh_config.username,
123-
opts.password.as_deref(),
124-
)?;
95+
}
96+
97+
// Authenticate with password or key
98+
if !session.authenticated() {
99+
match opts.key_storage.as_ref().and_then(|x| {
100+
x.resolve(ssh_config.host.as_str(), ssh_config.username.as_str())
101+
.or(x.resolve(
102+
ssh_config.resolved_host.as_str(),
103+
ssh_config.username.as_str(),
104+
))
105+
}) {
106+
Some(rsa_key) => {
107+
session_auth_with_rsakey(
108+
&mut session,
109+
&ssh_config.username,
110+
rsa_key.as_path(),
111+
opts.password.as_deref(),
112+
ssh_config.params.identity_file.as_deref(),
113+
)?;
114+
}
115+
None => {
116+
session_auth_with_password(
117+
&mut session,
118+
&ssh_config.username,
119+
opts.password.as_deref(),
120+
)?;
121+
}
125122
}
126123
}
127124
// Return session
@@ -199,6 +196,58 @@ fn set_algo_prefs(session: &mut Session, opts: &SshOpts, config: &Config) -> Rem
199196
Ok(())
200197
}
201198

199+
/// Authenticate on session with ssh agent
200+
fn session_auth_with_agent(
201+
session: &mut Session,
202+
username: &str,
203+
ssh_agent_config: &SshAgentIdentity,
204+
) -> RemoteResult<()> {
205+
let mut agent = session
206+
.agent()
207+
.map_err(|err| RemoteError::new_ex(RemoteErrorType::ConnectionError, err))?;
208+
209+
agent
210+
.connect()
211+
.map_err(|err| RemoteError::new_ex(RemoteErrorType::ConnectionError, err))?;
212+
213+
agent
214+
.list_identities()
215+
.map_err(|err| RemoteError::new_ex(RemoteErrorType::ConnectionError, err))?;
216+
217+
let mut connection_result = Err(RemoteError::new(RemoteErrorType::AuthenticationFailed));
218+
219+
for identity in agent
220+
.identities()
221+
.map_err(|err| RemoteError::new_ex(RemoteErrorType::ConnectionError, err))?
222+
{
223+
if ssh_agent_config.pubkey_matches(identity.blob()) {
224+
debug!("Trying to authenticate with ssh agent with key: {identity:?}");
225+
} else {
226+
continue;
227+
}
228+
match agent.userauth(username, &identity) {
229+
Ok(()) => {
230+
connection_result = Ok(());
231+
debug!("Authenticated with ssh agent with key: {identity:?}");
232+
break;
233+
}
234+
Err(err) => {
235+
debug!("SSH agent auth failed: {err}");
236+
connection_result = Err(RemoteError::new_ex(
237+
RemoteErrorType::AuthenticationFailed,
238+
err,
239+
));
240+
}
241+
}
242+
}
243+
244+
if let Err(err) = agent.disconnect() {
245+
warn!("Could not disconnect from ssh agent: {err}");
246+
}
247+
248+
connection_result
249+
}
250+
202251
/// Authenticate on session with private key
203252
fn session_auth_with_rsakey(
204253
session: &mut Session,
@@ -334,12 +383,12 @@ pub fn perform_shell_cmd_with_rc<S: AsRef<str>>(
334383
#[cfg(test)]
335384
mod test {
336385

337-
use super::*;
338386
#[cfg(feature = "with-containers")]
339-
use crate::mock::ssh as ssh_mock;
387+
use ssh2_config::ParseRule;
340388

389+
use super::*;
341390
#[cfg(feature = "with-containers")]
342-
use ssh2_config::ParseRule;
391+
use crate::mock::ssh as ssh_mock;
343392

344393
#[test]
345394
#[cfg(feature = "with-containers")]
@@ -349,7 +398,11 @@ mod test {
349398
let opts = SshOpts::new("sftp")
350399
.config_file(config_file.path(), ParseRule::ALLOW_UNKNOWN_FIELDS)
351400
.password("password");
352-
let session = connect(&opts).ok().unwrap();
401+
402+
if let Err(err) = connect(&opts) {
403+
panic!("Could not connect to server: {}", err);
404+
}
405+
let session = connect(&opts).unwrap();
353406
assert!(session.authenticated());
354407
}
355408

@@ -361,7 +414,7 @@ mod test {
361414
let opts = SshOpts::new("sftp")
362415
.config_file(config_file.path(), ParseRule::ALLOW_UNKNOWN_FIELDS)
363416
.key_storage(Box::new(ssh_mock::MockSshKeyStorage::default()));
364-
let session = connect(&opts).ok().unwrap();
417+
let session = connect(&opts).unwrap();
365418
assert!(session.authenticated());
366419
}
367420

@@ -373,7 +426,7 @@ mod test {
373426
.port(10022)
374427
.username("sftp")
375428
.password("password");
376-
let mut session = connect(&opts).ok().unwrap();
429+
let mut session = connect(&opts).unwrap();
377430
assert!(session.authenticated());
378431
// run commands
379432
assert!(perform_shell_cmd(&mut session, "pwd").is_ok());
@@ -387,7 +440,7 @@ mod test {
387440
.port(10022)
388441
.username("sftp")
389442
.password("password");
390-
let mut session = connect(&opts).ok().unwrap();
443+
let mut session = connect(&opts).unwrap();
391444
assert!(session.authenticated());
392445
// run commands
393446
assert_eq!(

src/ssh/config.rs

Lines changed: 0 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -10,29 +10,6 @@ use std::time::Duration;
1010
use remotefs::{RemoteError, RemoteErrorType, RemoteResult};
1111
use ssh2_config::{HostParams, ParseRule, SshConfig};
1212

13-
/**
14-
* MIT License
15-
*
16-
* remotefs - Copyright (c) 2021 Christian Visintin
17-
*
18-
* Permission is hereby granted, free of charge, to any person obtaining a copy
19-
* of this software and associated documentation files (the "Software"), to deal
20-
* in the Software without restriction, including without limitation the rights
21-
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
22-
* copies of the Software, and to permit persons to whom the Software is
23-
* furnished to do so, subject to the following conditions:
24-
*
25-
* The above copyright notice and this permission notice shall be included in all
26-
* copies or substantial portions of the Software.
27-
*
28-
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
29-
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
30-
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
31-
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
32-
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
33-
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
34-
* SOFTWARE.
35-
*/
3613
use super::SshOpts;
3714

3815
/// Ssh configuration params

0 commit comments

Comments
 (0)