Skip to content

Commit

Permalink
feat(dgw): maximum session lifetime enforcing
Browse files Browse the repository at this point in the history
This adds a new claim `jet_ttl` specifying the maximum lifetime for a
given session. Devolutions Gateway will kill the session if it is still
running after the deadline.
  • Loading branch information
CBenoit committed Sep 19, 2022
1 parent 0b8a8fc commit 9b80162
Show file tree
Hide file tree
Showing 14 changed files with 322 additions and 63 deletions.
20 changes: 13 additions & 7 deletions devolutions-gateway/openapi/doc/index.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -1027,7 +1027,7 @@ Service configuration diagnostic
[#SessionInfo]
=== _SessionInfo_


Information about an ongoing Gateway session

[.fields-SessionInfo]
[cols="2,1,2,4,1"]
Expand All @@ -1037,13 +1037,13 @@ Service configuration diagnostic
| application_protocol
| X
| String
|
| Protocol used during this session
|

| association_id
| X
| UUID
|
| Unique ID for this session
| uuid

| connection_mode
Expand All @@ -1055,27 +1055,33 @@ Service configuration diagnostic
| destination_host
|
| String
|
| Destination Host
|

| filtering_policy
| X
| Boolean
|
| Filtering Policy
|

| recording_policy
| X
| Boolean
|
| Recording Policy
|

| start_timestamp
| X
| Date
|
| Date this session was started
| date-time

| time_to_live
|
| Long
| Maximum session duration in minutes (0 is used for the infinite duration)
| int64

|===


Expand Down
14 changes: 8 additions & 6 deletions devolutions-gateway/openapi/dotnet-client/docs/SessionInfo.md
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
# Devolutions.Gateway.Client.Model.SessionInfo
Information about an ongoing Gateway session

## Properties

Name | Type | Description | Notes
------------ | ------------- | ------------- | -------------
**ApplicationProtocol** | **string** | |
**AssociationId** | **Guid** | |
**ApplicationProtocol** | **string** | Protocol used during this session |
**AssociationId** | **Guid** | Unique ID for this session |
**ConnectionMode** | **ConnectionMode** | |
**DestinationHost** | **string** | | [optional]
**FilteringPolicy** | **bool** | |
**RecordingPolicy** | **bool** | |
**StartTimestamp** | **DateTime** | |
**DestinationHost** | **string** | Destination Host | [optional]
**FilteringPolicy** | **bool** | Filtering Policy |
**RecordingPolicy** | **bool** | Recording Policy |
**StartTimestamp** | **DateTime** | Date this session was started |
**TimeToLive** | **long** | Maximum session duration in minutes (0 is used for the infinite duration) | [optional]

[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)

Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
namespace Devolutions.Gateway.Client.Model
{
/// <summary>
/// SessionInfo
/// Information about an ongoing Gateway session
/// </summary>
[DataContract(Name = "SessionInfo")]
public partial class SessionInfo : IEquatable<SessionInfo>, IValidatableObject
Expand All @@ -46,14 +46,15 @@ protected SessionInfo() { }
/// <summary>
/// Initializes a new instance of the <see cref="SessionInfo" /> class.
/// </summary>
/// <param name="applicationProtocol">applicationProtocol (required).</param>
/// <param name="associationId">associationId (required).</param>
/// <param name="applicationProtocol">Protocol used during this session (required).</param>
/// <param name="associationId">Unique ID for this session (required).</param>
/// <param name="connectionMode">connectionMode (required).</param>
/// <param name="destinationHost">destinationHost.</param>
/// <param name="filteringPolicy">filteringPolicy (required).</param>
/// <param name="recordingPolicy">recordingPolicy (required).</param>
/// <param name="startTimestamp">startTimestamp (required).</param>
public SessionInfo(string applicationProtocol = default(string), Guid associationId = default(Guid), ConnectionMode connectionMode = default(ConnectionMode), string destinationHost = default(string), bool filteringPolicy = default(bool), bool recordingPolicy = default(bool), DateTime startTimestamp = default(DateTime))
/// <param name="destinationHost">Destination Host.</param>
/// <param name="filteringPolicy">Filtering Policy (required).</param>
/// <param name="recordingPolicy">Recording Policy (required).</param>
/// <param name="startTimestamp">Date this session was started (required).</param>
/// <param name="timeToLive">Maximum session duration in minutes (0 is used for the infinite duration).</param>
public SessionInfo(string applicationProtocol = default(string), Guid associationId = default(Guid), ConnectionMode connectionMode = default(ConnectionMode), string destinationHost = default(string), bool filteringPolicy = default(bool), bool recordingPolicy = default(bool), DateTime startTimestamp = default(DateTime), long timeToLive = default(long))
{
// to ensure "applicationProtocol" is required (not null)
if (applicationProtocol == null)
Expand All @@ -67,44 +68,58 @@ protected SessionInfo() { }
this.RecordingPolicy = recordingPolicy;
this.StartTimestamp = startTimestamp;
this.DestinationHost = destinationHost;
this.TimeToLive = timeToLive;
}

/// <summary>
/// Gets or Sets ApplicationProtocol
/// Protocol used during this session
/// </summary>
/// <value>Protocol used during this session</value>
[DataMember(Name = "application_protocol", IsRequired = true, EmitDefaultValue = false)]
public string ApplicationProtocol { get; set; }

/// <summary>
/// Gets or Sets AssociationId
/// Unique ID for this session
/// </summary>
/// <value>Unique ID for this session</value>
[DataMember(Name = "association_id", IsRequired = true, EmitDefaultValue = false)]
public Guid AssociationId { get; set; }

/// <summary>
/// Gets or Sets DestinationHost
/// Destination Host
/// </summary>
/// <value>Destination Host</value>
[DataMember(Name = "destination_host", EmitDefaultValue = false)]
public string DestinationHost { get; set; }

/// <summary>
/// Gets or Sets FilteringPolicy
/// Filtering Policy
/// </summary>
/// <value>Filtering Policy</value>
[DataMember(Name = "filtering_policy", IsRequired = true, EmitDefaultValue = true)]
public bool FilteringPolicy { get; set; }

/// <summary>
/// Gets or Sets RecordingPolicy
/// Recording Policy
/// </summary>
/// <value>Recording Policy</value>
[DataMember(Name = "recording_policy", IsRequired = true, EmitDefaultValue = true)]
public bool RecordingPolicy { get; set; }

/// <summary>
/// Gets or Sets StartTimestamp
/// Date this session was started
/// </summary>
/// <value>Date this session was started</value>
[DataMember(Name = "start_timestamp", IsRequired = true, EmitDefaultValue = false)]
public DateTime StartTimestamp { get; set; }

/// <summary>
/// Maximum session duration in minutes (0 is used for the infinite duration)
/// </summary>
/// <value>Maximum session duration in minutes (0 is used for the infinite duration)</value>
[DataMember(Name = "time_to_live", EmitDefaultValue = false)]
public long TimeToLive { get; set; }

/// <summary>
/// Returns the string presentation of the object
/// </summary>
Expand All @@ -120,6 +135,7 @@ public override string ToString()
sb.Append(" FilteringPolicy: ").Append(FilteringPolicy).Append("\n");
sb.Append(" RecordingPolicy: ").Append(RecordingPolicy).Append("\n");
sb.Append(" StartTimestamp: ").Append(StartTimestamp).Append("\n");
sb.Append(" TimeToLive: ").Append(TimeToLive).Append("\n");
sb.Append("}\n");
return sb.ToString();
}
Expand Down Expand Up @@ -186,6 +202,10 @@ public bool Equals(SessionInfo input)
this.StartTimestamp == input.StartTimestamp ||
(this.StartTimestamp != null &&
this.StartTimestamp.Equals(input.StartTimestamp))
) &&
(
this.TimeToLive == input.TimeToLive ||
this.TimeToLive.Equals(input.TimeToLive)
);
}

