Replace lexical Rust fragments with real modules
This commit is contained in:
parent
f8849cdb5f
commit
247819250b
35 changed files with 3302 additions and 2671 deletions
|
|
@ -420,6 +420,17 @@ fn require_inputs(inputs: &ColdStartInputs<'_>) -> Result<(), ColdStartError> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
include!("cold_start/git_routing.rs");
|
||||
include!("cold_start/runtime_staging.rs");
|
||||
include!("cold_start/tests.rs");
|
||||
mod git_routing;
|
||||
mod runtime_staging;
|
||||
|
||||
use git_routing::GitRoutingLayout;
|
||||
#[cfg(test)]
|
||||
use git_routing::parse_global_excludes_path;
|
||||
pub(crate) use runtime_staging::RuntimeStaging;
|
||||
use runtime_staging::{
|
||||
FileIdentity, confined_resolve_flags, hex, host_path, open_or_create_private_directory,
|
||||
remove_staging_directory, require_private_file,
|
||||
};
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
|
|
|||
|
|
@ -1,11 +1,20 @@
|
|||
struct GitRoutingLayout {
|
||||
use super::{
|
||||
AtFlags, ColdStartError, ContainerId, File, FileIdentity, HostPath, Mode, OFlags, OsString,
|
||||
OsStringExt, OwnedFd, Path, PathBuf, PipeFlags, Read, ResolveFlags,
|
||||
ResolvedDomainConfiguration, RuntimeGitRoutingAuthority, RuntimeGitRoutingLaunch,
|
||||
TargetCommand, child_broker, confined_resolve_flags, failure, fchmod, git_common_directory,
|
||||
hex, host_path, open_or_create_private_directory, openat2, pipe_with, remove_staging_directory,
|
||||
require_private_file, statat,
|
||||
};
|
||||
|
||||
pub(super) struct GitRoutingLayout {
|
||||
_runtime_directory: RuntimeGitRoutingDirectory,
|
||||
workspace: File,
|
||||
workspace_label: PathBuf,
|
||||
isolated: File,
|
||||
isolated_label: PathBuf,
|
||||
mountpoint: File,
|
||||
mountpoint_label: PathBuf,
|
||||
pub(super) mountpoint_label: PathBuf,
|
||||
git_common: File,
|
||||
git_common_label: PathBuf,
|
||||
transient_hooks: File,
|
||||
|
|
@ -17,7 +26,7 @@ struct GitRoutingLayout {
|
|||
}
|
||||
|
||||
impl GitRoutingLayout {
|
||||
fn create(
|
||||
pub(super) fn create(
|
||||
configuration: &ResolvedDomainConfiguration,
|
||||
container_id: ContainerId,
|
||||
invoking_uid: u32,
|
||||
|
|
@ -122,7 +131,7 @@ impl GitRoutingLayout {
|
|||
}))
|
||||
}
|
||||
|
||||
fn launch_authority(
|
||||
pub(super) fn launch_authority(
|
||||
&self,
|
||||
configuration: &ResolvedDomainConfiguration,
|
||||
) -> Result<RuntimeGitRoutingLaunch, ColdStartError> {
|
||||
|
|
@ -148,7 +157,7 @@ impl GitRoutingLayout {
|
|||
.map_err(failure)
|
||||
}
|
||||
|
||||
fn mountpoint(&self) -> HostPath {
|
||||
pub(super) fn mountpoint(&self) -> HostPath {
|
||||
HostPath::new(
|
||||
self.mountpoint_label
|
||||
.as_os_str()
|
||||
|
|
@ -157,11 +166,11 @@ impl GitRoutingLayout {
|
|||
)
|
||||
}
|
||||
|
||||
fn host_root(&self) -> HostPath {
|
||||
pub(super) fn host_root(&self) -> HostPath {
|
||||
HostPath::new(self.workspace_label.as_os_str().as_encoded_bytes().to_vec())
|
||||
}
|
||||
|
||||
fn isolated_root(&self) -> HostPath {
|
||||
pub(super) fn isolated_root(&self) -> HostPath {
|
||||
HostPath::new(self.isolated_label.as_os_str().as_encoded_bytes().to_vec())
|
||||
}
|
||||
}
|
||||
|
|
@ -212,7 +221,7 @@ fn global_excludes_path(workspace: &Path) -> Result<Option<PathBuf>, ColdStartEr
|
|||
parse_global_excludes_path(workspace, &output)
|
||||
}
|
||||
|
||||
fn parse_global_excludes_path(
|
||||
pub(super) fn parse_global_excludes_path(
|
||||
workspace: &Path,
|
||||
output: &[u8],
|
||||
) -> Result<Option<PathBuf>, ColdStartError> {
|
||||
|
|
@ -242,4 +251,3 @@ fn default_global_excludes_path() -> Option<PathBuf> {
|
|||
})
|
||||
.map(|home| home.join("git/ignore"))
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,10 @@
|
|||
use super::{
|
||||
AsFd, AtFlags, ColdStartError, ConfinedPathPlan, ContainerId, File, FileExt, FileType, Mode,
|
||||
MountIdentity, OFlags, OsStr, OsString, OsStringExt, OwnedFd, PathBuf, PermissionsExt, RawDir,
|
||||
RegularFileMailbox, ResolveFlags, Write, failure, fchmod, fstat, mkdirat, mount_identity,
|
||||
openat2, statat, unlinkat,
|
||||
};
|
||||
|
||||
pub(crate) struct RuntimeStaging {
|
||||
runsc_root: OwnedFd,
|
||||
bundles: OwnedFd,
|
||||
|
|
@ -60,7 +67,7 @@ impl RuntimeStaging {
|
|||
Ok((credentials, requests, results))
|
||||
}
|
||||
|
||||
fn create(
|
||||
pub(crate) fn create(
|
||||
runtime_domain: &ConfinedPathPlan,
|
||||
container_id: ContainerId,
|
||||
escalation_client: &File,
|
||||
|
|
@ -145,7 +152,7 @@ impl RuntimeStaging {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn write_bundle(&self, bytes: &[u8]) -> Result<(), ColdStartError> {
|
||||
pub(crate) fn write_bundle(&self, bytes: &[u8]) -> Result<(), ColdStartError> {
|
||||
let descriptor = openat2(
|
||||
&self.bundle,
|
||||
"config.json",
|
||||
|
|
@ -208,13 +215,13 @@ fn require_executable_regular_file(source: &File, label: &str) -> Result<(), Col
|
|||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
struct FileIdentity {
|
||||
pub(super) struct FileIdentity {
|
||||
device: u64,
|
||||
inode: u64,
|
||||
}
|
||||
|
||||
impl FileIdentity {
|
||||
fn capture(descriptor: impl AsFd) -> Result<Self, ColdStartError> {
|
||||
pub(super) fn capture(descriptor: impl AsFd) -> Result<Self, ColdStartError> {
|
||||
let metadata = fstat(descriptor).map_err(failure)?;
|
||||
Ok(Self {
|
||||
device: metadata.st_dev,
|
||||
|
|
@ -250,7 +257,7 @@ impl Drop for RuntimeStaging {
|
|||
}
|
||||
}
|
||||
|
||||
fn open_or_create_private_directory(
|
||||
pub(super) fn open_or_create_private_directory(
|
||||
parent: impl AsFd,
|
||||
name: impl AsRef<std::ffi::OsStr>,
|
||||
invoking_uid: u32,
|
||||
|
|
@ -293,7 +300,7 @@ fn require_private_directory(
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn require_private_file(
|
||||
pub(super) fn require_private_file(
|
||||
descriptor: impl AsFd,
|
||||
invoking_uid: u32,
|
||||
mode: u32,
|
||||
|
|
@ -311,7 +318,7 @@ fn require_private_file(
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn remove_staging_directory(
|
||||
pub(super) fn remove_staging_directory(
|
||||
parent: impl AsFd,
|
||||
name: &std::ffi::OsStr,
|
||||
directory: impl AsFd,
|
||||
|
|
@ -369,18 +376,18 @@ fn remove_children(directory: impl AsFd) -> std::io::Result<()> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn confined_resolve_flags() -> ResolveFlags {
|
||||
pub(super) fn confined_resolve_flags() -> ResolveFlags {
|
||||
ResolveFlags::BENEATH
|
||||
| ResolveFlags::NO_MAGICLINKS
|
||||
| ResolveFlags::NO_SYMLINKS
|
||||
| ResolveFlags::NO_XDEV
|
||||
}
|
||||
|
||||
fn host_path(bytes: &[u8]) -> PathBuf {
|
||||
pub(super) fn host_path(bytes: &[u8]) -> PathBuf {
|
||||
PathBuf::from(OsString::from_vec(bytes.to_vec()))
|
||||
}
|
||||
|
||||
fn hex(bytes: &[u8]) -> String {
|
||||
pub(super) fn hex(bytes: &[u8]) -> String {
|
||||
use std::fmt::Write as _;
|
||||
|
||||
bytes.iter().fold(String::new(), |mut output, byte| {
|
||||
|
|
@ -388,4 +395,3 @@ fn hex(bytes: &[u8]) -> String {
|
|||
output
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,36 +1,33 @@
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::fs::File;
|
||||
use std::os::fd::OwnedFd;
|
||||
use std::fs::File;
|
||||
use std::os::fd::OwnedFd;
|
||||
|
||||
use rustix::pipe::{PipeFlags, pipe_with};
|
||||
use rustix::pipe::{PipeFlags, pipe_with};
|
||||
|
||||
use super::BootstrapOwnerGuard;
|
||||
use super::BootstrapOwnerGuard;
|
||||
|
||||
#[test]
|
||||
fn bootstrap_owner_guard_tracks_the_exact_inherited_pipe() {
|
||||
let (reader, writer) = pipe_with(PipeFlags::CLOEXEC).unwrap();
|
||||
let guard = BootstrapOwnerGuard::from_inherited(reader).unwrap();
|
||||
assert!(guard.is_live().unwrap());
|
||||
drop(writer);
|
||||
assert!(!guard.is_live().unwrap());
|
||||
}
|
||||
#[test]
|
||||
fn bootstrap_owner_guard_tracks_the_exact_inherited_pipe() {
|
||||
let (reader, writer) = pipe_with(PipeFlags::CLOEXEC).unwrap();
|
||||
let guard = BootstrapOwnerGuard::from_inherited(reader).unwrap();
|
||||
assert!(guard.is_live().unwrap());
|
||||
drop(writer);
|
||||
assert!(!guard.is_live().unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bootstrap_owner_guard_refuses_non_pipe_authority() {
|
||||
let descriptor: OwnedFd = File::open("/dev/null").unwrap().into();
|
||||
assert!(BootstrapOwnerGuard::from_inherited(descriptor).is_err());
|
||||
}
|
||||
#[test]
|
||||
fn bootstrap_owner_guard_refuses_non_pipe_authority() {
|
||||
let descriptor: OwnedFd = File::open("/dev/null").unwrap().into();
|
||||
assert!(BootstrapOwnerGuard::from_inherited(descriptor).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn repository_relative_global_excludes_are_workspace_relative() {
|
||||
let root = tempfile::tempdir().unwrap();
|
||||
let workspace = root.path().join("workspace");
|
||||
#[test]
|
||||
fn repository_relative_global_excludes_are_workspace_relative() {
|
||||
let root = tempfile::tempdir().unwrap();
|
||||
let workspace = root.path().join("workspace");
|
||||
|
||||
assert_eq!(
|
||||
super::parse_global_excludes_path(&workspace, b"policy/ignore\n").unwrap(),
|
||||
Some(workspace.join("policy/ignore"))
|
||||
);
|
||||
assert!(super::parse_global_excludes_path(&workspace, b"bad\0path\n").is_err());
|
||||
}
|
||||
assert_eq!(
|
||||
super::parse_global_excludes_path(&workspace, b"policy/ignore\n").unwrap(),
|
||||
Some(workspace.join("policy/ignore"))
|
||||
);
|
||||
assert!(super::parse_global_excludes_path(&workspace, b"bad\0path\n").is_err());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -649,7 +649,26 @@ impl SessionLifecycleAdapter for Arc<SessionRegistry> {
|
|||
}
|
||||
}
|
||||
|
||||
include!("control/execution.rs");
|
||||
include!("control/staging.rs");
|
||||
include!("control/publication.rs");
|
||||
include!("control/tests.rs");
|
||||
mod execution;
|
||||
mod publication;
|
||||
mod staging;
|
||||
|
||||
pub(crate) use execution::AcceptedControlConnection;
|
||||
use execution::{ControlPaths, ExecutionUnavailable, InteractiveExecutionUnavailable};
|
||||
pub use execution::{
|
||||
InteractiveControlSession, InteractiveSessionExecutor, NonInteractiveSessionExecutor,
|
||||
PublishedControlFace, RunscNonInteractiveExecutor,
|
||||
};
|
||||
#[cfg(test)]
|
||||
use execution::{NonInteractiveCommandRunner, hex_lower};
|
||||
#[cfg(test)]
|
||||
use publication::require_peer_uid;
|
||||
use publication::{
|
||||
certificate_digest, enrollment_binding, is_live_session, leaf_parameters, random_signing_key,
|
||||
remove_if_identity_matches, sha256, short_identity,
|
||||
};
|
||||
pub use staging::StagedControlFace;
|
||||
use staging::publication_recovery_ownership;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
|
|
|||
|
|
@ -1,15 +1,26 @@
|
|||
struct ControlPaths {
|
||||
root: PathBuf,
|
||||
root_file: File,
|
||||
root_identity: FileIdentity,
|
||||
socket: PathBuf,
|
||||
identity: PathBuf,
|
||||
staged_socket: PathBuf,
|
||||
staged_identity: PathBuf,
|
||||
use super::{
|
||||
Arc, AsFd, CONTROL_SOCKET_NAME, ContainerId, ControlError, File, FileIdentity,
|
||||
InteractiveSessionBatch, InteractiveSessionEvent, InteractiveSessionRequest, MetadataExt, Mode,
|
||||
NonInteractiveSessionMessage, NonInteractiveSessionRequest, OFlags, OpenOptions, OsStr,
|
||||
OsString, OsStringExt, PUBLICATION_IDENTITY_NAME, Path, PathBuf, ProcessExit,
|
||||
ProtocolPublicationIdentity, Read, RunscCommand, STAGED_CONTROL_SOCKET_NAME,
|
||||
STAGED_PUBLICATION_IDENTITY_NAME, ServerConfig, SessionExit, SessionId, SessionRegistry,
|
||||
TargetCommand, TrustAnchor, Write, child_broker, fchmod, fmt, io, openat,
|
||||
remove_if_identity_matches,
|
||||
};
|
||||
|
||||
pub(super) struct ControlPaths {
|
||||
pub(super) root: PathBuf,
|
||||
pub(super) root_file: File,
|
||||
pub(super) root_identity: FileIdentity,
|
||||
pub(super) socket: PathBuf,
|
||||
pub(super) identity: PathBuf,
|
||||
pub(super) staged_socket: PathBuf,
|
||||
pub(super) staged_identity: PathBuf,
|
||||
}
|
||||
|
||||
impl ControlPaths {
|
||||
fn open(root: &Path, uid: u32) -> Result<Self, ControlError> {
|
||||
pub(super) fn open(root: &Path, uid: u32) -> Result<Self, ControlError> {
|
||||
if !root.is_absolute() {
|
||||
return Err(ControlError::InvalidPath(
|
||||
"runtime Domain root is not absolute",
|
||||
|
|
@ -43,7 +54,7 @@ impl ControlPaths {
|
|||
})
|
||||
}
|
||||
|
||||
fn validate_root(&self, uid: u32) -> Result<(), ControlError> {
|
||||
pub(super) fn validate_root(&self, uid: u32) -> Result<(), ControlError> {
|
||||
let descriptor = self.root_file.metadata()?;
|
||||
let current = std::fs::symlink_metadata(&self.root)?;
|
||||
if FileIdentity::from_metadata(&descriptor) != self.root_identity
|
||||
|
|
@ -57,7 +68,7 @@ impl ControlPaths {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn create_named_identity(
|
||||
pub(super) fn create_named_identity(
|
||||
&self,
|
||||
name: &str,
|
||||
bytes: &[u8],
|
||||
|
|
@ -90,7 +101,7 @@ impl ControlPaths {
|
|||
Ok((file, identity))
|
||||
}
|
||||
|
||||
fn validate_identity(
|
||||
pub(super) fn validate_identity(
|
||||
&self,
|
||||
file: &File,
|
||||
expected: FileIdentity,
|
||||
|
|
@ -100,7 +111,7 @@ impl ControlPaths {
|
|||
self.validate_identity_at(&self.identity, file, expected, uid, expected_length)
|
||||
}
|
||||
|
||||
fn validate_identity_at(
|
||||
pub(super) fn validate_identity_at(
|
||||
&self,
|
||||
path: &Path,
|
||||
file: &File,
|
||||
|
|
@ -131,23 +142,23 @@ impl ControlPaths {
|
|||
}
|
||||
|
||||
pub struct PublishedControlFace {
|
||||
listener: std::os::unix::net::UnixListener,
|
||||
paths: ControlPaths,
|
||||
socket_identity: FileIdentity,
|
||||
identity_file: File,
|
||||
identity_file_identity: FileIdentity,
|
||||
publication_identity: ProtocolPublicationIdentity,
|
||||
publication_bytes: Vec<u8>,
|
||||
uid: u32,
|
||||
anchor: TrustAnchor,
|
||||
server_config: Arc<ServerConfig>,
|
||||
registry: Arc<SessionRegistry>,
|
||||
executor: Arc<dyn NonInteractiveSessionExecutor>,
|
||||
interactive_executor: Arc<dyn InteractiveSessionExecutor>,
|
||||
pub(super) listener: std::os::unix::net::UnixListener,
|
||||
pub(super) paths: ControlPaths,
|
||||
pub(super) socket_identity: FileIdentity,
|
||||
pub(super) identity_file: File,
|
||||
pub(super) identity_file_identity: FileIdentity,
|
||||
pub(super) publication_identity: ProtocolPublicationIdentity,
|
||||
pub(super) publication_bytes: Vec<u8>,
|
||||
pub(super) uid: u32,
|
||||
pub(super) anchor: TrustAnchor,
|
||||
pub(super) server_config: Arc<ServerConfig>,
|
||||
pub(super) registry: Arc<SessionRegistry>,
|
||||
pub(super) executor: Arc<dyn NonInteractiveSessionExecutor>,
|
||||
pub(super) interactive_executor: Arc<dyn InteractiveSessionExecutor>,
|
||||
}
|
||||
|
||||
pub(crate) struct AcceptedControlConnection {
|
||||
socket: std::os::unix::net::UnixStream,
|
||||
pub(super) socket: std::os::unix::net::UnixStream,
|
||||
}
|
||||
|
||||
impl AcceptedControlConnection {
|
||||
|
|
@ -166,7 +177,7 @@ pub trait NonInteractiveSessionExecutor: Send + Sync {
|
|||
) -> Result<SessionExit, ControlError>;
|
||||
}
|
||||
|
||||
struct ExecutionUnavailable;
|
||||
pub(super) struct ExecutionUnavailable;
|
||||
|
||||
impl NonInteractiveSessionExecutor for ExecutionUnavailable {
|
||||
fn execute(
|
||||
|
|
@ -196,7 +207,7 @@ pub trait InteractiveSessionExecutor: Send + Sync {
|
|||
) -> Result<Box<dyn InteractiveControlSession>, ControlError>;
|
||||
}
|
||||
|
||||
struct InteractiveExecutionUnavailable;
|
||||
pub(super) struct InteractiveExecutionUnavailable;
|
||||
|
||||
impl InteractiveSessionExecutor for InteractiveExecutionUnavailable {
|
||||
fn start(
|
||||
|
|
@ -208,13 +219,13 @@ impl InteractiveSessionExecutor for InteractiveExecutionUnavailable {
|
|||
}
|
||||
|
||||
pub struct RunscNonInteractiveExecutor {
|
||||
program: OsString,
|
||||
global_arguments: Vec<OsString>,
|
||||
container: OsString,
|
||||
runner: Arc<dyn NonInteractiveCommandRunner>,
|
||||
pub(super) program: OsString,
|
||||
pub(super) global_arguments: Vec<OsString>,
|
||||
pub(super) container: OsString,
|
||||
pub(super) runner: Arc<dyn NonInteractiveCommandRunner>,
|
||||
}
|
||||
|
||||
trait NonInteractiveCommandRunner: Send + Sync {
|
||||
pub(super) trait NonInteractiveCommandRunner: Send + Sync {
|
||||
fn run(
|
||||
&self,
|
||||
target: &TargetCommand,
|
||||
|
|
@ -343,7 +354,7 @@ fn emit_available(
|
|||
}
|
||||
}
|
||||
|
||||
fn hex_lower(bytes: &[u8]) -> String {
|
||||
pub(super) fn hex_lower(bytes: &[u8]) -> String {
|
||||
use fmt::Write as _;
|
||||
|
||||
bytes.iter().fold(String::new(), |mut output, byte| {
|
||||
|
|
@ -351,4 +362,3 @@ fn hex_lower(bytes: &[u8]) -> String {
|
|||
output
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,20 @@
|
|||
use super::{
|
||||
AcceptedControlConnection, Arc, AsFd, AsRawFd, CONTROL_ALPN, CONTROL_SOCKET_NAME,
|
||||
CertificateDigest, CertificateParams, ClientConfig, ClientConnection, ClientEnrollmentResponse,
|
||||
ClientId, ConfinedPathPlan, ControlClient, ControlError, ControlPaths, ControlRequest,
|
||||
ControlResponse, ControlSessionAdmission, Digest, DistinguishedName, DnType, Duration,
|
||||
ExecutionUnavailable, ExtendedKeyUsagePurpose, File, FileExt, FileIdentity, FileTypeExt,
|
||||
GenerationIdentity, HostCoordinatorDomainStore, InteractiveSessionBatch,
|
||||
InteractiveSessionEvent, InteractiveSessionExecutor, InteractiveSessionMessage, IsCa,
|
||||
KeyUsagePurpose, MAX_CONTROL_BYTES, MAX_ENROLLMENT_BYTES, MetadataExt, Mode,
|
||||
NonInteractiveSessionExecutor, NonInteractiveSessionMessage, OFlags, OffsetDateTime,
|
||||
PUBLICATION_IDENTITY_NAME, Path, PathBuf, PrivateKeyDer, ProtocolPublicationIdentity,
|
||||
ProtocolVersion, PublicationOwnership, PublishedControlFace, Read, ServerConnection,
|
||||
SessionBinding, SessionId, SessionPhase, SessionRegistrationResponse, SessionRegistry, Sha256,
|
||||
SigningKey, StagedControlFace, TlsCredential, TrustAnchor, Write, decode, encode, fmt, io,
|
||||
issue_session_capability, openat, publication_recovery_ownership, socket_peercred,
|
||||
};
|
||||
|
||||
impl PublishedControlFace {
|
||||
pub(crate) fn recovery_ownership(&self) -> Result<PublicationOwnership, ControlError> {
|
||||
publication_recovery_ownership(&self.paths)
|
||||
|
|
@ -511,9 +528,7 @@ impl PublishedControlFace {
|
|||
/// # Errors
|
||||
/// Returns framing, size, decoding, or TLS I/O failures.
|
||||
pub fn receive<
|
||||
Response: lazaret_core::records::SchemaType
|
||||
+ serde::de::DeserializeOwned
|
||||
+ serde::Serialize,
|
||||
Response: lazaret_core::records::SchemaType + serde::de::DeserializeOwned + serde::Serialize,
|
||||
>(
|
||||
stream: &mut impl Read,
|
||||
) -> Result<Response, ControlError> {
|
||||
|
|
@ -552,7 +567,7 @@ fn remove_exact_if_identity_matches(
|
|||
}
|
||||
}
|
||||
|
||||
fn remove_if_identity_matches(path: &Path, expected: FileIdentity) {
|
||||
pub(super) fn remove_if_identity_matches(path: &Path, expected: FileIdentity) {
|
||||
if std::fs::symlink_metadata(path)
|
||||
.ok()
|
||||
.is_some_and(|metadata| FileIdentity::from_metadata(&metadata) == expected)
|
||||
|
|
@ -632,7 +647,7 @@ fn require_interactive_batch(
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn leaf_parameters(
|
||||
pub(super) fn leaf_parameters(
|
||||
common_name: &str,
|
||||
usage: ExtendedKeyUsagePurpose,
|
||||
now: OffsetDateTime,
|
||||
|
|
@ -650,13 +665,13 @@ fn leaf_parameters(
|
|||
Ok(params)
|
||||
}
|
||||
|
||||
fn random_signing_key() -> Result<SigningKey, ControlError> {
|
||||
pub(super) fn random_signing_key() -> Result<SigningKey, ControlError> {
|
||||
let mut bytes = [0_u8; 32];
|
||||
getrandom::fill(&mut bytes).map_err(|_| ControlError::InvalidCertificateProfile)?;
|
||||
Ok(SigningKey::from_bytes(&bytes))
|
||||
}
|
||||
|
||||
fn enrollment_binding(
|
||||
pub(super) fn enrollment_binding(
|
||||
generation_identity: &GenerationIdentity,
|
||||
cli_id: ClientId,
|
||||
) -> Result<String, ControlError> {
|
||||
|
|
@ -664,15 +679,15 @@ fn enrollment_binding(
|
|||
.map_err(ControlError::from)
|
||||
}
|
||||
|
||||
fn certificate_digest(certificate: impl AsRef<[u8]>) -> CertificateDigest {
|
||||
pub(super) fn certificate_digest(certificate: impl AsRef<[u8]>) -> CertificateDigest {
|
||||
CertificateDigest::new(sha256(certificate.as_ref()))
|
||||
}
|
||||
|
||||
fn sha256(bytes: &[u8]) -> [u8; 32] {
|
||||
pub(super) fn sha256(bytes: &[u8]) -> [u8; 32] {
|
||||
Sha256::digest(bytes).into()
|
||||
}
|
||||
|
||||
fn short_identity(bytes: &[u8; 32]) -> String {
|
||||
pub(super) fn short_identity(bytes: &[u8; 32]) -> String {
|
||||
hex(&bytes[..8])
|
||||
}
|
||||
|
||||
|
|
@ -685,7 +700,7 @@ fn hex(bytes: &[u8]) -> String {
|
|||
encoded
|
||||
}
|
||||
|
||||
fn require_peer_uid(actual: u32, expected: u32) -> Result<(), ControlError> {
|
||||
pub(super) fn require_peer_uid(actual: u32, expected: u32) -> Result<(), ControlError> {
|
||||
if actual == expected {
|
||||
Ok(())
|
||||
} else {
|
||||
|
|
@ -693,7 +708,7 @@ fn require_peer_uid(actual: u32, expected: u32) -> Result<(), ControlError> {
|
|||
}
|
||||
}
|
||||
|
||||
fn is_live_session(phase: SessionPhase) -> bool {
|
||||
pub(super) fn is_live_session(phase: SessionPhase) -> bool {
|
||||
matches!(
|
||||
phase,
|
||||
SessionPhase::Registering
|
||||
|
|
@ -704,9 +719,7 @@ fn is_live_session(phase: SessionPhase) -> bool {
|
|||
}
|
||||
|
||||
fn read_frame<
|
||||
Record: lazaret_core::records::SchemaType
|
||||
+ serde::de::DeserializeOwned
|
||||
+ serde::Serialize,
|
||||
Record: lazaret_core::records::SchemaType + serde::de::DeserializeOwned + serde::Serialize,
|
||||
>(
|
||||
reader: &mut impl Read,
|
||||
maximum: usize,
|
||||
|
|
|
|||
|
|
@ -1,3 +1,13 @@
|
|||
use super::{
|
||||
Arc, CONTROL_SOCKET_NAME, ControlError, ControlPaths, DeviceId, ExecutionUnavailable, File,
|
||||
FileIdentity, FileTypeExt, HostPath, InodeId, InteractiveExecutionUnavailable,
|
||||
InteractiveSessionExecutor, MetadataExt, MountIdentity, NonInteractiveSessionExecutor,
|
||||
PUBLICATION_IDENTITY_NAME, Path, PermissionsExt, ProtocolPublicationIdentity,
|
||||
PublicationOwnership, PublishedControlFace, STAGED_CONTROL_SOCKET_NAME,
|
||||
STAGED_PUBLICATION_IDENTITY_NAME, ServerConfig, SessionRegistry, TrustAnchor, encode,
|
||||
mount_identity, remove_if_identity_matches,
|
||||
};
|
||||
|
||||
pub struct StagedControlFace {
|
||||
listener: Option<std::os::unix::net::UnixListener>,
|
||||
paths: ControlPaths,
|
||||
|
|
@ -194,7 +204,7 @@ impl Drop for StagedControlFace {
|
|||
}
|
||||
}
|
||||
|
||||
fn publication_recovery_ownership(
|
||||
pub(super) fn publication_recovery_ownership(
|
||||
paths: &ControlPaths,
|
||||
) -> Result<PublicationOwnership, ControlError> {
|
||||
let socket_path = if paths.staged_socket.exists() {
|
||||
|
|
@ -233,4 +243,3 @@ fn publication_path_identity(
|
|||
inode: InodeId::new(metadata.ino()),
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,193 +1,188 @@
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::ffi::OsString;
|
||||
use std::sync::Arc;
|
||||
use std::thread;
|
||||
use std::ffi::OsString;
|
||||
use std::sync::Arc;
|
||||
use std::thread;
|
||||
|
||||
use lazaret_core::identity::{
|
||||
ConfigurationFingerprint, CoordinatorId, DomainId, GenerationId, SandboxId,
|
||||
};
|
||||
use rustls::pki_types::{CertificateDer, PrivateKeyDer, PrivatePkcs8KeyDer, ServerName};
|
||||
use lazaret_core::identity::{
|
||||
ConfigurationFingerprint, CoordinatorId, DomainId, GenerationId, SandboxId,
|
||||
};
|
||||
use rustls::pki_types::{CertificateDer, PrivateKeyDer, PrivatePkcs8KeyDer, ServerName};
|
||||
|
||||
use super::*;
|
||||
use super::*;
|
||||
|
||||
fn generation() -> GenerationIdentity {
|
||||
GenerationIdentity {
|
||||
agent_profile: "trusted".into(),
|
||||
domain_id: DomainId::new([1; 32]),
|
||||
generation_id: GenerationId::new(7),
|
||||
configuration_fingerprint: ConfigurationFingerprint::new([2; 32]),
|
||||
coordinator_id: CoordinatorId::new([3; 32]),
|
||||
sandbox_id: SandboxId::new([4; 32]),
|
||||
}
|
||||
}
|
||||
|
||||
fn client_config_with_certificate(
|
||||
anchor: &TrustAnchor,
|
||||
certificate: CertificateDer<'static>,
|
||||
private_key: PrivatePkcs8KeyDer<'static>,
|
||||
) -> ClientConfig {
|
||||
let mut roots = RootCertStore::empty();
|
||||
roots.add(anchor.tls_ca_der.clone()).unwrap();
|
||||
let mut config = ClientConfig::builder_with_protocol_versions(&[&rustls::version::TLS13])
|
||||
.with_root_certificates(roots)
|
||||
.with_client_auth_cert(vec![certificate], PrivateKeyDer::Pkcs8(private_key))
|
||||
.unwrap();
|
||||
config.alpn_protocols = vec![CONTROL_ALPN.to_vec()];
|
||||
config.resumption = rustls::client::Resumption::disabled();
|
||||
config
|
||||
}
|
||||
|
||||
fn handshake_pair(
|
||||
server_config: ServerConfig,
|
||||
client_config: ClientConfig,
|
||||
) -> (Result<(), rustls::Error>, Result<(), rustls::Error>) {
|
||||
let (mut server_socket, mut client_socket) =
|
||||
std::os::unix::net::UnixStream::pair().unwrap();
|
||||
let server = thread::spawn(move || {
|
||||
let mut connection = ServerConnection::new(Arc::new(server_config)).unwrap();
|
||||
while connection.is_handshaking() {
|
||||
connection
|
||||
.complete_io(&mut server_socket)
|
||||
.map_err(|error| rustls::Error::General(error.to_string()))?;
|
||||
}
|
||||
Ok(())
|
||||
});
|
||||
let mut connection = ClientConnection::new(
|
||||
Arc::new(client_config),
|
||||
ServerName::try_from(CONTROL_SERVER_NAME).unwrap(),
|
||||
)
|
||||
.unwrap();
|
||||
let client = (|| {
|
||||
while connection.is_handshaking() {
|
||||
connection
|
||||
.complete_io(&mut client_socket)
|
||||
.map_err(|error| rustls::Error::General(error.to_string()))?;
|
||||
}
|
||||
Ok(())
|
||||
})();
|
||||
(server.join().unwrap(), client)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn exact_peer_uid_is_required() {
|
||||
assert!(require_peer_uid(1_000, 1_000).is_ok());
|
||||
assert!(matches!(
|
||||
require_peer_uid(1_001, 1_000),
|
||||
Err(ControlError::UnauthorizedPeer)
|
||||
));
|
||||
}
|
||||
|
||||
struct RecordingRunner {
|
||||
observed: Mutex<Option<(OsString, Vec<OsString>)>>,
|
||||
exit: ProcessExit,
|
||||
}
|
||||
|
||||
impl NonInteractiveCommandRunner for RecordingRunner {
|
||||
fn run(
|
||||
&self,
|
||||
target: &TargetCommand,
|
||||
_session_id: SessionId,
|
||||
_emit: &mut dyn FnMut(NonInteractiveSessionMessage) -> Result<(), ControlError>,
|
||||
) -> Result<ProcessExit, ControlError> {
|
||||
*self.observed.lock().unwrap() =
|
||||
Some((target.program().to_owned(), target.arguments().to_vec()));
|
||||
Ok(self.exit)
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn runsc_exec_uses_the_command_as_the_first_post_container_argument() {
|
||||
let runner = Arc::new(RecordingRunner {
|
||||
observed: Mutex::new(None),
|
||||
exit: ProcessExit {
|
||||
exit_code: Some(23),
|
||||
terminating_signal: None,
|
||||
},
|
||||
});
|
||||
let executor = RunscNonInteractiveExecutor {
|
||||
program: "runsc".into(),
|
||||
global_arguments: vec!["--root=/runtime".into()],
|
||||
container: "container-id".into(),
|
||||
runner: runner.clone(),
|
||||
};
|
||||
let session_id = SessionId::new([9; 32]);
|
||||
let exit = executor
|
||||
.execute(
|
||||
&NonInteractiveSessionRequest {
|
||||
protocol_version: ProtocolVersion::V1,
|
||||
session_id,
|
||||
argv: vec![b"printf".to_vec(), b"hello".to_vec()],
|
||||
},
|
||||
&mut |_| Ok(()),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(exit.exit_code(), Some(23));
|
||||
assert_eq!(
|
||||
runner.observed.lock().unwrap().as_ref().unwrap(),
|
||||
&(
|
||||
OsString::from("runsc"),
|
||||
vec![
|
||||
OsString::from("--root=/runtime"),
|
||||
OsString::from("exec"),
|
||||
OsString::from("-env"),
|
||||
OsString::from(format!(
|
||||
"LAZARET_SESSION_ID={}",
|
||||
hex_lower(session_id.as_bytes())
|
||||
)),
|
||||
OsString::from("container-id"),
|
||||
OsString::from("printf"),
|
||||
OsString::from("hello"),
|
||||
]
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rustls_refuses_client_leaf_with_server_role() {
|
||||
let anchor = TrustAnchor::create(generation()).unwrap();
|
||||
let key = KeyPair::generate_for(&PKCS_ED25519).unwrap();
|
||||
let certificate = leaf_parameters(
|
||||
"wrong-client-role.invalid",
|
||||
ExtendedKeyUsagePurpose::ServerAuth,
|
||||
OffsetDateTime::now_utc(),
|
||||
)
|
||||
.unwrap()
|
||||
.signed_by(&key, &anchor.tls_ca)
|
||||
.unwrap();
|
||||
let client_config = client_config_with_certificate(
|
||||
&anchor,
|
||||
certificate.der().clone(),
|
||||
PrivatePkcs8KeyDer::from(key.serialize_der()),
|
||||
);
|
||||
let (server, client) = handshake_pair(anchor.server_config().unwrap(), client_config);
|
||||
assert!(server.is_err() || client.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rustls_refuses_server_leaf_with_client_role() {
|
||||
let anchor = TrustAnchor::create(generation()).unwrap();
|
||||
let key = KeyPair::generate_for(&PKCS_ED25519).unwrap();
|
||||
let certificate = leaf_parameters(
|
||||
CONTROL_SERVER_NAME,
|
||||
ExtendedKeyUsagePurpose::ClientAuth,
|
||||
OffsetDateTime::now_utc(),
|
||||
)
|
||||
.unwrap()
|
||||
.signed_by(&key, &anchor.tls_ca)
|
||||
.unwrap();
|
||||
let server_config =
|
||||
ServerConfig::builder_with_protocol_versions(&[&rustls::version::TLS13])
|
||||
.with_no_client_auth()
|
||||
.with_single_cert(
|
||||
vec![certificate.der().clone()],
|
||||
PrivateKeyDer::Pkcs8(PrivatePkcs8KeyDer::from(key.serialize_der())),
|
||||
)
|
||||
.unwrap();
|
||||
let client_config =
|
||||
PublishedControlFace::client_config(&anchor.publication_identity(), None).unwrap();
|
||||
let (server, client) = handshake_pair(server_config, client_config);
|
||||
assert!(server.is_err() || client.is_err());
|
||||
fn generation() -> GenerationIdentity {
|
||||
GenerationIdentity {
|
||||
agent_profile: "trusted".into(),
|
||||
domain_id: DomainId::new([1; 32]),
|
||||
generation_id: GenerationId::new(7),
|
||||
configuration_fingerprint: ConfigurationFingerprint::new([2; 32]),
|
||||
coordinator_id: CoordinatorId::new([3; 32]),
|
||||
sandbox_id: SandboxId::new([4; 32]),
|
||||
}
|
||||
}
|
||||
|
||||
fn client_config_with_certificate(
|
||||
anchor: &TrustAnchor,
|
||||
certificate: CertificateDer<'static>,
|
||||
private_key: PrivatePkcs8KeyDer<'static>,
|
||||
) -> ClientConfig {
|
||||
let mut roots = RootCertStore::empty();
|
||||
roots.add(anchor.tls_ca_der.clone()).unwrap();
|
||||
let mut config = ClientConfig::builder_with_protocol_versions(&[&rustls::version::TLS13])
|
||||
.with_root_certificates(roots)
|
||||
.with_client_auth_cert(vec![certificate], PrivateKeyDer::Pkcs8(private_key))
|
||||
.unwrap();
|
||||
config.alpn_protocols = vec![CONTROL_ALPN.to_vec()];
|
||||
config.resumption = rustls::client::Resumption::disabled();
|
||||
config
|
||||
}
|
||||
|
||||
fn handshake_pair(
|
||||
server_config: ServerConfig,
|
||||
client_config: ClientConfig,
|
||||
) -> (Result<(), rustls::Error>, Result<(), rustls::Error>) {
|
||||
let (mut server_socket, mut client_socket) = std::os::unix::net::UnixStream::pair().unwrap();
|
||||
let server = thread::spawn(move || {
|
||||
let mut connection = ServerConnection::new(Arc::new(server_config)).unwrap();
|
||||
while connection.is_handshaking() {
|
||||
connection
|
||||
.complete_io(&mut server_socket)
|
||||
.map_err(|error| rustls::Error::General(error.to_string()))?;
|
||||
}
|
||||
Ok(())
|
||||
});
|
||||
let mut connection = ClientConnection::new(
|
||||
Arc::new(client_config),
|
||||
ServerName::try_from(CONTROL_SERVER_NAME).unwrap(),
|
||||
)
|
||||
.unwrap();
|
||||
let client = (|| {
|
||||
while connection.is_handshaking() {
|
||||
connection
|
||||
.complete_io(&mut client_socket)
|
||||
.map_err(|error| rustls::Error::General(error.to_string()))?;
|
||||
}
|
||||
Ok(())
|
||||
})();
|
||||
(server.join().unwrap(), client)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn exact_peer_uid_is_required() {
|
||||
assert!(require_peer_uid(1_000, 1_000).is_ok());
|
||||
assert!(matches!(
|
||||
require_peer_uid(1_001, 1_000),
|
||||
Err(ControlError::UnauthorizedPeer)
|
||||
));
|
||||
}
|
||||
|
||||
struct RecordingRunner {
|
||||
observed: Mutex<Option<(OsString, Vec<OsString>)>>,
|
||||
exit: ProcessExit,
|
||||
}
|
||||
|
||||
impl NonInteractiveCommandRunner for RecordingRunner {
|
||||
fn run(
|
||||
&self,
|
||||
target: &TargetCommand,
|
||||
_session_id: SessionId,
|
||||
_emit: &mut dyn FnMut(NonInteractiveSessionMessage) -> Result<(), ControlError>,
|
||||
) -> Result<ProcessExit, ControlError> {
|
||||
*self.observed.lock().unwrap() =
|
||||
Some((target.program().to_owned(), target.arguments().to_vec()));
|
||||
Ok(self.exit)
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn runsc_exec_uses_the_command_as_the_first_post_container_argument() {
|
||||
let runner = Arc::new(RecordingRunner {
|
||||
observed: Mutex::new(None),
|
||||
exit: ProcessExit {
|
||||
exit_code: Some(23),
|
||||
terminating_signal: None,
|
||||
},
|
||||
});
|
||||
let executor = RunscNonInteractiveExecutor {
|
||||
program: "runsc".into(),
|
||||
global_arguments: vec!["--root=/runtime".into()],
|
||||
container: "container-id".into(),
|
||||
runner: runner.clone(),
|
||||
};
|
||||
let session_id = SessionId::new([9; 32]);
|
||||
let exit = executor
|
||||
.execute(
|
||||
&NonInteractiveSessionRequest {
|
||||
protocol_version: ProtocolVersion::V1,
|
||||
session_id,
|
||||
argv: vec![b"printf".to_vec(), b"hello".to_vec()],
|
||||
},
|
||||
&mut |_| Ok(()),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(exit.exit_code(), Some(23));
|
||||
assert_eq!(
|
||||
runner.observed.lock().unwrap().as_ref().unwrap(),
|
||||
&(
|
||||
OsString::from("runsc"),
|
||||
vec![
|
||||
OsString::from("--root=/runtime"),
|
||||
OsString::from("exec"),
|
||||
OsString::from("-env"),
|
||||
OsString::from(format!(
|
||||
"LAZARET_SESSION_ID={}",
|
||||
hex_lower(session_id.as_bytes())
|
||||
)),
|
||||
OsString::from("container-id"),
|
||||
OsString::from("printf"),
|
||||
OsString::from("hello"),
|
||||
]
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rustls_refuses_client_leaf_with_server_role() {
|
||||
let anchor = TrustAnchor::create(generation()).unwrap();
|
||||
let key = KeyPair::generate_for(&PKCS_ED25519).unwrap();
|
||||
let certificate = leaf_parameters(
|
||||
"wrong-client-role.invalid",
|
||||
ExtendedKeyUsagePurpose::ServerAuth,
|
||||
OffsetDateTime::now_utc(),
|
||||
)
|
||||
.unwrap()
|
||||
.signed_by(&key, &anchor.tls_ca)
|
||||
.unwrap();
|
||||
let client_config = client_config_with_certificate(
|
||||
&anchor,
|
||||
certificate.der().clone(),
|
||||
PrivatePkcs8KeyDer::from(key.serialize_der()),
|
||||
);
|
||||
let (server, client) = handshake_pair(anchor.server_config().unwrap(), client_config);
|
||||
assert!(server.is_err() || client.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rustls_refuses_server_leaf_with_client_role() {
|
||||
let anchor = TrustAnchor::create(generation()).unwrap();
|
||||
let key = KeyPair::generate_for(&PKCS_ED25519).unwrap();
|
||||
let certificate = leaf_parameters(
|
||||
CONTROL_SERVER_NAME,
|
||||
ExtendedKeyUsagePurpose::ClientAuth,
|
||||
OffsetDateTime::now_utc(),
|
||||
)
|
||||
.unwrap()
|
||||
.signed_by(&key, &anchor.tls_ca)
|
||||
.unwrap();
|
||||
let server_config = ServerConfig::builder_with_protocol_versions(&[&rustls::version::TLS13])
|
||||
.with_no_client_auth()
|
||||
.with_single_cert(
|
||||
vec![certificate.der().clone()],
|
||||
PrivateKeyDer::Pkcs8(PrivatePkcs8KeyDer::from(key.serialize_der())),
|
||||
)
|
||||
.unwrap();
|
||||
let client_config =
|
||||
PublishedControlFace::client_config(&anchor.publication_identity(), None).unwrap();
|
||||
let (server, client) = handshake_pair(server_config, client_config);
|
||||
assert!(server.is_err() || client.is_err());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -986,5 +986,9 @@ impl EscalationCoordinator {
|
|||
}
|
||||
}
|
||||
|
||||
include!("escalation/service.rs");
|
||||
include!("escalation/local_ownership.rs");
|
||||
mod local_ownership;
|
||||
mod service;
|
||||
|
||||
pub use local_ownership::LocalExecutionOwnership;
|
||||
use local_ownership::{acknowledgment_prefix, validate_executable};
|
||||
pub(crate) use service::{EscalationService, EscalationServiceHandle, EscalationServiceInputs};
|
||||
|
|
|
|||
|
|
@ -1,10 +1,15 @@
|
|||
fn acknowledgment_prefix(request_id: RequestId) -> Vec<u8> {
|
||||
use super::{
|
||||
AsFd, ExecutionOwnershipRecorder, FileType, Mode, OsStrExt, ProcessOwnershipCommitAck,
|
||||
RequestId, StagedProcessOwnership, acknowledgment_file_name, fstat, io,
|
||||
};
|
||||
|
||||
pub(super) fn acknowledgment_prefix(request_id: RequestId) -> Vec<u8> {
|
||||
let example = acknowledgment_file_name(request_id, 0);
|
||||
let bytes = example.as_bytes();
|
||||
bytes[..bytes.len() - 26].to_vec()
|
||||
}
|
||||
|
||||
fn validate_executable(executable: impl AsFd, invoking_uid: u32) -> io::Result<()> {
|
||||
pub(super) fn validate_executable(executable: impl AsFd, invoking_uid: u32) -> io::Result<()> {
|
||||
let stat = fstat(executable)?;
|
||||
let file_type = FileType::from_raw_mode(stat.st_mode);
|
||||
let mode = Mode::from_raw_mode(stat.st_mode);
|
||||
|
|
|
|||
|
|
@ -1,3 +1,13 @@
|
|||
use super::{
|
||||
Arc, BTreeMap, DirectDeveloperExecutableResolver, Duration, EscalationCoordinator,
|
||||
EscalationError, EscalationOutcome, EscalationPhase, Instant, LocalExecutionOwnership,
|
||||
MAX_SIGNED_RECORD_BYTES, MailboxEntryIdentity, OsStr, OsStrExt, OsString,
|
||||
ProcessOwnershipContext, ProtocolPublicationIdentity, RegularFileMailbox, RequestId,
|
||||
ResolvedDomainConfiguration, SandboxCredentialBundle, SessionBinding, SessionId,
|
||||
SessionPromptTransport, SessionRegistrationResponse, SigningKey, VerifiedSession,
|
||||
acknowledgment_prefix, cancellation_file_name, io, is_ready_record, mailbox_session_name, mpsc,
|
||||
};
|
||||
|
||||
struct SessionAuthorityFiles {
|
||||
binding_name: OsString,
|
||||
binding_identity: MailboxEntryIdentity,
|
||||
|
|
@ -420,4 +430,3 @@ impl<'a> EscalationService<'a> {
|
|||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -468,5 +468,20 @@ const fn role_index(role: ParticipantRole) -> usize {
|
|||
}
|
||||
}
|
||||
|
||||
include!("recovery/reconciliation.rs");
|
||||
include!("recovery/tests.rs");
|
||||
mod reconciliation;
|
||||
|
||||
#[cfg(test)]
|
||||
use reconciliation::RecoveryBoundary;
|
||||
pub(crate) use reconciliation::recover_before_cold_start;
|
||||
use reconciliation::require_mount_absent;
|
||||
pub use reconciliation::{
|
||||
delete_runsc_container, prove_runtime_owners_absent, recovery_role_order,
|
||||
};
|
||||
#[cfg(test)]
|
||||
use reconciliation::{
|
||||
hex_lower, recover_before_cold_start_with_opener, recover_publication, role,
|
||||
seal_runtime_owners,
|
||||
};
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
|
|
|||
|
|
@ -1,3 +1,18 @@
|
|||
use super::{
|
||||
ArtifactLease, AsRawFd, AtFlags, CONTROL_SOCKET_NAME, Catalog, CatalogError, ConfinedPathType,
|
||||
DomainStoreMutation, DurableDecision, FdFlags, Generation, GenerationId,
|
||||
HostCoordinatorDomainStore, HostPath, Mode, MountIdentity, OFlags, OsStrExt,
|
||||
PUBLICATION_IDENTITY_NAME, Participant, ParticipantOwnership, ParticipantRole,
|
||||
ParticipantState, Path, PidfsProcessIdentityV1, PidfsRecoveryMatch, PreparedLowerStore,
|
||||
ResolvedDomainConfiguration, RuntimeOwnersAbsent, RuntimeProcessRole, RuntimeRecoveryOwnership,
|
||||
STAGED_CONTROL_SOCKET_NAME, STAGED_PUBLICATION_IDENTITY_NAME, SealedProcessMatch,
|
||||
SealedProcessOwner, SealedRuntimeOwners, SessionPhase, StateAnchorAbsenceObservation,
|
||||
StateAnchorCoordinator, StateAnchorCoordinatorError, StateAnchorLayout,
|
||||
StateAnchorRecoveryOwner, TargetCommand, child_broker, current_mount_identity, fcntl_setfd,
|
||||
fstat, io, match_recovery_process, mount_identity, openat, recover_state_reset,
|
||||
runtime_recovery_ownership, statat, unlinkat,
|
||||
};
|
||||
|
||||
#[must_use]
|
||||
pub fn recovery_role_order() -> [ParticipantRole; 7] {
|
||||
[
|
||||
|
|
@ -41,7 +56,7 @@ pub fn prove_runtime_owners_absent(
|
|||
seal_runtime_owners(ownership, match_recovery_process)?.prove_absent()
|
||||
}
|
||||
|
||||
fn seal_runtime_owners<Open>(
|
||||
pub(super) fn seal_runtime_owners<Open>(
|
||||
ownership: &RuntimeRecoveryOwnership,
|
||||
mut open: Open,
|
||||
) -> io::Result<SealedRuntimeOwners>
|
||||
|
|
@ -70,7 +85,7 @@ where
|
|||
})
|
||||
}
|
||||
|
||||
fn require_mount_absent(
|
||||
pub(super) fn require_mount_absent(
|
||||
path: Option<&HostPath>,
|
||||
mount: Option<MountIdentity>,
|
||||
label: &str,
|
||||
|
|
@ -138,7 +153,7 @@ where
|
|||
)
|
||||
}
|
||||
|
||||
fn recover_before_cold_start_with_opener<Failpoint, Open, Delete>(
|
||||
pub(super) fn recover_before_cold_start_with_opener<Failpoint, Open, Delete>(
|
||||
configuration: &ResolvedDomainConfiguration,
|
||||
store: &HostCoordinatorDomainStore,
|
||||
catalog: &Catalog,
|
||||
|
|
@ -316,7 +331,7 @@ where
|
|||
.map_err(|error| error.to_string())
|
||||
}
|
||||
|
||||
fn recover_publication(
|
||||
pub(super) fn recover_publication(
|
||||
configuration: &ResolvedDomainConfiguration,
|
||||
store: &HostCoordinatorDomainStore,
|
||||
generation_id: GenerationId,
|
||||
|
|
@ -815,7 +830,7 @@ fn clean_role_after_absence(
|
|||
.map_err(|error| error.to_string())
|
||||
}
|
||||
|
||||
fn role(
|
||||
pub(super) fn role(
|
||||
store: &HostCoordinatorDomainStore,
|
||||
generation_id: GenerationId,
|
||||
role_name: ParticipantRole,
|
||||
|
|
@ -830,7 +845,7 @@ fn role(
|
|||
}))
|
||||
}
|
||||
|
||||
fn hex_lower(bytes: &[u8]) -> String {
|
||||
pub(super) fn hex_lower(bytes: &[u8]) -> String {
|
||||
const HEX: &[u8; 16] = b"0123456789abcdef";
|
||||
let mut output = String::with_capacity(bytes.len() * 2);
|
||||
for byte in bytes {
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -372,7 +372,11 @@ impl<'store> SandboxCoordinator<'store> {
|
|||
}
|
||||
}
|
||||
|
||||
include!("runtime/acquisition.rs");
|
||||
include!("runtime/committed.rs");
|
||||
mod acquisition;
|
||||
mod committed;
|
||||
|
||||
pub use acquisition::{AwaitingAttachedRunsc, AwaitingSandboxReadiness};
|
||||
pub use committed::CommittedSandboxRuntime;
|
||||
use committed::{cleanup_supervisor, sandbox_participant, sandbox_role};
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
|
|
|||
|
|
@ -1,3 +1,13 @@
|
|||
use super::{
|
||||
CanonicalDerivedNetworkPolicy, CommittedSandboxRuntime, CoordinatorChannel,
|
||||
GitRoutingCoordinator, MountIdentity, NamespaceIdentity, NetworkCoordinator,
|
||||
NetworkProbeRequest, NetworkReadinessEvidence, PidfsProcessIdentityV1, PipeFlags,
|
||||
PreparedArtifactId, ProcessOwnershipCommitAck, ReachabilityExpectation, RetainedProcess,
|
||||
RuntimeSupervisorLaunch, SandboxCoordinator, SandboxCoordinatorError, SandboxHandoffAuthority,
|
||||
StagedProcessOwnership, StateAnchorCoordinator, SupervisorEvent, child_broker,
|
||||
cleanup_supervisor, current_process_identity, io, pipe_with, private_seqpacket_pair,
|
||||
};
|
||||
|
||||
pub struct AwaitingAttachedRunsc {
|
||||
supervisor: Option<RetainedProcess>,
|
||||
control: Option<CoordinatorChannel>,
|
||||
|
|
@ -562,4 +572,3 @@ impl Drop for AwaitingSandboxReadiness {
|
|||
let _ = cleanup_supervisor(&mut self.supervisor);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,17 @@
|
|||
use super::{
|
||||
AttachedRunscSignal, CoordinatorChannel, Duration, GenerationId, GitRoutingCoordinator,
|
||||
Instant, MountIdentity, NamespaceIdentity, NetworkReadinessEvidence, Participant,
|
||||
ParticipantOwnership, ParticipantRole, ParticipantState, PidfsProcessIdentityV1,
|
||||
RetainedProcess, SUPERVISOR_EXIT_TIMEOUT, SandboxCoordinator, SandboxCoordinatorError,
|
||||
SandboxOwnership, Signal, StateAnchorAbsenceObservation, SupervisorEvent, child_broker, io,
|
||||
};
|
||||
|
||||
pub struct CommittedSandboxRuntime {
|
||||
generation_id: GenerationId,
|
||||
supervisor: Option<RetainedProcess>,
|
||||
control: CoordinatorChannel,
|
||||
lifetime: Option<std::os::fd::OwnedFd>,
|
||||
network_evidence: Option<NetworkReadinessEvidence>,
|
||||
pub(super) generation_id: GenerationId,
|
||||
pub(super) supervisor: Option<RetainedProcess>,
|
||||
pub(super) control: CoordinatorChannel,
|
||||
pub(super) lifetime: Option<std::os::fd::OwnedFd>,
|
||||
pub(super) network_evidence: Option<NetworkReadinessEvidence>,
|
||||
}
|
||||
|
||||
impl CommittedSandboxRuntime {
|
||||
|
|
@ -250,7 +258,7 @@ impl Drop for CommittedSandboxRuntime {
|
|||
}
|
||||
}
|
||||
|
||||
fn sandbox_participant(
|
||||
pub(super) fn sandbox_participant(
|
||||
generation_id: GenerationId,
|
||||
state: ParticipantState,
|
||||
ownership: SandboxOwnership,
|
||||
|
|
@ -264,14 +272,17 @@ fn sandbox_participant(
|
|||
.map_err(|_| SandboxCoordinatorError::InvalidState("Sandbox participant shape is invalid"))
|
||||
}
|
||||
|
||||
fn sandbox_role(participants: &[Participant], generation_id: GenerationId) -> Option<&Participant> {
|
||||
pub(super) fn sandbox_role(
|
||||
participants: &[Participant],
|
||||
generation_id: GenerationId,
|
||||
) -> Option<&Participant> {
|
||||
participants.iter().find(|participant| {
|
||||
participant.generation_id() == generation_id
|
||||
&& participant.role() == ParticipantRole::Sandbox
|
||||
})
|
||||
}
|
||||
|
||||
fn cleanup_supervisor(
|
||||
pub(super) fn cleanup_supervisor(
|
||||
supervisor: &mut Option<RetainedProcess>,
|
||||
) -> Result<(), SandboxCoordinatorError> {
|
||||
let Some(process) = supervisor.take() else {
|
||||
|
|
@ -306,4 +317,3 @@ fn wait_and_reap_supervisor(
|
|||
child_broker().reap(process)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -668,7 +668,11 @@ struct TestTree {
|
|||
|
||||
impl TestTree {
|
||||
fn new() -> Self {
|
||||
let path = std::env::temp_dir().join(format!(
|
||||
let parent = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
|
||||
.join("../../target/test-artifacts/host-state-anchor");
|
||||
fs::create_dir_all(&parent).unwrap();
|
||||
let parent = parent.canonicalize().unwrap();
|
||||
let path = parent.join(format!(
|
||||
"lazaret-state-anchor-durable-{}-{}",
|
||||
std::process::id(),
|
||||
SEQUENCE.fetch_add(1, Ordering::Relaxed)
|
||||
|
|
|
|||
|
|
@ -1,9 +1,7 @@
|
|||
use std::collections::{HashMap, VecDeque};
|
||||
use std::ffi::OsString;
|
||||
use std::fs::File;
|
||||
use std::io::{self, Read, Write};
|
||||
use std::os::fd::AsRawFd;
|
||||
use std::os::unix::ffi::OsStringExt as _;
|
||||
use std::io;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::time::{Duration, Instant};
|
||||
|
|
@ -653,6 +651,19 @@ impl TerminalDomainStore for HostCoordinatorDomainStore {
|
|||
}
|
||||
}
|
||||
|
||||
include!("terminal/interactive.rs");
|
||||
include!("terminal/engine.rs");
|
||||
include!("terminal/runsc.rs");
|
||||
mod engine;
|
||||
mod interactive;
|
||||
mod runsc;
|
||||
|
||||
use engine::release_physical;
|
||||
use interactive::ActiveTerminal;
|
||||
#[cfg(test)]
|
||||
use interactive::RunscInteractiveControlSession;
|
||||
pub use interactive::{RunscInteractiveSessionExecutor, SessionTerminalEngine};
|
||||
pub use runsc::RunscSessionTerminalFactory;
|
||||
use runsc::{clean_terminal, copy_terminal_state, terminal_snapshot};
|
||||
#[cfg(test)]
|
||||
use runsc::{commit_running_or_cleanup, staged_terminal};
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue