mirror of
https://github.com/warp-tech/warpgate.git
synced 2024-09-20 14:56:18 +08:00
fixed ssh connection deadlock
This commit is contained in:
parent
0c5e3aa36f
commit
564fc1736a
37
Cargo.lock
generated
37
Cargo.lock
generated
|
@ -649,6 +649,17 @@ version = "1.0.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "chacha20"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c7fc89c7c5b9e7a02dfe45cd2367bae382f9ed31c61ca8debe5f827c420a2f08"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cipher 0.4.3",
|
||||
"cpufeatures",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "checked_int_cast"
|
||||
version = "1.0.0"
|
||||
|
@ -2444,9 +2455,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
|
|||
|
||||
[[package]]
|
||||
name = "openssl-src"
|
||||
version = "111.20.0+1.1.1o"
|
||||
version = "111.22.0+1.1.1q"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "92892c4f87d56e376e469ace79f1128fdaded07646ddf73aa0be4706ff712dec"
|
||||
checksum = "8f31f0d509d1c1ae9cada2f9539ff8f37933831fd5098879e482aa687d659853"
|
||||
dependencies = [
|
||||
"cc",
|
||||
]
|
||||
|
@ -2818,6 +2829,17 @@ dependencies = [
|
|||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "poly1305"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8159bd90725d2df49889a078b54f4f79e87f1f8a8444194cdca81d38f5393abf"
|
||||
dependencies = [
|
||||
"cpufeatures",
|
||||
"opaque-debug",
|
||||
"universal-hash 0.5.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "polyval"
|
||||
version = "0.5.3"
|
||||
|
@ -3166,14 +3188,15 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "russh"
|
||||
version = "0.34.0-beta.8"
|
||||
version = "0.34.0-beta.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ccd8be93ee0b54a8a6b74c77ecef946185f0acbfb5234ea66666887621381e85"
|
||||
checksum = "cc91dececaa1d3d96b81bc9b471cf95d32136d1bceb4233ecbe70523175ba219"
|
||||
dependencies = [
|
||||
"aes 0.8.1",
|
||||
"aes-gcm 0.10.1",
|
||||
"bitflags",
|
||||
"byteorder",
|
||||
"chacha20",
|
||||
"ctr 0.9.1",
|
||||
"digest 0.10.3",
|
||||
"flate2",
|
||||
|
@ -3185,6 +3208,7 @@ dependencies = [
|
|||
"num-bigint",
|
||||
"once_cell",
|
||||
"openssl",
|
||||
"poly1305",
|
||||
"rand",
|
||||
"russh-cryptovec",
|
||||
"russh-keys",
|
||||
|
@ -3208,9 +3232,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "russh-keys"
|
||||
version = "0.22.0-beta.4"
|
||||
version = "0.22.0-beta.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "727c26a99d90f1b43ed80d42256c7788ccfe0ce4524634298f9bdbb1d5ceb7c6"
|
||||
checksum = "8859858504ed8ae1064d20c5989201f993969c401149976267b832ab2842e52f"
|
||||
dependencies = [
|
||||
"aes 0.8.1",
|
||||
"bcrypt-pbkdf",
|
||||
|
@ -4589,7 +4613,6 @@ dependencies = [
|
|||
"dialoguer",
|
||||
"futures",
|
||||
"notify",
|
||||
"openssl",
|
||||
"qrcode",
|
||||
"rcgen",
|
||||
"sd-notify",
|
||||
|
|
|
@ -2,7 +2,7 @@ import subprocess
|
|||
from textwrap import dedent
|
||||
|
||||
from .conftest import ProcessManager
|
||||
from .util import wait_port, wait_mysql_port, mysql_client_ssl_opt
|
||||
from .util import wait_port, wait_mysql_port, mysql_client_ssl_opt, mysql_client_opts
|
||||
|
||||
|
||||
class Test:
|
||||
|
@ -43,7 +43,7 @@ class Test:
|
|||
'127.0.0.1',
|
||||
'--port',
|
||||
str(wg_ports["mysql"]),
|
||||
'--enable-cleartext-plugin',
|
||||
*mysql_client_opts,
|
||||
mysql_client_ssl_opt,
|
||||
'db',
|
||||
],
|
||||
|
@ -63,7 +63,7 @@ class Test:
|
|||
'127.0.0.1',
|
||||
'--port',
|
||||
str(wg_ports["mysql"]),
|
||||
'--enable-cleartext-plugin',
|
||||
*mysql_client_opts,
|
||||
mysql_client_ssl_opt,
|
||||
'db',
|
||||
],
|
||||
|
|
|
@ -21,7 +21,7 @@ def wg_port(processes, ssh_port, password_123_hash):
|
|||
- name: ssh
|
||||
allow_roles: [role]
|
||||
ssh:
|
||||
host: localhost
|
||||
host: 127.0.0.1
|
||||
port: {ssh_port}
|
||||
- name: ssh-bad-domain
|
||||
allow_roles: [role]
|
||||
|
@ -69,7 +69,7 @@ class Test:
|
|||
stderr=subprocess.PIPE,
|
||||
)
|
||||
|
||||
stdout, stderr = ssh_client.communicate()
|
||||
stdout, stderr = ssh_client.communicate(timeout=10)
|
||||
assert b'stdout' == stdout
|
||||
assert stderr.endswith(b'stderr')
|
||||
|
||||
|
|
|
@ -94,16 +94,18 @@ class Test:
|
|||
|
||||
ssh_client = processes.start_ssh_client(
|
||||
'user:ssh@localhost',
|
||||
'-v',
|
||||
'-p',
|
||||
str(wg_ports['ssh']),
|
||||
'-o',
|
||||
'IdentityFile=ssh-keys/id_rsa',
|
||||
'-o',
|
||||
'PreferredAuthentications=publickey',
|
||||
'-o', 'PubkeyAcceptedKeyTypes=+ssh-rsa',
|
||||
'ls',
|
||||
'/bin/sh',
|
||||
)
|
||||
assert ssh_client.communicate()[0] == b'/bin/sh\n'
|
||||
assert ssh_client.communicate(timeout=10)[0] == b'/bin/sh\n'
|
||||
assert ssh_client.returncode == 0
|
||||
|
||||
ssh_client = processes.start_ssh_client(
|
||||
|
@ -114,8 +116,9 @@ class Test:
|
|||
'IdentityFile=ssh-keys/id_ed25519',
|
||||
'-o',
|
||||
'PreferredAuthentications=publickey',
|
||||
'-o', 'PubkeyAcceptedKeyTypes=+ssh-rsa',
|
||||
'ls',
|
||||
'/bin/sh',
|
||||
)
|
||||
assert ssh_client.communicate()[0] == b''
|
||||
assert ssh_client.communicate(timeout=10)[0] == b''
|
||||
assert ssh_client.returncode != 0
|
||||
|
|
|
@ -10,9 +10,11 @@ import time
|
|||
last_port = 1234
|
||||
|
||||
mysql_client_ssl_opt = '--ssl'
|
||||
mysql_client_opts = []
|
||||
if 'GITHUB_ACTION' in os.environ:
|
||||
# Github uses MySQL instead of MariaDB
|
||||
mysql_client_ssl_opt = '--ssl-mode=REQUIRED'
|
||||
mysql_client_opts = ['--enable-cleartext-plugin']
|
||||
|
||||
|
||||
def alloc_port():
|
||||
|
|
|
@ -120,7 +120,8 @@ impl Api {
|
|||
};
|
||||
|
||||
let mut auth_state_store = services.auth_state_store.lock().await;
|
||||
let state_arc = get_auth_state_for_request(&username, session, &mut auth_state_store).await?;
|
||||
let state_arc =
|
||||
get_auth_state_for_request(&username, session, &mut auth_state_store).await?;
|
||||
|
||||
let mut state = state_arc.lock().await;
|
||||
let mut cp = services.config_provider.lock().await;
|
||||
|
|
|
@ -35,7 +35,9 @@ use warpgate_common::{
|
|||
};
|
||||
use warpgate_web::Assets;
|
||||
|
||||
use crate::common::{endpoint_admin_auth, endpoint_auth, page_auth, COOKIE_MAX_AGE, SESSION_COOKIE_NAME};
|
||||
use crate::common::{
|
||||
endpoint_admin_auth, endpoint_auth, page_auth, COOKIE_MAX_AGE, SESSION_COOKIE_NAME,
|
||||
};
|
||||
use crate::error::error_page;
|
||||
use crate::middleware::{CookieHostMiddleware, TicketMiddleware};
|
||||
use crate::session::{SessionStore, SharedSessionStorage};
|
||||
|
|
|
@ -5,8 +5,7 @@ use tracing::*;
|
|||
use crate::session_handle::WarpgateServerHandleFromRequest;
|
||||
|
||||
pub async fn span_for_request(req: &Request) -> poem::Result<Span> {
|
||||
let handle = WarpgateServerHandleFromRequest::from_request_without_body(req)
|
||||
.await;
|
||||
let handle = WarpgateServerHandleFromRequest::from_request_without_body(req).await;
|
||||
|
||||
Ok(match handle {
|
||||
Ok(ref handle) => {
|
||||
|
|
|
@ -1,16 +1,13 @@
|
|||
#![feature(type_alias_impl_trait, let_else, try_blocks)]
|
||||
use warpgate_protocol_http::api;
|
||||
use regex::Regex;
|
||||
use poem_openapi::OpenApiService;
|
||||
use regex::Regex;
|
||||
use warpgate_protocol_http::api;
|
||||
|
||||
#[allow(clippy::unwrap_used)]
|
||||
pub fn main() {
|
||||
let api_service = OpenApiService::new(
|
||||
api::get(),
|
||||
"Warpgate HTTP proxy",
|
||||
env!("CARGO_PKG_VERSION"),
|
||||
)
|
||||
.server("/@warpgate/api");
|
||||
let api_service =
|
||||
OpenApiService::new(api::get(), "Warpgate HTTP proxy", env!("CARGO_PKG_VERSION"))
|
||||
.server("/@warpgate/api");
|
||||
|
||||
let spec = api_service.spec();
|
||||
let re = Regex::new(r"PaginatedResponse<(?P<name>\w+)>").unwrap();
|
||||
|
|
|
@ -5,7 +5,7 @@ use poem::{Endpoint, Middleware, Request};
|
|||
use serde::Deserialize;
|
||||
use warpgate_common::{authorize_ticket, Secret, Services};
|
||||
|
||||
use crate::common::{SessionExt};
|
||||
use crate::common::SessionExt;
|
||||
|
||||
pub struct TicketMiddleware {}
|
||||
|
||||
|
|
|
@ -12,8 +12,8 @@ bimap = "0.6"
|
|||
bytes = "1.2"
|
||||
dialoguer = "0.10"
|
||||
futures = "0.3"
|
||||
russh = { version = "0.34.0-beta.8", features = ["openssl"] }
|
||||
russh-keys = { version = "0.22.0-beta.4", features = ["openssl"] }
|
||||
russh = { version = "0.34.0-beta.10", features = ["vendored-openssl"] }
|
||||
russh-keys = { version = "0.22.0-beta.5", features = ["vendored-openssl"] }
|
||||
sea-orm = { version = "^0.9", features = [
|
||||
"runtime-tokio-native-tls",
|
||||
], default-features = false }
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use anyhow::Result;
|
||||
use bytes::{Bytes, BytesMut};
|
||||
use russh::client::Channel;
|
||||
use russh::client::Msg;
|
||||
use russh::Channel;
|
||||
use tokio::sync::mpsc::{UnboundedReceiver, UnboundedSender};
|
||||
use tracing::*;
|
||||
use uuid::Uuid;
|
||||
|
@ -10,7 +11,7 @@ use super::error::SshClientError;
|
|||
use crate::{ChannelOperation, RCEvent};
|
||||
|
||||
pub struct DirectTCPIPChannel {
|
||||
client_channel: Channel,
|
||||
client_channel: Channel<Msg>,
|
||||
channel_id: Uuid,
|
||||
ops_rx: UnboundedReceiver<ChannelOperation>,
|
||||
events_tx: UnboundedSender<RCEvent>,
|
||||
|
@ -19,7 +20,7 @@ pub struct DirectTCPIPChannel {
|
|||
|
||||
impl DirectTCPIPChannel {
|
||||
pub fn new(
|
||||
client_channel: Channel,
|
||||
client_channel: Channel<Msg>,
|
||||
channel_id: Uuid,
|
||||
ops_rx: UnboundedReceiver<ChannelOperation>,
|
||||
events_tx: UnboundedSender<RCEvent>,
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use anyhow::Result;
|
||||
use bytes::{Bytes, BytesMut};
|
||||
use russh::client::Channel;
|
||||
use russh::client::Msg;
|
||||
use russh::Channel;
|
||||
use tokio::sync::mpsc::{UnboundedReceiver, UnboundedSender};
|
||||
use tracing::*;
|
||||
use uuid::Uuid;
|
||||
|
@ -10,7 +11,7 @@ use super::error::SshClientError;
|
|||
use crate::{ChannelOperation, RCEvent};
|
||||
|
||||
pub struct SessionChannel {
|
||||
client_channel: Channel,
|
||||
client_channel: Channel<Msg>,
|
||||
channel_id: Uuid,
|
||||
ops_rx: UnboundedReceiver<ChannelOperation>,
|
||||
events_tx: UnboundedSender<RCEvent>,
|
||||
|
@ -19,7 +20,7 @@ pub struct SessionChannel {
|
|||
|
||||
impl SessionChannel {
|
||||
pub fn new(
|
||||
client_channel: Channel,
|
||||
client_channel: Channel<Msg>,
|
||||
channel_id: Uuid,
|
||||
ops_rx: UnboundedReceiver<ChannelOperation>,
|
||||
events_tx: UnboundedSender<RCEvent>,
|
||||
|
@ -102,6 +103,7 @@ impl SessionChannel {
|
|||
match channel_event {
|
||||
Some(russh::ChannelMsg::Data { data }) => {
|
||||
let bytes: &[u8] = &data;
|
||||
debug!("channel data: {bytes:?}");
|
||||
self.events_tx.send(RCEvent::Output(
|
||||
self.channel_id,
|
||||
Bytes::from(BytesMut::from(bytes)),
|
||||
|
@ -137,7 +139,10 @@ impl SessionChannel {
|
|||
ext,
|
||||
}).map_err(|_| SshClientError::MpscError)?;
|
||||
}
|
||||
None => {
|
||||
Some(msg) => {
|
||||
debug!("unhandled channel message: {:?}", msg);
|
||||
}
|
||||
None => {
|
||||
self.events_tx.send(RCEvent::Close(self.channel_id)).map_err(|_| SshClientError::MpscError)?;
|
||||
break
|
||||
},
|
||||
|
|
|
@ -17,7 +17,7 @@ use russh::client::Handle;
|
|||
use russh::{Preferred, Sig};
|
||||
use russh_keys::key::{self, PublicKey};
|
||||
use tokio::sync::mpsc::error::SendError;
|
||||
use tokio::sync::mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender};
|
||||
use tokio::sync::mpsc::{self, unbounded_channel, UnboundedReceiver, UnboundedSender};
|
||||
use tokio::sync::{oneshot, Mutex};
|
||||
use tokio::task::JoinHandle;
|
||||
use tracing::*;
|
||||
|
@ -84,17 +84,21 @@ pub enum RCEvent {
|
|||
ext: u32,
|
||||
},
|
||||
ConnectionError(ConnectionError),
|
||||
HostKeyReceived(PublicKey),
|
||||
HostKeyUnknown(PublicKey, oneshot::Sender<bool>),
|
||||
// ForwardedTCPIP(Uuid, DirectTCPIPParams),
|
||||
Done,
|
||||
}
|
||||
|
||||
pub type RCCommandReply = oneshot::Sender<Result<(), SshClientError>>;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum SshClientConnectionEvent {
|
||||
HostKeyReceived(PublicKey),
|
||||
HostKeyUnknown(PublicKey, oneshot::Sender<bool>),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum RCCommand {
|
||||
Connect(TargetSSHOptions),
|
||||
Connect(TargetSSHOptions, mpsc::Sender<SshClientConnectionEvent>),
|
||||
Channel(Uuid, ChannelOperation),
|
||||
// ForwardTCPIP(String, u32),
|
||||
// CancelTCPIPForward(String, u32),
|
||||
|
@ -304,26 +308,28 @@ impl RemoteClient {
|
|||
|
||||
async fn handle_command(&mut self, cmd: RCCommand) -> Result<bool, SshClientError> {
|
||||
match cmd {
|
||||
RCCommand::Connect(options) => match self.connect(options).await {
|
||||
Ok(_) => {
|
||||
self.set_state(RCState::Connected)
|
||||
.map_err(SshClientError::other)?;
|
||||
let ops = self.pending_ops.drain(..).collect::<Vec<_>>();
|
||||
for (id, op) in ops {
|
||||
self.apply_channel_op(id, op).await?;
|
||||
RCCommand::Connect(options, event_sender) => {
|
||||
match self.connect(options, event_sender).await {
|
||||
Ok(_) => {
|
||||
self.set_state(RCState::Connected)
|
||||
.map_err(SshClientError::other)?;
|
||||
let ops = self.pending_ops.drain(..).collect::<Vec<_>>();
|
||||
for (id, op) in ops {
|
||||
self.apply_channel_op(id, op).await?;
|
||||
}
|
||||
// let forwards = self.pending_forwards.drain(..).collect::<Vec<_>>();
|
||||
// for (address, port) in forwards {
|
||||
// self.tcpip_forward(address, port).await?;
|
||||
// }
|
||||
}
|
||||
Err(e) => {
|
||||
debug!("Connect error: {}", e);
|
||||
let _ = self.tx.send(RCEvent::ConnectionError(e));
|
||||
self.set_disconnected();
|
||||
return Ok(true);
|
||||
}
|
||||
// let forwards = self.pending_forwards.drain(..).collect::<Vec<_>>();
|
||||
// for (address, port) in forwards {
|
||||
// self.tcpip_forward(address, port).await?;
|
||||
// }
|
||||
}
|
||||
Err(e) => {
|
||||
debug!("Connect error: {}", e);
|
||||
let _ = self.tx.send(RCEvent::ConnectionError(e));
|
||||
self.set_disconnected();
|
||||
return Ok(true);
|
||||
}
|
||||
},
|
||||
}
|
||||
RCCommand::Channel(ch, op) => {
|
||||
self.apply_channel_op(ch, op).await?;
|
||||
}
|
||||
|
@ -335,7 +341,11 @@ impl RemoteClient {
|
|||
Ok(false)
|
||||
}
|
||||
|
||||
async fn connect(&mut self, ssh_options: TargetSSHOptions) -> Result<(), ConnectionError> {
|
||||
async fn connect(
|
||||
&mut self,
|
||||
ssh_options: TargetSSHOptions,
|
||||
event_sender: mpsc::Sender<SshClientConnectionEvent>,
|
||||
) -> Result<(), ConnectionError> {
|
||||
let address_str = format!("{}:{}", ssh_options.host, ssh_options.port);
|
||||
let address = match address_str
|
||||
.to_socket_addrs()
|
||||
|
@ -381,10 +391,10 @@ impl RemoteClient {
|
|||
Some(event) = event_rx.recv() => {
|
||||
match event {
|
||||
ClientHandlerEvent::HostKeyReceived(key) => {
|
||||
self.tx.send(RCEvent::HostKeyReceived(key)).map_err(|_| ConnectionError::Internal)?;
|
||||
event_sender.send(SshClientConnectionEvent::HostKeyReceived(key)).await.map_err(|_| ConnectionError::Internal)?;
|
||||
}
|
||||
ClientHandlerEvent::HostKeyUnknown(key, reply) => {
|
||||
self.tx.send(RCEvent::HostKeyUnknown(key, reply)).map_err(|_| ConnectionError::Internal)?;
|
||||
event_sender.send(SshClientConnectionEvent::HostKeyUnknown(key, reply)).await.map_err(|_| ConnectionError::Internal)?;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@ pub use common::*;
|
|||
pub use keys::*;
|
||||
use russh_keys::PublicKeyBase64;
|
||||
pub use server::run_server;
|
||||
use tokio::sync::oneshot;
|
||||
use tokio::sync::{oneshot, mpsc};
|
||||
use uuid::Uuid;
|
||||
use warpgate_common::{
|
||||
ProtocolName, ProtocolServer, Services, SshHostKeyVerificationMode, Target, TargetOptions,
|
||||
|
@ -56,31 +56,14 @@ impl ProtocolServer for SSHProtocolServer {
|
|||
|
||||
let mut handles = RemoteClient::create(Uuid::new_v4(), self.services.clone());
|
||||
|
||||
let _ = handles.command_tx.send((RCCommand::Connect(ssh_options), oneshot::channel().0));
|
||||
let (tx, mut rx) = mpsc::channel(10);
|
||||
let _ = handles
|
||||
.command_tx
|
||||
.send((RCCommand::Connect(ssh_options, tx), oneshot::channel().0));
|
||||
|
||||
while let Some(event) = handles.event_rx.recv().await {
|
||||
while let Some(event) = rx.recv().await {
|
||||
match event {
|
||||
RCEvent::ConnectionError(err) => {
|
||||
if let ConnectionError::HostKeyMismatch {
|
||||
ref received_key_type,
|
||||
ref received_key_base64,
|
||||
ref known_key_type,
|
||||
ref known_key_base64,
|
||||
} = err
|
||||
{
|
||||
println!("\n");
|
||||
println!("Stored key ({}): {}", known_key_type, known_key_base64);
|
||||
println!(
|
||||
"Received key ({}): {}",
|
||||
received_key_type, received_key_base64
|
||||
);
|
||||
println!("Host key doesn't match the stored one.");
|
||||
println!("If you know that the key is correct (e.g. it has been changed),");
|
||||
println!("you can remove the old key in the Warpgate management UI and try again");
|
||||
}
|
||||
return Err(TargetTestError::ConnectionError(format!("{:?}", err)));
|
||||
}
|
||||
RCEvent::HostKeyUnknown(key, reply) => {
|
||||
SshClientConnectionEvent::HostKeyUnknown(key, reply) => {
|
||||
println!("\nHost key ({}): {}", key.name(), key.public_key_base64());
|
||||
println!("There is no trusted {} key for this host.", key.name());
|
||||
|
||||
|
@ -111,6 +94,32 @@ impl ProtocolServer for SSHProtocolServer {
|
|||
}
|
||||
}
|
||||
}
|
||||
SshClientConnectionEvent::HostKeyReceived(_) => (),
|
||||
}
|
||||
}
|
||||
|
||||
while let Some(event) = handles.event_rx.recv().await {
|
||||
match event {
|
||||
RCEvent::ConnectionError(err) => {
|
||||
if let ConnectionError::HostKeyMismatch {
|
||||
ref received_key_type,
|
||||
ref received_key_base64,
|
||||
ref known_key_type,
|
||||
ref known_key_base64,
|
||||
} = err
|
||||
{
|
||||
println!("\n");
|
||||
println!("Stored key ({}): {}", known_key_type, known_key_base64);
|
||||
println!(
|
||||
"Received key ({}): {}",
|
||||
received_key_type, received_key_base64
|
||||
);
|
||||
println!("Host key doesn't match the stored one.");
|
||||
println!("If you know that the key is correct (e.g. it has been changed),");
|
||||
println!("you can remove the old key in the Warpgate management UI and try again");
|
||||
}
|
||||
return Err(TargetTestError::ConnectionError(format!("{:?}", err)));
|
||||
}
|
||||
RCEvent::State(state) => match state {
|
||||
RCState::Connected => {
|
||||
return Ok(());
|
||||
|
|
|
@ -7,7 +7,7 @@ use std::net::SocketAddr;
|
|||
use std::sync::Arc;
|
||||
|
||||
use anyhow::Result;
|
||||
use russh::MethodSet;
|
||||
use russh::{MethodSet, Preferred};
|
||||
pub use russh_handler::ServerHandler;
|
||||
pub use session::ServerSession;
|
||||
use tokio::io::{AsyncRead, AsyncWrite};
|
||||
|
@ -26,7 +26,16 @@ pub async fn run_server(services: Services, address: SocketAddr) -> Result<()> {
|
|||
connection_timeout: Some(std::time::Duration::from_secs(300)),
|
||||
methods: MethodSet::PUBLICKEY | MethodSet::PASSWORD | MethodSet::KEYBOARD_INTERACTIVE,
|
||||
keys: load_host_keys(&config)?,
|
||||
..Default::default()
|
||||
preferred: Preferred {
|
||||
key: &[
|
||||
russh_keys::key::ED25519,
|
||||
russh_keys::key::RSA_SHA2_256,
|
||||
russh_keys::key::RSA_SHA2_512,
|
||||
russh_keys::key::SSH_RSA,
|
||||
],
|
||||
..<_>::default()
|
||||
},
|
||||
..<_>::default()
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -80,8 +89,9 @@ async fn _run_stream<R>(
|
|||
handler: ServerHandler,
|
||||
) -> Result<()>
|
||||
where
|
||||
R: AsyncRead + AsyncWrite + Unpin + Debug,
|
||||
R: AsyncRead + AsyncWrite + Unpin + Debug + Send + 'static,
|
||||
{
|
||||
russh::server::run_stream(config, socket, handler).await?;
|
||||
let session = russh::server::run_stream(config, socket, handler).await?;
|
||||
session.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -40,17 +40,17 @@ impl russh::server::Handler for ServerHandler {
|
|||
async { Ok((self, s)) }.boxed()
|
||||
}
|
||||
|
||||
fn channel_open_session(self, channel: ChannelId, mut session: Session) -> Self::FutureUnit {
|
||||
fn channel_open_session(self, channel: ChannelId, mut session: Session) -> Self::FutureBool {
|
||||
async move {
|
||||
{
|
||||
let allowed = {
|
||||
let mut this_session = self.session.lock().await;
|
||||
let span = this_session.make_logging_span();
|
||||
this_session
|
||||
._channel_open_session(ServerChannelId(channel), &mut session)
|
||||
.instrument(span)
|
||||
.await?;
|
||||
}
|
||||
Ok((self, session))
|
||||
.await?
|
||||
};
|
||||
Ok((self, session, allowed))
|
||||
}
|
||||
.boxed()
|
||||
}
|
||||
|
@ -358,11 +358,11 @@ impl russh::server::Handler for ServerHandler {
|
|||
originator_address: &str,
|
||||
originator_port: u32,
|
||||
mut session: Session,
|
||||
) -> Self::FutureUnit {
|
||||
) -> Self::FutureBool {
|
||||
let host_to_connect = host_to_connect.to_string();
|
||||
let originator_address = originator_address.to_string();
|
||||
async move {
|
||||
{
|
||||
let allowed = {
|
||||
let mut this_session = self.session.lock().await;
|
||||
let span = this_session.make_logging_span();
|
||||
this_session
|
||||
|
@ -377,9 +377,9 @@ impl russh::server::Handler for ServerHandler {
|
|||
&mut session,
|
||||
)
|
||||
.instrument(span)
|
||||
.await?;
|
||||
}
|
||||
Ok((self, session))
|
||||
.await?
|
||||
};
|
||||
Ok((self, session, allowed))
|
||||
}
|
||||
.boxed()
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ use russh::server::Session;
|
|||
use russh::{CryptoVec, MethodSet, Sig};
|
||||
use russh_keys::key::PublicKey;
|
||||
use russh_keys::PublicKeyBase64;
|
||||
use tokio::sync::mpsc::{UnboundedReceiver, UnboundedSender};
|
||||
use tokio::sync::mpsc::{self, UnboundedReceiver, UnboundedSender};
|
||||
use tokio::sync::{broadcast, oneshot, Mutex};
|
||||
use tracing::*;
|
||||
use uuid::Uuid;
|
||||
|
@ -34,7 +34,8 @@ use crate::compat::ContextExt;
|
|||
use crate::server::service_output::ERASE_PROGRESS_SPINNER;
|
||||
use crate::{
|
||||
ChannelOperation, ConnectionError, DirectTCPIPParams, PtyRequest, RCCommand, RCCommandReply,
|
||||
RCEvent, RCState, RemoteClient, ServerChannelId, SshClientError, X11Request,
|
||||
RCEvent, RCState, RemoteClient, ServerChannelId, SshClientConnectionEvent, SshClientError,
|
||||
X11Request,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
|
@ -309,11 +310,28 @@ impl ServerSession {
|
|||
TargetSelection::Found(target, ssh_options) => {
|
||||
if self.rc_state == RCState::NotInitialized {
|
||||
self.rc_state = RCState::Connecting;
|
||||
self.send_command(RCCommand::Connect(ssh_options))
|
||||
let (tx, mut rx) = mpsc::channel(10);
|
||||
self.send_command(RCCommand::Connect(ssh_options, tx))
|
||||
.map_err(|_| anyhow::anyhow!("cannot send command"))?;
|
||||
self.service_output.show_progress();
|
||||
self.emit_service_message(&format!("Selected target: {}", target.name))
|
||||
.await?;
|
||||
|
||||
while let Some(event) = rx.recv().await {
|
||||
match event {
|
||||
SshClientConnectionEvent::HostKeyReceived(key) => {
|
||||
self.emit_service_message(&format!(
|
||||
"Host key ({}): {}",
|
||||
key.name(),
|
||||
key.public_key_base64()
|
||||
))
|
||||
.await?;
|
||||
}
|
||||
SshClientConnectionEvent::HostKeyUnknown(key, reply) => {
|
||||
self.handle_unknown_host_key(key, reply).await?;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -521,17 +539,6 @@ impl ServerSession {
|
|||
})
|
||||
.await?;
|
||||
}
|
||||
RCEvent::HostKeyReceived(key) => {
|
||||
self.emit_service_message(&format!(
|
||||
"Host key ({}): {}",
|
||||
key.name(),
|
||||
key.public_key_base64()
|
||||
))
|
||||
.await?;
|
||||
}
|
||||
RCEvent::HostKeyUnknown(key, reply) => {
|
||||
self.handle_unknown_host_key(key, reply).await?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
@ -623,16 +630,23 @@ impl ServerSession {
|
|||
&mut self,
|
||||
server_channel_id: ServerChannelId,
|
||||
session: &mut Session,
|
||||
) -> Result<()> {
|
||||
) -> Result<bool> {
|
||||
let channel = Uuid::new_v4();
|
||||
self.channel_map.insert(server_channel_id, channel);
|
||||
|
||||
info!(%channel, "Opening session channel");
|
||||
self.all_channels.push(channel);
|
||||
self.session_handle = Some(session.handle());
|
||||
self.send_command_and_wait(RCCommand::Channel(channel, ChannelOperation::OpenShell))
|
||||
.await?;
|
||||
Ok(())
|
||||
match self
|
||||
.send_command_and_wait(RCCommand::Channel(channel, ChannelOperation::OpenShell))
|
||||
.await
|
||||
{
|
||||
Ok(()) => {
|
||||
self.all_channels.push(channel);
|
||||
Ok(true)
|
||||
}
|
||||
Err(SshClientError::ChannelFailure) => Ok(false),
|
||||
Err(x) => Err(x.into()),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn _channel_open_direct_tcpip(
|
||||
|
@ -640,37 +654,45 @@ impl ServerSession {
|
|||
channel: ServerChannelId,
|
||||
params: DirectTCPIPParams,
|
||||
session: &mut Session,
|
||||
) -> Result<()> {
|
||||
) -> Result<bool> {
|
||||
let uuid = Uuid::new_v4();
|
||||
self.channel_map.insert(channel, uuid);
|
||||
|
||||
info!(%channel, "Opening direct TCP/IP channel from {}:{} to {}:{}", params.originator_address, params.originator_port, params.host_to_connect, params.port_to_connect);
|
||||
|
||||
let recorder = self
|
||||
.traffic_recorder_for(¶ms.host_to_connect, params.port_to_connect)
|
||||
.await;
|
||||
if let Some(recorder) = recorder {
|
||||
#[allow(clippy::unwrap_used)]
|
||||
let mut recorder = recorder.connection(TrafficConnectionParams {
|
||||
dst_addr: Ipv4Addr::from_str("2.2.2.2").unwrap(),
|
||||
dst_port: params.port_to_connect as u16,
|
||||
src_addr: Ipv4Addr::from_str("1.1.1.1").unwrap(),
|
||||
src_port: params.originator_port as u16,
|
||||
});
|
||||
if let Err(error) = recorder.write_connection_setup().await {
|
||||
error!(%channel, ?error, "Failed to record connection setup");
|
||||
}
|
||||
self.traffic_connection_recorders.insert(uuid, recorder);
|
||||
}
|
||||
|
||||
self.all_channels.push(uuid);
|
||||
self.session_handle = Some(session.handle());
|
||||
self.send_command_and_wait(RCCommand::Channel(
|
||||
uuid,
|
||||
ChannelOperation::OpenDirectTCPIP(params),
|
||||
))
|
||||
.await?;
|
||||
Ok(())
|
||||
match self
|
||||
.send_command_and_wait(RCCommand::Channel(
|
||||
uuid,
|
||||
ChannelOperation::OpenDirectTCPIP(params.clone()),
|
||||
))
|
||||
.await
|
||||
{
|
||||
Ok(()) => {
|
||||
self.all_channels.push(uuid);
|
||||
|
||||
let recorder = self
|
||||
.traffic_recorder_for(¶ms.host_to_connect, params.port_to_connect)
|
||||
.await;
|
||||
if let Some(recorder) = recorder {
|
||||
#[allow(clippy::unwrap_used)]
|
||||
let mut recorder = recorder.connection(TrafficConnectionParams {
|
||||
dst_addr: Ipv4Addr::from_str("2.2.2.2").unwrap(),
|
||||
dst_port: params.port_to_connect as u16,
|
||||
src_addr: Ipv4Addr::from_str("1.1.1.1").unwrap(),
|
||||
src_port: params.originator_port as u16,
|
||||
});
|
||||
if let Err(error) = recorder.write_connection_setup().await {
|
||||
error!(%channel, ?error, "Failed to record connection setup");
|
||||
}
|
||||
self.traffic_connection_recorders.insert(uuid, recorder);
|
||||
}
|
||||
|
||||
Ok(true)
|
||||
}
|
||||
Err(SshClientError::ChannelFailure) => Ok(false),
|
||||
Err(x) => Err(x.into()),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn _channel_pty_request(
|
||||
|
|
|
@ -19,7 +19,6 @@ dhat = {version = "0.3", optional = true}
|
|||
dialoguer = "0.10"
|
||||
futures = "0.3"
|
||||
notify = "^5.0.0-beta.1"
|
||||
openssl = {version = "0.10", features = ["vendored"]}# Embed OpenSSL
|
||||
qrcode = "0.12"
|
||||
rcgen = {version = "0.9", features = ["zeroize"]}
|
||||
serde_yaml = "0.8.23"
|
||||
|
|
Loading…
Reference in a new issue