Skip to content

Commit

Permalink
OXY-1404: Revert most of #60 and expose get_current_thread_seccomp_mo…
Browse files Browse the repository at this point in the history
…de (#74)

(copied from #72's description) While updating Oxy with foundations 4.0,
I discovered a flaw with the previous approach:
[docker](https://docs.docker.com/engine/security/seccomp/#pass-a-profile-for-a-container),
by default, uses seccomp to block 44 system calls. This means that, with
the approach used in #60,
code calling enable_syscall_sandboxing would get an error because
seccomp is already active if it happened to be running inside a docker
container - even though it could still safely call
enable_syscall_sandboxing.

As described in #60, the
original motivation for that change was because of the use of the
[tokio::runtime::Builder::on_thread_start](https://docs.rs/tokio/latest/tokio/runtime/struct.Builder.html#method.on_thread_start)
hook leading to double seccomp initialization. This hook was used to
ensure that any threads initialized before seccomp is configured on the
main thread also had seccomp active, but it led to a risk of double
seccomp initialization leading to a crash.

This PR rolls back most of the changes in #60, instead simply exposing
the `get_current_thread_seccomp_mode` method so applications can decide
for themselves how to handle these scenarios. In our case, we'll use it
to log a warning if we're about to enable seccomp and it's already
enabled.
  • Loading branch information
OmegaJak authored Oct 3, 2024
1 parent 5bb11aa commit f299126
Show file tree
Hide file tree
Showing 2 changed files with 6 additions and 78 deletions.
7 changes: 2 additions & 5 deletions examples/http_server/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,6 @@ use std::net::{SocketAddr, TcpListener as StdTcpListener};
use std::sync::Arc;
use tokio::net::{TcpListener, TcpStream};

#[cfg(target_os = "linux")]
use foundations::security::SandboxingInitializationError;

#[tokio::main]
async fn main() -> BootstrapResult<()> {
// Obtain service information from Cargo.toml
Expand Down Expand Up @@ -72,7 +69,7 @@ async fn main() -> BootstrapResult<()> {
// doesn't include `listen` syscall that we used before to spawn `TcpListener`'s, so if malicious
// actor attempt to create a new server endpoint the process will terminate.
#[cfg(target_os = "linux")]
sandbox_syscalls().map_err(foundations::BootstrapError::from)?;
sandbox_syscalls()?;

// Start serving endpoints.
let mut endpoint_futures = FuturesUnordered::new();
Expand Down Expand Up @@ -243,7 +240,7 @@ async fn respond(
}

#[cfg(target_os = "linux")]
fn sandbox_syscalls() -> Result<(), SandboxingInitializationError> {
fn sandbox_syscalls() -> BootstrapResult<()> {
use foundations::security::common_syscall_allow_lists::{
ASYNC, NET_SOCKET_API, SERVICE_BASICS,
};
Expand Down
77 changes: 4 additions & 73 deletions foundations/src/security/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,8 @@ mod sys {
}

use self::internal::RawRule;
use crate::{BootstrapError, BootstrapResult};
use anyhow::{anyhow, bail};
use std::fmt::Display;
use crate::BootstrapResult;
use anyhow::bail;
use sys::PR_GET_SECCOMP;

pub use self::syscalls::Syscall;
Expand Down Expand Up @@ -102,46 +101,6 @@ impl<T: Into<u64>> From<T> for ArgCmpValue {
}
}

/// Errors that can be produced when initializing sandboxing using
/// [`enable_syscall_sandboxing`]
#[derive(Debug)]
pub enum SandboxingInitializationError {
/// Sandboxing has already been initialized on the current thread
/// with the given [`SeccompMode`]
AlreadyInitialized(SeccompMode),

/// Some other error occurred during initialization
Other(BootstrapError),
}

impl Display for SandboxingInitializationError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
SandboxingInitializationError::AlreadyInitialized(mode) => write!(
f,
"seccomp has already been initialized and is in mode {:?}",
mode
),
SandboxingInitializationError::Other(e) => e.fmt(f),
}
}
}

impl From<BootstrapError> for SandboxingInitializationError {
fn from(err: BootstrapError) -> Self {
SandboxingInitializationError::Other(err)
}
}

impl From<SandboxingInitializationError> for BootstrapError {
fn from(value: SandboxingInitializationError) -> Self {
match value {
SandboxingInitializationError::Other(e) => e,
e @ SandboxingInitializationError::AlreadyInitialized(_) => anyhow!("{}", e),
}
}
}

/// Syscall argument comparators to be used in [`Rule`].
///
/// Argument comparators add additional filtering layer to rules allowing to compare syscall's
Expand Down Expand Up @@ -356,7 +315,7 @@ pub enum SeccompMode {
}

/// Enables [seccomp]-based syscall sandboxing in the current thread and all the threads spawned
/// by it, if the current thread does not already have sandboxing enabled.
/// by it.
///
/// Calling the function early in the `main` function effectively enables seccomp for the whole
/// process.
Expand All @@ -366,10 +325,6 @@ pub enum SeccompMode {
/// of allowed syscalls. In addition, the crate provides [`common_syscall_allow_lists`] that can be
/// merged into the user-provided allow lists.
///
/// To setup sandboxing on a thread has already been sandboxed, use
/// [`enable_syscall_sandboxing_ignore_existing`] on a thread allowing the `prctl` and `seccomp`
/// syscalls.
///
/// [seccomp]: https://man7.org/linux/man-pages/man2/seccomp.2.html
///
/// # Examples
Expand Down Expand Up @@ -409,30 +364,6 @@ pub enum SeccompMode {
pub fn enable_syscall_sandboxing(
violation_action: ViolationAction,
exception_rules: &Vec<Rule>,
) -> Result<(), SandboxingInitializationError> {
let current_mode = get_current_thread_seccomp_mode()?;
if current_mode != SeccompMode::None {
return Err(SandboxingInitializationError::AlreadyInitialized(
current_mode,
));
}

enable_syscall_sandboxing_ignore_existing(violation_action, exception_rules)
.map_err(|e| e.into())
}

/// Attempts to enable [seccomp]-based syscall sandboxing in the current thread and all
/// the threads spawned by it, regardless of whether this thread is already sandboxed.
///
/// If called after sandboxing was previously set up, this will likely violate rules
/// not configured to allow the `prctl` and `seccomp` syscalls.
///
/// See [`enable_syscall_sandboxing`] for more details.
///
/// [seccomp]: https://man7.org/linux/man-pages/man2/seccomp.2.html
pub fn enable_syscall_sandboxing_ignore_existing(
violation_action: ViolationAction,
exception_rules: &Vec<Rule>,
) -> BootstrapResult<()> {
let ctx = unsafe { sys::seccomp_init(violation_action as u32) };

Expand Down Expand Up @@ -529,7 +460,7 @@ pub fn forbid_x86_64_cpu_cycle_counter() {
/// ```
///
/// [prctl(PR_GET_SECCOMP)]: https://linuxman7.org/linux/man-pages/man2/PR_GET_SECCOMP.2const.html
fn get_current_thread_seccomp_mode() -> BootstrapResult<SeccompMode> {
pub fn get_current_thread_seccomp_mode() -> BootstrapResult<SeccompMode> {
let current_seccomp_mode = unsafe { sys::prctl(PR_GET_SECCOMP as i32) };
match current_seccomp_mode {
0 => Ok(SeccompMode::None),
Expand Down

0 comments on commit f299126

Please sign in to comment.