first push
This commit is contained in:
commit
ba5bfdd66e
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
/target
|
82
Cargo.lock
generated
Normal file
82
Cargo.lock
generated
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 4
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anyhow"
|
||||||
|
version = "1.0.94"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c1fd03a028ef38ba2276dce7e33fcd6369c158a1bca17946c4b1b701891c1ff7"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cidr"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bfc95a0c21d5409adc146dbbb152b5c65aaea32bc2d2f57cf12f850bffdd7ab8"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "daemon"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"cidr",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro2"
|
||||||
|
version = "1.0.92"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quote"
|
||||||
|
version = "1.0.37"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde"
|
||||||
|
version = "1.0.215"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6513c1ad0b11a9376da888e3e0baa0077f1aed55c17f50e7b2397136129fb88f"
|
||||||
|
dependencies = [
|
||||||
|
"serde_derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_derive"
|
||||||
|
version = "1.0.215"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "2.0.90"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-ident"
|
||||||
|
version = "1.0.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83"
|
9
Cargo.toml
Normal file
9
Cargo.toml
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
[package]
|
||||||
|
name = "daemon"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
anyhow = "1.0.94"
|
||||||
|
cidr = { version = "0.3.0", features = ["serde"] }
|
||||||
|
serde = { version = "1.0.215", features = ["derive"] }
|
56
scripts/start_qemu_vm.sh
Normal file
56
scripts/start_qemu_vm.sh
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
[[ -z "$VM_UUID" ]] || {
|
||||||
|
echo "Environment variable VM_UUID is not set."
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
source "/etc/detee/daemon/vms/$VM_UUID"
|
||||||
|
|
||||||
|
mandatory_vars=("IF_DEVICE" "IF_NAME" "IF_TYPE" "KERNEL" \
|
||||||
|
"INITRD" "PARAMS" "CPU_TYPE" "VCPUS" "MEMORY" \
|
||||||
|
"MAX_MEMORY" "DISK" "DISK_SIZE")
|
||||||
|
for var in "${mandatory_vars[@]}"; do
|
||||||
|
if [ -z "${!var}" ]; then
|
||||||
|
echo "Environment variable $var is not set."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ "$IF_TYPE" == "macvtap" || "$IF_TYPE" == "ipvtap" ]]; then
|
||||||
|
ip link add link $IF_DEVICE name $IF_NAME type $IF_TYPE mode bridge
|
||||||
|
ip link set $IF_NAME up
|
||||||
|
ip link set $IF_NAME promisc on
|
||||||
|
vtap_index="$(cat /sys/class/net/${IF_NAME}/ifindex)"
|
||||||
|
vtap_addr="$(cat /sys/class/net/${IF_NAME}/address)"
|
||||||
|
qemu_device_params="-netdev tap,id=hostnet1,fd=3 3<>/dev/tap${macvtap_index}"
|
||||||
|
qemu_device_params+=" -device virtio-net-pci,netdev=hostnet1,mac=${macvtap_addr},romfile="
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$IF_TYPE" == "NAT" ]]; then
|
||||||
|
ports=""
|
||||||
|
for port_pair in "$NAT_PORT_FW"; do
|
||||||
|
host_port="$( echo $port_pair | cut -d ':' -f1 )"
|
||||||
|
guest_port="$( echo $port_pair | cut -d ':' -f2 )"
|
||||||
|
ports+=",hostfwd=tcp::${host_port}-:${guest_port}"
|
||||||
|
done
|
||||||
|
qemu_device_params="-netdev user,id=vmnic${ports}"
|
||||||
|
qemu_device_params+=" -device virtio-net-pci,netdev=vmnic,romfile="
|
||||||
|
fi
|
||||||
|
|
||||||
|
# TODO: also handle bridge device (when IPs are public, but the host is the gateway)
|
||||||
|
|
||||||
|
vm_disk="/root/dtrfs/arch-1-ghe0.qcow2"
|
||||||
|
|
||||||
|
[[ -f $DISK ]] || qemu-img create -f qcow2 ${DISK} ${DISK_SIZE}
|
||||||
|
|
||||||
|
qemu-system-x86_64 $qemu_device_params \
|
||||||
|
-enable-kvm -cpu $CPU_TYPE -vga none \
|
||||||
|
-machine q35,confidential-guest-support=sev0,memory-backend=ram1 \
|
||||||
|
-smp $VCPUS,maxcpus=255 -m $MEMORY,slots=5,maxmem=$MAX_MEMORY \
|
||||||
|
-no-reboot -bios /usr/share/edk2/ovmf/OVMF.amdsev.fd \
|
||||||
|
-drive file=${DISK},if=none,id=disk0,format=qcow2 \
|
||||||
|
-device virtio-blk-pci,drive=disk0 \
|
||||||
|
-object memory-backend-memfd,id=ram1,size=$MEMORY,share=true,prealloc=false \
|
||||||
|
-object sev-snp-guest,id=sev0,cbitpos=51,reduced-phys-bits=1,kernel-hashes=on \
|
||||||
|
-kernel $KERNEL -append "$PARAMS" -initrd $INITRD \
|
||||||
|
-nographic -monitor pty -serial mon:stdio -monitor unix:monitor,server,nowait
|
36
src/config.rs
Normal file
36
src/config.rs
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
#![allow(dead_code)]
|
||||||
|
use cidr::Ipv4Cidr;
|
||||||
|
use cidr::Ipv6Cidr;
|
||||||
|
use core::net::Ipv4Addr;
|
||||||
|
use core::net::Ipv6Addr;
|
||||||
|
|
||||||
|
struct Volume {
|
||||||
|
path: String,
|
||||||
|
// maximum allowed storage in MB
|
||||||
|
max_storage: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Interface {
|
||||||
|
r#type: InterfaceType,
|
||||||
|
name: String,
|
||||||
|
ipv4_ranges: Vec<Ipv4Cidr>,
|
||||||
|
reserved_v4_addrs: Vec<Ipv4Addr>,
|
||||||
|
ipv6_ranges: Vec<Ipv6Cidr>,
|
||||||
|
reserved_v6_addrs: Vec<Ipv6Addr>,
|
||||||
|
// TODO: add bandwidth
|
||||||
|
}
|
||||||
|
|
||||||
|
enum InterfaceType {
|
||||||
|
NAT,
|
||||||
|
MACVTAP,
|
||||||
|
IPVTAP,
|
||||||
|
Bridge,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Config {
|
||||||
|
max_cores_per_vm: u64,
|
||||||
|
max_cpu_reservation: u64,
|
||||||
|
max_mem_reservation: u64,
|
||||||
|
network_interfaces: Vec<Interface>,
|
||||||
|
volumes: Vec<Volume>,
|
||||||
|
}
|
7
src/constants.rs
Normal file
7
src/constants.rs
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
#![allow(dead_code)]
|
||||||
|
|
||||||
|
pub(crate) const DEFAULT_OVMF: &str = "/usr/share/edk2/ovmf/OVMF.amdsev.fd";
|
||||||
|
pub(crate) const BOOT_DIR: &str = "/var/lib/libvirt/detee/";
|
||||||
|
pub(crate) const VM_CONFIG_DIR: &str = "/etc/detee/daemon/vms/";
|
||||||
|
pub(crate) const CONFIG_PATH: &str = "/etc/detee/daemon/config.json";
|
||||||
|
pub(crate) const START_VM_SH: &str = "/usr/local/bin/detee/start_qemu_vm.sh";
|
7
src/main.rs
Normal file
7
src/main.rs
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
mod config;
|
||||||
|
mod state;
|
||||||
|
mod constants;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
println!("Hello, world!");
|
||||||
|
}
|
131
src/state.rs
Normal file
131
src/state.rs
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
#![allow(dead_code)]
|
||||||
|
use std::fs::remove_file;
|
||||||
|
use crate::constants::*;
|
||||||
|
use anyhow::Result;
|
||||||
|
use std::fs::File;
|
||||||
|
use std::io::Write;
|
||||||
|
|
||||||
|
enum NIC {
|
||||||
|
NAT { device: String },
|
||||||
|
// TODO: figure how to calculate IF_NAME based on index
|
||||||
|
MACVTAP { name: String, device: String },
|
||||||
|
IPVTAP { name: String, device: String },
|
||||||
|
Bridge { device: String },
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NIC {
|
||||||
|
fn if_type(&self) -> String {
|
||||||
|
match self {
|
||||||
|
NIC::IPVTAP { .. } => format!("ipvtap"),
|
||||||
|
NIC::MACVTAP { .. } => format!("macvtap"),
|
||||||
|
NIC::NAT { .. } => format!("nat"),
|
||||||
|
NIC::Bridge { .. } => format!("bridge"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn device_name(&self) -> String {
|
||||||
|
match self {
|
||||||
|
NIC::IPVTAP { device, .. } => device.clone(),
|
||||||
|
NIC::MACVTAP { device, .. } => device.clone(),
|
||||||
|
NIC::NAT { device, .. } => device.clone(),
|
||||||
|
NIC::Bridge { device, .. } => device.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn vtap_name(&self) -> Option<String> {
|
||||||
|
match self {
|
||||||
|
NIC::IPVTAP { name, .. } => Some(name.clone()),
|
||||||
|
NIC::MACVTAP { name, .. } => Some(name.clone()),
|
||||||
|
NIC::NAT { .. } | NIC::Bridge { .. } => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_vtap(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
NIC::IPVTAP { .. } | NIC::MACVTAP { .. } => true,
|
||||||
|
NIC::NAT { .. } | NIC::Bridge { .. } => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct VM {
|
||||||
|
uuid: String,
|
||||||
|
hostname: String,
|
||||||
|
ip: String,
|
||||||
|
// requires short format (example: 24)
|
||||||
|
subnet: String,
|
||||||
|
gateway: String,
|
||||||
|
nameserver: String,
|
||||||
|
admin_key: String,
|
||||||
|
// TODO: add support for multiple NICs
|
||||||
|
nic: NIC,
|
||||||
|
cpu_type: String,
|
||||||
|
vcpus: u32,
|
||||||
|
// memory in MB
|
||||||
|
memory: u32,
|
||||||
|
disk_absolute_path: String,
|
||||||
|
// disk size in GB
|
||||||
|
disk_size: u32,
|
||||||
|
kernel_path: String,
|
||||||
|
initrd_path: String,
|
||||||
|
ovmf_path: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VM {
|
||||||
|
pub fn kernel_params(&self) -> String {
|
||||||
|
let ip_string = format!(
|
||||||
|
"detee_net={}_{}_{}_{}",
|
||||||
|
self.ip, self.subnet, self.gateway, self.nameserver
|
||||||
|
);
|
||||||
|
let admin_key = format!("detee_admin={}", self.admin_key);
|
||||||
|
let hostname = format!("detee_name={}", self.hostname);
|
||||||
|
format!("{} {} {}", ip_string, admin_key, hostname)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn export_vm_env(&self) -> String {
|
||||||
|
let mut vars = String::new();
|
||||||
|
|
||||||
|
vars += &format!("IF_DEVICE={}\n", self.nic.device_name());
|
||||||
|
if let Some(vtap_name) = self.nic.vtap_name() {
|
||||||
|
vars += &format!("IF_NAME={}\n", vtap_name);
|
||||||
|
}
|
||||||
|
vars += &format!("IF_TYPE={}\n", self.nic.if_type());
|
||||||
|
vars += &format!("KERNEL={}\n", self.kernel_path);
|
||||||
|
vars += &format!("INITRD={}\n", self.initrd_path);
|
||||||
|
vars += &format!("PARAMS={}\n", self.kernel_params());
|
||||||
|
vars += &format!("CPU_TYPE={}\n", self.cpu_type);
|
||||||
|
vars += &format!("VCPUS={}\n", self.vcpus);
|
||||||
|
vars += &format!("MEMORY={}MB\n", self.memory);
|
||||||
|
vars += &format!("MAX_MEMORY={}MB\n", self.memory + 256);
|
||||||
|
vars += &format!("DISK={}\n", self.disk_absolute_path);
|
||||||
|
vars += &format!("DISK_SIZE={}GB\n", self.disk_size);
|
||||||
|
|
||||||
|
todo!();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write_systemd_unit_file(&self) -> Result<()> {
|
||||||
|
let mut contents = String::new();
|
||||||
|
contents += &format!("[Unit]");
|
||||||
|
contents += &format!("Description=DeTEE {}", self.uuid);
|
||||||
|
contents += &format!("After=network.target");
|
||||||
|
contents += &format!("");
|
||||||
|
contents += &format!("[Service]");
|
||||||
|
contents += &format!("Type=simple");
|
||||||
|
contents += &format!("Environment=VM_UUID={}", self.uuid);
|
||||||
|
contents += &format!("ExecStart={}", START_VM_SH);
|
||||||
|
contents += &format!("ExecStop=/bin/kill -s SIGINT $MAINPID");
|
||||||
|
contents += &format!("Restart=always");
|
||||||
|
contents += &format!("");
|
||||||
|
contents += &format!("[Install]");
|
||||||
|
contents += &format!("WantedBy=multi-user.target");
|
||||||
|
|
||||||
|
let mut file = File::create(VM_CONFIG_DIR.to_string() + "/" + &self.uuid)?;
|
||||||
|
file.write_all(contents.as_bytes())?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn delete_systemd_unit_file(&self) -> Result<()> {
|
||||||
|
remove_file(VM_CONFIG_DIR.to_string() + "/" + &self.uuid)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user