mirror of
https://github.com/kata-containers/kata-containers.git
synced 2025-07-02 02:02:24 +00:00
forwarder: Drop privileges when using hybrid VSOCK
Hybrid VSOCK requires `root` privileges to access the sandbox-specific host-side AF_UNIX socket created by the hypervisor (CLH or FC). However, once the socket has been bound, privileges can be dropped, allowing the forwarder to run as user `nobody`. Fixes: #2905. Signed-off-by: James O. D. Hunt <james.o.hunt@intel.com>
This commit is contained in:
parent
b67fa9e450
commit
6abccb92ce
32
src/trace-forwarder/Cargo.lock
generated
32
src/trace-forwarder/Cargo.lock
generated
@ -71,9 +71,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitflags"
|
name = "bitflags"
|
||||||
version = "1.2.1"
|
version = "1.3.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
|
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bumpalo"
|
name = "bumpalo"
|
||||||
@ -335,6 +335,7 @@ dependencies = [
|
|||||||
"nix 0.21.0",
|
"nix 0.21.0",
|
||||||
"opentelemetry",
|
"opentelemetry",
|
||||||
"opentelemetry-jaeger",
|
"opentelemetry-jaeger",
|
||||||
|
"privdrop",
|
||||||
"protobuf",
|
"protobuf",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
@ -354,9 +355,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.97"
|
version = "0.2.105"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "12b8adadd720df158f4d70dfe7ccc6adb0472d7c55ca83445f6a5ab3e36f8fb6"
|
checksum = "869d572136620d55835903746bcb5cdc54cb2851fd0aeec53220b4bb65ef3013"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "log"
|
name = "log"
|
||||||
@ -433,6 +434,19 @@ dependencies = [
|
|||||||
"memoffset",
|
"memoffset",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nix"
|
||||||
|
version = "0.23.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f305c2c2e4c39a82f7bf0bf65fb557f9070ce06781d4f2454295cc34b1c43188"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"cc",
|
||||||
|
"cfg-if 1.0.0",
|
||||||
|
"libc",
|
||||||
|
"memoffset",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num-integer"
|
name = "num-integer"
|
||||||
version = "0.1.43"
|
version = "0.1.43"
|
||||||
@ -546,6 +560,16 @@ version = "0.2.8"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "237a5ed80e274dbc66f86bd59c1e25edc039660be53194b5fe0a482e0f2612ea"
|
checksum = "237a5ed80e274dbc66f86bd59c1e25edc039660be53194b5fe0a482e0f2612ea"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "privdrop"
|
||||||
|
version = "0.5.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4c02cf257b10e4b807bccadb19630d5dea7e0369c3c5e84673ee8e58dc8da6a5"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"nix 0.23.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro-hack"
|
name = "proc-macro-hack"
|
||||||
version = "0.5.19"
|
version = "0.5.19"
|
||||||
|
@ -29,6 +29,7 @@ tracing-subscriber = "0.2.18"
|
|||||||
|
|
||||||
logging = { path = "../../pkg/logging" }
|
logging = { path = "../../pkg/logging" }
|
||||||
slog = "2.5.2"
|
slog = "2.5.2"
|
||||||
|
privdrop = "0.5.1"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
tempfile = "3.1.0"
|
tempfile = "3.1.0"
|
||||||
|
@ -141,7 +141,8 @@ You can now proceed as normal to create the "foo" Kata container.
|
|||||||
> directory, and since that directory is owned by the `root` user, the trace
|
> directory, and since that directory is owned by the `root` user, the trace
|
||||||
> forwarder must also be run as `root`. This requirement is unique to
|
> forwarder must also be run as `root`. This requirement is unique to
|
||||||
> hypervisors that use hybrid VSOCK: QEMU does not require special privileges
|
> hypervisors that use hybrid VSOCK: QEMU does not require special privileges
|
||||||
> to run the trace forwarder.
|
> to run the trace forwarder. To reduce the impact of this, once the forwarder
|
||||||
|
> is running it drops privileges to run as user `nobody`.
|
||||||
|
|
||||||
## Full details
|
## Full details
|
||||||
|
|
||||||
|
@ -16,45 +16,6 @@ const DEFAULT_TRACE_NAME: &str = "kata-agent";
|
|||||||
|
|
||||||
const ABOUT_TEXT: &str = "Kata Containers Trace Forwarder";
|
const ABOUT_TEXT: &str = "Kata Containers Trace Forwarder";
|
||||||
|
|
||||||
const DESCRIPTION_TEXT: &str = r#"
|
|
||||||
DESCRIPTION:
|
|
||||||
Kata Containers component that runs on the host and forwards
|
|
||||||
trace data from the container to a trace collector on the host.
|
|
||||||
|
|
||||||
This tool requires agent tracing to be enabled in the Kata
|
|
||||||
configuration file. It uses VSOCK to listen for trace data originating
|
|
||||||
from the Kata agent running inside the Kata Container.
|
|
||||||
|
|
||||||
The variety of VSOCK used depends on the configuration hypervisor:
|
|
||||||
|
|
||||||
|------------------------|--------------------|----------------|
|
|
||||||
| Hypervisor | Type of VSOCK | Run as user |
|
|
||||||
|------------------------|--------------------|----------------|
|
|
||||||
| Cloud Hypervisor (CLH) | Firecracker Hybrid | privileged |
|
|
||||||
|------------------------|--------------------|----------------|
|
|
||||||
| QEMU | Standard | non-privileged |
|
|
||||||
|------------------------|--------------------|----------------|
|
|
||||||
| Firecracker (FC) | Firecracker Hybrid | privileged |
|
|
||||||
|------------------------|--------------------|----------------|
|
|
||||||
|
|
||||||
Key:
|
|
||||||
|
|
||||||
- Firecracker Hybrid VSOCK: See the Firecracker
|
|
||||||
VSOCK documentation.
|
|
||||||
- Standard VSOCK: see vsock(7).
|
|
||||||
|
|
||||||
The way this tool is run depends on the configured hypervisor.
|
|
||||||
See EXAMPLES for further information.
|
|
||||||
|
|
||||||
Note that Hybrid VSOCK requries root privileges. Due to the way the
|
|
||||||
hybrid protocol works, the specified "master socket" itself is not used: to
|
|
||||||
communicate with the agent, this tool must generate a socket path using
|
|
||||||
the specified socket path as a prefix. Since the master socket will be
|
|
||||||
created in a root-owned directory when the Kata Containers VM (sandbox) is
|
|
||||||
created, this tool must be run as root to allow it to create the second
|
|
||||||
agent-specific socket.
|
|
||||||
"#;
|
|
||||||
|
|
||||||
const DEFAULT_LOG_LEVEL: slog::Level = slog::Level::Info;
|
const DEFAULT_LOG_LEVEL: slog::Level = slog::Level::Info;
|
||||||
|
|
||||||
// VSOCK port this program listens to for trace data, sent by the agent.
|
// VSOCK port this program listens to for trace data, sent by the agent.
|
||||||
@ -84,6 +45,50 @@ fn announce(logger: &Logger, version: &str, dump_only: bool) {
|
|||||||
"dump-only" => dump_only);
|
"dump-only" => dump_only);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn make_description_text() -> String {
|
||||||
|
format!(
|
||||||
|
r#" DESCRIPTION:
|
||||||
|
Kata Containers component that runs on the host and forwards
|
||||||
|
trace data from the container to a trace collector on the host.
|
||||||
|
|
||||||
|
This tool requires agent tracing to be enabled in the Kata
|
||||||
|
configuration file. It uses VSOCK to listen for trace data originating
|
||||||
|
from the Kata agent running inside the Kata Container.
|
||||||
|
|
||||||
|
The variety of VSOCK used depends on the configuration hypervisor:
|
||||||
|
|
||||||
|
|------------------------|--------------------|----------------|
|
||||||
|
| Hypervisor | Type of VSOCK | Run as user |
|
||||||
|
|------------------------|--------------------|----------------|
|
||||||
|
| Cloud Hypervisor (CLH) | Firecracker Hybrid | privileged |
|
||||||
|
|------------------------|--------------------|----------------|
|
||||||
|
| QEMU | Standard | non-privileged |
|
||||||
|
|------------------------|--------------------|----------------|
|
||||||
|
| Firecracker (FC) | Firecracker Hybrid | privileged |
|
||||||
|
|------------------------|--------------------|----------------|
|
||||||
|
|
||||||
|
Key:
|
||||||
|
|
||||||
|
- Firecracker Hybrid VSOCK: See the Firecracker
|
||||||
|
VSOCK documentation.
|
||||||
|
- Standard VSOCK: see vsock(7).
|
||||||
|
|
||||||
|
The way this tool is run depends on the configured hypervisor.
|
||||||
|
See EXAMPLES for further information.
|
||||||
|
|
||||||
|
Note that Hybrid VSOCK requries root privileges initially. Due to the way the
|
||||||
|
hybrid protocol works, the specified "master socket" itself is not used: to
|
||||||
|
communicate with the agent, this tool must generate a socket path using
|
||||||
|
the specified socket path as a prefix. Since the master socket will be
|
||||||
|
created in a root-owned directory when the Kata Containers VM (sandbox) is
|
||||||
|
created, this tool must be run as root to allow it to create the second
|
||||||
|
agent-specific socket. However, once the forwarder has started running, it
|
||||||
|
drops privileges and will continue running as user {user:?}.
|
||||||
|
"#,
|
||||||
|
user = server::NON_PRIV_USER
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
fn make_examples_text(program_name: &str) -> String {
|
fn make_examples_text(program_name: &str) -> String {
|
||||||
format!(
|
format!(
|
||||||
r#"EXAMPLES:
|
r#"EXAMPLES:
|
||||||
@ -135,7 +140,7 @@ fn real_main() -> Result<()> {
|
|||||||
.version(version)
|
.version(version)
|
||||||
.version_short("v")
|
.version_short("v")
|
||||||
.about(ABOUT_TEXT)
|
.about(ABOUT_TEXT)
|
||||||
.long_about(DESCRIPTION_TEXT)
|
.long_about(&*make_description_text())
|
||||||
.after_help(&*make_examples_text(name))
|
.after_help(&*make_examples_text(name))
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name("dump-only")
|
Arg::with_name("dump-only")
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
use crate::handler;
|
use crate::handler;
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
use opentelemetry::sdk::export::trace::SpanExporter;
|
use opentelemetry::sdk::export::trace::SpanExporter;
|
||||||
|
use privdrop::PrivDrop;
|
||||||
use slog::{debug, o, Logger};
|
use slog::{debug, o, Logger};
|
||||||
use std::os::unix::io::AsRawFd;
|
use std::os::unix::io::AsRawFd;
|
||||||
use std::os::unix::net::UnixListener;
|
use std::os::unix::net::UnixListener;
|
||||||
@ -13,6 +14,12 @@ use vsock::{SockAddr, VsockListener};
|
|||||||
|
|
||||||
use crate::tracer;
|
use crate::tracer;
|
||||||
|
|
||||||
|
// Username that is assumed to exist, used when dropping root privileges
|
||||||
|
// when running with Hybrid VSOCK.
|
||||||
|
pub const NON_PRIV_USER: &str = "nobody";
|
||||||
|
|
||||||
|
const ROOT_DIR: &str = "/";
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub enum VsockType {
|
pub enum VsockType {
|
||||||
Standard { port: u32, cid: u32 },
|
Standard { port: u32, cid: u32 },
|
||||||
@ -79,12 +86,29 @@ impl VsockTraceServer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn drop_privs(logger: &Logger) -> Result<()> {
|
||||||
|
debug!(logger, "Dropping privileges"; "new-user" => NON_PRIV_USER);
|
||||||
|
|
||||||
|
nix::unistd::chdir(ROOT_DIR)
|
||||||
|
.map_err(|e| anyhow!("Unable to chdir to {:?}: {:?}", ROOT_DIR, e))?;
|
||||||
|
|
||||||
|
PrivDrop::default()
|
||||||
|
.user(NON_PRIV_USER)
|
||||||
|
.apply()
|
||||||
|
.map_err(|e| anyhow!("Failed to drop privileges to user {}: {}", NON_PRIV_USER, e))?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
fn start_hybrid_vsock(
|
fn start_hybrid_vsock(
|
||||||
logger: Logger,
|
logger: Logger,
|
||||||
exporter: &mut dyn SpanExporter,
|
exporter: &mut dyn SpanExporter,
|
||||||
socket_path: &str,
|
socket_path: &str,
|
||||||
dump_only: bool,
|
dump_only: bool,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
|
let logger =
|
||||||
|
logger.new(o!("vsock-type" => "hybrid", "vsock-socket-path" => socket_path.to_string()));
|
||||||
|
|
||||||
let effective = nix::unistd::Uid::effective();
|
let effective = nix::unistd::Uid::effective();
|
||||||
|
|
||||||
if !effective.is_root() {
|
if !effective.is_root() {
|
||||||
@ -96,9 +120,10 @@ fn start_hybrid_vsock(
|
|||||||
|
|
||||||
let listener = UnixListener::bind(socket_path)?;
|
let listener = UnixListener::bind(socket_path)?;
|
||||||
|
|
||||||
debug!(logger, "Waiting for connections";
|
// Having bound to the socket, drop privileges
|
||||||
"vsock-type" => "hybrid",
|
drop_privs(&logger)?;
|
||||||
"vsock-socket-path" => socket_path);
|
|
||||||
|
debug!(logger, "Waiting for connections");
|
||||||
|
|
||||||
for conn in listener.incoming() {
|
for conn in listener.incoming() {
|
||||||
let conn = conn?;
|
let conn = conn?;
|
||||||
|
Loading…
Reference in New Issue
Block a user