Expand Down Expand Up @@ -217,6 +237,7 @@ public override int GetHashCode()
{
hashCode = (hashCode * 59) + this.StartTimestamp.GetHashCode();
}
hashCode = (hashCode * 59) + this.TimeToLive.GetHashCode();
return hashCode;
}
}
Expand Down
12 changes: 12 additions & 0 deletions devolutions-gateway/openapi/gateway-api.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,7 @@ components:
- Rsa
SessionInfo:
type: object
description: Information about an ongoing Gateway session
required:
- association_id
- application_protocol
Expand All @@ -366,20 +367,31 @@ components:
properties:
application_protocol:
type: string
description: Protocol used during this session
association_id:
type: string
format: uuid
description: Unique ID for this session
connection_mode:
$ref: '#/components/schemas/ConnectionMode'
destination_host:
type: string
description: Destination Host
filtering_policy:
type: boolean
description: Filtering Policy
recording_policy:
type: boolean
description: Recording Policy
start_timestamp:
type: string
format: date-time
description: Date this session was started
time_to_live:
type: integer
format: int64
description: Maximum session duration in minutes (0 is used for the infinite
duration)
SubProvisionerKey:
type: object
required:
Expand Down
25 changes: 25 additions & 0 deletions devolutions-gateway/openapi/ts-angular-client/model/sessionInfo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,38 @@
import { ConnectionMode } from './connectionMode';


