feat(shim-mgmt): iptables handler

Support the handlers in runtime, which are used by kata-ctl iptables series of commands in runtime.

Fixes: #5370
Signed-off-by: Ji-Xinyou <jerryji0414@outlook.com>
This commit is contained in:
Ji-Xinyou 2022-09-08 14:13:41 +08:00
parent 288e337a6f
commit f8f97c1e22
8 changed files with 179 additions and 18 deletions

View File

@ -113,5 +113,7 @@ impl_agent!(
create_sandbox | crate::CreateSandboxRequest | crate::Empty | None,
destroy_sandbox | crate::Empty | crate::Empty | None,
copy_file | crate::CopyFileRequest | crate::Empty | None,
get_oom_event | crate::Empty | crate::OomEventResponse | Some(0)
get_oom_event | crate::Empty | crate::OomEventResponse | Some(0),
get_ip_tables | crate::GetIPTablesRequest | crate::GetIPTablesResponse | None,
set_ip_tables | crate::SetIPTablesRequest | crate::SetIPTablesResponse | None
);

View File

@ -16,14 +16,15 @@ use crate::{
ARPNeighbor, ARPNeighbors, AddArpNeighborRequest, AgentDetails, BlkioStats,
BlkioStatsEntry, CgroupStats, CheckRequest, CloseStdinRequest, ContainerID,
CopyFileRequest, CpuStats, CpuUsage, CreateContainerRequest, CreateSandboxRequest, Device,
Empty, ExecProcessRequest, FSGroup, FSGroupChangePolicy, GuestDetailsResponse,
HealthCheckResponse, HugetlbStats, IPAddress, IPFamily, Interface, Interfaces,
KernelModule, MemHotplugByProbeRequest, MemoryData, MemoryStats, NetworkStats,
OnlineCPUMemRequest, PidsStats, ReadStreamRequest, ReadStreamResponse,
RemoveContainerRequest, ReseedRandomDevRequest, Route, Routes, SetGuestDateTimeRequest,
SignalProcessRequest, StatsContainerResponse, Storage, StringUser, ThrottlingData,
TtyWinResizeRequest, UpdateContainerRequest, UpdateInterfaceRequest, UpdateRoutesRequest,
VersionCheckResponse, WaitProcessRequest, WriteStreamRequest,
Empty, ExecProcessRequest, FSGroup, FSGroupChangePolicy, GetIPTablesRequest,
GetIPTablesResponse, GuestDetailsResponse, HealthCheckResponse, HugetlbStats, IPAddress,
IPFamily, Interface, Interfaces, KernelModule, MemHotplugByProbeRequest, MemoryData,
MemoryStats, NetworkStats, OnlineCPUMemRequest, PidsStats, ReadStreamRequest,
ReadStreamResponse, RemoveContainerRequest, ReseedRandomDevRequest, Route, Routes,
SetGuestDateTimeRequest, SetIPTablesRequest, SetIPTablesResponse, SignalProcessRequest,
StatsContainerResponse, Storage, StringUser, ThrottlingData, TtyWinResizeRequest,
UpdateContainerRequest, UpdateInterfaceRequest, UpdateRoutesRequest, VersionCheckResponse,
WaitProcessRequest, WriteStreamRequest,
},
OomEventResponse, WaitProcessResponse, WriteStreamResponse,
};
@ -388,6 +389,41 @@ impl From<agent::WriteStreamResponse> for WriteStreamResponse {
}
}
impl From<GetIPTablesRequest> for agent::GetIPTablesRequest {
fn from(from: GetIPTablesRequest) -> Self {
Self {
is_ipv6: from.is_ipv6,
..Default::default()
}
}
}
impl From<agent::GetIPTablesResponse> for GetIPTablesResponse {
fn from(from: agent::GetIPTablesResponse) -> Self {
Self {
data: from.get_data().to_vec(),
}
}
}
impl From<SetIPTablesRequest> for agent::SetIPTablesRequest {
fn from(from: SetIPTablesRequest) -> Self {
Self {
is_ipv6: from.is_ipv6,
data: from.data,
..Default::default()
}
}
}
impl From<agent::SetIPTablesResponse> for SetIPTablesResponse {
fn from(from: agent::SetIPTablesResponse) -> Self {
Self {
data: from.get_data().to_vec(),
}
}
}
impl From<ExecProcessRequest> for agent::ExecProcessRequest {
fn from(from: ExecProcessRequest) -> Self {
Self {

View File

@ -16,11 +16,12 @@ pub mod types;
pub use types::{
ARPNeighbor, ARPNeighbors, AddArpNeighborRequest, BlkioStatsEntry, CheckRequest,
CloseStdinRequest, ContainerID, ContainerProcessID, CopyFileRequest, CreateContainerRequest,
CreateSandboxRequest, Empty, ExecProcessRequest, GetGuestDetailsRequest, GuestDetailsResponse,
HealthCheckResponse, IPAddress, IPFamily, Interface, Interfaces, ListProcessesRequest,
MemHotplugByProbeRequest, OnlineCPUMemRequest, OomEventResponse, ReadStreamRequest,
ReadStreamResponse, RemoveContainerRequest, ReseedRandomDevRequest, Route, Routes,
SetGuestDateTimeRequest, SignalProcessRequest, StatsContainerResponse, Storage,
CreateSandboxRequest, Empty, ExecProcessRequest, GetGuestDetailsRequest, GetIPTablesRequest,
GetIPTablesResponse, GuestDetailsResponse, HealthCheckResponse, IPAddress, IPFamily, Interface,
Interfaces, ListProcessesRequest, MemHotplugByProbeRequest, OnlineCPUMemRequest,
OomEventResponse, ReadStreamRequest, ReadStreamResponse, RemoveContainerRequest,
ReseedRandomDevRequest, Route, Routes, SetGuestDateTimeRequest, SetIPTablesRequest,
SetIPTablesResponse, SignalProcessRequest, StatsContainerResponse, Storage,
TtyWinResizeRequest, UpdateContainerRequest, UpdateInterfaceRequest, UpdateRoutesRequest,
VersionCheckResponse, WaitProcessRequest, WaitProcessResponse, WriteStreamRequest,
WriteStreamResponse,
@ -85,4 +86,6 @@ pub trait Agent: AgentManager + HealthService + Send + Sync {
// utils
async fn copy_file(&self, req: CopyFileRequest) -> Result<Empty>;
async fn get_oom_event(&self, req: Empty) -> Result<OomEventResponse>;
async fn get_ip_tables(&self, req: GetIPTablesRequest) -> Result<GetIPTablesResponse>;
async fn set_ip_tables(&self, req: SetIPTablesRequest) -> Result<SetIPTablesResponse>;
}

View File

@ -205,6 +205,27 @@ pub struct UpdateContainerRequest {
pub mounts: Vec<oci::Mount>,
}
#[derive(PartialEq, Clone, Default, Debug)]
pub struct GetIPTablesRequest {
pub is_ipv6: bool,
}
#[derive(PartialEq, Clone, Default, Debug)]
pub struct GetIPTablesResponse {
pub data: Vec<u8>,
}
#[derive(PartialEq, Clone, Default, Debug)]
pub struct SetIPTablesRequest {
pub is_ipv6: bool,
pub data: Vec<u8>,
}
#[derive(PartialEq, Clone, Default, Debug)]
pub struct SetIPTablesResponse {
pub data: Vec<u8>,
}
#[derive(PartialEq, Clone, Default)]
pub struct WriteStreamRequest {
pub process_id: ContainerProcessID,

View File

@ -16,4 +16,8 @@ pub trait Sandbox: Send + Sync {
// agent function
async fn agent_sock(&self) -> Result<String>;
// utils
async fn set_iptables(&self, is_ipv6: bool, data: Vec<u8>) -> Result<Vec<u8>>;
async fn get_iptables(&self, is_ipv6: bool) -> Result<Vec<u8>>;
}

View File

@ -12,7 +12,7 @@ use std::{path::Path, path::PathBuf, time::Duration};
use super::server::mgmt_socket_addr;
use anyhow::{anyhow, Context, Result};
use hyper::{Body, Client, Response};
use hyper::{Body, Client, Method, Request, Response};
use hyperlocal::{UnixClientExt, UnixConnector, Uri};
/// Shim management client with timeout
@ -58,4 +58,22 @@ impl MgmtClient {
None => work.await.context("failed to GET"),
}
}
/// The http PUT method for client
pub async fn put(&self, uri: &str, data: Vec<u8>) -> Result<Response<Body>> {
let url: hyper::Uri = Uri::new(&self.sock_path, uri).into();
let request = Request::builder()
.method(Method::PUT)
.uri(url)
.body(Body::from(data))
.unwrap();
let work = self.client.request(request);
match self.timeout {
Some(timeout) => match tokio::time::timeout(timeout, work).await {
Ok(result) => result.map_err(|e| anyhow!(e)),
Err(_) => Err(anyhow!("TIMEOUT")),
},
None => work.await.context("failed to PUT"),
}
}
}

View File

@ -7,11 +7,12 @@
// This defines the handlers corresponding to the url when a request is sent to destined url,
// the handler function should be invoked, and the corresponding data will be in the response
use anyhow::{anyhow, Result};
use common::Sandbox;
use hyper::{Body, Method, Request, Response, Result, StatusCode};
use hyper::{Body, Method, Request, Response, StatusCode};
use std::sync::Arc;
use super::server::AGENT_URL;
use super::server::{AGENT_URL, IP6_TABLE_URL, IP_TABLE_URL};
// main router for response, this works as a multiplexer on
// http arrival which invokes the corresponding handler function
@ -27,6 +28,12 @@ pub(crate) async fn handler_mux(
);
match (req.method(), req.uri().path()) {
(&Method::GET, AGENT_URL) => agent_url_handler(sandbox, req).await,
(&Method::PUT, IP_TABLE_URL) | (&Method::GET, IP_TABLE_URL) => {
ip_table_handler(sandbox, req).await
}
(&Method::PUT, IP6_TABLE_URL) | (&Method::GET, IP6_TABLE_URL) => {
ipv6_table_handler(sandbox, req).await
}
_ => Ok(not_found(req).await),
}
}
@ -50,3 +57,49 @@ async fn agent_url_handler(
.unwrap_or_else(|_| String::from(""));
Ok(Response::new(Body::from(agent_sock)))
}
/// the ipv4 handler of iptable operation
async fn ip_table_handler(sandbox: Arc<dyn Sandbox>, req: Request<Body>) -> Result<Response<Body>> {
generic_ip_table_handler(sandbox, req, false).await
}
/// the ipv6 handler of iptable operation
async fn ipv6_table_handler(
sandbox: Arc<dyn Sandbox>,
req: Request<Body>,
) -> Result<Response<Body>> {
generic_ip_table_handler(sandbox, req, true).await
}
/// the generic iptable handler, for both ipv4 and ipv6
/// this requires iptables-series binaries to be inside guest rootfs
async fn generic_ip_table_handler(
sandbox: Arc<dyn Sandbox>,
req: Request<Body>,
is_ipv6: bool,
) -> Result<Response<Body>> {
info!(sl!(), "handler: iptable ipv6?: {}", is_ipv6);
match *req.method() {
Method::GET => match sandbox.get_iptables(is_ipv6).await {
Ok(data) => {
let body = Body::from(data);
Response::builder().body(body).map_err(|e| anyhow!(e))
}
_ => {
Err(anyhow!("Failed to get iptable"))
}
},
Method::PUT => {
let data = hyper::body::to_bytes(req.into_body()).await?;
match sandbox.set_iptables(is_ipv6, data.to_vec()).await {
Ok(resp_data) => Response::builder()
.body(Body::from(resp_data))
.map_err(|e| anyhow!(e)),
_ => Err(anyhow!("Failed to set iptable")),
}
}
_ => Err(anyhow!("IP Tables only takes PUT and GET")),
}
}

View File

@ -6,7 +6,9 @@
use std::sync::Arc;
use agent::{self, kata::KataAgent, types::KernelModule, Agent};
use agent::{
self, kata::KataAgent, types::KernelModule, Agent, GetIPTablesRequest, SetIPTablesRequest,
};
use anyhow::{anyhow, Context, Result};
use async_trait::async_trait;
use common::{
@ -269,6 +271,28 @@ impl Sandbox for VirtSandbox {
async fn agent_sock(&self) -> Result<String> {
self.agent.agent_sock().await
}
async fn set_iptables(&self, is_ipv6: bool, data: Vec<u8>) -> Result<Vec<u8>> {
info!(sl!(), "sb: set_iptables invoked");
let req = SetIPTablesRequest { is_ipv6, data };
let resp = self
.agent
.set_ip_tables(req)
.await
.context("sandbox: failed to set iptables")?;
Ok(resp.data)
}
async fn get_iptables(&self, is_ipv6: bool) -> Result<Vec<u8>> {
info!(sl!(), "sb: get_iptables invoked");
let req = GetIPTablesRequest { is_ipv6 };
let resp = self
.agent
.get_ip_tables(req)
.await
.context("sandbox: failed to get iptables")?;
Ok(resp.data)
}
}
#[async_trait]