Skip to content

Commit

Permalink
fix(dgw)!: jet_rec claim is now a string (#957)
Browse files Browse the repository at this point in the history
Possible values are:

- `none`: No policy to enforce (recording is optional)

- `stream`: An external application (e.g.: RDM) must push the
  recording stream via a separate websocket connection

- `proxy`: Session must be recorded directly at Devolutions Gateway
  level (not implemented yet)
  • Loading branch information
CBenoit authored Aug 1, 2024
1 parent 9c776a8 commit 59bb0af
Show file tree
Hide file tree
Showing 10 changed files with 85 additions and 30 deletions.
20 changes: 16 additions & 4 deletions crates/devolutions-gateway-generators/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use devolutions_gateway::session::{ConnectionModeDetails, SessionInfo};
use devolutions_gateway::target_addr::TargetAddr;
use devolutions_gateway::token::{
self, AccessScope, ApplicationProtocol, Protocol, MAX_SUBKEY_TOKEN_VALIDITY_DURATION_SECS,
self, AccessScope, ApplicationProtocol, Protocol, RecordingPolicy, MAX_SUBKEY_TOKEN_VALIDITY_DURATION_SECS,
};
use proptest::collection::vec;
use proptest::option;
Expand Down Expand Up @@ -50,6 +50,15 @@ pub fn application_protocol() -> impl Strategy<Value = ApplicationProtocol> {
.no_shrink()
}

pub fn recording_policy() -> impl Strategy<Value = RecordingPolicy> {
prop_oneof![
Just(RecordingPolicy::None),
Just(RecordingPolicy::Stream),
Just(RecordingPolicy::Proxy),
]
.no_shrink()
}

pub fn target_addr() -> impl Strategy<Value = TargetAddr> {
"[a-z]{1,10}\\.[a-z]{1,5}:[0-9]{3,4}".prop_map(|addr| TargetAddr::parse(&addr, None).unwrap())
}
Expand Down Expand Up @@ -157,7 +166,7 @@ pub struct AssociationClaims {
pub jet_ap: ApplicationProtocol,
#[serde(flatten)]
pub jet_cm: ConnectionMode,
pub jet_rec: bool,
pub jet_rec: RecordingPolicy,
pub jet_flt: bool,
pub nbf: i64,
pub exp: i64,
Expand All @@ -174,7 +183,7 @@ pub fn any_association_claims(now: i64, validity_duration: i64) -> impl Strategy
(
uuid_typed(),
jet_ap_and_jet_cm(),
any::<bool>(),
recording_policy(),
any::<bool>(),
uuid_typed(),
)
Expand Down Expand Up @@ -239,6 +248,7 @@ pub fn any_bridge_claims(now: i64, validity_duration: i64) -> impl Strategy<Valu
#[derive(Debug, Clone, Serialize)]
pub struct JmuxClaims {
pub jet_aid: Uuid,
pub jet_rec: RecordingPolicy,
pub dst_hst: String,
#[serde(skip_serializing_if = "Vec::is_empty")]
pub dst_addl: Vec<String>,
Expand All @@ -251,13 +261,15 @@ pub struct JmuxClaims {
pub fn any_jmux_claims(now: i64, validity_duration: i64) -> impl Strategy<Value = JmuxClaims> {
(
uuid_typed(),
recording_policy(),
host(),
alternate_hosts(),
application_protocol(),
uuid_typed(),
)
.prop_map(move |(jet_aid, dst_hst, dst_addl, jet_ap, jti)| JmuxClaims {
.prop_map(move |(jet_aid, jet_rec, dst_hst, dst_addl, jet_ap, jti)| JmuxClaims {
jet_aid,
jet_rec,
dst_hst,
dst_addl,
jet_ap,
Expand Down
7 changes: 4 additions & 3 deletions devolutions-gateway/src/api/fwd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use crate::http::HttpError;
use crate::proxy::Proxy;
use crate::session::{ConnectionModeDetails, SessionInfo, SessionMessageSender};
use crate::subscriber::SubscriberSender;
use crate::token::{ApplicationProtocol, AssociationTokenClaims, ConnectionMode, Protocol};
use crate::token::{ApplicationProtocol, AssociationTokenClaims, ConnectionMode, Protocol, RecordingPolicy};
use crate::{utils, DgwState};

pub fn make_router<S>(state: DgwState) -> Router<S> {
Expand Down Expand Up @@ -144,8 +144,9 @@ where
with_tls,
} = self;

if claims.jet_rec {
anyhow::bail!("can't meet recording policy");
match claims.jet_rec {
RecordingPolicy::None | RecordingPolicy::Stream => (),
RecordingPolicy::Proxy => anyhow::bail!("can't meet recording policy"),
}

let ConnectionMode::Fwd { targets, .. } = claims.jet_cm else {
Expand Down
6 changes: 3 additions & 3 deletions devolutions-gateway/src/api/webapp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use crate::config::{WebAppAuth, WebAppConf, WebAppUser};
use crate::extract::WebAppToken;
use crate::http::HttpError;
use crate::target_addr::TargetAddr;
use crate::token::ApplicationProtocol;
use crate::token::{ApplicationProtocol, RecordingPolicy};
use crate::DgwState;

pub fn make_router<S>(state: DgwState) -> Router<S> {
Expand Down Expand Up @@ -334,7 +334,7 @@ pub(crate) async fn sign_session_token(
targets: nonempty::NonEmpty::new(destination.clone()),
creds: None,
},
jet_rec: false,
jet_rec: RecordingPolicy::None,
jet_flt: false,
jet_ttl: crate::token::SessionTtl::Unlimited,
exp,
Expand All @@ -361,7 +361,7 @@ pub(crate) async fn sign_session_token(
JmuxTokenClaims {
jet_aid: session_id,
jet_ap: protocol,
jet_rec: false,
jet_rec: RecordingPolicy::None,
hosts: nonempty::NonEmpty::new(destination.clone()),
jet_ttl: crate::token::SessionTtl::Unlimited,
exp,
Expand Down
7 changes: 4 additions & 3 deletions devolutions-gateway/src/generic_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use crate::rdp_pcb::{extract_association_claims, read_pcb};
use crate::recording::ActiveRecordings;
use crate::session::{ConnectionModeDetails, SessionInfo, SessionMessageSender};
use crate::subscriber::SubscriberSender;
use crate::token::{ConnectionMode, CurrentJrl, TokenCache};
use crate::token::{ConnectionMode, CurrentJrl, RecordingPolicy, TokenCache};
use crate::utils;

#[derive(TypedBuilder)]
Expand Down Expand Up @@ -73,8 +73,9 @@ where
anyhow::bail!("TCP rendezvous not supported");
}
ConnectionMode::Fwd { targets, creds: None } => {
if claims.jet_rec {
anyhow::bail!("can't meet recording policy");
match claims.jet_rec {
RecordingPolicy::None | RecordingPolicy::Stream => (),
RecordingPolicy::Proxy => anyhow::bail!("can't meet recording policy"),
}

trace!("Select and connect to target");
Expand Down
7 changes: 6 additions & 1 deletion devolutions-gateway/src/jmux.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use std::sync::Arc;

use crate::session::{ConnectionModeDetails, SessionInfo, SessionMessageSender};
use crate::subscriber::SubscriberSender;
use crate::token::JmuxTokenClaims;
use crate::token::{JmuxTokenClaims, RecordingPolicy};

use anyhow::Context as _;
use devolutions_gateway_task::ChildTask;
Expand All @@ -20,6 +20,11 @@ pub async fn handle(
) -> anyhow::Result<()> {
use jmux_proxy::{FilteringRule, JmuxConfig};

match claims.jet_rec {
RecordingPolicy::None | RecordingPolicy::Stream => (),
RecordingPolicy::Proxy => anyhow::bail!("can't meet recording policy"),
}

let (reader, writer) = tokio::io::split(stream);
let reader = Box::new(reader) as ErasedRead;
let writer = Box::new(writer) as ErasedWrite;
Expand Down
2 changes: 1 addition & 1 deletion devolutions-gateway/src/recording.rs
Original file line number Diff line number Diff line change
Expand Up @@ -704,7 +704,7 @@ pub async fn remux(input_path: Utf8PathBuf) {
fn remux_impl(input_path: Utf8PathBuf) -> anyhow::Result<()> {
let input_file_name = input_path
.file_name()
.ok_or(anyhow::anyhow!("input file has no file name"))?;
.ok_or_else(|| anyhow::anyhow!("invalid path (not a file): {input_path}"))?;

let remuxed_file_name = format!("remuxed_{input_file_name}");

Expand Down
1 change: 0 additions & 1 deletion devolutions-gateway/src/service.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
use anyhow::Context as _;
use camino::Utf8PathBuf;
use devolutions_gateway::config::{Conf, ConfHandle};
use devolutions_gateway::listener::GatewayListener;
use devolutions_gateway::log::GatewayLog;
Expand Down
6 changes: 3 additions & 3 deletions devolutions-gateway/src/session.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::recording::RecordingMessageSender;
use crate::subscriber;
use crate::target_addr::TargetAddr;
use crate::token::{ApplicationProtocol, SessionTtl};
use crate::token::{ApplicationProtocol, RecordingPolicy, SessionTtl};
use anyhow::Context as _;
use async_trait::async_trait;
use core::fmt;
Expand Down Expand Up @@ -52,8 +52,8 @@ impl SessionInfo {
}

#[must_use]
pub fn with_recording_policy(mut self, value: bool) -> Self {
self.recording_policy = value;
pub fn with_recording_policy(mut self, value: RecordingPolicy) -> Self {
self.recording_policy = value != RecordingPolicy::None;
self
}

Expand Down
24 changes: 19 additions & 5 deletions devolutions-gateway/src/token.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ impl AccessTokenClaims {
}
}

// ----- Known application protocols -----
// ----- Application protocols -----

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
#[serde(untagged)]
Expand Down Expand Up @@ -308,6 +308,20 @@ impl RecordingOperation {
}
}

// ----- recording policy ----- //

#[derive(Serialize, Deserialize, Default, Clone, Copy, Debug, PartialEq, Eq)]
#[serde(rename_all = "lowercase")]
pub enum RecordingPolicy {
/// No policy specified, recording is optional
#[default]
None,
/// An external application (e.g.: RDM) must push the recording stream via a separate websocket connection
Stream,
/// Session must be recorded directly at Devolutions Gateway level
Proxy,
}

// ----- association claims ----- //

#[derive(Clone)]
Expand Down Expand Up @@ -370,7 +384,7 @@ pub struct AssociationTokenClaims {
pub jet_cm: ConnectionMode,

/// Recording Policy
pub jet_rec: bool,
pub jet_rec: RecordingPolicy,

/// Filtering Policy
pub jet_flt: bool,
Expand Down Expand Up @@ -465,7 +479,7 @@ pub struct JmuxTokenClaims {
pub jet_ap: ApplicationProtocol,

/// Recording Policy
pub jet_rec: bool,
pub jet_rec: RecordingPolicy,

/// Max duration
pub jet_ttl: SessionTtl,
Expand Down Expand Up @@ -1175,7 +1189,7 @@ mod serde_impl {
#[serde(flatten)]
jet_cm: ConnectionModeHelper,
#[serde(default)]
jet_rec: bool,
jet_rec: RecordingPolicy,
#[serde(default)]
jet_flt: bool,
#[serde(default)]
Expand All @@ -1194,7 +1208,7 @@ mod serde_impl {
#[serde(default)]
jet_ap: ApplicationProtocol,
#[serde(default)]
jet_rec: bool,
jet_rec: RecordingPolicy,
jet_aid: Uuid,
#[serde(default)]
jet_ttl: SessionTtl,
Expand Down
35 changes: 29 additions & 6 deletions tools/tokengen/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,11 @@ fn main() -> Result<(), Box<dyn Error>> {
dst_hst: Some(&dst_hst),
jet_cm: "fwd",
jet_ap: jet_ap.unwrap_or(ApplicationProtocol::Unknown),
jet_rec,
jet_rec: if jet_rec {
RecordingPolicy::Stream
} else {
RecordingPolicy::None
},
jet_aid: jet_aid.unwrap_or_else(Uuid::new_v4),
jet_ttl,
jet_gw_id: app.jet_gw_id,
Expand All @@ -64,7 +68,7 @@ fn main() -> Result<(), Box<dyn Error>> {
dst_hst: Some(&dst_hst),
jet_cm: "fwd",
jet_ap: ApplicationProtocol::Rdp,
jet_rec: false,
jet_rec: RecordingPolicy::None,
jet_aid: jet_aid.unwrap_or_else(Uuid::new_v4),
jet_ttl: None,
jet_gw_id: app.jet_gw_id,
Expand All @@ -89,7 +93,11 @@ fn main() -> Result<(), Box<dyn Error>> {
dst_hst: None,
jet_cm: "rdv",
jet_ap: jet_ap.unwrap_or(ApplicationProtocol::Unknown),
jet_rec,
jet_rec: if jet_rec {
RecordingPolicy::Stream
} else {
RecordingPolicy::None
},
jet_aid: jet_aid.unwrap_or_else(Uuid::new_v4),
jet_ttl: None,
jet_gw_id: app.jet_gw_id,
Expand Down Expand Up @@ -119,7 +127,11 @@ fn main() -> Result<(), Box<dyn Error>> {
dst_hst: &dst_hst,
dst_addl: dst_addl.iter().map(|o| o.as_str()).collect(),
jet_ap: jet_ap.unwrap_or(ApplicationProtocol::Unknown),
jet_rec,
jet_rec: if jet_rec {
RecordingPolicy::Stream
} else {
RecordingPolicy::None
},
jet_aid: jet_aid.unwrap_or_else(Uuid::new_v4),
jet_ttl,
jet_gw_id: app.jet_gw_id,
Expand Down Expand Up @@ -303,7 +315,7 @@ struct AssociationClaims<'a> {
jti: Uuid,
jet_cm: &'a str,
jet_ap: ApplicationProtocol,
jet_rec: bool,
jet_rec: RecordingPolicy,
jet_aid: Uuid,
#[serde(skip_serializing_if = "Option::is_none")]
jet_ttl: Option<u64>,
Expand Down Expand Up @@ -337,7 +349,7 @@ struct JmuxClaims<'a> {
dst_hst: &'a str,
dst_addl: Vec<&'a str>,
jet_ap: ApplicationProtocol,
jet_rec: bool,
jet_rec: RecordingPolicy,
jet_aid: Uuid,
#[serde(skip_serializing_if = "Option::is_none")]
jet_ttl: Option<u64>,
Expand Down Expand Up @@ -436,6 +448,17 @@ pub enum RecordingOperation {
Pull,
}

#[derive(Serialize, Deserialize, Default, Clone, Copy, Debug, PartialEq, Eq)]
#[serde(rename_all = "lowercase")]
pub enum RecordingPolicy {
#[default]
None,
/// An external application (e.g.: RDM) must push the recording stream via a separate websocket connection
Stream,
/// Session must be recorded directly at Devolutions Gateway level
Proxy,
}

macro_rules! impl_from_str {
($ty:ty) => {
impl std::str::FromStr for $ty {
Expand Down

0 comments on commit 59bb0af

Please sign in to comment.