Make external codec boundaries explicit and exhaustive
This commit is contained in:
parent
76de24c75c
commit
d7de87fb8f
2 changed files with 319 additions and 25 deletions
|
|
@ -469,6 +469,10 @@ mod handoff_tests {
|
|||
|
||||
#[test]
|
||||
fn v1_vector_and_non_utf8_runsc_arguments_are_stable() {
|
||||
assert!(
|
||||
crate::supervision::RUNTIME_SUPERVISOR_SERIALIZED_RECORD_TYPE_NAMES
|
||||
.contains(&<RuntimeSupervisorHandoff as lazaret_core::records::SchemaType>::NAME)
|
||||
);
|
||||
let directory = |number, identity| RuntimeDescriptorV1 {
|
||||
number,
|
||||
device: identity,
|
||||
|
|
|
|||
|
|
@ -1,15 +1,241 @@
|
|||
use std::ffi::OsString;
|
||||
|
||||
use lazaret_core::identity::{CoordinatorId, DomainId, GenerationId};
|
||||
use lazaret_core::records::HostProcessOwnershipSlot;
|
||||
use lazaret_core::identity::{
|
||||
BootId, ConfigurationFingerprint, CoordinatorId, DerivedPolicyDigest, DomainId, GenerationId,
|
||||
HostPidLocator, NetworkSidecarId, PidfsDeviceId, PidfsInodeId, PidfsProcessAuthorityV1,
|
||||
PidfsProcessIdentityV1, ProcessStartTimeTicks,
|
||||
};
|
||||
use lazaret_core::records::{
|
||||
HostProcessOwnershipSlot, NetworkObservation, NetworkReachabilityProbe,
|
||||
NetworkReadinessAttestation, NetworkReadinessAttestationFields, PolicyDelivery,
|
||||
ProcessOwnershipCommitAck, SchemaType, StagedProcessOwnership,
|
||||
};
|
||||
use lazaret_linux::ipc::private_seqpacket_pair;
|
||||
use lazaret_linux::process::ChildStandardIoPolicy;
|
||||
use rustix::pipe::{PipeFlags, pipe_with};
|
||||
use sha2::{Digest, Sha256};
|
||||
|
||||
use crate::network::NetworkProbeRequest;
|
||||
|
||||
use super::runtime::{attached_runsc_standard_io, runsc_delete_target, runsc_target};
|
||||
use super::*;
|
||||
|
||||
fn process() -> PidfsProcessIdentityV1 {
|
||||
PidfsProcessIdentityV1::new(
|
||||
PidfsProcessAuthorityV1 {
|
||||
boot_id: BootId::new([6; 16]),
|
||||
pidfs_st_dev: PidfsDeviceId::new(7),
|
||||
pidfs_st_ino: PidfsInodeId::new(8),
|
||||
},
|
||||
HostPidLocator::new(9),
|
||||
ProcessStartTimeTicks::new(10),
|
||||
)
|
||||
}
|
||||
|
||||
fn staged() -> StagedProcessOwnership {
|
||||
StagedProcessOwnership::v1(
|
||||
DomainId::new([1; 32]),
|
||||
GenerationId::new(2),
|
||||
CoordinatorId::new([3; 32]),
|
||||
HostProcessOwnershipSlot::SandboxRuntimeSupervisor,
|
||||
process(),
|
||||
)
|
||||
}
|
||||
|
||||
fn event_tag(event: &SupervisorEvent) -> &'static str {
|
||||
match event {
|
||||
SupervisorEvent::StateAnchorStaged(_) => "StateAnchorStaged",
|
||||
SupervisorEvent::StateAnchorReady { .. } => "StateAnchorReady",
|
||||
SupervisorEvent::GitRoutingStaged(_) => "GitRoutingStaged",
|
||||
SupervisorEvent::GitRoutingReady { .. } => "GitRoutingReady",
|
||||
SupervisorEvent::SidecarStarted => "SidecarStarted",
|
||||
SupervisorEvent::HelperStaged(_) => "HelperStaged",
|
||||
SupervisorEvent::HelperReady(_) => "HelperReady",
|
||||
SupervisorEvent::NetworkAttestation(_) => "NetworkAttestation",
|
||||
SupervisorEvent::NetworkProbe(_) => "NetworkProbe",
|
||||
SupervisorEvent::HelperExited => "HelperExited",
|
||||
SupervisorEvent::Staged(_) => "Staged",
|
||||
SupervisorEvent::Ready { .. } => "Ready",
|
||||
SupervisorEvent::SignalApplied(_) => "SignalApplied",
|
||||
SupervisorEvent::SandboxStopped => "SandboxStopped",
|
||||
SupervisorEvent::GitRoutingStopped { .. } => "GitRoutingStopped",
|
||||
SupervisorEvent::SidecarStopped => "SidecarStopped",
|
||||
SupervisorEvent::StateAnchorUnmounted { .. } => "StateAnchorUnmounted",
|
||||
SupervisorEvent::StateAnchorAbsenceObserved { .. } => "StateAnchorAbsenceObserved",
|
||||
SupervisorEvent::Exited => "Exited",
|
||||
SupervisorEvent::AttachedRunscExited => "AttachedRunscExited",
|
||||
SupervisorEvent::Stopped => "Stopped",
|
||||
SupervisorEvent::Refused(_) => "Refused",
|
||||
}
|
||||
}
|
||||
|
||||
fn message_tag(message: &CoordinatorMessage) -> &'static str {
|
||||
match message {
|
||||
CoordinatorMessage::MountStateAnchor => "MountStateAnchor",
|
||||
CoordinatorMessage::StateAnchorStagedAck(_) => "StateAnchorStagedAck",
|
||||
CoordinatorMessage::StartGitRouting => "StartGitRouting",
|
||||
CoordinatorMessage::GitRoutingStagedAck(_) => "GitRoutingStagedAck",
|
||||
CoordinatorMessage::StartSidecar => "StartSidecar",
|
||||
CoordinatorMessage::HelperStagedAck(_) => "HelperStagedAck",
|
||||
CoordinatorMessage::NetworkAttestationAccepted(_) => "NetworkAttestationAccepted",
|
||||
CoordinatorMessage::NetworkReadinessCommitted => "NetworkReadinessCommitted",
|
||||
CoordinatorMessage::StagedAck(_) => "StagedAck",
|
||||
CoordinatorMessage::SandboxCommitted => "SandboxCommitted",
|
||||
CoordinatorMessage::SignalAttachedRunsc(_) => "SignalAttachedRunsc",
|
||||
CoordinatorMessage::StopSandbox => "StopSandbox",
|
||||
CoordinatorMessage::StopGitRouting => "StopGitRouting",
|
||||
CoordinatorMessage::StopSidecar => "StopSidecar",
|
||||
CoordinatorMessage::UnmountStateAnchor => "UnmountStateAnchor",
|
||||
CoordinatorMessage::ObserveStateAnchorAbsence { .. } => "ObserveStateAnchorAbsence",
|
||||
CoordinatorMessage::Exit => "Exit",
|
||||
CoordinatorMessage::Stop => "Stop",
|
||||
}
|
||||
}
|
||||
|
||||
fn representative_events(
|
||||
namespace: NamespaceIdentity,
|
||||
mount: MountIdentity,
|
||||
staged: StagedProcessOwnership,
|
||||
attestation: NetworkReadinessAttestation,
|
||||
probe: NetworkReachabilityProbe,
|
||||
) -> Vec<SupervisorEvent> {
|
||||
vec![
|
||||
SupervisorEvent::StateAnchorStaged(staged),
|
||||
SupervisorEvent::StateAnchorReady {
|
||||
staged,
|
||||
user_namespace: namespace,
|
||||
mount_namespace: namespace,
|
||||
mount,
|
||||
},
|
||||
SupervisorEvent::GitRoutingStaged(staged),
|
||||
SupervisorEvent::GitRoutingReady { staged, mount },
|
||||
SupervisorEvent::SidecarStarted,
|
||||
SupervisorEvent::HelperStaged(staged),
|
||||
SupervisorEvent::HelperReady(staged),
|
||||
SupervisorEvent::NetworkAttestation(attestation),
|
||||
SupervisorEvent::NetworkProbe(probe),
|
||||
SupervisorEvent::HelperExited,
|
||||
SupervisorEvent::Staged(staged),
|
||||
SupervisorEvent::Ready {
|
||||
staged,
|
||||
sentry_pid_inventory: 14,
|
||||
},
|
||||
SupervisorEvent::SignalApplied(AttachedRunscSignal::Continue),
|
||||
SupervisorEvent::SandboxStopped,
|
||||
SupervisorEvent::GitRoutingStopped { mount },
|
||||
SupervisorEvent::SidecarStopped,
|
||||
SupervisorEvent::StateAnchorUnmounted { mount },
|
||||
SupervisorEvent::StateAnchorAbsenceObserved {
|
||||
user_namespace: namespace,
|
||||
mount_namespace: namespace,
|
||||
mount,
|
||||
observed: mount,
|
||||
},
|
||||
SupervisorEvent::Exited,
|
||||
SupervisorEvent::AttachedRunscExited,
|
||||
SupervisorEvent::Stopped,
|
||||
SupervisorEvent::Refused("refused".into()),
|
||||
]
|
||||
}
|
||||
|
||||
fn representative_messages(
|
||||
namespace: NamespaceIdentity,
|
||||
mount: MountIdentity,
|
||||
acknowledgment: ProcessOwnershipCommitAck,
|
||||
request: NetworkProbeRequest,
|
||||
) -> Vec<CoordinatorMessage> {
|
||||
vec![
|
||||
CoordinatorMessage::MountStateAnchor,
|
||||
CoordinatorMessage::StateAnchorStagedAck(acknowledgment),
|
||||
CoordinatorMessage::StartGitRouting,
|
||||
CoordinatorMessage::GitRoutingStagedAck(acknowledgment),
|
||||
CoordinatorMessage::StartSidecar,
|
||||
CoordinatorMessage::HelperStagedAck(acknowledgment),
|
||||
CoordinatorMessage::NetworkAttestationAccepted(request),
|
||||
CoordinatorMessage::NetworkReadinessCommitted,
|
||||
CoordinatorMessage::StagedAck(acknowledgment),
|
||||
CoordinatorMessage::SandboxCommitted,
|
||||
CoordinatorMessage::SignalAttachedRunsc(AttachedRunscSignal::Kill),
|
||||
CoordinatorMessage::StopSandbox,
|
||||
CoordinatorMessage::StopGitRouting,
|
||||
CoordinatorMessage::StopSidecar,
|
||||
CoordinatorMessage::UnmountStateAnchor,
|
||||
CoordinatorMessage::ObserveStateAnchorAbsence {
|
||||
user_namespace: namespace,
|
||||
mount_namespace: namespace,
|
||||
mount,
|
||||
},
|
||||
CoordinatorMessage::Exit,
|
||||
CoordinatorMessage::Stop,
|
||||
]
|
||||
}
|
||||
|
||||
fn assert_event_variant_coverage(events: &[SupervisorEvent]) {
|
||||
assert_eq!(
|
||||
events.iter().map(event_tag).collect::<Vec<_>>(),
|
||||
[
|
||||
"StateAnchorStaged",
|
||||
"StateAnchorReady",
|
||||
"GitRoutingStaged",
|
||||
"GitRoutingReady",
|
||||
"SidecarStarted",
|
||||
"HelperStaged",
|
||||
"HelperReady",
|
||||
"NetworkAttestation",
|
||||
"NetworkProbe",
|
||||
"HelperExited",
|
||||
"Staged",
|
||||
"Ready",
|
||||
"SignalApplied",
|
||||
"SandboxStopped",
|
||||
"GitRoutingStopped",
|
||||
"SidecarStopped",
|
||||
"StateAnchorUnmounted",
|
||||
"StateAnchorAbsenceObserved",
|
||||
"Exited",
|
||||
"AttachedRunscExited",
|
||||
"Stopped",
|
||||
"Refused",
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
fn assert_message_variant_coverage(messages: &[CoordinatorMessage]) {
|
||||
assert_eq!(
|
||||
messages.iter().map(message_tag).collect::<Vec<_>>(),
|
||||
[
|
||||
"MountStateAnchor",
|
||||
"StateAnchorStagedAck",
|
||||
"StartGitRouting",
|
||||
"GitRoutingStagedAck",
|
||||
"StartSidecar",
|
||||
"HelperStagedAck",
|
||||
"NetworkAttestationAccepted",
|
||||
"NetworkReadinessCommitted",
|
||||
"StagedAck",
|
||||
"SandboxCommitted",
|
||||
"SignalAttachedRunsc",
|
||||
"StopSandbox",
|
||||
"StopGitRouting",
|
||||
"StopSidecar",
|
||||
"UnmountStateAnchor",
|
||||
"ObserveStateAnchorAbsence",
|
||||
"Exit",
|
||||
"Stop",
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
fn corpus_digest<T: SchemaType + serde::Serialize>(records: &[T]) -> [u8; 32] {
|
||||
let mut corpus = Sha256::new();
|
||||
for record in records {
|
||||
let record = encode(record).unwrap();
|
||||
corpus.update(record.len().to_be_bytes());
|
||||
corpus.update(record);
|
||||
}
|
||||
corpus.finalize().into()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn private_control_record_vectors_are_stable() {
|
||||
let namespace = NamespaceIdentity {
|
||||
|
|
@ -21,32 +247,78 @@ fn private_control_record_vectors_are_stable() {
|
|||
device: lazaret_core::identity::DeviceId::new(4),
|
||||
inode: lazaret_core::identity::InodeId::new(5),
|
||||
};
|
||||
let records = [
|
||||
encode(&AttachedRunscSignal::Continue).unwrap(),
|
||||
encode(&SupervisorEvent::StateAnchorAbsenceObserved {
|
||||
user_namespace: namespace,
|
||||
mount_namespace: namespace,
|
||||
mount,
|
||||
observed: mount,
|
||||
})
|
||||
.unwrap(),
|
||||
encode(&CoordinatorMessage::ObserveStateAnchorAbsence {
|
||||
user_namespace: namespace,
|
||||
mount_namespace: namespace,
|
||||
mount,
|
||||
})
|
||||
.unwrap(),
|
||||
let staged = staged();
|
||||
let acknowledgment = ProcessOwnershipCommitAck::v1(staged);
|
||||
let attestation = NetworkReadinessAttestation::v1(NetworkReadinessAttestationFields {
|
||||
domain_id: DomainId::new([1; 32]),
|
||||
generation_id: GenerationId::new(2),
|
||||
configuration_fingerprint: ConfigurationFingerprint::new([11; 32]),
|
||||
network_sidecar_id: NetworkSidecarId::new([12; 32]),
|
||||
derived_policy_digest: DerivedPolicyDigest::new([13; 32]),
|
||||
enforcement_namespace: namespace,
|
||||
runtime_supervisor: process(),
|
||||
connectivity_helper: process(),
|
||||
observation: NetworkObservation {
|
||||
policy_delivery: PolicyDelivery::SealedMemfd,
|
||||
helper_ready: true,
|
||||
observed_routes: Vec::new(),
|
||||
routes_attested: true,
|
||||
},
|
||||
});
|
||||
let probe = NetworkReachabilityProbe {
|
||||
domain_id: DomainId::new([1; 32]),
|
||||
generation_id: GenerationId::new(2),
|
||||
enforcement_namespace: namespace,
|
||||
denied_destinations: Vec::new(),
|
||||
permitted_destinations: Vec::new(),
|
||||
dns_observations: Vec::new(),
|
||||
};
|
||||
let request = NetworkProbeRequest {
|
||||
domain_id: DomainId::new([1; 32]),
|
||||
generation_id: GenerationId::new(2),
|
||||
enforcement_namespace: namespace,
|
||||
denied: Vec::new(),
|
||||
permitted: Vec::new(),
|
||||
dns: Vec::new(),
|
||||
};
|
||||
let signals = [
|
||||
AttachedRunscSignal::Stop,
|
||||
AttachedRunscSignal::Continue,
|
||||
AttachedRunscSignal::Kill,
|
||||
];
|
||||
let mut corpus = Sha256::new();
|
||||
for record in records {
|
||||
corpus.update(record.len().to_be_bytes());
|
||||
corpus.update(record);
|
||||
}
|
||||
let events = representative_events(namespace, mount, staged, attestation, probe);
|
||||
let messages = representative_messages(namespace, mount, acknowledgment, request);
|
||||
assert_eq!(
|
||||
RUNTIME_SUPERVISOR_SERIALIZED_RECORD_TYPE_NAMES,
|
||||
&[
|
||||
"RuntimeSupervisorHandoff",
|
||||
<NetworkProbeRequest as SchemaType>::NAME,
|
||||
<AttachedRunscSignal as SchemaType>::NAME,
|
||||
<SupervisorEvent as SchemaType>::NAME,
|
||||
<CoordinatorMessage as SchemaType>::NAME,
|
||||
]
|
||||
);
|
||||
assert_event_variant_coverage(&events);
|
||||
assert_message_variant_coverage(&messages);
|
||||
assert_eq!(
|
||||
corpus.finalize().as_slice(),
|
||||
[
|
||||
59, 166, 131, 49, 39, 47, 193, 4, 70, 106, 107, 188, 204, 94, 145, 7, 250, 164, 101,
|
||||
22, 121, 174, 24, 13, 138, 108, 62, 97, 17, 162, 129, 89,
|
||||
corpus_digest(&signals),
|
||||
corpus_digest(&events),
|
||||
corpus_digest(&messages),
|
||||
],
|
||||
[
|
||||
[
|
||||
81, 139, 171, 185, 206, 78, 197, 44, 238, 115, 83, 251, 40, 178, 113, 209, 41, 53,
|
||||
251, 52, 93, 195, 13, 90, 91, 33, 97, 9, 204, 249, 127, 100,
|
||||
],
|
||||
[
|
||||
46, 250, 76, 205, 43, 212, 214, 67, 162, 9, 72, 170, 22, 44, 123, 161, 14, 206, 29,
|
||||
244, 137, 251, 60, 194, 96, 96, 92, 216, 50, 97, 244, 47,
|
||||
],
|
||||
[
|
||||
227, 133, 56, 78, 199, 235, 191, 196, 191, 7, 154, 135, 223, 104, 221, 62, 240,
|
||||
253, 202, 161, 22, 254, 133, 59, 22, 126, 218, 254, 81, 114, 240, 120,
|
||||
],
|
||||
]
|
||||
);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue