Enforce exact recovery ownership before cold start

This commit is contained in:
ms2k 2026-07-17 06:48:49 +09:00
commit 2bc5982a62

View file

@ -13,7 +13,8 @@ use ed25519_dalek::SigningKey;
use lazaret_core::codec::{CodecError, decode, encode};
use lazaret_core::configuration::ConfinedPathPlan;
use lazaret_core::identity::{
CertificateDigest, ClientId, ContainerId, SessionAnchorId, SessionId, TrustManifestDigest,
CertificateDigest, ClientId, ContainerId, DeviceId, HostPath, InodeId, MountIdentity,
SessionAnchorId, SessionId, TrustManifestDigest,
};
use lazaret_core::protocol::{
ProtocolValidationError, issue_protocol_credential, issue_session_capability,
@ -23,9 +24,10 @@ use lazaret_core::records::{
ClientEnrollmentRequest, ClientEnrollmentResponse, ControlRequest, ControlResponse,
DurableDecision, GenerationIdentity, NonInteractiveSessionMessage,
NonInteractiveSessionRequest, ProtocolCredential, ProtocolCredentialRole,
ProtocolPublicationIdentity, ProtocolSubjectId, ProtocolVersion, SandboxCredentialBundle,
Session, SessionBinding, SessionExit, SessionPhase, SessionRegistrationRequest,
SessionRegistrationResponse, TlsCredential, TlsRole, TlsSubjectId, TrustManifest,
ProtocolPublicationIdentity, ProtocolSubjectId, ProtocolVersion, PublicationOwnership,
SandboxCredentialBundle, Session, SessionBinding, SessionExit, SessionPhase,
SessionRegistrationRequest, SessionRegistrationResponse, TlsCredential, TlsRole, TlsSubjectId,
TrustManifest,
};
use rcgen::{
BasicConstraints, CertificateParams, CertificateSigningRequestParams, CertifiedIssuer,
@ -46,6 +48,7 @@ use crate::domain_store::{DomainStoreError, DomainStoreMutation, HostCoordinator
use crate::lifecycle::{
HostCoordinatorLifecycle, LifecycleAction, SessionAdmissionIdentity, SessionLifecycleAdapter,
};
use lazaret_linux::filesystem::mount_identity;
use lazaret_linux::process::{ProcessExit, TargetCommand, child_broker};
use lazaret_runtime_supervisor::sandbox::RunscCommand;
@ -53,10 +56,10 @@ pub const CONTROL_ALPN: &[u8] = b"lazaret-control/1";
pub const CONTROL_SERVER_NAME: &str = "coordinator.lazaret.invalid";
pub const MAX_ENROLLMENT_BYTES: usize = 8_192;
const MAX_CONTROL_BYTES: usize = 65_536;
const CONTROL_SOCKET_NAME: &str = "c.sock";
const PUBLICATION_IDENTITY_NAME: &str = "protocol-identity.postcard";
const STAGED_CONTROL_SOCKET_NAME: &str = ".c.staged";
const STAGED_PUBLICATION_IDENTITY_NAME: &str = ".protocol-identity.staged";
pub(crate) const CONTROL_SOCKET_NAME: &str = "c.sock";
pub(crate) const PUBLICATION_IDENTITY_NAME: &str = "protocol-identity.postcard";
pub(crate) const STAGED_CONTROL_SOCKET_NAME: &str = ".c.staged";
pub(crate) const STAGED_PUBLICATION_IDENTITY_NAME: &str = ".protocol-identity.staged";
#[derive(Debug)]
pub enum ControlError {
@ -1009,6 +1012,10 @@ pub struct StagedControlFace {
}
impl StagedControlFace {
pub(crate) fn recovery_ownership(&self) -> Result<PublicationOwnership, ControlError> {
publication_recovery_ownership(&self.paths)
}
/// Stages an undiscoverable private control face before durable publication.
///
/// # Errors
@ -1160,7 +1167,51 @@ impl Drop for StagedControlFace {
}
}
fn publication_recovery_ownership(
paths: &ControlPaths,
) -> Result<PublicationOwnership, ControlError> {
let socket_path = if paths.staged_socket.exists() {
&paths.staged_socket
} else {
&paths.socket
};
let identity_path = if paths.staged_identity.exists() {
&paths.staged_identity
} else {
&paths.identity
};
Ok(PublicationOwnership {
staged_socket_path: Some(HostPath::new(
STAGED_CONTROL_SOCKET_NAME.as_bytes().to_vec(),
)),
staged_identity_path: Some(HostPath::new(
STAGED_PUBLICATION_IDENTITY_NAME.as_bytes().to_vec(),
)),
socket_path: Some(HostPath::new(CONTROL_SOCKET_NAME.as_bytes().to_vec())),
identity_path: Some(HostPath::new(PUBLICATION_IDENTITY_NAME.as_bytes().to_vec())),
socket_identity: Some(publication_path_identity(paths, socket_path)?),
identity_file_identity: Some(publication_path_identity(paths, identity_path)?),
})
}
fn publication_path_identity(
paths: &ControlPaths,
path: &Path,
) -> Result<MountIdentity, ControlError> {
let metadata = std::fs::symlink_metadata(path)?;
let root = mount_identity(&paths.root_file)?;
Ok(MountIdentity {
mount_id: root.mount_id,
device: DeviceId::new(metadata.dev()),
inode: InodeId::new(metadata.ino()),
})
}
impl PublishedControlFace {
pub(crate) fn recovery_ownership(&self) -> Result<PublicationOwnership, ControlError> {
publication_recovery_ownership(&self.paths)
}
/// Selects blocking or polling admission for the publication listener.
///
/// # Errors