/**
* Information about an ongoing Gateway session
*/
export interface SessionInfo {
/**
* Protocol used during this session
*/
application_protocol: string;
/**
* Unique ID for this session
*/
association_id: string;
connection_mode: ConnectionMode;
/**
* Destination Host
*/
destination_host?: string;
/**
* Filtering Policy
*/
filtering_policy: boolean;
/**
* Recording Policy
*/
recording_policy: boolean;
/**
* Date this session was started
*/
start_timestamp: string;
/**
* Maximum session duration in minutes (0 is used for the infinite duration)
*/
time_to_live?: number;
}

1 change: 1 addition & 0 deletions devolutions-gateway/src/generic_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ impl GenericClient {
destination_host: selected_target.clone(),
},
)
.with_ttl(association_claims.jet_ttl)
.with_recording_policy(recording_policy)
.with_filtering_policy(filtering_policy);

Expand Down
2 changes: 2 additions & 0 deletions devolutions-gateway/src/jet_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ async fn handle_build_tls_proxy(
response.association_claims.jet_ap,
ConnectionModeDetails::Rdv,
)
.with_ttl(response.association_claims.jet_ttl)
.with_recording_policy(response.association_claims.jet_rec)
.with_filtering_policy(response.association_claims.jet_flt);

Expand Down Expand Up @@ -181,6 +182,7 @@ async fn handle_build_proxy(
response.association_claims.jet_ap,
ConnectionModeDetails::Rdv,
)
.with_ttl(response.association_claims.jet_ttl)
.with_recording_policy(response.association_claims.jet_rec)
.with_filtering_policy(response.association_claims.jet_flt);

Expand Down
1 change: 1 addition & 0 deletions devolutions-gateway/src/jet_rendezvous_tcp_proxy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ impl JetRendezvousTcpProxy {
}

let info = SessionInfo::new(association_id, claims.jet_ap.clone(), ConnectionModeDetails::Rdv)
.with_ttl(claims.jet_ttl)
.with_recording_policy(claims.jet_rec)
.with_filtering_policy(claims.jet_flt);

Expand Down
11 changes: 11 additions & 0 deletions devolutions-gateway/src/openapi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,15 +35,26 @@ use uuid::Uuid;
)]
pub struct ApiDoc;

/// Information about an ongoing Gateway session
#[allow(dead_code)]
#[derive(utoipa::ToSchema)]
struct SessionInfo {
/// Unique ID for this session
association_id: Uuid,
/// Protocol used during this session
application_protocol: String,
/// Recording Policy
recording_policy: bool,
/// Filtering Policy
filtering_policy: bool,
/// Date this session was started
start_timestamp: DateTime<Utc>,
/// Maximum session duration in minutes (0 is used for the infinite duration)
// NOTE: Optional purely for client code generation (this field didn't always exist)
time_to_live: Option<u64>,
/// Jet Connection Mode
connection_mode: ConnectionMode,
/// Destination Host
destination_host: Option<String>,
}

Expand Down
13 changes: 10 additions & 3 deletions devolutions-gateway/src/proxy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use crate::session::{SessionInfo, SessionManagerHandle};
use crate::subscriber::SubscriberSender;
use crate::token::{ApplicationProtocol, Protocol};
use camino::Utf8PathBuf;
use futures::future::Either;
use std::net::SocketAddr;
use std::sync::Arc;
use tokio::io::{AsyncRead, AsyncWrite};
Expand Down Expand Up @@ -109,9 +110,15 @@ where
)
.await?;

let res = tokio::select! {
res = transport::forward_bidirectional(self.transport_a, self.transport_b) => res.map(|_| ()),
_ = notify_kill.notified() => Ok(()),
let forward_fut = transport::forward_bidirectional(self.transport_a, self.transport_b);
tokio::pin!(forward_fut);

let kill_notified = notify_kill.notified();
tokio::pin!(kill_notified);

let res = match futures::future::select(forward_fut, kill_notified).await {
Either::Left((res, _)) => res.map(|_| ()),
Either::Right(_) => Ok(()),
};

crate::session::remove_session_in_progress(&self.sessions, &self.subscriber_tx, session_id).await?;
Expand Down
2 changes: 2 additions & 0 deletions devolutions-gateway/src/rdp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ impl RdpClient {
destination_host: destination_host.clone(),
},
)
.with_ttl(association_claims.jet_ttl)
.with_recording_policy(association_claims.jet_rec)
.with_filtering_policy(association_claims.jet_flt);

Expand Down Expand Up @@ -226,6 +227,7 @@ impl RdpClient {
association_claims.jet_ap,
ConnectionModeDetails::Fwd { destination_host },
)
.with_ttl(association_claims.jet_ttl)
.with_recording_policy(association_claims.jet_rec)
.with_filtering_policy(association_claims.jet_flt);

Expand Down
Loading

0 comments on commit 9b80162

Please sign in to comment.