Set and validate the publication identity through its retained descriptor so restrictive process masks cannot weaken or remove the required mode.
117 lines
3.6 KiB
Rust
117 lines
3.6 KiB
Rust
use std::fs::OpenOptions;
|
|
use std::os::unix::fs::{MetadataExt, PermissionsExt};
|
|
use std::path::PathBuf;
|
|
use std::sync::Arc;
|
|
|
|
use lazaret_core::identity::{
|
|
ConfigurationFingerprint, CoordinatorId, DomainId, GenerationId, HostPath, NetworkSidecarId,
|
|
SandboxId,
|
|
};
|
|
use lazaret_core::records::{Domain, DurableDecision, Generation, GenerationIdentity};
|
|
use lazaret_host_coordinator::control::{PublishedControlFace, TrustAnchor};
|
|
use lazaret_host_coordinator::domain_store::{DomainStoreMutation, HostCoordinatorDomainStore};
|
|
use lazaret_linux::filesystem::try_exclusive_flock;
|
|
use rustix::fs::Mode;
|
|
use rustix::process::{getuid, umask};
|
|
|
|
struct TestDirectory(PathBuf);
|
|
|
|
impl TestDirectory {
|
|
fn new() -> Self {
|
|
let path = std::env::temp_dir().join(format!(
|
|
"lazaret-control-restrictive-umask-{}",
|
|
std::process::id()
|
|
));
|
|
std::fs::create_dir(&path).unwrap();
|
|
std::fs::set_permissions(&path, std::fs::Permissions::from_mode(0o700)).unwrap();
|
|
Self(path)
|
|
}
|
|
|
|
fn path(&self, name: &str) -> PathBuf {
|
|
self.0.join(name)
|
|
}
|
|
}
|
|
|
|
impl Drop for TestDirectory {
|
|
fn drop(&mut self) {
|
|
std::fs::remove_dir_all(&self.0).unwrap();
|
|
}
|
|
}
|
|
|
|
struct UmaskGuard(Mode);
|
|
|
|
impl Drop for UmaskGuard {
|
|
fn drop(&mut self) {
|
|
umask(self.0);
|
|
}
|
|
}
|
|
|
|
fn bytes(value: u8) -> [u8; 32] {
|
|
[value; 32]
|
|
}
|
|
|
|
fn generation_identity() -> GenerationIdentity {
|
|
GenerationIdentity {
|
|
agent_profile: "trusted".into(),
|
|
domain_id: DomainId::new(bytes(1)),
|
|
generation_id: GenerationId::new(7),
|
|
configuration_fingerprint: ConfigurationFingerprint::new(bytes(2)),
|
|
coordinator_id: CoordinatorId::new(bytes(17)),
|
|
sandbox_id: SandboxId::new(bytes(27)),
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn publication_identity_mode_is_exact_under_restrictive_umask() {
|
|
let directory = TestDirectory::new();
|
|
let identity = generation_identity();
|
|
let lock = OpenOptions::new()
|
|
.read(true)
|
|
.write(true)
|
|
.create_new(true)
|
|
.open(directory.path("domain.lock"))
|
|
.unwrap();
|
|
let lock = try_exclusive_flock(lock).unwrap();
|
|
let domain = Domain {
|
|
domain_id: identity.domain_id,
|
|
profile_name: identity.agent_profile.clone(),
|
|
canonical_workspace: HostPath::new(b"/workspace/project".to_vec()),
|
|
};
|
|
let store = Arc::new(
|
|
HostCoordinatorDomainStore::open(lock, &directory.path("domain.redb"), &domain).unwrap(),
|
|
);
|
|
for durable_decision in [
|
|
DurableDecision::None,
|
|
DurableDecision::Preparing,
|
|
DurableDecision::Published,
|
|
] {
|
|
store
|
|
.apply(&[DomainStoreMutation::PutGeneration(Generation {
|
|
domain_id: identity.domain_id,
|
|
generation_id: identity.generation_id,
|
|
configuration_fingerprint: identity.configuration_fingerprint,
|
|
coordinator_id: identity.coordinator_id,
|
|
network_sidecar_id: NetworkSidecarId::new(bytes(4)),
|
|
sandbox_id: identity.sandbox_id,
|
|
durable_decision,
|
|
})])
|
|
.unwrap();
|
|
}
|
|
|
|
let _umask = UmaskGuard(umask(Mode::from_raw_mode(0o777)));
|
|
let face = PublishedControlFace::publish(
|
|
&directory.0,
|
|
getuid().as_raw(),
|
|
TrustAnchor::create(identity).unwrap(),
|
|
store,
|
|
)
|
|
.unwrap();
|
|
assert_eq!(
|
|
face.identity_path(),
|
|
directory.path("protocol-identity.postcard")
|
|
);
|
|
let metadata = std::fs::symlink_metadata(face.identity_path()).unwrap();
|
|
assert!(metadata.file_type().is_file());
|
|
assert_eq!(metadata.uid(), getuid().as_raw());
|
|
assert_eq!(metadata.mode() & 0o7777, 0o600);
|
|
}
|