From 4a8ecee8a12bb16cb8e7b97df6e7a43d2cd88c46 Mon Sep 17 00:00:00 2001 From: Vladyslav Nikonov Date: Tue, 18 Jun 2024 12:46:16 +0300 Subject: [PATCH 1/3] feat(gateway): Add API to trigger Gateway update --- Cargo.lock | 1 + devolutions-gateway/Cargo.toml | 1 + devolutions-gateway/openapi/gateway-api.yaml | Bin 21360 -> 44190 bytes .../openapi/subscriber-api.yaml | Bin 2452 -> 4910 bytes devolutions-gateway/src/api/mod.rs | 4 +- devolutions-gateway/src/api/update.rs | 63 ++++++++++++++++++ devolutions-gateway/src/extract.rs | 19 ++++++ devolutions-gateway/src/openapi.rs | 2 + devolutions-gateway/src/token.rs | 2 + .../src/AccessScope.cs | 1 + 10 files changed, 92 insertions(+), 1 deletion(-) create mode 100644 devolutions-gateway/src/api/update.rs diff --git a/Cargo.lock b/Cargo.lock index 57d521549..98e0899b4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -957,6 +957,7 @@ dependencies = [ "camino", "ceviche", "cfg-if", + "devolutions-agent-shared", "devolutions-gateway-generators", "devolutions-gateway-task", "devolutions-log", diff --git a/devolutions-gateway/Cargo.toml b/devolutions-gateway/Cargo.toml index 1b9755747..21526c5b2 100644 --- a/devolutions-gateway/Cargo.toml +++ b/devolutions-gateway/Cargo.toml @@ -18,6 +18,7 @@ openapi = ["dep:utoipa"] # In-house transport = { path = "../crates/transport" } jmux-proxy = { path = "../crates/jmux-proxy" } +devolutions-agent-shared = { path = "../crates/devolutions-agent-shared" } devolutions-gateway-task = { path = "../crates/devolutions-gateway-task" } devolutions-log = { path = "../crates/devolutions-log" } ironrdp-pdu = { version = "0.1", git = "https://github.com/Devolutions/IronRDP", rev = "4844e77b7f65024d85ba74b1824013eda6eb32b2" } diff --git a/devolutions-gateway/openapi/gateway-api.yaml b/devolutions-gateway/openapi/gateway-api.yaml index 4180e7f22466559ea9656eeb13b42ea0349ef9da..e5e9a771362c7c4e1e8f0e21d44266f8200f0b03 100644 GIT binary patch literal 44190 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 JQTZXm{|8o^anJw& literal 21360 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 xMQP4oEU>(state: crate::DgwState) -> axum::Router { @@ -27,7 +28,8 @@ pub fn make_router(state: crate::DgwState) -> axum::Router { .route("/jet/rdp", axum::routing::get(rdp::handler)) .nest("/jet/fwd", fwd::make_router(state.clone())) .nest("/jet/webapp", webapp::make_router(state.clone())) - .nest("/jet/net", net::make_router(state.clone())); + .nest("/jet/net", net::make_router(state.clone())) + .route("/jet/update", axum::routing::get(update::start_update)); if state.conf_handle.get_conf().web_app.enabled { router = router.route( diff --git a/devolutions-gateway/src/api/update.rs b/devolutions-gateway/src/api/update.rs new file mode 100644 index 000000000..7b1ee23c4 --- /dev/null +++ b/devolutions-gateway/src/api/update.rs @@ -0,0 +1,63 @@ +use axum::extract::Query; +use axum::Json; +use hyper::StatusCode; + +use devolutions_agent_shared::{get_updater_file_path, ProductUpdateInfo, UpdateJson, VersionSpecification}; + +use crate::extract::UpdateScope; +use crate::http::{HttpError, HttpErrorBuilder}; + +#[derive(Deserialize)] +#[serde(rename_all = "camelCase")] +pub(crate) struct UpdateQueryParam { + version: VersionSpecification, +} + +#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))] +#[derive(Serialize)] +pub(crate) struct UpdateResponse {} + +/// Starts Devolutions Gateway update process +#[cfg_attr(feature = "openapi", utoipa::path( + get, + operation_id = "Update", + tag = "Update", + path = "/jet/update", + responses( + (status = 200, description = "Update request has been processed successfully", body = UpdateResponse), + (status = 400, description = "Bad request"), + (status = 401, description = "Invalid or missing authorization token"), + (status = 403, description = "Insufficient permissions"), + (status = 500, description = "Agent updater service is malfunctioning"), + (status = 503, description = "Agent updater service is unavailable"), + ), + security(("scope_token" = ["gateway.update"])), +))] +pub(super) async fn start_update( + Query(query): Query, + _scope: UpdateScope, +) -> Result, HttpError> { + let target_version = query.version; + + let updater_file_path = get_updater_file_path(); + + if !updater_file_path.exists() { + error!("Failed to start Gateway update, `update.json` does not exist (should be created by Devolutions Agent)"); + + return Err(HttpErrorBuilder::new(StatusCode::SERVICE_UNAVAILABLE).msg("Agent updater service is unavailable")); + } + + let update_json = UpdateJson { + gateway: Some(ProductUpdateInfo { target_version }), + }; + + serde_json::to_string(&update_json) + .ok() + .and_then(|serialized| std::fs::write(updater_file_path, serialized).ok()) + .ok_or_else(|| { + error!("Failed to write new Gateway version to `update.json`"); + HttpErrorBuilder::new(StatusCode::INTERNAL_SERVER_ERROR).msg("Agent updater service is unavailable") + })?; + + Ok(Json(UpdateResponse {})) +} diff --git a/devolutions-gateway/src/extract.rs b/devolutions-gateway/src/extract.rs index 770792475..e1d97d1d3 100644 --- a/devolutions-gateway/src/extract.rs +++ b/devolutions-gateway/src/extract.rs @@ -294,6 +294,25 @@ where } } +#[derive(Clone, Copy)] +pub struct UpdateScope; + +#[async_trait] +impl FromRequestParts for UpdateScope +where + S: Send + Sync, +{ + type Rejection = HttpError; + + async fn from_request_parts(parts: &mut Parts, state: &S) -> Result { + match ScopeToken::from_request_parts(parts, state).await?.0.scope { + AccessScope::Wildcard => Ok(Self), + AccessScope::Update => Ok(Self), + _ => Err(HttpError::forbidden().msg("invalid scope for route")), + } + } +} + #[derive(Clone)] pub struct WebAppToken(pub WebAppTokenClaims); diff --git a/devolutions-gateway/src/openapi.rs b/devolutions-gateway/src/openapi.rs index e62b313cb..d493faeaf 100644 --- a/devolutions-gateway/src/openapi.rs +++ b/devolutions-gateway/src/openapi.rs @@ -20,6 +20,7 @@ use uuid::Uuid; crate::api::jrec::pull_recording_file, crate::api::webapp::sign_app_token, crate::api::webapp::sign_session_token, + crate::api::update::start_update, // crate::api::net::get_net_config, ), components(schemas( @@ -39,6 +40,7 @@ use uuid::Uuid; crate::token::AccessScope, crate::api::webapp::AppTokenSignRequest, crate::api::webapp::AppTokenContentType, + crate::api::update::UpdateResponse, // crate::api::net::NetworkInterface, SessionTokenContentType, SessionTokenSignRequest, diff --git a/devolutions-gateway/src/token.rs b/devolutions-gateway/src/token.rs index 9870f66e4..b841f7627 100644 --- a/devolutions-gateway/src/token.rs +++ b/devolutions-gateway/src/token.rs @@ -410,6 +410,8 @@ pub enum AccessScope { RecordingDelete, #[serde(rename = "gateway.recordings.read")] RecordingsRead, + #[serde(rename = "gateway.update")] + Update, } #[derive(Clone, Deserialize)] diff --git a/utils/dotnet/Devolutions.Gateway.Utils/src/AccessScope.cs b/utils/dotnet/Devolutions.Gateway.Utils/src/AccessScope.cs index d1812655b..53af11428 100644 --- a/utils/dotnet/Devolutions.Gateway.Utils/src/AccessScope.cs +++ b/utils/dotnet/Devolutions.Gateway.Utils/src/AccessScope.cs @@ -22,6 +22,7 @@ internal AccessScope(string value) public static AccessScope GatewayHeartbeatRead = new AccessScope("gateway.heartbeat.read"); public static AccessScope GatewayRecordingDelete = new AccessScope("gateway.recording.delete"); public static AccessScope GatewayRecordingsRead = new AccessScope("gateway.recordings.read"); + public static AccessScope GatewayUpdate = new AccessScope("gateway.update"); public override string? ToString() { From bfe3e540ce2f2e528597af0a1f4234e9804eaf20 Mon Sep 17 00:00:00 2001 From: Vladyslav Nikonov Date: Fri, 21 Jun 2024 16:18:19 +0300 Subject: [PATCH 2/3] Refactoring --- devolutions-gateway/openapi/gateway-api.yaml | Bin 44190 -> 21657 bytes .../openapi/subscriber-api.yaml | Bin 4910 -> 2367 bytes devolutions-gateway/src/api/mod.rs | 2 +- devolutions-gateway/src/api/update.rs | 32 +++++++++++------- devolutions-gateway/src/openapi.rs | 2 +- 5 files changed, 21 insertions(+), 15 deletions(-) diff --git a/devolutions-gateway/openapi/gateway-api.yaml b/devolutions-gateway/openapi/gateway-api.yaml index e5e9a771362c7c4e1e8f0e21d44266f8200f0b03..89132719b8f0ce3597fd006f2d0bc6dc80493306 100644 GIT binary patch literal 21657 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 literal 44190 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 JQTZXm{|8o^anJw& diff --git a/devolutions-gateway/openapi/subscriber-api.yaml b/devolutions-gateway/openapi/subscriber-api.yaml index 55babd7a400e5ae7e16a3c76d8d9b1226cf50f33..396058b5d2b834aa266b098794f4ae442d716cff 100644 GIT binary patch literal 2367 zcmb7GO>f&U487-95ZHj6oMdTFIjuv7AsB`Y=x)OhC^ns_wIoj;DY9QbQnqY)O@=j> zq!#t~_#R2uw5UZZH*m99EpBE~*Jd*V@X|N9feKHi30@lQii7a@EY5`sdskYyM@w-P z^6!=-HgNZC3j!S4lPnQba2}*;8&s(2mj+`iwBQX`EK!~i?nij|)WL%`Ue>Y{F)3vz zjrOASObS2?MXd5`TU{>V5h@{@4W+4#d!O-MX!I1&5GCy}hxxMozPxKidBkG9SW(Og zt&4@MSL@rwn&fDOKRUj%tPao1vGNY4HLmAHf{8;6WR?Xyi&>VBjvVg68H-9H;gpy@19)q(-oX6LvNWnSnh@Qxi*(CT zl|K1=0A#+>BGuw;NHtn?1mSQH7GS$^oOEsnbszB%p+D>t-$b^!$uS3(Ehs^Wa ze<6MlRWd5S@$Nc@tv-oHR%9zE=^VQ%fGIgJ)R%Ev^w}6qG|ksKhcEs!!9$0&rR6mypmj%`D)zjHv#B8Y zyXajb$Nj_UJHdsuVr*{!=}~19#DV`;^%YVR5BnHeue9uibEcHE$17g){wN(p|1yQH zR5VzZi`SLoyrmh+AZlZk@bqq?LbZ~4QCAn9vb=xs%-l)n7Y6R- z$Rv~KMYoe#e&(|qeCT;y8lfpb%02eqRl`CU@=*^@^SuHl_dRW|2Ywf&Ie)Rh`eHhH nj^3vQ5!V5)^_&E_Zi!=IwhkOa1##9X4Se)?uRSB0LXOI15MNMBlUUWB5VO*5NAD>Y-KX z)9P6pmYT)rnXb3$Uxu65Q)_$^4#P)TY}NOtu79cBsQ-h0r(vu4AJntGvxp7v+1y#d z`pfVxoa;K#w~2FZHG8VNDy(F&(dbG~R`MQ?R{E}Y&(`XxwF=g^x^LC0bT^Ol7xEe_ zuk}0jzY1>#{#UYEN6)U}`oy--6Jm8-rm@{>-c-FEUSI~k_(n{n70)$lD%{;3^N47o zm6ma)??n?{6mUP1jk#8L8_Vb|n1Ei{r?*1+eK;20o9O?wMjwXL$ON|qUR&9*xQXOc zJUI=|G>VZ=SS zV@_T{o2a%^&2jjom5HuA6AO3;7g53eBN_dZc4V%70j-1ZM0~?basw-wr%hyGXO(u2 zJ3|BxM*GBij`bqm*)FxZ2mKUp#x^@o&|;3q*gS};I?=Tp@4ow%X1kf}!#l1U`Pngl z9-pC)sXlnu2*(@k+fwXFV=+IUN&S!Y^OgL-i=o!a>pXoV9^}+ptp)#B4=uCmB1VW7 zQSwBQlCwyzTiptTf>7p9AIJ6MY}Y zmoz)&l;E|Gj91HB_>=AwjGn5GOxb4h4LO=v(=))O#@E3RMz_C(-=p=Mayv!z^Fpem zTns}scXk{_d>|)I4Wy`PKNhOWZ{Ua|d z!>{6XIR;SsmT_D}MBO*?6ROI`fuC#f1TQCIrJuP=HHPuS*e^AQT$ejfISPI5!%fZ} z`JB%T^i4fZRp-|H`hQ|pR_PG^>JA-h05y+_xX?W@qFH-qpf-Ra=W$nOiBn&?Ut>j< zx!B(+_LMwA1uOXiv#27(LuQ-pB0E&u@<3^38Nt^(8{J95ZGo2KG_31YdG%NPk ze>iWu>c$KZH8R2}!LPpQ%jl}jfB&ulxKM zW8gd8$Ko*>yb(`wkz&8&MK03s=TkW^P)+*hx(l`JeQbO#c=&YJ=Ogj(state: crate::DgwState) -> axum::Router { .nest("/jet/fwd", fwd::make_router(state.clone())) .nest("/jet/webapp", webapp::make_router(state.clone())) .nest("/jet/net", net::make_router(state.clone())) - .route("/jet/update", axum::routing::get(update::start_update)); + .route("/jet/update", axum::routing::post(update::trigger_update_check)); if state.conf_handle.get_conf().web_app.enabled { router = router.route( diff --git a/devolutions-gateway/src/api/update.rs b/devolutions-gateway/src/api/update.rs index 7b1ee23c4..056520e6f 100644 --- a/devolutions-gateway/src/api/update.rs +++ b/devolutions-gateway/src/api/update.rs @@ -17,9 +17,13 @@ pub(crate) struct UpdateQueryParam { #[derive(Serialize)] pub(crate) struct UpdateResponse {} -/// Starts Devolutions Gateway update process +/// Triggers Devolutions Gateway update process. +/// +/// This is done via updating `Agent/update.json` file, which is then read by Devolutions Agent +/// when changes are detected. If the version written to `update.json` is indeed higher than the +/// currently installed version, Devolutions Agent will proceed with the update process. #[cfg_attr(feature = "openapi", utoipa::path( - get, + post, operation_id = "Update", tag = "Update", path = "/jet/update", @@ -33,7 +37,7 @@ pub(crate) struct UpdateResponse {} ), security(("scope_token" = ["gateway.update"])), ))] -pub(super) async fn start_update( +pub(super) async fn trigger_update_check( Query(query): Query, _scope: UpdateScope, ) -> Result, HttpError> { @@ -42,22 +46,24 @@ pub(super) async fn start_update( let updater_file_path = get_updater_file_path(); if !updater_file_path.exists() { - error!("Failed to start Gateway update, `update.json` does not exist (should be created by Devolutions Agent)"); - - return Err(HttpErrorBuilder::new(StatusCode::SERVICE_UNAVAILABLE).msg("Agent updater service is unavailable")); + return Err(HttpErrorBuilder::new(StatusCode::SERVICE_UNAVAILABLE).msg("Agent updater service is not installed")); } let update_json = UpdateJson { gateway: Some(ProductUpdateInfo { target_version }), }; - serde_json::to_string(&update_json) - .ok() - .and_then(|serialized| std::fs::write(updater_file_path, serialized).ok()) - .ok_or_else(|| { - error!("Failed to write new Gateway version to `update.json`"); - HttpErrorBuilder::new(StatusCode::INTERNAL_SERVER_ERROR).msg("Agent updater service is unavailable") - })?; + let update_json = serde_json::to_string(&update_json).map_err( + HttpError::internal() + .with_msg("failed to serialize the update manifest") + .err(), + )?; + + std::fs::write(updater_file_path, update_json).map_err( + HttpError::internal() + .with_msg("failed to write the new `update.json` manifest on disk") + .err(), + )?; Ok(Json(UpdateResponse {})) } diff --git a/devolutions-gateway/src/openapi.rs b/devolutions-gateway/src/openapi.rs index d493faeaf..9f76f8156 100644 --- a/devolutions-gateway/src/openapi.rs +++ b/devolutions-gateway/src/openapi.rs @@ -20,7 +20,7 @@ use uuid::Uuid; crate::api::jrec::pull_recording_file, crate::api::webapp::sign_app_token, crate::api::webapp::sign_session_token, - crate::api::update::start_update, + crate::api::update::trigger_update_check, // crate::api::net::get_net_config, ), components(schemas( From 4ceee29d29da4c14394291f1f716559224f23741 Mon Sep 17 00:00:00 2001 From: Vladyslav Nikonov Date: Tue, 25 Jun 2024 10:11:29 +0300 Subject: [PATCH 3/3] Fixed formatting and reverted openapi --- devolutions-gateway/openapi/gateway-api.yaml | 51 +++++++------------ .../openapi/subscriber-api.yaml | 3 +- devolutions-gateway/src/api/update.rs | 4 +- 3 files changed, 22 insertions(+), 36 deletions(-) diff --git a/devolutions-gateway/openapi/gateway-api.yaml b/devolutions-gateway/openapi/gateway-api.yaml index 89132719b..4180e7f22 100644 --- a/devolutions-gateway/openapi/gateway-api.yaml +++ b/devolutions-gateway/openapi/gateway-api.yaml @@ -7,13 +7,14 @@ info: email: infos@devolutions.net license: name: MIT/Apache-2.0 - version: 2024.2.3 + version: 2024.1.5 paths: /jet/config: patch: tags: - Config summary: Modifies configuration + description: Modifies configuration operationId: PatchConfig requestBody: description: JSON-encoded configuration patch @@ -42,6 +43,8 @@ paths: - Diagnostics summary: Retrieves server's clock in order to diagnose clock drifting. description: |- + Retrieves server's clock in order to diagnose clock drifting. + This route is not secured by access token. Indeed, this route is used to retrieve server's clock when diagnosing clock drifting. If there is clock drift, token validation will fail because claims such as `nbf` will then @@ -60,6 +63,8 @@ paths: - Diagnostics summary: Retrieves a subset of the configuration, for diagnosis purposes. description: |- + Retrieves a subset of the configuration, for diagnosis purposes. + This route primary function is to help with configuration diagnosis (e.g.: ID mismatch, hostname mismatch, outdated version). In addition, it may be used to retrieve the listener URLs. This information can be used to provide configuration auto-filling, in order to assist the end user. @@ -88,6 +93,7 @@ paths: tags: - Diagnostics summary: Retrieves latest logs. + description: Retrieves latest logs. operationId: GetLogs responses: '200': @@ -112,6 +118,7 @@ paths: tags: - Health summary: Performs a health check + description: Performs a health check operationId: GetHealth responses: '200': @@ -127,6 +134,7 @@ paths: tags: - Heartbeat summary: Performs a heartbeat check + description: Performs a heartbeat check operationId: GetHeartbeat responses: '200': @@ -149,6 +157,7 @@ paths: tags: - Jrec summary: Lists all recordings stored on this instance + description: Lists all recordings stored on this instance operationId: ListRecordings responses: '200': @@ -174,6 +183,7 @@ paths: tags: - Jrec summary: Retrieves a recording file for a given session + description: Retrieves a recording file for a given session operationId: PullRecordingFile parameters: - name: id @@ -213,6 +223,7 @@ paths: tags: - Jrl summary: Updates JRL (Json Revocation List) using a JRL token + description: Updates JRL (Json Revocation List) using a JRL token operationId: UpdateJrl responses: '200': @@ -232,6 +243,7 @@ paths: tags: - Jrl summary: Retrieves current JRL (Json Revocation List) info + description: Retrieves current JRL (Json Revocation List) info operationId: GetJrlInfo responses: '200': @@ -256,6 +268,7 @@ paths: tags: - Sessions summary: Terminate forcefully a running session + description: Terminate forcefully a running session operationId: TerminateSession parameters: - name: id @@ -286,6 +299,7 @@ paths: tags: - Sessions summary: Lists running sessions + description: Lists running sessions operationId: GetSessions responses: '200': @@ -307,41 +321,12 @@ paths: security: - scope_token: - gateway.sessions.read - /jet/update: - post: - tags: - - Update - summary: Triggers Devolutions Gateway update process. - description: |- - This is done via updating `Agent/update.json` file, which is then read by Devolutions Agent - when changes are detected. If the version written to `update.json` is indeed higher than the - currently installed version, Devolutions Agent will proceed with the update process. - operationId: Update - responses: - '200': - description: Update request has been processed successfully - content: - application/json: - schema: - $ref: '#/components/schemas/UpdateResponse' - '400': - description: Bad request - '401': - description: Invalid or missing authorization token - '403': - description: Insufficient permissions - '500': - description: Agent updater service is malfunctioning - '503': - description: Agent updater service is unavailable - security: - - scope_token: - - gateway.update /jet/webapp/app-token: post: tags: - WebApp summary: Requests a web application token using the configured authorization method + description: Requests a web application token using the configured authorization method operationId: SignAppToken requestBody: description: JSON-encoded payload specifying the desired claims @@ -373,6 +358,7 @@ paths: tags: - WebApp summary: Requests a session token using a web application token + description: Requests a session token using a web application token operationId: SignSessionToken requestBody: description: JSON-encoded payload specifying the desired claims @@ -413,7 +399,6 @@ components: - gateway.heartbeat.read - gateway.recording.delete - gateway.recordings.read - - gateway.update AppTokenContentType: type: string enum: @@ -726,8 +711,6 @@ components: Url: type: string description: HTTP URL where notification messages are to be sent - UpdateResponse: - type: object securitySchemes: jrec_token: type: http diff --git a/devolutions-gateway/openapi/subscriber-api.yaml b/devolutions-gateway/openapi/subscriber-api.yaml index 396058b5d..327c09fa3 100644 --- a/devolutions-gateway/openapi/subscriber-api.yaml +++ b/devolutions-gateway/openapi/subscriber-api.yaml @@ -7,13 +7,14 @@ info: email: infos@devolutions.net license: name: MIT/Apache-2.0 - version: 2024.2.3 + version: 2024.1.5 paths: /dgw/subscriber: post: tags: - Subscriber summary: Process a message originating from a Devolutions Gateway instance + description: Process a message originating from a Devolutions Gateway instance operationId: PostMessage requestBody: description: Message diff --git a/devolutions-gateway/src/api/update.rs b/devolutions-gateway/src/api/update.rs index 056520e6f..2e55112c7 100644 --- a/devolutions-gateway/src/api/update.rs +++ b/devolutions-gateway/src/api/update.rs @@ -46,7 +46,9 @@ pub(super) async fn trigger_update_check( let updater_file_path = get_updater_file_path(); if !updater_file_path.exists() { - return Err(HttpErrorBuilder::new(StatusCode::SERVICE_UNAVAILABLE).msg("Agent updater service is not installed")); + return Err( + HttpErrorBuilder::new(StatusCode::SERVICE_UNAVAILABLE).msg("Agent updater service is not installed") + ); } let update_json = UpdateJson {