Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ TEEPOD_RPC_LISTEN_PORT=9080
TEEPOD_CID_POOL_START=10000
# CID pool size
TEEPOD_CID_POOL_SIZE=1000
# Whether port mapping is enabled
TEEPOD_PORT_MAPPING_ENABLED=false

KMS_RPC_LISTEN_PORT=9043
TPROXY_RPC_LISTEN_PORT=9010
Expand Down Expand Up @@ -218,6 +220,12 @@ kms_url = "https://kms.$BASE_DOMAIN:$KMS_RPC_LISTEN_PORT"
tproxy_url = "https://tproxy.$BASE_DOMAIN:$TPROXY_RPC_LISTEN_PORT"
cid_start = $TEEPOD_CID_POOL_START
cid_pool_size = $TEEPOD_CID_POOL_SIZE
[cvm.port_mapping]
enabled = $TEEPOD_PORT_MAPPING_ENABLED
address = "127.0.0.1"
range = [
{ protocol = "tcp", from = 1, to = 20000 },
]

[gateway]
base_domain = "$TPROXY_PUBLIC_DOMAIN"
Expand Down
18 changes: 16 additions & 2 deletions teepod/rpc/proto/teepod_rpc.proto
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,18 @@ message CreateVMRequest {
uint32 memory = 5;
// Disk size in GB
uint32 disk_size = 6;
// Port mapping
repeated PortMapping ports = 7;
}

// Message for port mapping
message PortMapping {
// Protocol
string protocol = 1;
// Host port
uint32 host_port = 2;
// VM port
uint32 vm_port = 3;
}

// Message for upgrading an app request
Expand All @@ -51,9 +63,11 @@ message UpgradeAppRequest {
}

// Message for VM list response
message VMListResponse {
message StatusResponse {
// List of VMs
repeated VmInfo vms = 1;
// Port mapping enabled
bool port_mapping_enabled = 2;
}

// Service definition for Teepod
Expand All @@ -70,7 +84,7 @@ service Teepod {
rpc UpgradeApp(UpgradeAppRequest) returns (Id);

// RPC to list all VMs
rpc ListVms(google.protobuf.Empty) returns (VMListResponse);
rpc Status(google.protobuf.Empty) returns (StatusResponse);
// RPC to list all available images
rpc ListImages(google.protobuf.Empty) returns (ImageListResponse);
}
Expand Down
19 changes: 14 additions & 5 deletions teepod/src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,21 +15,30 @@
//! └── shared
//! └── app-compose.json
//! ```
use crate::config::Config;
use crate::config::{Config, Protocol};
use crate::vm::run::{Image, TdxConfig, VmConfig, VmMonitor};

use anyhow::{bail, Context, Result};
use bon::Builder;
use fs_err as fs;
use id_pool::IdPool;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use tracing::error;
use std::net::IpAddr;
use std::path::{Path, PathBuf};
use std::sync::{Arc, Mutex};
use teepod_rpc::VmInfo;

mod id_pool;

#[derive(Deserialize, Serialize, Debug)]
pub struct PortMapping {
pub address: IpAddr,
pub protocol: Protocol,
pub from: u16,
pub to: u16,
}

#[derive(Deserialize, Serialize, Builder)]
pub struct Manifest {
id: String,
Expand All @@ -39,7 +48,7 @@ pub struct Manifest {
memory: u32,
disk_size: u32,
image: String,
port_map: HashMap<u16, u16>,
port_map: Vec<PortMapping>,
created_at_ms: u64,
}

Expand Down Expand Up @@ -150,7 +159,7 @@ impl App {
memory: manifest.memory,
image,
tdx_config: Some(TdxConfig { cid }),
port_map: Default::default(),
port_map: manifest.port_map,
disk_size: manifest.disk_size,
created_at_ms: manifest.created_at_ms,
};
Expand Down Expand Up @@ -206,7 +215,7 @@ impl App {
let vm_path = entry.path();
if vm_path.is_dir() {
if let Err(err) = self.load_vm(vm_path) {
println!("Failed to load VM: {err}");
error!("Failed to load VM: {err}");
}
}
}
Expand Down
65 changes: 62 additions & 3 deletions teepod/src/config.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
use std::path::PathBuf;
use std::{net::IpAddr, path::PathBuf, str::FromStr};

use anyhow::{Context, Result};
use anyhow::{bail, Context, Result};
use rocket::figment::{
providers::{Format, Toml},
Figment,
};
use serde::Deserialize;
use serde::{Deserialize, Serialize};

pub const CONFIG_FILENAME: &str = "teepod.toml";
pub const SYSTEM_CONFIG_FILENAME: &str = "/etc/teepod/teepod.toml";
Expand All @@ -22,6 +22,63 @@ pub fn load_config_figment(config_file: Option<&str>) -> Figment {
.merge(leaf_config)
}

#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "lowercase")]
pub enum Protocol {
Tcp,
Udp,
}

impl FromStr for Protocol {
type Err = anyhow::Error;

fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(match s {
"tcp" => Protocol::Tcp,
"udp" => Protocol::Udp,
_ => bail!("Invalid protocol: {s}"),
})
}
}

impl Protocol {
pub fn as_str(&self) -> &str {
match self {
Protocol::Tcp => "tcp",
Protocol::Udp => "udp",
}
}
}

#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct PortRange {
pub protocol: Protocol,
pub from: u16,
pub to: u16,
}

