Own the patched runsc artifact and bind recovery identity
This commit is contained in:
parent
c474fa4a23
commit
2c420012bd
33 changed files with 1967 additions and 130 deletions
|
|
@ -89,6 +89,8 @@ digest_identity!(
|
||||||
DerivedPolicyDigest,
|
DerivedPolicyDigest,
|
||||||
ReadinessFingerprint,
|
ReadinessFingerprint,
|
||||||
BundleDigest,
|
BundleDigest,
|
||||||
|
RunscBuildId,
|
||||||
|
RunscDigest,
|
||||||
SelectedManifestDigest,
|
SelectedManifestDigest,
|
||||||
IdMapDigest,
|
IdMapDigest,
|
||||||
CertificateDigest,
|
CertificateDigest,
|
||||||
|
|
|
||||||
|
|
@ -10,8 +10,9 @@ use crate::identity::{
|
||||||
GuestProcessGroupId, GuestProcessId, GuestProcessStartTimeTicks, HostPath, IdMapDigest,
|
GuestProcessGroupId, GuestProcessId, GuestProcessStartTimeTicks, HostPath, IdMapDigest,
|
||||||
InodeId, MountIdentity, NamespaceIdentity, NetworkSidecarId, PidfsProcessAuthorityV1,
|
InodeId, MountIdentity, NamespaceIdentity, NetworkSidecarId, PidfsProcessAuthorityV1,
|
||||||
PidfsProcessIdentityV1, PolicyId, PrepareOperationId, PreparedArtifactId, ProtocolCredentialId,
|
PidfsProcessIdentityV1, PolicyId, PrepareOperationId, PreparedArtifactId, ProtocolCredentialId,
|
||||||
ReadinessFingerprint, RequestDigest, RequestId, SandboxId, SelectedManifestDigest,
|
ReadinessFingerprint, RequestDigest, RequestId, RunscBuildId, RunscDigest, SandboxId,
|
||||||
SessionAnchorId, SessionId, SourceIdentity, StreamDigest, TrustManifestDigest,
|
SelectedManifestDigest, SessionAnchorId, SessionId, SourceIdentity, StreamDigest,
|
||||||
|
TrustManifestDigest,
|
||||||
};
|
};
|
||||||
|
|
||||||
mod configuration;
|
mod configuration;
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,8 @@ use super::{
|
||||||
DeviceId, DomainId, FileDescriptor, GenerationId, GuestProcessGroupId, GuestProcessId,
|
DeviceId, DomainId, FileDescriptor, GenerationId, GuestProcessGroupId, GuestProcessId,
|
||||||
GuestProcessStartTimeTicks, HostPath, InodeId, MountIdentity, NamespaceIdentity,
|
GuestProcessStartTimeTicks, HostPath, InodeId, MountIdentity, NamespaceIdentity,
|
||||||
NetworkReadinessEvidence, NetworkSidecarId, PidfsProcessIdentityV1, PreparedArtifactId,
|
NetworkReadinessEvidence, NetworkSidecarId, PidfsProcessIdentityV1, PreparedArtifactId,
|
||||||
ReadinessFingerprint, RecordInvariantError, RecordTransitionError, RequestId,
|
ReadinessFingerprint, RecordInvariantError, RecordTransitionError, RequestId, RunscBuildId,
|
||||||
STORAGE_CATALOG_SCHEMA_ID, STORAGE_CATALOG_SCHEMA_VERSION, SandboxId, Serialize,
|
RunscDigest, STORAGE_CATALOG_SCHEMA_ID, STORAGE_CATALOG_SCHEMA_VERSION, SandboxId, Serialize,
|
||||||
SessionAnchorId, SessionId, SourceIdentity, fmt,
|
SessionAnchorId, SessionId, SourceIdentity, fmt,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -212,6 +212,18 @@ pub enum PersistedHostProcessOwnership {
|
||||||
Unassigned,
|
Unassigned,
|
||||||
Staged(PidfsProcessIdentityV1),
|
Staged(PidfsProcessIdentityV1),
|
||||||
Committed(HostProcessIdentity),
|
Committed(HostProcessIdentity),
|
||||||
|
Runsc {
|
||||||
|
build_id: RunscBuildId,
|
||||||
|
digest: RunscDigest,
|
||||||
|
process: RunscProcessOwnership,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
|
||||||
|
pub enum RunscProcessOwnership {
|
||||||
|
Unassigned,
|
||||||
|
Staged(PidfsProcessIdentityV1),
|
||||||
|
Committed(HostProcessIdentity),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PersistedHostProcessOwnership {
|
impl PersistedHostProcessOwnership {
|
||||||
|
|
@ -230,23 +242,103 @@ impl PersistedHostProcessOwnership {
|
||||||
Self::Committed(HostProcessIdentity::pidfs_v1(identity))
|
Self::Committed(HostProcessIdentity::pidfs_v1(identity))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub const fn runsc_unassigned(build_id: RunscBuildId, digest: RunscDigest) -> Self {
|
||||||
|
Self::Runsc {
|
||||||
|
build_id,
|
||||||
|
digest,
|
||||||
|
process: RunscProcessOwnership::Unassigned,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub const fn stage_runsc(&self, identity: PidfsProcessIdentityV1) -> Option<Self> {
|
||||||
|
match self {
|
||||||
|
Self::Runsc {
|
||||||
|
build_id,
|
||||||
|
digest,
|
||||||
|
process: RunscProcessOwnership::Unassigned,
|
||||||
|
} => Some(Self::Runsc {
|
||||||
|
build_id: *build_id,
|
||||||
|
digest: *digest,
|
||||||
|
process: RunscProcessOwnership::Staged(identity),
|
||||||
|
}),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub const fn commit_runsc(&self, identity: PidfsProcessIdentityV1) -> Option<Self> {
|
||||||
|
match self {
|
||||||
|
Self::Runsc {
|
||||||
|
build_id,
|
||||||
|
digest,
|
||||||
|
process: RunscProcessOwnership::Staged(_),
|
||||||
|
} => Some(Self::Runsc {
|
||||||
|
build_id: *build_id,
|
||||||
|
digest: *digest,
|
||||||
|
process: RunscProcessOwnership::Committed(HostProcessIdentity::pidfs_v1(identity)),
|
||||||
|
}),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub const fn runsc_identity(&self) -> Option<(RunscBuildId, RunscDigest)> {
|
||||||
|
match self {
|
||||||
|
Self::Runsc {
|
||||||
|
build_id, digest, ..
|
||||||
|
} => Some((*build_id, *digest)),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub const fn is_staged(&self) -> bool {
|
pub const fn is_staged(&self) -> bool {
|
||||||
matches!(self, Self::Staged(_))
|
matches!(
|
||||||
|
self,
|
||||||
|
Self::Staged(_)
|
||||||
|
| Self::Runsc {
|
||||||
|
process: RunscProcessOwnership::Staged(_),
|
||||||
|
..
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub const fn is_unassigned(&self) -> bool {
|
pub const fn is_unassigned(&self) -> bool {
|
||||||
matches!(self, Self::Unassigned)
|
matches!(
|
||||||
|
self,
|
||||||
|
Self::Unassigned
|
||||||
|
| Self::Runsc {
|
||||||
|
process: RunscProcessOwnership::Unassigned,
|
||||||
|
..
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub const fn is_current_committed(&self) -> bool {
|
pub const fn is_current_committed(&self) -> bool {
|
||||||
matches!(self, Self::Committed(_))
|
matches!(
|
||||||
|
self,
|
||||||
|
Self::Committed(_)
|
||||||
|
| Self::Runsc {
|
||||||
|
process: RunscProcessOwnership::Committed(_),
|
||||||
|
..
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const fn is_initial_acquisition(&self) -> bool {
|
const fn is_initial_acquisition(&self) -> bool {
|
||||||
matches!(self, Self::Unassigned | Self::Staged(_))
|
matches!(
|
||||||
|
self,
|
||||||
|
Self::Unassigned
|
||||||
|
| Self::Staged(_)
|
||||||
|
| Self::Runsc {
|
||||||
|
process: RunscProcessOwnership::Unassigned | RunscProcessOwnership::Staged(_),
|
||||||
|
..
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const fn is_initial_or_current_committed(&self) -> bool {
|
const fn is_initial_or_current_committed(&self) -> bool {
|
||||||
|
|
@ -257,9 +349,21 @@ impl PersistedHostProcessOwnership {
|
||||||
/// Unassigned ownership has no positive authority.
|
/// Unassigned ownership has no positive authority.
|
||||||
pub fn authoritative_pidfs(&self) -> Result<&PidfsProcessIdentityV1, ProcessAuthorityRefusal> {
|
pub fn authoritative_pidfs(&self) -> Result<&PidfsProcessIdentityV1, ProcessAuthorityRefusal> {
|
||||||
match self {
|
match self {
|
||||||
Self::Unassigned => Err(ProcessAuthorityRefusal::Unassigned),
|
Self::Unassigned
|
||||||
Self::Staged(identity) => Ok(identity),
|
| Self::Runsc {
|
||||||
Self::Committed(identity) => Ok(identity.pidfs()),
|
process: RunscProcessOwnership::Unassigned,
|
||||||
|
..
|
||||||
|
} => Err(ProcessAuthorityRefusal::Unassigned),
|
||||||
|
Self::Staged(identity)
|
||||||
|
| Self::Runsc {
|
||||||
|
process: RunscProcessOwnership::Staged(identity),
|
||||||
|
..
|
||||||
|
} => Ok(identity),
|
||||||
|
Self::Committed(identity)
|
||||||
|
| Self::Runsc {
|
||||||
|
process: RunscProcessOwnership::Committed(identity),
|
||||||
|
..
|
||||||
|
} => Ok(identity.pidfs()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -267,9 +371,21 @@ impl PersistedHostProcessOwnership {
|
||||||
/// Unassigned or staged ownership is not serviceable.
|
/// Unassigned or staged ownership is not serviceable.
|
||||||
pub fn serviceable_pidfs(&self) -> Result<&PidfsProcessIdentityV1, ProcessOwnershipRefusal> {
|
pub fn serviceable_pidfs(&self) -> Result<&PidfsProcessIdentityV1, ProcessOwnershipRefusal> {
|
||||||
match self {
|
match self {
|
||||||
Self::Unassigned => Err(ProcessOwnershipRefusal::Unassigned),
|
Self::Unassigned
|
||||||
Self::Staged(_) => Err(ProcessOwnershipRefusal::Staged),
|
| Self::Runsc {
|
||||||
Self::Committed(identity) => Ok(identity.pidfs()),
|
process: RunscProcessOwnership::Unassigned,
|
||||||
|
..
|
||||||
|
} => Err(ProcessOwnershipRefusal::Unassigned),
|
||||||
|
Self::Staged(_)
|
||||||
|
| Self::Runsc {
|
||||||
|
process: RunscProcessOwnership::Staged(_),
|
||||||
|
..
|
||||||
|
} => Err(ProcessOwnershipRefusal::Staged),
|
||||||
|
Self::Committed(identity)
|
||||||
|
| Self::Runsc {
|
||||||
|
process: RunscProcessOwnership::Committed(identity),
|
||||||
|
..
|
||||||
|
} => Ok(identity.pidfs()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -640,6 +640,24 @@ pub(super) fn validate_process_progress(
|
||||||
old: &PersistedHostProcessOwnership,
|
old: &PersistedHostProcessOwnership,
|
||||||
next: &PersistedHostProcessOwnership,
|
next: &PersistedHostProcessOwnership,
|
||||||
) -> Result<(), RecordTransitionError> {
|
) -> Result<(), RecordTransitionError> {
|
||||||
|
if let (
|
||||||
|
PersistedHostProcessOwnership::Runsc {
|
||||||
|
build_id: old_build,
|
||||||
|
digest: old_digest,
|
||||||
|
process: old_process,
|
||||||
|
},
|
||||||
|
PersistedHostProcessOwnership::Runsc {
|
||||||
|
build_id: next_build,
|
||||||
|
digest: next_digest,
|
||||||
|
process: next_process,
|
||||||
|
},
|
||||||
|
) = (old, next)
|
||||||
|
{
|
||||||
|
if old_build != next_build || old_digest != next_digest {
|
||||||
|
return Err(RecordTransitionError::EvidenceChanged);
|
||||||
|
}
|
||||||
|
return validate_runsc_process_progress(old_process, next_process);
|
||||||
|
}
|
||||||
match (old, next) {
|
match (old, next) {
|
||||||
(
|
(
|
||||||
PersistedHostProcessOwnership::Unassigned,
|
PersistedHostProcessOwnership::Unassigned,
|
||||||
|
|
@ -672,6 +690,24 @@ pub(super) fn validate_process_progress(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn validate_runsc_process_progress(
|
||||||
|
old: &super::RunscProcessOwnership,
|
||||||
|
next: &super::RunscProcessOwnership,
|
||||||
|
) -> Result<(), RecordTransitionError> {
|
||||||
|
use super::RunscProcessOwnership::{Committed, Staged, Unassigned};
|
||||||
|
match (old, next) {
|
||||||
|
(Unassigned, Unassigned | Staged(_)) => Ok(()),
|
||||||
|
(Unassigned, Committed(_)) => Err(RecordTransitionError::ProcessStageSkipped),
|
||||||
|
(Staged(_) | Committed(_), Unassigned) | (Committed(_), Staged(_)) => {
|
||||||
|
Err(RecordTransitionError::EvidenceLost)
|
||||||
|
}
|
||||||
|
(Staged(old), Staged(next)) if old == next => Ok(()),
|
||||||
|
(Staged(old), Committed(next)) if old == next.pidfs() => Ok(()),
|
||||||
|
(Committed(old), Committed(next)) if old == next => Ok(()),
|
||||||
|
_ => Err(RecordTransitionError::EvidenceChanged),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub(super) fn validate_existing_process_progress(
|
pub(super) fn validate_existing_process_progress(
|
||||||
old: &PersistedHostProcessOwnership,
|
old: &PersistedHostProcessOwnership,
|
||||||
next: &PersistedHostProcessOwnership,
|
next: &PersistedHostProcessOwnership,
|
||||||
|
|
|
||||||
|
|
@ -16,12 +16,12 @@ use super::{
|
||||||
PreparedLowerOwnership, ProcessOwnershipCommitAck, ProcessOwnershipMessageVersion,
|
PreparedLowerOwnership, ProcessOwnershipCommitAck, ProcessOwnershipMessageVersion,
|
||||||
ProtocolCredential, ProtocolCredentialRole, ProtocolPublicationIdentity, PtyIdentity,
|
ProtocolCredential, ProtocolCredentialRole, ProtocolPublicationIdentity, PtyIdentity,
|
||||||
PublicationOwnership, ResolvedAutoApprovalRule, ResolvedVolume, Resolver, ResultStream,
|
PublicationOwnership, ResolvedAutoApprovalRule, ResolvedVolume, Resolver, ResultStream,
|
||||||
SandboxCredentialBundle, SandboxOwnership, Session, SessionBinding, SessionClose, SessionExit,
|
RunscProcessOwnership, SandboxCredentialBundle, SandboxOwnership, Session, SessionBinding,
|
||||||
SessionExitOutcome, SessionRegistrationRequest, SessionRegistrationResponse,
|
SessionClose, SessionExit, SessionExitOutcome, SessionRegistrationRequest,
|
||||||
SessionRequestCapability, SessionTerminal, SessionTerminalEvent, SessionTerminalMessage,
|
SessionRegistrationResponse, SessionRequestCapability, SessionTerminal, SessionTerminalEvent,
|
||||||
SessionTerminalState, SignedProtocolRecord, StagedProcessOwnership, StateAnchorOwnership,
|
SessionTerminalMessage, SessionTerminalState, SignedProtocolRecord, StagedProcessOwnership,
|
||||||
StateAnchorReleaseEvidence, StorageCatalog, StorageCatalogMetadata, StoreMetadata,
|
StateAnchorOwnership, StateAnchorReleaseEvidence, StorageCatalog, StorageCatalogMetadata,
|
||||||
TerminalInput, TerminalOutput, TerminalPrompt, TerminalResize, TerminalSignal,
|
StoreMetadata, TerminalInput, TerminalOutput, TerminalPrompt, TerminalResize, TerminalSignal,
|
||||||
TerminalSignalKind, TlsCredential, TlsRole, TrustManifest, TrustedHostConfigurationV1,
|
TerminalSignalKind, TlsCredential, TlsRole, TrustManifest, TrustedHostConfigurationV1,
|
||||||
};
|
};
|
||||||
use crate::protocol::{BootstrapWaitOutcome, BootstrapWaitRequest};
|
use crate::protocol::{BootstrapWaitOutcome, BootstrapWaitRequest};
|
||||||
|
|
@ -60,6 +60,7 @@ schema_types!(
|
||||||
"PidfsProcessIdentityV1" => PidfsProcessIdentityV1,
|
"PidfsProcessIdentityV1" => PidfsProcessIdentityV1,
|
||||||
"HostProcessIdentity" => HostProcessIdentity,
|
"HostProcessIdentity" => HostProcessIdentity,
|
||||||
"PersistedHostProcessOwnership" => PersistedHostProcessOwnership,
|
"PersistedHostProcessOwnership" => PersistedHostProcessOwnership,
|
||||||
|
"RunscProcessOwnership" => RunscProcessOwnership,
|
||||||
"HostProcessOwnershipSlot" => HostProcessOwnershipSlot,
|
"HostProcessOwnershipSlot" => HostProcessOwnershipSlot,
|
||||||
"ProcessOwnershipMessageVersion" => ProcessOwnershipMessageVersion,
|
"ProcessOwnershipMessageVersion" => ProcessOwnershipMessageVersion,
|
||||||
"StagedProcessOwnership" => StagedProcessOwnership,
|
"StagedProcessOwnership" => StagedProcessOwnership,
|
||||||
|
|
|
||||||
|
|
@ -11,8 +11,9 @@ use lazaret_core::identity::{
|
||||||
HostPidLocator, IdMapDigest, InodeId, MountId, MountIdentity, NamespaceIdentity,
|
HostPidLocator, IdMapDigest, InodeId, MountId, MountIdentity, NamespaceIdentity,
|
||||||
NetworkSidecarId, PidfsDeviceId, PidfsInodeId, PidfsProcessAuthorityV1, PidfsProcessIdentityV1,
|
NetworkSidecarId, PidfsDeviceId, PidfsInodeId, PidfsProcessAuthorityV1, PidfsProcessIdentityV1,
|
||||||
PolicyId, PrepareOperationId, PreparedArtifactId, ProcessStartTimeTicks, ProtocolCredentialId,
|
PolicyId, PrepareOperationId, PreparedArtifactId, ProcessStartTimeTicks, ProtocolCredentialId,
|
||||||
ReadinessFingerprint, RequestDigest, RequestId, SandboxId, SelectedManifestDigest,
|
ReadinessFingerprint, RequestDigest, RequestId, RunscBuildId, RunscDigest, SandboxId,
|
||||||
SessionAnchorId, SessionId, SourceIdentity, StreamDigest, TrustManifestDigest,
|
SelectedManifestDigest, SessionAnchorId, SessionId, SourceIdentity, StreamDigest,
|
||||||
|
TrustManifestDigest,
|
||||||
};
|
};
|
||||||
use lazaret_core::network::readiness_fingerprint as derive_readiness_fingerprint;
|
use lazaret_core::network::readiness_fingerprint as derive_readiness_fingerprint;
|
||||||
use lazaret_core::protocol::{BootstrapWaitOutcome, BootstrapWaitRequest};
|
use lazaret_core::protocol::{BootstrapWaitOutcome, BootstrapWaitRequest};
|
||||||
|
|
@ -27,6 +28,33 @@ struct SchemaCorpus {
|
||||||
digest: Sha256,
|
digest: Sha256,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn sandbox_ownership_decodes_records_without_runsc_identity() {
|
||||||
|
#[derive(Serialize)]
|
||||||
|
struct LegacySandboxOwnership {
|
||||||
|
container_id: Option<ContainerId>,
|
||||||
|
runtime_root: Option<HostPath>,
|
||||||
|
runtime_root_identity: Option<MountIdentity>,
|
||||||
|
bundle_digest: Option<BundleDigest>,
|
||||||
|
host_coordinator: PersistedHostProcessOwnership,
|
||||||
|
runtime_supervisor: PersistedHostProcessOwnership,
|
||||||
|
attached_runsc: PersistedHostProcessOwnership,
|
||||||
|
}
|
||||||
|
|
||||||
|
let encoded = postcard::to_stdvec(&LegacySandboxOwnership {
|
||||||
|
container_id: None,
|
||||||
|
runtime_root: None,
|
||||||
|
runtime_root_identity: None,
|
||||||
|
bundle_digest: None,
|
||||||
|
host_coordinator: PersistedHostProcessOwnership::unassigned(),
|
||||||
|
runtime_supervisor: PersistedHostProcessOwnership::unassigned(),
|
||||||
|
attached_runsc: PersistedHostProcessOwnership::unassigned(),
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
let decoded = postcard::from_bytes::<SandboxOwnership>(&encoded).unwrap();
|
||||||
|
assert!(decoded.attached_runsc.is_unassigned());
|
||||||
|
}
|
||||||
|
|
||||||
impl SchemaCorpus {
|
impl SchemaCorpus {
|
||||||
fn insert(&mut self, name: &'static str, encoded: &[u8]) -> bool {
|
fn insert(&mut self, name: &'static str, encoded: &[u8]) -> bool {
|
||||||
self.digest.update(name.len().to_be_bytes());
|
self.digest.update(name.len().to_be_bytes());
|
||||||
|
|
@ -529,7 +557,13 @@ fn complete_ownership(role: ParticipantRole, state: ParticipantState) -> Partici
|
||||||
bundle_digest: Some(BundleDigest::new(opaque(43))),
|
bundle_digest: Some(BundleDigest::new(opaque(43))),
|
||||||
host_coordinator: owned_process(30),
|
host_coordinator: owned_process(30),
|
||||||
runtime_supervisor: owned_process(21),
|
runtime_supervisor: owned_process(21),
|
||||||
attached_runsc: owned_process(44),
|
attached_runsc: PersistedHostProcessOwnership::Runsc {
|
||||||
|
build_id: RunscBuildId::new(opaque(44)),
|
||||||
|
digest: RunscDigest::new(opaque(45)),
|
||||||
|
process: RunscProcessOwnership::Committed(HostProcessIdentity::pidfs_v1(process(
|
||||||
|
44,
|
||||||
|
))),
|
||||||
|
},
|
||||||
}),
|
}),
|
||||||
ParticipantRole::Publication => ParticipantOwnership::Publication(PublicationOwnership {
|
ParticipantRole::Publication => ParticipantOwnership::Publication(PublicationOwnership {
|
||||||
staged_socket_path: Some(HostPath::new(b"staged-socket".to_vec())),
|
staged_socket_path: Some(HostPath::new(b"staged-socket".to_vec())),
|
||||||
|
|
|
||||||
|
|
@ -129,6 +129,7 @@ fn every_schema_shape_round_trips_and_rejects_malformed_or_unknown_versions() {
|
||||||
PersistedHostProcessOwnership::staged(pidfs_identity),
|
PersistedHostProcessOwnership::staged(pidfs_identity),
|
||||||
&mut seen,
|
&mut seen,
|
||||||
);
|
);
|
||||||
|
verify_shape(RunscProcessOwnership::Staged(pidfs_identity), &mut seen);
|
||||||
verify_shape(
|
verify_shape(
|
||||||
HostProcessOwnershipSlot::SessionTerminalExecClient(session_id()),
|
HostProcessOwnershipSlot::SessionTerminalExecClient(session_id()),
|
||||||
&mut seen,
|
&mut seen,
|
||||||
|
|
@ -425,7 +426,13 @@ fn every_schema_shape_round_trips_and_rejects_malformed_or_unknown_versions() {
|
||||||
bundle_digest: Some(BundleDigest::new(opaque(66))),
|
bundle_digest: Some(BundleDigest::new(opaque(66))),
|
||||||
host_coordinator: owned_process(30),
|
host_coordinator: owned_process(30),
|
||||||
runtime_supervisor: owned_process(21),
|
runtime_supervisor: owned_process(21),
|
||||||
attached_runsc: owned_process(67),
|
attached_runsc: PersistedHostProcessOwnership::Runsc {
|
||||||
|
build_id: RunscBuildId::new(opaque(67)),
|
||||||
|
digest: RunscDigest::new(opaque(71)),
|
||||||
|
process: RunscProcessOwnership::Committed(HostProcessIdentity::pidfs_v1(process(
|
||||||
|
67,
|
||||||
|
))),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
&mut seen,
|
&mut seen,
|
||||||
);
|
);
|
||||||
|
|
@ -642,8 +649,8 @@ persistent_dirs = []
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
seen.digest(),
|
seen.digest(),
|
||||||
[
|
[
|
||||||
23, 80, 80, 177, 148, 141, 234, 213, 240, 108, 167, 148, 252, 121, 212, 128, 5, 195,
|
54, 187, 13, 149, 227, 157, 1, 54, 234, 66, 127, 9, 34, 65, 173, 20, 214, 200, 126, 67,
|
||||||
176, 126, 71, 76, 141, 196, 174, 125, 212, 31, 115, 155, 178, 139,
|
1, 216, 152, 76, 167, 32, 177, 218, 0, 192, 175, 28,
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -52,6 +52,17 @@ fn lifecycle_corpus(digest: &mut Sha256) {
|
||||||
PersistedHostProcessOwnership::Unassigned => PersistedHostProcessOwnership::unassigned(),
|
PersistedHostProcessOwnership::Unassigned => PersistedHostProcessOwnership::unassigned(),
|
||||||
PersistedHostProcessOwnership::Staged(_) => PersistedHostProcessOwnership::staged(process(1)),
|
PersistedHostProcessOwnership::Staged(_) => PersistedHostProcessOwnership::staged(process(1)),
|
||||||
PersistedHostProcessOwnership::Committed(_) => PersistedHostProcessOwnership::committed(process(2)),
|
PersistedHostProcessOwnership::Committed(_) => PersistedHostProcessOwnership::committed(process(2)),
|
||||||
|
PersistedHostProcessOwnership::Runsc { .. } => PersistedHostProcessOwnership::runsc_unassigned(
|
||||||
|
RunscBuildId::new(opaque(3)),
|
||||||
|
RunscDigest::new(opaque(4)),
|
||||||
|
),
|
||||||
|
});
|
||||||
|
enum_corpus!(digest, RunscProcessOwnership, {
|
||||||
|
RunscProcessOwnership::Unassigned => RunscProcessOwnership::Unassigned,
|
||||||
|
RunscProcessOwnership::Staged(_) => RunscProcessOwnership::Staged(process(5)),
|
||||||
|
RunscProcessOwnership::Committed(_) => RunscProcessOwnership::Committed(
|
||||||
|
HostProcessIdentity::pidfs_v1(process(6)),
|
||||||
|
),
|
||||||
});
|
});
|
||||||
enum_corpus!(digest, ProcessOwnershipMessageVersion, {
|
enum_corpus!(digest, ProcessOwnershipMessageVersion, {
|
||||||
ProcessOwnershipMessageVersion::V1 => ProcessOwnershipMessageVersion::V1,
|
ProcessOwnershipMessageVersion::V1 => ProcessOwnershipMessageVersion::V1,
|
||||||
|
|
@ -314,8 +325,8 @@ fn every_serialized_enum_variant_has_fixed_shape_evidence() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
digest.finalize().as_slice(),
|
digest.finalize().as_slice(),
|
||||||
[
|
[
|
||||||
199, 150, 218, 48, 172, 14, 153, 234, 245, 198, 178, 249, 47, 35, 217, 83, 28, 188,
|
63, 126, 242, 211, 164, 165, 11, 185, 180, 135, 109, 135, 92, 142, 111, 169, 142, 215,
|
||||||
123, 152, 3, 15, 150, 61, 79, 255, 244, 107, 132, 27, 167, 123,
|
49, 4, 170, 169, 119, 237, 235, 161, 192, 89, 92, 93, 88, 35,
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,9 @@ use std::sync::Arc;
|
||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
|
|
||||||
use lazaret_core::configuration::ResolvedDomainConfiguration;
|
use lazaret_core::configuration::ResolvedDomainConfiguration;
|
||||||
use lazaret_core::identity::{BundleDigest, ContainerId, HostPath, MountIdentity};
|
use lazaret_core::identity::{
|
||||||
|
BundleDigest, ContainerId, HostPath, MountIdentity, RunscBuildId, RunscDigest,
|
||||||
|
};
|
||||||
use lazaret_core::network::CanonicalDerivedNetworkPolicy;
|
use lazaret_core::network::CanonicalDerivedNetworkPolicy;
|
||||||
use lazaret_core::records::Session;
|
use lazaret_core::records::Session;
|
||||||
use lazaret_core::records::{
|
use lazaret_core::records::{
|
||||||
|
|
@ -33,7 +35,7 @@ use crate::lifecycle::{
|
||||||
};
|
};
|
||||||
use crate::network::NetworkCoordinator;
|
use crate::network::NetworkCoordinator;
|
||||||
use crate::runtime::{
|
use crate::runtime::{
|
||||||
AwaitingAttachedRunsc, CommittedSandboxRuntime, SandboxCoordinator,
|
AwaitingAttachedRunsc, CommittedSandboxRuntime, SandboxCoordinator, SandboxIntent,
|
||||||
StateAnchorAbsenceObservation,
|
StateAnchorAbsenceObservation,
|
||||||
};
|
};
|
||||||
use crate::state_anchor::{StateAnchorCoordinator, StateAnchorReleaseAuthority};
|
use crate::state_anchor::{StateAnchorCoordinator, StateAnchorReleaseAuthority};
|
||||||
|
|
@ -74,6 +76,9 @@ pub struct BootstrapPlan<'a> {
|
||||||
pub container_id: ContainerId,
|
pub container_id: ContainerId,
|
||||||
pub runtime_root: PathBuf,
|
pub runtime_root: PathBuf,
|
||||||
pub bundle_digest: BundleDigest,
|
pub bundle_digest: BundleDigest,
|
||||||
|
pub runsc_program: PathBuf,
|
||||||
|
pub runsc_build_id: RunscBuildId,
|
||||||
|
pub runsc_digest: RunscDigest,
|
||||||
pub invoking_uid: u32,
|
pub invoking_uid: u32,
|
||||||
pub non_interactive_executor: Arc<dyn NonInteractiveSessionExecutor>,
|
pub non_interactive_executor: Arc<dyn NonInteractiveSessionExecutor>,
|
||||||
pub interactive_executor: Arc<dyn InteractiveSessionExecutor>,
|
pub interactive_executor: Arc<dyn InteractiveSessionExecutor>,
|
||||||
|
|
@ -263,17 +268,20 @@ impl<'a> CoordinatorLifecycleAdapter<'a> {
|
||||||
fn start_runtime_supervisor(&mut self) -> Result<(), LifecycleServiceError> {
|
fn start_runtime_supervisor(&mut self) -> Result<(), LifecycleServiceError> {
|
||||||
let sandbox = self.sandbox();
|
let sandbox = self.sandbox();
|
||||||
sandbox
|
sandbox
|
||||||
.begin_intent(
|
.begin_intent(SandboxIntent {
|
||||||
self.plan.generation_identity.generation_id,
|
generation_id: self.plan.generation_identity.generation_id,
|
||||||
self.plan.container_id,
|
container_id: self.plan.container_id,
|
||||||
&self.plan.runtime_root,
|
runtime_root: &self.plan.runtime_root,
|
||||||
self.plan
|
runtime_root_identity: self
|
||||||
|
.plan
|
||||||
.runtime_staging
|
.runtime_staging
|
||||||
.runsc_root_identity()
|
.runsc_root_identity()
|
||||||
.map_err(failure)?,
|
.map_err(failure)?,
|
||||||
self.plan.bundle_digest,
|
bundle_digest: self.plan.bundle_digest,
|
||||||
current_process_identity().map_err(failure)?,
|
runsc_build_id: self.plan.runsc_build_id,
|
||||||
)
|
runsc_digest: self.plan.runsc_digest,
|
||||||
|
host_coordinator: current_process_identity().map_err(failure)?,
|
||||||
|
})
|
||||||
.map_err(failure)?;
|
.map_err(failure)?;
|
||||||
let launch = &self.plan.runtime_launch;
|
let launch = &self.plan.runtime_launch;
|
||||||
self.awaiting_runtime = Some(
|
self.awaiting_runtime = Some(
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,9 @@ use lazaret_core::identity::{
|
||||||
};
|
};
|
||||||
use lazaret_core::records::{Domain, Session, SessionPhase};
|
use lazaret_core::records::{Domain, Session, SessionPhase};
|
||||||
use lazaret_linux::filesystem::try_exclusive_flock;
|
use lazaret_linux::filesystem::try_exclusive_flock;
|
||||||
use lazaret_runtime_supervisor::sandbox::{DOMAIN_OPEN_FILE_LIMIT, default_sandbox_layout};
|
use lazaret_runtime_supervisor::sandbox::{
|
||||||
|
DOMAIN_OPEN_FILE_LIMIT, RUNSC_EXECUTABLE_NAME, default_sandbox_layout,
|
||||||
|
};
|
||||||
use rustix::fs::{Mode, OFlags, openat};
|
use rustix::fs::{Mode, OFlags, openat};
|
||||||
use rustix::process::{Resource, Rlimit, getrlimit, setrlimit};
|
use rustix::process::{Resource, Rlimit, getrlimit, setrlimit};
|
||||||
|
|
||||||
|
|
@ -141,6 +143,7 @@ fn run_private_client_bootstrap(arguments: &[OsString]) -> Result<(), PrivateCli
|
||||||
InstalledProductPaths {
|
InstalledProductPaths {
|
||||||
escalation_client: &installed.join("lazaret-escalation-client"),
|
escalation_client: &installed.join("lazaret-escalation-client"),
|
||||||
sandbox_init: &installed.join("lazaret-sandbox-init"),
|
sandbox_init: &installed.join("lazaret-sandbox-init"),
|
||||||
|
runsc: &installed.join(RUNSC_EXECUTABLE_NAME),
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
.map_err(failure)?;
|
.map_err(failure)?;
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,9 @@ use std::path::{Path, PathBuf};
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
use lazaret_core::configuration::{ConfinedPathPlan, ResolvedDomainConfiguration};
|
use lazaret_core::configuration::{ConfinedPathPlan, ResolvedDomainConfiguration};
|
||||||
use lazaret_core::identity::{BundleDigest, ContainerId, HostPath, MountIdentity};
|
use lazaret_core::identity::{
|
||||||
|
BundleDigest, ContainerId, HostPath, MountIdentity, RunscBuildId, RunscDigest,
|
||||||
|
};
|
||||||
use lazaret_core::network::derive_ipv4_policy;
|
use lazaret_core::network::derive_ipv4_policy;
|
||||||
use lazaret_core::records::{
|
use lazaret_core::records::{
|
||||||
Generation, GenerationIdentity, HostProcessOwnershipSlot, PreparedArtifact, Session,
|
Generation, GenerationIdentity, HostProcessOwnershipSlot, PreparedArtifact, Session,
|
||||||
|
|
@ -84,6 +86,10 @@ pub struct ColdStartInputs<'a> {
|
||||||
pub invoking_uid: u32,
|
pub invoking_uid: u32,
|
||||||
pub escalation_client: &'a File,
|
pub escalation_client: &'a File,
|
||||||
pub sandbox_init: &'a File,
|
pub sandbox_init: &'a File,
|
||||||
|
pub runsc: &'a File,
|
||||||
|
pub runsc_program: &'a Path,
|
||||||
|
pub runsc_build_id: RunscBuildId,
|
||||||
|
pub runsc_digest: RunscDigest,
|
||||||
pub user_namespace_map: UserNamespaceMap,
|
pub user_namespace_map: UserNamespaceMap,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -144,6 +150,9 @@ pub struct ColdStart<'a> {
|
||||||
container_id: ContainerId,
|
container_id: ContainerId,
|
||||||
runtime_root: PathBuf,
|
runtime_root: PathBuf,
|
||||||
bundle_digest: BundleDigest,
|
bundle_digest: BundleDigest,
|
||||||
|
runsc_program: PathBuf,
|
||||||
|
runsc_build_id: RunscBuildId,
|
||||||
|
runsc_digest: RunscDigest,
|
||||||
invoking_uid: u32,
|
invoking_uid: u32,
|
||||||
staging: RuntimeStaging,
|
staging: RuntimeStaging,
|
||||||
git_routing: Option<GitRoutingLayout>,
|
git_routing: Option<GitRoutingLayout>,
|
||||||
|
|
@ -192,6 +201,7 @@ impl<'a> ColdStart<'a> {
|
||||||
.map_err(|error| failure(format!("protected runtime files: {error}")))?;
|
.map_err(|error| failure(format!("protected runtime files: {error}")))?;
|
||||||
let request = SandboxBuildRequest {
|
let request = SandboxBuildRequest {
|
||||||
configuration: inputs.configuration,
|
configuration: inputs.configuration,
|
||||||
|
runsc_program: inputs.runsc_program,
|
||||||
container_id: inputs.container_id,
|
container_id: inputs.container_id,
|
||||||
expected_network_namespace,
|
expected_network_namespace,
|
||||||
root_process: inputs.root_process,
|
root_process: inputs.root_process,
|
||||||
|
|
@ -269,6 +279,9 @@ impl<'a> ColdStart<'a> {
|
||||||
container_id: inputs.container_id,
|
container_id: inputs.container_id,
|
||||||
runtime_root: runtime_domain.join("runsc"),
|
runtime_root: runtime_domain.join("runsc"),
|
||||||
bundle_digest,
|
bundle_digest,
|
||||||
|
runsc_program: inputs.runsc_program.to_owned(),
|
||||||
|
runsc_build_id: inputs.runsc_build_id,
|
||||||
|
runsc_digest: inputs.runsc_digest,
|
||||||
invoking_uid: inputs.invoking_uid,
|
invoking_uid: inputs.invoking_uid,
|
||||||
staging,
|
staging,
|
||||||
git_routing,
|
git_routing,
|
||||||
|
|
@ -390,6 +403,9 @@ impl<'a> ColdStart<'a> {
|
||||||
container_id: self.container_id,
|
container_id: self.container_id,
|
||||||
runtime_root: self.runtime_root.clone(),
|
runtime_root: self.runtime_root.clone(),
|
||||||
bundle_digest: self.bundle_digest,
|
bundle_digest: self.bundle_digest,
|
||||||
|
runsc_program: self.runsc_program.clone(),
|
||||||
|
runsc_build_id: self.runsc_build_id,
|
||||||
|
runsc_digest: self.runsc_digest,
|
||||||
invoking_uid: self.invoking_uid,
|
invoking_uid: self.invoking_uid,
|
||||||
non_interactive_executor: Arc::new(
|
non_interactive_executor: Arc::new(
|
||||||
RunscNonInteractiveExecutor::from_run_command(
|
RunscNonInteractiveExecutor::from_run_command(
|
||||||
|
|
|
||||||
|
|
@ -271,7 +271,7 @@ impl RunscNonInteractiveExecutor {
|
||||||
container_id: ContainerId,
|
container_id: ContainerId,
|
||||||
environment: BTreeMap<String, EnvironmentValue>,
|
environment: BTreeMap<String, EnvironmentValue>,
|
||||||
) -> Result<Self, ControlError> {
|
) -> Result<Self, ControlError> {
|
||||||
if command.program() != OsStr::new("runsc") {
|
if !command.program().as_encoded_bytes().starts_with(b"/") {
|
||||||
return Err(ControlError::InvalidRequest);
|
return Err(ControlError::InvalidRequest);
|
||||||
}
|
}
|
||||||
let run = command
|
let run = command
|
||||||
|
|
|
||||||
|
|
@ -457,7 +457,7 @@ pub use reconciliation::{
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
use reconciliation::{
|
use reconciliation::{
|
||||||
hex_lower, recover_before_cold_start_with_opener, recover_publication, role,
|
hex_lower, recover_before_cold_start_with_opener, recover_publication, role,
|
||||||
seal_runtime_owners,
|
seal_runtime_owners, validate_recorded_runsc,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,15 @@
|
||||||
use std::io;
|
use std::fs::File;
|
||||||
|
use std::io::{self, Read};
|
||||||
use std::os::fd::AsRawFd;
|
use std::os::fd::AsRawFd;
|
||||||
use std::os::unix::ffi::OsStrExt;
|
use std::os::unix::ffi::OsStrExt;
|
||||||
use std::path::Path;
|
use std::os::unix::fs::PermissionsExt;
|
||||||
|
use std::path::{Component, Path};
|
||||||
|
|
||||||
use lazaret_core::configuration::{ConfinedPathType, ResolvedDomainConfiguration};
|
use lazaret_core::configuration::{ConfinedPathType, ResolvedDomainConfiguration};
|
||||||
use lazaret_core::identity::{GenerationId, HostPath, MountIdentity, PidfsProcessIdentityV1};
|
use lazaret_core::identity::{GenerationId, HostPath, MountIdentity, PidfsProcessIdentityV1};
|
||||||
use lazaret_core::records::{
|
use lazaret_core::records::{
|
||||||
ArtifactLease, DurableDecision, Generation, Participant, ParticipantOwnership, ParticipantRole,
|
ArtifactLease, DurableDecision, Generation, Participant, ParticipantOwnership, ParticipantRole,
|
||||||
ParticipantState, SessionPhase,
|
ParticipantState, SandboxOwnership, SessionPhase,
|
||||||
};
|
};
|
||||||
use lazaret_linux::filesystem::{current_mount_identity, mount_identity};
|
use lazaret_linux::filesystem::{current_mount_identity, mount_identity};
|
||||||
use lazaret_linux::process::{
|
use lazaret_linux::process::{
|
||||||
|
|
@ -16,8 +18,9 @@ use lazaret_linux::process::{
|
||||||
use lazaret_prepared_lower::PreparedLowerStore;
|
use lazaret_prepared_lower::PreparedLowerStore;
|
||||||
use lazaret_state_anchor::StateAnchorLayout;
|
use lazaret_state_anchor::StateAnchorLayout;
|
||||||
use lazaret_storage_catalog::{Catalog, CatalogError};
|
use lazaret_storage_catalog::{Catalog, CatalogError};
|
||||||
use rustix::fs::{AtFlags, Mode, OFlags, fstat, openat, statat, unlinkat};
|
use rustix::fs::{AtFlags, Mode, OFlags, ResolveFlags, fstat, openat, openat2, statat, unlinkat};
|
||||||
use rustix::io::{FdFlags, fcntl_setfd};
|
use rustix::io::{FdFlags, fcntl_setfd};
|
||||||
|
use sha2::{Digest, Sha256};
|
||||||
|
|
||||||
use crate::control::{
|
use crate::control::{
|
||||||
CONTROL_SOCKET_NAME, PUBLICATION_IDENTITY_NAME, STAGED_CONTROL_SOCKET_NAME,
|
CONTROL_SOCKET_NAME, PUBLICATION_IDENTITY_NAME, STAGED_CONTROL_SOCKET_NAME,
|
||||||
|
|
@ -176,7 +179,14 @@ where
|
||||||
prepared,
|
prepared,
|
||||||
failpoint,
|
failpoint,
|
||||||
match_recovery_process,
|
match_recovery_process,
|
||||||
|root, container| delete_runsc_container(root, container, std::ffi::OsStr::new("runsc")),
|
|root, container, sandbox| {
|
||||||
|
let executable = std::env::current_exe().map_err(|error| error.to_string())?;
|
||||||
|
let installed = executable
|
||||||
|
.parent()
|
||||||
|
.ok_or("Host Coordinator executable has no installation directory")?;
|
||||||
|
let program = validate_recorded_runsc(sandbox, installed)?;
|
||||||
|
delete_runsc_container(root, container, &program)
|
||||||
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -192,7 +202,7 @@ pub(super) fn recover_before_cold_start_with_opener<Failpoint, Open, Delete>(
|
||||||
where
|
where
|
||||||
Failpoint: FnMut(RecoveryBoundary, GenerationId) -> Result<(), String>,
|
Failpoint: FnMut(RecoveryBoundary, GenerationId) -> Result<(), String>,
|
||||||
Open: FnMut(PidfsProcessIdentityV1) -> io::Result<PidfsRecoveryMatch>,
|
Open: FnMut(PidfsProcessIdentityV1) -> io::Result<PidfsRecoveryMatch>,
|
||||||
Delete: FnMut(&std::os::fd::OwnedFd, &str) -> Result<(), String>,
|
Delete: FnMut(&std::os::fd::OwnedFd, &str, &SandboxOwnership) -> Result<(), String>,
|
||||||
{
|
{
|
||||||
recover_state_reset(configuration, store).map_err(|error| error.to_string())?;
|
recover_state_reset(configuration, store).map_err(|error| error.to_string())?;
|
||||||
failpoint(RecoveryBoundary::Snapshot, GenerationId::new(0))?;
|
failpoint(RecoveryBoundary::Snapshot, GenerationId::new(0))?;
|
||||||
|
|
@ -250,7 +260,7 @@ fn recover_generation<Failpoint, Open, Delete>(
|
||||||
where
|
where
|
||||||
Failpoint: FnMut(RecoveryBoundary, GenerationId) -> Result<(), String>,
|
Failpoint: FnMut(RecoveryBoundary, GenerationId) -> Result<(), String>,
|
||||||
Open: FnMut(PidfsProcessIdentityV1) -> io::Result<PidfsRecoveryMatch>,
|
Open: FnMut(PidfsProcessIdentityV1) -> io::Result<PidfsRecoveryMatch>,
|
||||||
Delete: FnMut(&std::os::fd::OwnedFd, &str) -> Result<(), String>,
|
Delete: FnMut(&std::os::fd::OwnedFd, &str, &SandboxOwnership) -> Result<(), String>,
|
||||||
{
|
{
|
||||||
let generation_id = generation.generation_id;
|
let generation_id = generation.generation_id;
|
||||||
let snapshot = store.snapshot().map_err(|error| error.to_string())?;
|
let snapshot = store.snapshot().map_err(|error| error.to_string())?;
|
||||||
|
|
@ -481,7 +491,7 @@ fn require_sandbox_absent(
|
||||||
configuration: &ResolvedDomainConfiguration,
|
configuration: &ResolvedDomainConfiguration,
|
||||||
store: &HostCoordinatorDomainStore,
|
store: &HostCoordinatorDomainStore,
|
||||||
generation_id: GenerationId,
|
generation_id: GenerationId,
|
||||||
delete: &mut impl FnMut(&std::os::fd::OwnedFd, &str) -> Result<(), String>,
|
delete: &mut impl FnMut(&std::os::fd::OwnedFd, &str, &SandboxOwnership) -> Result<(), String>,
|
||||||
) -> Result<(), String> {
|
) -> Result<(), String> {
|
||||||
let Some(participant) = role(store, generation_id, ParticipantRole::Sandbox)? else {
|
let Some(participant) = role(store, generation_id, ParticipantRole::Sandbox)? else {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
|
|
@ -524,7 +534,7 @@ fn require_sandbox_absent(
|
||||||
if !rustix::fs::FileType::from_raw_mode(stat.st_mode).is_dir() {
|
if !rustix::fs::FileType::from_raw_mode(stat.st_mode).is_dir() {
|
||||||
return Err("recorded runsc container changed type".into());
|
return Err("recorded runsc container changed type".into());
|
||||||
}
|
}
|
||||||
delete(&root, &container)?;
|
delete(&root, &container, sandbox)?;
|
||||||
match statat(&root, &container, AtFlags::SYMLINK_NOFOLLOW) {
|
match statat(&root, &container, AtFlags::SYMLINK_NOFOLLOW) {
|
||||||
Err(rustix::io::Errno::NOENT) => {}
|
Err(rustix::io::Errno::NOENT) => {}
|
||||||
Err(error) => return Err(error.to_string()),
|
Err(error) => return Err(error.to_string()),
|
||||||
|
|
@ -535,6 +545,50 @@ fn require_sandbox_absent(
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(super) fn validate_recorded_runsc(
|
||||||
|
sandbox: &SandboxOwnership,
|
||||||
|
installed: &Path,
|
||||||
|
) -> Result<std::ffi::OsString, String> {
|
||||||
|
let (build_id, expected) = sandbox
|
||||||
|
.attached_runsc
|
||||||
|
.runsc_identity()
|
||||||
|
.ok_or("Sandbox runsc executable identity is absent")?;
|
||||||
|
let path = installed.join(format!("lazaret-runsc-{}", hex_lower(build_id.as_bytes())));
|
||||||
|
if !path.is_absolute()
|
||||||
|
|| path
|
||||||
|
.components()
|
||||||
|
.any(|component| !matches!(component, Component::RootDir | Component::Normal(_)))
|
||||||
|
{
|
||||||
|
return Err("Sandbox runsc executable path is not absolute and normalized".into());
|
||||||
|
}
|
||||||
|
let descriptor = openat2(
|
||||||
|
rustix::fs::CWD,
|
||||||
|
&path,
|
||||||
|
OFlags::RDONLY | OFlags::NOFOLLOW | OFlags::CLOEXEC,
|
||||||
|
Mode::empty(),
|
||||||
|
ResolveFlags::NO_MAGICLINKS | ResolveFlags::NO_SYMLINKS,
|
||||||
|
)
|
||||||
|
.map_err(|error| error.to_string())?;
|
||||||
|
let mut file = File::from(descriptor);
|
||||||
|
let metadata = file.metadata().map_err(|error| error.to_string())?;
|
||||||
|
if !metadata.is_file() || metadata.permissions().mode() & 0o111 == 0 {
|
||||||
|
return Err("Sandbox runsc executable is not an executable regular file".into());
|
||||||
|
}
|
||||||
|
let mut digest = Sha256::new();
|
||||||
|
let mut buffer = vec![0_u8; 64 * 1024];
|
||||||
|
loop {
|
||||||
|
let read = file.read(&mut buffer).map_err(|error| error.to_string())?;
|
||||||
|
if read == 0 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
digest.update(&buffer[..read]);
|
||||||
|
}
|
||||||
|
if digest.finalize().as_slice() != expected.as_bytes() {
|
||||||
|
return Err("Sandbox runsc executable digest changed".into());
|
||||||
|
}
|
||||||
|
Ok(path.into_os_string())
|
||||||
|
}
|
||||||
|
|
||||||
/// Deletes one exact container beneath a retained, already-verified runsc root descriptor.
|
/// Deletes one exact container beneath a retained, already-verified runsc root descriptor.
|
||||||
///
|
///
|
||||||
/// # Errors
|
/// # Errors
|
||||||
|
|
|
||||||
|
|
@ -13,8 +13,8 @@ use lazaret_core::configuration::{
|
||||||
use lazaret_core::identity::{
|
use lazaret_core::identity::{
|
||||||
BootId, BundleDigest, ContainerId, CoordinatorId, DeviceId, HostPidLocator, IdMapDigest,
|
BootId, BundleDigest, ContainerId, CoordinatorId, DeviceId, HostPidLocator, IdMapDigest,
|
||||||
InodeId, MountId, NetworkSidecarId, PidfsDeviceId, PidfsInodeId, PidfsProcessAuthorityV1,
|
InodeId, MountId, NetworkSidecarId, PidfsDeviceId, PidfsInodeId, PidfsProcessAuthorityV1,
|
||||||
PolicyId, PrepareOperationId, PreparedArtifactId, ProcessStartTimeTicks, SandboxId,
|
PolicyId, PrepareOperationId, PreparedArtifactId, ProcessStartTimeTicks, RunscBuildId,
|
||||||
SelectedManifestDigest, SourceIdentity,
|
RunscDigest, SandboxId, SelectedManifestDigest, SourceIdentity,
|
||||||
};
|
};
|
||||||
use lazaret_core::records::{
|
use lazaret_core::records::{
|
||||||
ArtifactLease, ArtifactState, DurableDecision, GitRoutingOwnership, HelperOwnership,
|
ArtifactLease, ArtifactState, DurableDecision, GitRoutingOwnership, HelperOwnership,
|
||||||
|
|
@ -28,6 +28,7 @@ use lazaret_runtime_supervisor::sandbox::default_sandbox_layout;
|
||||||
use lazaret_state_anchor::StateAnchorLayout;
|
use lazaret_state_anchor::StateAnchorLayout;
|
||||||
use lazaret_storage_catalog::Catalog;
|
use lazaret_storage_catalog::Catalog;
|
||||||
use rustix::fs::{AtFlags, unlinkat};
|
use rustix::fs::{AtFlags, unlinkat};
|
||||||
|
use sha2::{Digest, Sha256};
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::control::{
|
use crate::control::{
|
||||||
|
|
@ -36,6 +37,34 @@ use crate::control::{
|
||||||
};
|
};
|
||||||
use crate::domain_store::{DomainStoreMutation, HostCoordinatorDomainStore};
|
use crate::domain_store::{DomainStoreMutation, HostCoordinatorDomainStore};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn recorded_runsc_identity_refuses_replacement() {
|
||||||
|
let tree = tempfile::tempdir().unwrap();
|
||||||
|
let build_id = RunscBuildId::new([13; 32]);
|
||||||
|
let program = tree
|
||||||
|
.path()
|
||||||
|
.join(format!("lazaret-runsc-{}", hex_lower(build_id.as_bytes())));
|
||||||
|
fs::copy("/bin/true", &program).unwrap();
|
||||||
|
let ownership = SandboxOwnership {
|
||||||
|
container_id: None,
|
||||||
|
runtime_root: None,
|
||||||
|
runtime_root_identity: None,
|
||||||
|
bundle_digest: None,
|
||||||
|
host_coordinator: PersistedHostProcessOwnership::unassigned(),
|
||||||
|
runtime_supervisor: PersistedHostProcessOwnership::unassigned(),
|
||||||
|
attached_runsc: PersistedHostProcessOwnership::runsc_unassigned(
|
||||||
|
build_id,
|
||||||
|
RunscDigest::new(Sha256::digest(fs::read(&program).unwrap()).into()),
|
||||||
|
),
|
||||||
|
};
|
||||||
|
assert_eq!(
|
||||||
|
validate_recorded_runsc(&ownership, tree.path()).unwrap(),
|
||||||
|
program
|
||||||
|
);
|
||||||
|
fs::copy("/bin/false", &program).unwrap();
|
||||||
|
assert!(validate_recorded_runsc(&ownership, tree.path()).is_err());
|
||||||
|
}
|
||||||
|
|
||||||
fn process(value: u64) -> PidfsProcessIdentityV1 {
|
fn process(value: u64) -> PidfsProcessIdentityV1 {
|
||||||
PidfsProcessIdentityV1::new(
|
PidfsProcessIdentityV1::new(
|
||||||
PidfsProcessAuthorityV1 {
|
PidfsProcessAuthorityV1 {
|
||||||
|
|
@ -373,7 +402,7 @@ fn recovery_preflight_opens_every_exact_owner_once() {
|
||||||
*opened.entry(identity.host_pid().get()).or_insert(0) += 1;
|
*opened.entry(identity.host_pid().get()).or_insert(0) += 1;
|
||||||
Ok(PidfsRecoveryMatch::BootMismatch)
|
Ok(PidfsRecoveryMatch::BootMismatch)
|
||||||
},
|
},
|
||||||
|root, container| {
|
|root, container, _| {
|
||||||
unlinkat(root, container, AtFlags::REMOVEDIR).map_err(|error| error.to_string())
|
unlinkat(root, container, AtFlags::REMOVEDIR).map_err(|error| error.to_string())
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
@ -460,7 +489,7 @@ fn publication_replacement_and_runsc_root_replacement_refuse_untouched() {
|
||||||
&fixture.prepared,
|
&fixture.prepared,
|
||||||
|_, _| Ok(()),
|
|_, _| Ok(()),
|
||||||
|_| Ok(PidfsRecoveryMatch::BootMismatch),
|
|_| Ok(PidfsRecoveryMatch::BootMismatch),
|
||||||
|_, _| {
|
|_, _, _| {
|
||||||
deletes += 1;
|
deletes += 1;
|
||||||
Ok(())
|
Ok(())
|
||||||
},
|
},
|
||||||
|
|
@ -509,7 +538,7 @@ impl RecoveryFixture {
|
||||||
&self.prepared,
|
&self.prepared,
|
||||||
failpoint,
|
failpoint,
|
||||||
match_recovery_process,
|
match_recovery_process,
|
||||||
|root, container| {
|
|root, container, _| {
|
||||||
unlinkat(root, container, AtFlags::REMOVEDIR).map_err(|error| error.to_string())
|
unlinkat(root, container, AtFlags::REMOVEDIR).map_err(|error| error.to_string())
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ use std::time::{Duration, Instant};
|
||||||
use crate::state_anchor::StateAnchorCoordinator;
|
use crate::state_anchor::StateAnchorCoordinator;
|
||||||
use lazaret_core::identity::{
|
use lazaret_core::identity::{
|
||||||
BundleDigest, ContainerId, GenerationId, HostPath, MountIdentity, NamespaceIdentity,
|
BundleDigest, ContainerId, GenerationId, HostPath, MountIdentity, NamespaceIdentity,
|
||||||
PidfsProcessIdentityV1, PreparedArtifactId,
|
PidfsProcessIdentityV1, PreparedArtifactId, RunscBuildId, RunscDigest,
|
||||||
};
|
};
|
||||||
use lazaret_core::network::CanonicalDerivedNetworkPolicy;
|
use lazaret_core::network::CanonicalDerivedNetworkPolicy;
|
||||||
use lazaret_core::records::{
|
use lazaret_core::records::{
|
||||||
|
|
@ -40,6 +40,18 @@ pub struct StateAnchorAbsenceObservation {
|
||||||
pub observed: MountIdentity,
|
pub observed: MountIdentity,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub struct SandboxIntent<'path> {
|
||||||
|
pub generation_id: GenerationId,
|
||||||
|
pub container_id: ContainerId,
|
||||||
|
pub runtime_root: &'path Path,
|
||||||
|
pub runtime_root_identity: MountIdentity,
|
||||||
|
pub bundle_digest: BundleDigest,
|
||||||
|
pub runsc_build_id: RunscBuildId,
|
||||||
|
pub runsc_digest: RunscDigest,
|
||||||
|
pub host_coordinator: PidfsProcessIdentityV1,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum SandboxCoordinatorError {
|
pub enum SandboxCoordinatorError {
|
||||||
Store(DomainStoreError),
|
Store(DomainStoreError),
|
||||||
|
|
@ -104,15 +116,17 @@ impl<'store> SandboxCoordinator<'store> {
|
||||||
///
|
///
|
||||||
/// # Errors
|
/// # Errors
|
||||||
/// Refuses a missing generation, changed physical identity, or existing nonmatching state.
|
/// Refuses a missing generation, changed physical identity, or existing nonmatching state.
|
||||||
pub fn begin_intent(
|
pub fn begin_intent(&self, request: SandboxIntent<'_>) -> Result<(), SandboxCoordinatorError> {
|
||||||
&self,
|
let SandboxIntent {
|
||||||
generation_id: GenerationId,
|
generation_id,
|
||||||
container_id: ContainerId,
|
container_id,
|
||||||
runtime_root: &Path,
|
runtime_root,
|
||||||
runtime_root_identity: MountIdentity,
|
runtime_root_identity,
|
||||||
bundle_digest: BundleDigest,
|
bundle_digest,
|
||||||
host_coordinator: PidfsProcessIdentityV1,
|
runsc_build_id,
|
||||||
) -> Result<(), SandboxCoordinatorError> {
|
runsc_digest,
|
||||||
|
host_coordinator,
|
||||||
|
} = request;
|
||||||
let snapshot = self.domain_store.snapshot()?;
|
let snapshot = self.domain_store.snapshot()?;
|
||||||
if !snapshot
|
if !snapshot
|
||||||
.generations
|
.generations
|
||||||
|
|
@ -130,7 +144,10 @@ impl<'store> SandboxCoordinator<'store> {
|
||||||
bundle_digest: Some(bundle_digest),
|
bundle_digest: Some(bundle_digest),
|
||||||
host_coordinator: PersistedHostProcessOwnership::committed(host_coordinator),
|
host_coordinator: PersistedHostProcessOwnership::committed(host_coordinator),
|
||||||
runtime_supervisor: PersistedHostProcessOwnership::unassigned(),
|
runtime_supervisor: PersistedHostProcessOwnership::unassigned(),
|
||||||
attached_runsc: PersistedHostProcessOwnership::unassigned(),
|
attached_runsc: PersistedHostProcessOwnership::runsc_unassigned(
|
||||||
|
runsc_build_id,
|
||||||
|
runsc_digest,
|
||||||
|
),
|
||||||
};
|
};
|
||||||
let intent = sandbox_participant(generation_id, ParticipantState::Intent, ownership)?;
|
let intent = sandbox_participant(generation_id, ParticipantState::Intent, ownership)?;
|
||||||
match sandbox_role(&snapshot.participants, generation_id) {
|
match sandbox_role(&snapshot.participants, generation_id) {
|
||||||
|
|
@ -216,7 +233,12 @@ impl<'store> SandboxCoordinator<'store> {
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
ownership.runtime_supervisor = PersistedHostProcessOwnership::committed(rs);
|
ownership.runtime_supervisor = PersistedHostProcessOwnership::committed(rs);
|
||||||
ownership.attached_runsc = PersistedHostProcessOwnership::staged(staged.identity());
|
ownership.attached_runsc = ownership
|
||||||
|
.attached_runsc
|
||||||
|
.stage_runsc(staged.identity())
|
||||||
|
.ok_or(SandboxCoordinatorError::InvalidState(
|
||||||
|
"attached runsc identity is absent",
|
||||||
|
))?;
|
||||||
self.put_intent(current.generation_id(), ownership)?;
|
self.put_intent(current.generation_id(), ownership)?;
|
||||||
Ok(ProcessOwnershipCommitAck::v1(staged))
|
Ok(ProcessOwnershipCommitAck::v1(staged))
|
||||||
}
|
}
|
||||||
|
|
@ -231,7 +253,8 @@ impl<'store> SandboxCoordinator<'store> {
|
||||||
) -> Result<(), SandboxCoordinatorError> {
|
) -> Result<(), SandboxCoordinatorError> {
|
||||||
self.require_staged(staged, HostProcessOwnershipSlot::SandboxAttachedRunsc)?;
|
self.require_staged(staged, HostProcessOwnershipSlot::SandboxAttachedRunsc)?;
|
||||||
let (_, mut ownership) = self.intent_ownership(staged.generation_id())?;
|
let (_, mut ownership) = self.intent_ownership(staged.generation_id())?;
|
||||||
if ownership.attached_runsc != PersistedHostProcessOwnership::staged(staged.identity())
|
if ownership.attached_runsc.authoritative_pidfs().ok() != Some(&staged.identity())
|
||||||
|
|| !ownership.attached_runsc.is_staged()
|
||||||
|| !ownership.runtime_supervisor.is_current_committed()
|
|| !ownership.runtime_supervisor.is_current_committed()
|
||||||
|| !ownership.host_coordinator.is_current_committed()
|
|| !ownership.host_coordinator.is_current_committed()
|
||||||
{
|
{
|
||||||
|
|
@ -239,7 +262,12 @@ impl<'store> SandboxCoordinator<'store> {
|
||||||
"Sandbox readiness does not match staged ownership",
|
"Sandbox readiness does not match staged ownership",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
ownership.attached_runsc = PersistedHostProcessOwnership::committed(staged.identity());
|
ownership.attached_runsc = ownership
|
||||||
|
.attached_runsc
|
||||||
|
.commit_runsc(staged.identity())
|
||||||
|
.ok_or(SandboxCoordinatorError::InvalidState(
|
||||||
|
"attached runsc identity is absent",
|
||||||
|
))?;
|
||||||
self.domain_store
|
self.domain_store
|
||||||
.apply(&[DomainStoreMutation::PutParticipant(sandbox_participant(
|
.apply(&[DomainStoreMutation::PutParticipant(sandbox_participant(
|
||||||
staged.generation_id(),
|
staged.generation_id(),
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,8 @@ use std::path::Path;
|
||||||
|
|
||||||
use lazaret_core::identity::{
|
use lazaret_core::identity::{
|
||||||
BootId, ConfigurationFingerprint, CoordinatorId, DomainId, HostPidLocator, NetworkSidecarId,
|
BootId, ConfigurationFingerprint, CoordinatorId, DomainId, HostPidLocator, NetworkSidecarId,
|
||||||
PidfsDeviceId, PidfsInodeId, PidfsProcessAuthorityV1, ProcessStartTimeTicks, SandboxId,
|
PidfsDeviceId, PidfsInodeId, PidfsProcessAuthorityV1, ProcessStartTimeTicks, RunscBuildId,
|
||||||
|
RunscDigest, SandboxId,
|
||||||
};
|
};
|
||||||
use lazaret_core::records::{Domain, DurableDecision, Generation};
|
use lazaret_core::records::{Domain, DurableDecision, Generation};
|
||||||
use lazaret_linux::filesystem::{current_mount_identity, try_exclusive_flock};
|
use lazaret_linux::filesystem::{current_mount_identity, try_exclusive_flock};
|
||||||
|
|
@ -87,14 +88,16 @@ fn sandbox_staging_readiness_and_release_preserve_exact_ownership() {
|
||||||
let store = store(&directory);
|
let store = store(&directory);
|
||||||
let coordinator = SandboxCoordinator::new(&store);
|
let coordinator = SandboxCoordinator::new(&store);
|
||||||
coordinator
|
coordinator
|
||||||
.begin_intent(
|
.begin_intent(SandboxIntent {
|
||||||
generation().generation_id,
|
generation_id: generation().generation_id,
|
||||||
ContainerId::new(bytes(6)),
|
container_id: ContainerId::new(bytes(6)),
|
||||||
directory.path(),
|
runtime_root: directory.path(),
|
||||||
current_mount_identity(directory.path()).unwrap(),
|
runtime_root_identity: current_mount_identity(directory.path()).unwrap(),
|
||||||
BundleDigest::new(bytes(7)),
|
bundle_digest: BundleDigest::new(bytes(7)),
|
||||||
process(100),
|
runsc_build_id: RunscBuildId::new(bytes(8)),
|
||||||
)
|
runsc_digest: RunscDigest::new(bytes(8)),
|
||||||
|
host_coordinator: process(100),
|
||||||
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let rs = staged(
|
let rs = staged(
|
||||||
HostProcessOwnershipSlot::SandboxRuntimeSupervisor,
|
HostProcessOwnershipSlot::SandboxRuntimeSupervisor,
|
||||||
|
|
@ -123,9 +126,14 @@ fn sandbox_staging_readiness_and_release_preserve_exact_ownership() {
|
||||||
ownership.runtime_supervisor,
|
ownership.runtime_supervisor,
|
||||||
PersistedHostProcessOwnership::committed(process(101))
|
PersistedHostProcessOwnership::committed(process(101))
|
||||||
);
|
);
|
||||||
|
assert!(ownership.attached_runsc.is_staged());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
ownership.attached_runsc,
|
ownership.attached_runsc.authoritative_pidfs().unwrap(),
|
||||||
PersistedHostProcessOwnership::staged(process(102))
|
&process(102)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
ownership.attached_runsc.runsc_identity(),
|
||||||
|
Some((RunscBuildId::new(bytes(8)), RunscDigest::new(bytes(8))))
|
||||||
);
|
);
|
||||||
coordinator.commit_ready(runsc).unwrap();
|
coordinator.commit_ready(runsc).unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
|
@ -152,14 +160,16 @@ fn mismatched_and_skipped_acquisition_boundaries_refuse_without_state_change() {
|
||||||
let store = store(&directory);
|
let store = store(&directory);
|
||||||
let coordinator = SandboxCoordinator::new(&store);
|
let coordinator = SandboxCoordinator::new(&store);
|
||||||
coordinator
|
coordinator
|
||||||
.begin_intent(
|
.begin_intent(SandboxIntent {
|
||||||
generation().generation_id,
|
generation_id: generation().generation_id,
|
||||||
ContainerId::new(bytes(6)),
|
container_id: ContainerId::new(bytes(6)),
|
||||||
directory.path(),
|
runtime_root: directory.path(),
|
||||||
current_mount_identity(directory.path()).unwrap(),
|
runtime_root_identity: current_mount_identity(directory.path()).unwrap(),
|
||||||
BundleDigest::new(bytes(7)),
|
bundle_digest: BundleDigest::new(bytes(7)),
|
||||||
process(100),
|
runsc_build_id: RunscBuildId::new(bytes(8)),
|
||||||
)
|
runsc_digest: RunscDigest::new(bytes(8)),
|
||||||
|
host_coordinator: process(100),
|
||||||
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let runsc = staged(HostProcessOwnershipSlot::SandboxAttachedRunsc, process(102));
|
let runsc = staged(HostProcessOwnershipSlot::SandboxAttachedRunsc, process(102));
|
||||||
assert!(coordinator.stage_attached_runsc(runsc).is_err());
|
assert!(coordinator.stage_attached_runsc(runsc).is_err());
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
|
use std::io::Read;
|
||||||
use std::os::unix::ffi::OsStringExt;
|
use std::os::unix::ffi::OsStringExt;
|
||||||
use std::os::unix::fs::PermissionsExt;
|
use std::os::unix::fs::PermissionsExt;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
|
@ -8,7 +9,7 @@ use std::sync::Arc;
|
||||||
use lazaret_core::configuration::ResolvedDomainConfiguration;
|
use lazaret_core::configuration::ResolvedDomainConfiguration;
|
||||||
use lazaret_core::identity::{
|
use lazaret_core::identity::{
|
||||||
ConfigurationFingerprint, ContainerId, CoordinatorId, DomainId, GenerationId, NetworkSidecarId,
|
ConfigurationFingerprint, ContainerId, CoordinatorId, DomainId, GenerationId, NetworkSidecarId,
|
||||||
PrepareOperationId, SandboxId,
|
PrepareOperationId, RunscBuildId, RunscDigest, SandboxId,
|
||||||
};
|
};
|
||||||
use lazaret_core::records::{DurableDecision, Generation, PreparedArtifact, VolumeKind};
|
use lazaret_core::records::{DurableDecision, Generation, PreparedArtifact, VolumeKind};
|
||||||
use lazaret_linux::filesystem::SharedFileLock;
|
use lazaret_linux::filesystem::SharedFileLock;
|
||||||
|
|
@ -26,6 +27,7 @@ use lazaret_runtime_supervisor::sandbox::{
|
||||||
use lazaret_sandbox_contract::RUNTIME_FILE_DESTINATIONS;
|
use lazaret_sandbox_contract::RUNTIME_FILE_DESTINATIONS;
|
||||||
use lazaret_storage_catalog::{Catalog, CatalogError};
|
use lazaret_storage_catalog::{Catalog, CatalogError};
|
||||||
use rustix::fs::{Mode, OFlags, ResolveFlags, openat2};
|
use rustix::fs::{Mode, OFlags, ResolveFlags, openat2};
|
||||||
|
use sha2::Digest;
|
||||||
|
|
||||||
use crate::cold_start::ColdStartInputs;
|
use crate::cold_start::ColdStartInputs;
|
||||||
use crate::domain_store::HostCoordinatorDomainStore;
|
use crate::domain_store::HostCoordinatorDomainStore;
|
||||||
|
|
@ -72,6 +74,7 @@ fn failure(error: impl fmt::Display) -> RuntimeSelectionError {
|
||||||
pub struct InstalledProductPaths<'a> {
|
pub struct InstalledProductPaths<'a> {
|
||||||
pub escalation_client: &'a Path,
|
pub escalation_client: &'a Path,
|
||||||
pub sandbox_init: &'a Path,
|
pub sandbox_init: &'a Path,
|
||||||
|
pub runsc: &'a Path,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct RuntimeSelection {
|
pub struct RuntimeSelection {
|
||||||
|
|
@ -87,6 +90,10 @@ pub struct RuntimeSelection {
|
||||||
container_id: ContainerId,
|
container_id: ContainerId,
|
||||||
escalation_client: File,
|
escalation_client: File,
|
||||||
sandbox_init: File,
|
sandbox_init: File,
|
||||||
|
runsc: File,
|
||||||
|
runsc_program: PathBuf,
|
||||||
|
runsc_build_id: RunscBuildId,
|
||||||
|
runsc_digest: RunscDigest,
|
||||||
user_namespace_map: UserNamespaceMap,
|
user_namespace_map: UserNamespaceMap,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -125,6 +132,10 @@ impl RuntimeSelection {
|
||||||
invoking_uid: rustix::process::getuid().as_raw(),
|
invoking_uid: rustix::process::getuid().as_raw(),
|
||||||
escalation_client: &self.escalation_client,
|
escalation_client: &self.escalation_client,
|
||||||
sandbox_init: &self.sandbox_init,
|
sandbox_init: &self.sandbox_init,
|
||||||
|
runsc: &self.runsc,
|
||||||
|
runsc_program: &self.runsc_program,
|
||||||
|
runsc_build_id: self.runsc_build_id,
|
||||||
|
runsc_digest: self.runsc_digest,
|
||||||
user_namespace_map: self.user_namespace_map.clone(),
|
user_namespace_map: self.user_namespace_map.clone(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -164,6 +175,8 @@ async fn select_runtime_for_platform(
|
||||||
let user_namespace_map = UserNamespaceMap::resolve_current().map_err(failure)?;
|
let user_namespace_map = UserNamespaceMap::resolve_current().map_err(failure)?;
|
||||||
let escalation_client = open_installed_executable(product_paths.escalation_client)?;
|
let escalation_client = open_installed_executable(product_paths.escalation_client)?;
|
||||||
let sandbox_init = open_installed_executable(product_paths.sandbox_init)?;
|
let sandbox_init = open_installed_executable(product_paths.sandbox_init)?;
|
||||||
|
let runsc = open_installed_executable(product_paths.runsc)?;
|
||||||
|
let runsc_digest = RunscDigest::new(executable_digest(&runsc)?);
|
||||||
let catalog = Catalog::under_storage_root(configuration.storage_root()).map_err(failure)?;
|
let catalog = Catalog::under_storage_root(configuration.storage_root()).map_err(failure)?;
|
||||||
match catalog.initialize() {
|
match catalog.initialize() {
|
||||||
Ok(()) | Err(CatalogError::AlreadyInitialized) => {}
|
Ok(()) | Err(CatalogError::AlreadyInitialized) => {}
|
||||||
|
|
@ -242,6 +255,12 @@ async fn select_runtime_for_platform(
|
||||||
container_id: ContainerId::new(random_id()?),
|
container_id: ContainerId::new(random_id()?),
|
||||||
escalation_client,
|
escalation_client,
|
||||||
sandbox_init,
|
sandbox_init,
|
||||||
|
runsc,
|
||||||
|
runsc_program: product_paths.runsc.to_owned(),
|
||||||
|
runsc_build_id: RunscBuildId::new(
|
||||||
|
lazaret_runtime_supervisor::sandbox::RUNSC_BUILD_ID_BYTES,
|
||||||
|
),
|
||||||
|
runsc_digest,
|
||||||
user_namespace_map,
|
user_namespace_map,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -316,6 +335,18 @@ fn next_generation(store: &HostCoordinatorDomainStore) -> Result<u64, RuntimeSel
|
||||||
}
|
}
|
||||||
|
|
||||||
fn open_installed_executable(path: &Path) -> Result<File, RuntimeSelectionError> {
|
fn open_installed_executable(path: &Path) -> Result<File, RuntimeSelectionError> {
|
||||||
|
if !path.is_absolute()
|
||||||
|
|| path.components().any(|component| {
|
||||||
|
!matches!(
|
||||||
|
component,
|
||||||
|
std::path::Component::RootDir | std::path::Component::Normal(_)
|
||||||
|
)
|
||||||
|
})
|
||||||
|
{
|
||||||
|
return Err(failure(
|
||||||
|
"installed product path is not absolute and normalized",
|
||||||
|
));
|
||||||
|
}
|
||||||
let descriptor = openat2(
|
let descriptor = openat2(
|
||||||
rustix::fs::CWD,
|
rustix::fs::CWD,
|
||||||
path,
|
path,
|
||||||
|
|
@ -334,6 +365,20 @@ fn open_installed_executable(path: &Path) -> Result<File, RuntimeSelectionError>
|
||||||
Ok(file)
|
Ok(file)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn executable_digest(file: &File) -> Result<[u8; 32], RuntimeSelectionError> {
|
||||||
|
let mut source = file.try_clone().map_err(failure)?;
|
||||||
|
let mut digest = sha2::Sha256::new();
|
||||||
|
let mut buffer = vec![0_u8; 64 * 1024];
|
||||||
|
loop {
|
||||||
|
let read = source.read(&mut buffer).map_err(failure)?;
|
||||||
|
if read == 0 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
digest.update(&buffer[..read]);
|
||||||
|
}
|
||||||
|
Ok(digest.finalize().into())
|
||||||
|
}
|
||||||
|
|
||||||
fn distinct_generation_ids()
|
fn distinct_generation_ids()
|
||||||
-> Result<(CoordinatorId, NetworkSidecarId, SandboxId), RuntimeSelectionError> {
|
-> Result<(CoordinatorId, NetworkSidecarId, SandboxId), RuntimeSelectionError> {
|
||||||
for _ in 0..8 {
|
for _ in 0..8 {
|
||||||
|
|
@ -445,6 +490,7 @@ mod tests {
|
||||||
InstalledProductPaths {
|
InstalledProductPaths {
|
||||||
escalation_client: Path::new("/missing-escalation-client"),
|
escalation_client: Path::new("/missing-escalation-client"),
|
||||||
sandbox_init: Path::new("/missing-sandbox-init"),
|
sandbox_init: Path::new("/missing-sandbox-init"),
|
||||||
|
runsc: Path::new("/missing-runsc"),
|
||||||
},
|
},
|
||||||
&observation,
|
&observation,
|
||||||
));
|
));
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,8 @@ use std::sync::Arc;
|
||||||
use lazaret_core::configuration::{ConfigurationLoadRequest, load_trusted_configuration};
|
use lazaret_core::configuration::{ConfigurationLoadRequest, load_trusted_configuration};
|
||||||
use lazaret_core::identity::{
|
use lazaret_core::identity::{
|
||||||
ContainerId, CoordinatorId, DeviceId, GenerationId, HostPath, IdMapDigest, InodeId, PolicyId,
|
ContainerId, CoordinatorId, DeviceId, GenerationId, HostPath, IdMapDigest, InodeId, PolicyId,
|
||||||
PreparedArtifactId, SandboxId, SelectedManifestDigest, SourceIdentity,
|
PreparedArtifactId, RunscBuildId, RunscDigest, SandboxId, SelectedManifestDigest,
|
||||||
|
SourceIdentity,
|
||||||
};
|
};
|
||||||
use lazaret_core::records::{ArtifactState, Domain, DurableDecision, Generation, PreparedArtifact};
|
use lazaret_core::records::{ArtifactState, Domain, DurableDecision, Generation, PreparedArtifact};
|
||||||
use lazaret_host_coordinator::cold_start::{ColdStart, ColdStartInputs};
|
use lazaret_host_coordinator::cold_start::{ColdStart, ColdStartInputs};
|
||||||
|
|
@ -280,6 +281,10 @@ impl ColdStartFixture {
|
||||||
invoking_uid: getuid().as_raw(),
|
invoking_uid: getuid().as_raw(),
|
||||||
escalation_client,
|
escalation_client,
|
||||||
sandbox_init: escalation_client,
|
sandbox_init: escalation_client,
|
||||||
|
runsc: escalation_client,
|
||||||
|
runsc_program: Path::new("/bin/true"),
|
||||||
|
runsc_build_id: RunscBuildId::new([11; 32]),
|
||||||
|
runsc_digest: RunscDigest::new([12; 32]),
|
||||||
user_namespace_map: test_user_namespace_map(),
|
user_namespace_map: test_user_namespace_map(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ use lazaret_core::configuration::{
|
||||||
};
|
};
|
||||||
use lazaret_core::identity::{
|
use lazaret_core::identity::{
|
||||||
BundleDigest, ClientId, ContainerId, CoordinatorId, GenerationId, HostPath, NetworkSidecarId,
|
BundleDigest, ClientId, ContainerId, CoordinatorId, GenerationId, HostPath, NetworkSidecarId,
|
||||||
PidfsProcessIdentityV1, SandboxId, SessionAnchorId, SessionId,
|
PidfsProcessIdentityV1, RunscBuildId, RunscDigest, SandboxId, SessionAnchorId, SessionId,
|
||||||
};
|
};
|
||||||
use lazaret_core::network::derive_ipv4_policy;
|
use lazaret_core::network::derive_ipv4_policy;
|
||||||
use lazaret_core::records::{
|
use lazaret_core::records::{
|
||||||
|
|
@ -28,7 +28,7 @@ use lazaret_host_coordinator::domain_store::{DomainStoreMutation, HostCoordinato
|
||||||
use lazaret_host_coordinator::git_routing::GitRoutingCoordinator;
|
use lazaret_host_coordinator::git_routing::GitRoutingCoordinator;
|
||||||
use lazaret_host_coordinator::network::NetworkCoordinator;
|
use lazaret_host_coordinator::network::NetworkCoordinator;
|
||||||
use lazaret_host_coordinator::runtime::{
|
use lazaret_host_coordinator::runtime::{
|
||||||
AwaitingAttachedRunsc, CommittedSandboxRuntime, SandboxCoordinator,
|
AwaitingAttachedRunsc, CommittedSandboxRuntime, SandboxCoordinator, SandboxIntent,
|
||||||
};
|
};
|
||||||
use lazaret_host_coordinator::terminal::{
|
use lazaret_host_coordinator::terminal::{
|
||||||
NoActivePrompt, RunscSessionTerminalFactory, SessionTerminalEngine, TerminalAcquisition,
|
NoActivePrompt, RunscSessionTerminalFactory, SessionTerminalEngine, TerminalAcquisition,
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue