file-restore-daemon: add binary with virtio-vsock API server
Implements the base of a small daemon to run within a file-restore VM. The binary spawns an API server on a virtio-vsock socket, listening for connections from the host. This happens mostly manually via the standard Unix socket API, since tokio/hyper do not have support for vsock built in. Once we have the accept'ed file descriptor, we can create a UnixStream and use our tower service implementation for that. The binary is deliberately not installed in the usual $PATH location, since it shouldn't be executed on the host by a user anyway. For now, only the API calls 'status' and 'stop' are implemented, to demonstrate and test proxmox::api functionality. Authorization is provided via a custom ApiAuth only checking a header value against a static /ticket file. Since the REST server implementation uses the log!() macro, we can redirect its output to stdout by registering env_logger as the logging target. env_logger is already in our dependency tree via zstd/bindgen. Signed-off-by: Stefan Reiter <s.reiter@proxmox.com> Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
This commit is contained in:
committed by
Thomas Lamprecht
parent
26858dba84
commit
dd9cef56fc
62
src/bin/proxmox_restore_daemon/api.rs
Normal file
62
src/bin/proxmox_restore_daemon/api.rs
Normal file
@ -0,0 +1,62 @@
|
||||
///! File-restore API running inside the restore VM
|
||||
use anyhow::Error;
|
||||
use serde_json::Value;
|
||||
use std::fs;
|
||||
|
||||
use proxmox::api::{api, ApiMethod, Permission, Router, RpcEnvironment, SubdirMap};
|
||||
use proxmox::list_subdirs_api_method;
|
||||
|
||||
use proxmox_backup::api2::types::*;
|
||||
|
||||
// NOTE: All API endpoints must have Permission::Superuser, as the configs for authentication do
|
||||
// not exist within the restore VM. Safety is guaranteed by checking a ticket via a custom ApiAuth.
|
||||
|
||||
const SUBDIRS: SubdirMap = &[
|
||||
("status", &Router::new().get(&API_METHOD_STATUS)),
|
||||
("stop", &Router::new().get(&API_METHOD_STOP)),
|
||||
];
|
||||
|
||||
pub const ROUTER: Router = Router::new()
|
||||
.get(&list_subdirs_api_method!(SUBDIRS))
|
||||
.subdirs(SUBDIRS);
|
||||
|
||||
fn read_uptime() -> Result<f32, Error> {
|
||||
let uptime = fs::read_to_string("/proc/uptime")?;
|
||||
// unwrap the Option, if /proc/uptime is empty we have bigger problems
|
||||
Ok(uptime.split_ascii_whitespace().next().unwrap().parse()?)
|
||||
}
|
||||
|
||||
#[api(
|
||||
access: {
|
||||
description: "Permissions are handled outside restore VM.",
|
||||
permission: &Permission::Superuser,
|
||||
},
|
||||
returns: {
|
||||
type: RestoreDaemonStatus,
|
||||
}
|
||||
)]
|
||||
/// General status information
|
||||
fn status(
|
||||
_param: Value,
|
||||
_info: &ApiMethod,
|
||||
_rpcenv: &mut dyn RpcEnvironment,
|
||||
) -> Result<RestoreDaemonStatus, Error> {
|
||||
Ok(RestoreDaemonStatus {
|
||||
uptime: read_uptime()? as i64,
|
||||
})
|
||||
}
|
||||
|
||||
#[api(
|
||||
access: {
|
||||
description: "Permissions are handled outside restore VM.",
|
||||
permission: &Permission::Superuser,
|
||||
},
|
||||
)]
|
||||
/// Stop the restore VM immediately, this will never return if successful
|
||||
fn stop() {
|
||||
use nix::sys::reboot;
|
||||
println!("/stop called, shutting down");
|
||||
let err = reboot::reboot(reboot::RebootMode::RB_POWER_OFF).unwrap_err();
|
||||
println!("'reboot' syscall failed: {}", err);
|
||||
std::process::exit(1);
|
||||
}
|
45
src/bin/proxmox_restore_daemon/auth.rs
Normal file
45
src/bin/proxmox_restore_daemon/auth.rs
Normal file
@ -0,0 +1,45 @@
|
||||
//! Authentication via a static ticket file
|
||||
use anyhow::{bail, format_err, Error};
|
||||
|
||||
use std::fs::File;
|
||||
use std::io::prelude::*;
|
||||
|
||||
use proxmox_backup::api2::types::Authid;
|
||||
use proxmox_backup::config::cached_user_info::CachedUserInfo;
|
||||
use proxmox_backup::server::auth::{ApiAuth, AuthError};
|
||||
|
||||
const TICKET_FILE: &str = "/ticket";
|
||||
|
||||
pub struct StaticAuth {
|
||||
ticket: String,
|
||||
}
|
||||
|
||||
impl ApiAuth for StaticAuth {
|
||||
fn check_auth(
|
||||
&self,
|
||||
headers: &http::HeaderMap,
|
||||
_method: &hyper::Method,
|
||||
_user_info: &CachedUserInfo,
|
||||
) -> Result<Authid, AuthError> {
|
||||
match headers.get(hyper::header::AUTHORIZATION) {
|
||||
Some(header) if header.to_str().unwrap_or("") == &self.ticket => {
|
||||
Ok(Authid::root_auth_id().to_owned())
|
||||
}
|
||||
_ => {
|
||||
return Err(AuthError::Generic(format_err!(
|
||||
"invalid file restore ticket provided"
|
||||
)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ticket_auth() -> Result<StaticAuth, Error> {
|
||||
let mut ticket_file = File::open(TICKET_FILE)?;
|
||||
let mut ticket = String::new();
|
||||
let len = ticket_file.read_to_string(&mut ticket)?;
|
||||
if len <= 0 {
|
||||
bail!("invalid ticket: cannot be empty");
|
||||
}
|
||||
Ok(StaticAuth { ticket })
|
||||
}
|
5
src/bin/proxmox_restore_daemon/mod.rs
Normal file
5
src/bin/proxmox_restore_daemon/mod.rs
Normal file
@ -0,0 +1,5 @@
|
||||
///! File restore VM related functionality
|
||||
mod api;
|
||||
pub use api::*;
|
||||
|
||||
pub mod auth;
|
Reference in New Issue
Block a user