impl PortRange {
pub fn contains(&self, protocol: &str, port: u16) -> bool {
self.protocol.as_str() == protocol && port >= self.from && port <= self.to
}
}

#[derive(Debug, Clone, Deserialize)]
pub struct PortMappingConfig {
pub enabled: bool,
pub address: IpAddr,
pub range: Vec<PortRange>,
}

impl PortMappingConfig {
pub fn is_allowed(&self, protocol: &str, port: u16) -> bool {
if !self.enabled {
return false;
}
self.range.iter().any(|r| r.contains(protocol, port))
}
}

#[derive(Debug, Clone, Deserialize)]
pub struct CvmConfig {
pub ca_cert: PathBuf,
Expand All @@ -37,6 +94,8 @@ pub struct CvmConfig {
pub cid_start: u32,
/// The size of the CID pool that allocates CIDs to VMs
pub cid_pool_size: u32,
/// Port mapping configuration
pub port_mapping: PortMappingConfig,
}

#[derive(Debug, Clone, Deserialize)]
Expand Down
51 changes: 42 additions & 9 deletions teepod/src/console.html
Original file line number Diff line number Diff line change
Expand Up @@ -241,8 +241,10 @@
background: rgba(0, 0, 0, 0.5);
display: flex;
justify-content: center;
align-items: center;
align-items: flex-start;
z-index: 1000;
overflow-y: auto;
padding: 20px;
}

.dialog {
Expand All @@ -251,6 +253,7 @@
border-radius: 8px;
width: 500px;
max-width: 90%;
margin: 20px auto;
}

.upgrade {
Expand Down Expand Up @@ -424,6 +427,20 @@ <h2>Deploy a new instance</h2>
placeholder="Paste your docker-compose.yml here" required></textarea>
</div>

<div class="form-group full-width" v-if="config.portMappingEnabled">
<label>Port Mappings</label>
<div v-for="(port, index) in vmForm.ports" :key="index" style="display: flex; gap: 10px; margin-bottom: 10px;">
<select v-model="port.protocol" style="width: 100px;">
<option value="tcp">TCP</option>
<option value="udp">UDP</option>
</select>
<input type="number" v-model.number="port.host_port" placeholder="Host Port" style="width: 120px;">
<input type="number" v-model.number="port.vm_port" placeholder="VM Port" style="width: 120px;">
<button type="button" class="action-btn danger" @click="removePort(index)">Remove</button>
</div>
<button type="button" class="action-btn" @click="addPort">Add Port Mapping</button>
</div>

<div class="vm-actions">
<button type="submit" class="action-btn primary">Deploy</button>
<button type="button" class="action-btn remove"
Expand Down Expand Up @@ -528,7 +545,8 @@ <h3>Upgrade VM</h3>
compose_file: '',
vcpu: 1,
memory: 1024,
disk_size: 20
disk_size: 20,
ports: []
});
const images = ref([]);

Expand All @@ -541,6 +559,9 @@ <h3>Upgrade VM</h3>
const errorMessage = ref('');

const showCreateDialog = ref(false);
const config = ref({
portMappingEnabled: false
});

const rpcCall = async (method, params) => {
const response = await fetch(`/prpc/Teepod.${method}?json`, {
Expand All @@ -559,9 +580,12 @@ <h3>Upgrade VM</h3>

const loadVMList = async () => {
try {
const response = await rpcCall('ListVms');
const response = await rpcCall('Status');
const data = await response.json();
vms.value = data.vms;
config.value = {
portMappingEnabled: data.port_mapping_enabled
};
} catch (error) {
console.error('Error loading VM list:', error);
}
Expand Down Expand Up @@ -699,12 +723,6 @@ <h3>Upgrade VM</h3>
Run:
<div class="command-container">
<code class="command-code">./kms-allow-upgrade.sh ${upgradeDialog.value.vm.app_id} ${data.id}</code>
<button
@click="navigator.clipboard.writeText('./kms-allow-upgrade.sh ' + upgradeDialog.value.vm.app_id + ' ' + data.id)"
class="copy-button"
title="Copy command">
📋
</button>
</div>
</li>
<li>Restart the instance</li>
Expand All @@ -716,6 +734,18 @@ <h3>Upgrade VM</h3>
}
};

const addPort = () => {
vmForm.value.ports.push({
protocol: 'tcp',
host_port: null,
vm_port: null
});
};

const removePort = (index) => {
vmForm.value.ports.splice(index, 1);
};

onMounted(() => {
(async () => {
while (true) {
Expand Down Expand Up @@ -748,6 +778,9 @@ <h3>Upgrade VM</h3>
loadUpgradeFile,
upgradeVM,
showCreateDialog,
addPort,
removePort,
config,
};
}
}).mount('#app');
Expand Down
2 changes: 2 additions & 0 deletions teepod/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ struct Args {

#[rocket::main]
async fn main() -> Result<()> {
tracing_subscriber::fmt::init();

let args = Args::parse();
let figment = config::load_config_figment(args.config.as_deref());
let config = Config::extract_or_default(&figment)?;
Expand Down
Loading