Skip to content

Commit

Permalink
Add ssh tunnel feature (#1)
Browse files Browse the repository at this point in the history
  • Loading branch information
ccqpein authored Mar 21, 2021
1 parent cda6105 commit c26c135
Show file tree
Hide file tree
Showing 6 changed files with 247 additions and 90 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
+ `v0.8.0`
- Add SSH agent tunnel feature
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "supervisor-rs"
version = "0.7.1"
version = "0.8.0"
authors = ["ccQpein"]
edition = "2018"
description = "Manage (Start/Stop/Restart/etc.) processings on server."
Expand All @@ -12,6 +12,7 @@ readme = "README.md"
yaml-rust = "0.4"
chrono = { version = "0.4", features = ["serde"] }
openssl = "0.10.26"
ssh2 = "0.9"

[[bin]]
name = "supervisor-rs-server"
Expand Down
27 changes: 27 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,33 @@ listener_addr: "::1" # only listen local ipv6

`ipv6` field only used when there is **no** `listener_addr` given, or `supervisor-rs` server side will ignore `ipv6`. If there is no `listener_addr` given, and `ipv6` is true, `supervisor-rs` will start with listen ipv6 address `::`.

### SSH-agent tunnel feature ###

Defaultly, `supervisor-rs-server` listens `0.0.0.0` that means all servers those can reach `supervisor-rs-server`'s host can send command to `supervisor-rs-server`. Then we have [key-pair](#use-key-pairs-authenticate-clients) feature for encrypting/authorization of client's identity.

If we are in inter-network, listen `0.0.0.0` or specific ip addresses doesn't that matter. Firewall and router table can do the job for us. Beside, [key-pair](#use-key-pairs-authenticate-clients) can make sure only some people can send `supervisor-rs-server`.

However. if we deploy in some cloud services, our server open to the weird world. We may don't trust outside, and still want to ssh login host server and do our jobs. This is the reason that why this feature come.

**How to**

**Server Side:**

Don't need any additional configs for turn on this feature. For me, I just change `listener_addr` from `0.0.0.0` to `127.0.0.1` for making sure all outside computer cannot talk to `supervisor-rs-server`.

Also, make sure `supervisor-rs-client` in your server's PATH. If you install `supervisor-rs` by `cargo install supervisor-rs`, I guess you already have it.

**Client Side:**

Assume our `supervisor-rs-server` hosted on `192.168.3.3`. And we can ssh login that server with `ssh -i ~/.ssh/key [email protected]`

Then:

1. Tell ssh in your computer that `192.168.3.3` use key `~/.ssh/key` (by change `~/.ssh/config`). So you can ssh login without give which key you need to use, like `ssh [email protected]`
2. `ssh-add ~/.ssh/key` for adding key in ssh-agent.

After these, you can run `supervisor-rs-client check on ssh://[email protected]`. Every usages are same, you just need to change host (ip address) field to `ssh://{username}@{hostip}`.

### What if accident happens ###

* if supervisor-rs be killed by `kill`, children won't stop, they will be taken by system.
Expand Down
123 changes: 36 additions & 87 deletions src/bin/client.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,7 @@
use std::env;
use std::io::prelude::*;
use std::net::{IpAddr, SocketAddr, TcpStream};
use std::time::Duration;
use supervisor_rs::client::{Command, Ops};

const CANNOT_REACH_SERVER_ERROR: &'static str =
"\nLooks like client cannot reach server side, make sure you start supervisor-rs-server on host you want to reach. \
Maybe it is network problem, or even worse, server app terminated. \
If server app terminated, all children were running become zombies. Check them out.";
use std::net::IpAddr;
use std::str::FromStr;
use supervisor_rs::client::*;

fn main() {
let arguments = env::args();
Expand All @@ -20,78 +14,52 @@ fn main() {
}
};

//println!("this is command {:?}", cache_command);

if let Ops::Help = cache_command.get_ops() {
println!("{}", help());
return;
}

// build streams, parse all host
let mut streams: Vec<TcpStream> = {
let mut streams: Vec<ConnectionStream> = {
if let Some(pairs) = cache_command.prep_obj_pairs() {
//parse ip address
//only accept ip address
// parse ip address
// only accept ip address
let ip_pair = pairs.iter().filter(|x| x.0.is_on());
let addrs: Vec<IpAddr> = {
// ip address format can be "127.0.0.1" or "127.0.0.1, 127.0.0.2"
// this part need collect all addressed and *flatten* it
let addresses = ip_pair
.map(|des| {
des.1
.split(|x| x == ',' || x == ' ')
.filter(|x| *x != "")
.collect::<Vec<&str>>()
})
.flatten()
.collect::<Vec<&str>>();

// change ip to UpAddr
let mut result: Vec<IpAddr> = vec![];
for a in addresses {
match a.parse::<IpAddr>() {
Ok(ad) => result.push(ad),
Err(e) => {
println!("something wrong when parse des ip address {}: {}", a, e);
return;
// ip address format can be "127.0.0.1" or "127.0.0.1, 127.0.0.2"
// or "ssh://username@ipaddress"
// or "ssh://username@ipaddress ,ssh://username1@ipaddress1"
match ip_fields_parser(ip_pair) {
Ok(addrs) => {
let mut a = vec![];
//creat socket
for addr in addrs {
match ConnectionStream::new(addr) {
Ok(s) => a.push(s),
Err(e) => {
println!("{}", e.to_string());
return;
}
}
};
}
result
};

//dbg!(&addrs);
//creat socket
let mut _streams: Vec<TcpStream> = vec![];
for addr in addrs {
let sock = SocketAddr::new(addr, 33889);
match TcpStream::connect_timeout(&sock, Duration::new(5, 0)) {
Ok(s) => _streams.push(s),
Err(e) => {
println!("error of {}: {}; {}", addr, e, CANNOT_REACH_SERVER_ERROR);
return;
}
};
a
}
Err(e) => {
println!("{}", e.to_string());
return;
}
}
_streams
} else {
vec![]
}
};

if streams.len() == 0 {
// If don't have prep, give local address (ipv4)
let mut _streams: Vec<TcpStream> = vec![];
let sock = SocketAddr::new("127.0.0.1".parse::<IpAddr>().unwrap(), 33889);
match TcpStream::connect_timeout(&sock, Duration::new(5, 0)) {
Ok(s) => _streams.push(s),
Err(e) => {
println!("error of 127.0.0.1: {}; {}", e, CANNOT_REACH_SERVER_ERROR);
println!("Maybe you are listening on ipv6? try to use ::1 as host");
return;
}
}
streams = _streams
streams =
vec![
ConnectionStream::new(IpFields::Normal(IpAddr::from_str("127.0.0.1").unwrap()))
.unwrap(),
];
}

// Here to check/make encrypt data
Expand All @@ -103,29 +71,11 @@ fn main() {

//send same commands to all servers
for mut stream in streams {
let address = if let Ok(ad) = stream.peer_addr() {
ad.to_string()
} else {
String::from("Unknow address")
};

if let Err(e) = stream.write_all(&data_2_server) {
println!("Error from {}:\n {}", address, e);
return;
};

if let Err(e) = stream.flush() {
println!("Error from {}:\n {}", address, e);
return;
};

let mut response = String::new();
if let Err(e) = stream.read_to_string(&mut response) {
println!("Error from {}:\n {}", address, e);
return;
};

print!("Server {} response:\n{}", address, response);
print!(
"Server {} response:\n{}",
stream.address().unwrap(),
stream.send_comm(&data_2_server).unwrap()
);
}
}

Expand Down Expand Up @@ -156,7 +106,6 @@ https://github.com/ccqpein/supervisor-rs#usage
#[cfg(test)]
mod tests {
use super::*;
use supervisor_rs::client::*;

#[test]
fn ip_address_parse() {
Expand Down
Loading

0 comments on commit c26c135

Please sign in to comment.