contrary to the PDM node one or the PVE/PBS ones, this doesn't spawn a termproxy instance, but just generates the ticket used for authenticating the websocket upgrade on the PDM side.
the returned port is hard-coded as 0 to be compatible with the rest of the stack that expects one. Signed-off-by: Fabian Grünbichler <[email protected]> --- server/src/api/pve/node.rs | 83 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 80 insertions(+), 3 deletions(-) diff --git a/server/src/api/pve/node.rs b/server/src/api/pve/node.rs index 301c0b1..a1a784e 100644 --- a/server/src/api/pve/node.rs +++ b/server/src/api/pve/node.rs @@ -1,10 +1,17 @@ -use anyhow::Error; +use anyhow::{bail, format_err, Error}; +use serde_json::{json, Value}; -use proxmox_router::{list_subdirs_api_method, Permission, Router, SubdirMap}; +use proxmox_auth_api::{ + ticket::{Empty, Ticket}, + Keyring, +}; +use proxmox_router::{list_subdirs_api_method, Permission, Router, RpcEnvironment, SubdirMap}; use proxmox_schema::api; use proxmox_sortable_macro::sortable; -use pdm_api_types::{remotes::REMOTE_ID_SCHEMA, NODE_SCHEMA, PRIV_RESOURCE_AUDIT}; +use pdm_api_types::{ + remotes::REMOTE_ID_SCHEMA, Authid, NODE_SCHEMA, PRIV_RESOURCE_AUDIT, PRIV_SYS_CONSOLE, +}; use pve_api_types::StorageContent; use crate::api::pve::storage; @@ -18,6 +25,7 @@ const SUBDIRS: SubdirMap = &sorted!([ ("apt", &crate::api::remote_updates::APT_ROUTER), ("rrddata", &super::rrddata::NODE_RRD_ROUTER), ("network", &Router::new().get(&API_METHOD_GET_NETWORK)), + ("termproxy", &Router::new().post(&API_METHOD_SHELL_TICKET)), ("storage", &STORAGE_ROUTER), ("status", &Router::new().get(&API_METHOD_GET_STATUS)), ]); @@ -129,3 +137,72 @@ async fn get_status(remote: String, node: String) -> Result<pve_api_types::NodeS let result = client.node_status(&node).await?; Ok(result) } + +fn encode_term_ticket_path(remote: &str, node: &str) -> String { + format!("/shell/{remote}/{node}") +} + +#[api( + protected: true, + input: { + properties: { + remote: { schema: REMOTE_ID_SCHEMA }, + node: { + schema: NODE_SCHEMA, + }, + }, + }, + returns: { + type: Object, + description: "Object with the user and ticket", + properties: { + user: { + description: "User that obtained the VNC ticket.", + type: String, + }, + ticket: { + description: "VNC ticket used to authenticate websocket upgrade.", + type: String, + }, + port: { + description: "Always '0'.", + type: Integer, + } + } + }, + access: { + description: "Restricted to users", + permission: &Permission::Privilege(&["resource", "{remote}", "node", "{node}"], PRIV_SYS_CONSOLE, false), + } +)] +/// Call termproxy and return shell ticket +async fn shell_ticket( + remote: String, + node: String, + rpcenv: &mut dyn RpcEnvironment, +) -> Result<Value, Error> { + // intentionally user only for now + let auth_id: Authid = rpcenv + .get_auth_id() + .ok_or_else(|| format_err!("no authid available"))? + .parse()?; + + if auth_id.is_token() { + bail!("API tokens cannot access this API endpoint"); + } + + let userid = auth_id.user(); + let path = encode_term_ticket_path(&remote, &node); + + let private_auth_keyring = + Keyring::with_private_key(crate::auth::key::private_auth_key().clone()); + + let ticket = Ticket::new(crate::auth::TERM_PREFIX, &Empty)? + .sign(&private_auth_keyring, Some(&format!("{}{}", userid, path)))?; + + Ok(json!({ + "user": userid, + "ticket": ticket, + "port": 0, + })) +} -- 2.47.3 _______________________________________________ pve-devel mailing list [email protected] https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
