120 lines
3.8 KiB
Rust
120 lines
3.8 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 parent = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../../target/t/control-umask");
|
|
std::fs::create_dir_all(&parent).unwrap();
|
|
let parent = parent.canonicalize().unwrap();
|
|
let path = parent.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);
|
|
}
|