1 ///! Daemon binary to run inside a micro-VM for secure single file restore of disk images
3 use std
::io
::prelude
::*;
5 io
::{FromRawFd, RawFd}
,
9 use std
::sync
::{Arc, Mutex}
;
11 use anyhow
::{bail, format_err, Error}
;
12 use lazy_static
::lazy_static
;
13 use log
::{error, info}
;
14 use tokio
::sync
::mpsc
;
15 use tokio_stream
::wrappers
::ReceiverStream
;
17 use proxmox_router
::RpcEnvironmentType
;
19 use pbs_client
::DEFAULT_VSOCK_PORT
;
20 use proxmox_rest_server
::{ApiConfig, RestServer}
;
22 mod proxmox_restore_daemon
;
23 use proxmox_restore_daemon
::*;
25 /// Maximum amount of pending requests. If saturated, virtio-vsock returns ETIMEDOUT immediately.
26 /// We should never have more than a few requests in queue, so use a low number.
27 pub const MAX_PENDING
: usize = 32;
29 /// Will be present in base initramfs
30 pub const VM_DETECT_FILE
: &str = "/restore-vm-marker";
33 /// The current disks state. Use for accessing data on the attached snapshots.
34 pub static ref DISK_STATE
: Arc
<Mutex
<DiskState
>> = {
35 Arc
::new(Mutex
::new(DiskState
::scan().unwrap()))
39 /// This is expected to be run by 'proxmox-file-restore' within a mini-VM
40 fn main() -> Result
<(), Error
> {
41 if !Path
::new(VM_DETECT_FILE
).exists() {
43 "This binary is not supposed to be run manually, use 'proxmox-file-restore' instead."
47 // don't have a real syslog (and no persistance), so use env_logger to print to a log file (via
48 // stdout to a serial terminal attached by QEMU)
49 env_logger
::Builder
::from_env(env_logger
::Env
::default().default_filter_or("info"))
50 .write_style(env_logger
::WriteStyle
::Never
)
51 .format_timestamp_millis()
54 info
!("setup basic system environment...");
55 setup_system_env().map_err(|err
| format_err
!("system environment setup failed: {}", err
))?
;
57 // scan all attached disks now, before starting the API
58 // this will panic and stop the VM if anything goes wrong
59 info
!("scanning all disks...");
61 let _disk_state
= DISK_STATE
.lock().unwrap();
64 info
!("disk scan complete, starting main runtime...");
66 proxmox_async
::runtime
::main(run())
69 /// ensure we have our /run dirs, system users and stuff like that setup
70 fn setup_system_env() -> Result
<(), Error
> {
71 // the API may save some stuff there, e.g., the memcon tracking file
72 // we do not care much, but it's way less headache to just create it
73 std
::fs
::create_dir_all("/run/proxmox-backup")?
;
75 // we now ensure that all lock files are owned by the backup user, and as we reuse the
76 // specialized REST module from pbs api/daemon we have some checks there for user/acl stuff
77 // that gets locked, and thus needs the backup system user to work.
78 std
::fs
::create_dir_all("/etc")?
;
79 let mut passwd
= File
::create("/etc/passwd")?
;
80 writeln
!(passwd
, "root:x:0:0:root:/root:/bin/sh")?
;
81 writeln
!(passwd
, "backup:x:34:34:backup:/var/backups:/usr/sbin/nologin")?
;
83 let mut group
= File
::create("/etc/group")?
;
84 writeln
!(group
, "root:x:0:")?
;
85 writeln
!(group
, "backup:x:34:")?
;
91 async
fn run() -> Result
<(), Error
> {
94 let adaptor
= StaticAuthAdapter
::new()
95 .map_err(|err
| format_err
!("reading ticket file failed: {}", err
))?
;
97 let config
= ApiConfig
::new("", &ROUTER
, RpcEnvironmentType
::PUBLIC
, adaptor
)?
;
98 let rest_server
= RestServer
::new(config
);
100 let vsock_fd
= get_vsock_fd()?
;
101 let connections
= accept_vsock_connections(vsock_fd
);
102 let receiver_stream
= ReceiverStream
::new(connections
);
103 let acceptor
= hyper
::server
::accept
::from_stream(receiver_stream
);
105 hyper
::Server
::builder(acceptor
).serve(rest_server
).await?
;
107 bail
!("hyper server exited");
110 fn accept_vsock_connections(
112 ) -> mpsc
::Receiver
<Result
<tokio
::net
::UnixStream
, Error
>> {
113 use nix
::sys
::socket
::*;
114 let (sender
, receiver
) = mpsc
::channel(MAX_PENDING
);
116 tokio
::spawn(async
move {
118 let stream
: Result
<tokio
::net
::UnixStream
, Error
> = tokio
::task
::block_in_place(|| {
119 // we need to accept manually, as UnixListener aborts if socket type != AF_UNIX ...
120 let client_fd
= accept(vsock_fd
)?
;
121 let stream
= unsafe { net::UnixStream::from_raw_fd(client_fd) }
;
122 stream
.set_nonblocking(true)?
;
123 tokio
::net
::UnixStream
::from_std(stream
).map_err(|err
| err
.into())
128 if sender
.send(Ok(stream
)).await
.is_err() {
129 error
!("connection accept channel was closed");
133 error
!("error accepting vsock connetion: {}", err
);
142 fn get_vsock_fd() -> Result
<RawFd
, Error
> {
143 use nix
::sys
::socket
::*;
144 let sock_fd
= socket(
145 AddressFamily
::Vsock
,
150 let sock_addr
= VsockAddr
::new(libc
::VMADDR_CID_ANY
, DEFAULT_VSOCK_PORT
as u32);
151 bind(sock_fd
, &SockAddr
::Vsock(sock_addr
))?
;
152 listen(sock_fd
, MAX_PENDING
)?
;