Default profiles to persistent system paths

This commit is contained in:
ms2k 2026-07-22 03:23:38 +09:00
commit acd0b4cc8c
5 changed files with 36 additions and 3 deletions

View file

@ -5,6 +5,7 @@ pub const STORAGE_CATALOG_SCHEMA_ID: &str = "lazaret-storage-catalog-v1";
pub const DOMAIN_STORE_SCHEMA_VERSION: u32 = 1; pub const DOMAIN_STORE_SCHEMA_VERSION: u32 = 1;
pub const STORAGE_CATALOG_SCHEMA_VERSION: u32 = 1; pub const STORAGE_CATALOG_SCHEMA_VERSION: u32 = 1;
pub const TRUSTED_HOST_CONFIGURATION_SCHEMA_ID: &str = "lazaret-trusted-host-config-v1"; pub const TRUSTED_HOST_CONFIGURATION_SCHEMA_ID: &str = "lazaret-trusted-host-config-v1";
pub const DEFAULT_PERSISTENT_DIRS: [&str; 5] = ["/root", "/usr", "/var", "/etc", "/opt"];
#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
#[serde(deny_unknown_fields)] #[serde(deny_unknown_fields)]
@ -74,7 +75,7 @@ pub struct ProfileConfigurationV1 {
pub image: String, pub image: String,
#[serde(default = "default_isolate_gitignored")] #[serde(default = "default_isolate_gitignored")]
pub isolate_gitignored: bool, pub isolate_gitignored: bool,
#[serde(default)] #[serde(default = "default_persistent_dirs")]
pub persistent_dirs: Vec<String>, pub persistent_dirs: Vec<String>,
#[serde(default)] #[serde(default)]
pub allow_egress: Vec<String>, pub allow_egress: Vec<String>,
@ -92,6 +93,10 @@ const fn default_isolate_gitignored() -> bool {
true true
} }
fn default_persistent_dirs() -> Vec<String> {
DEFAULT_PERSISTENT_DIRS.map(str::to_owned).into()
}
#[derive(Clone, Debug, Eq, PartialEq, Serialize)] #[derive(Clone, Debug, Eq, PartialEq, Serialize)]
pub struct TrustedHostConfigurationV1 { pub struct TrustedHostConfigurationV1 {
#[serde(rename = "schema")] #[serde(rename = "schema")]

View file

@ -137,7 +137,10 @@ image = "example.invalid/agent:v1"
let parsed = parse_trusted_configuration(minimal.as_bytes()).unwrap(); let parsed = parse_trusted_configuration(minimal.as_bytes()).unwrap();
let profile = &parsed.profiles()["default"]; let profile = &parsed.profiles()["default"];
assert!(profile.isolate_gitignored); assert!(profile.isolate_gitignored);
assert!(profile.persistent_dirs.is_empty()); assert_eq!(
profile.persistent_dirs,
["/root", "/usr", "/var", "/etc", "/opt"]
);
assert!(profile.allow_egress.is_empty()); assert!(profile.allow_egress.is_empty());
assert!(profile.deny_egress.is_empty()); assert!(profile.deny_egress.is_empty());
assert!(profile.env.is_empty()); assert!(profile.env.is_empty());
@ -148,6 +151,23 @@ image = "example.invalid/agent:v1"
assert!(parsed.network().default_allow.is_empty()); assert!(parsed.network().default_allow.is_empty());
assert!(parsed.network().default_deny.is_empty()); assert!(parsed.network().default_deny.is_empty());
for (value, expected) in [
("[]", Vec::<String>::new()),
("[\"/nix\"]", vec!["/nix".to_owned()]),
] {
let explicit = minimal.replace(
"image = \"example.invalid/agent:v1\"",
&format!("image = \"example.invalid/agent:v1\"\npersistent_dirs = {value}"),
);
assert_eq!(
parse_trusted_configuration(explicit.as_bytes())
.unwrap()
.profiles()["default"]
.persistent_dirs,
expected
);
}
for invalid in [ for invalid in [
minimal.replace("schema", "unknown"), minimal.replace("schema", "unknown"),
format!("{minimal}unknown = true\n"), format!("{minimal}unknown = true\n"),
@ -271,6 +291,10 @@ fn repository_example_loads_with_ipv4_normalization_and_private_denies() {
let config = write_configuration(&tree, include_str!("../../../docs/example.toml")); let config = write_configuration(&tree, include_str!("../../../docs/example.toml"));
let resolved = load_trusted_configuration(request(&tree, config, BTreeMap::new())).unwrap(); let resolved = load_trusted_configuration(request(&tree, config, BTreeMap::new())).unwrap();
assert_eq!(resolved.selected_profile(), "default"); assert_eq!(resolved.selected_profile(), "default");
assert_eq!(
resolved.fingerprint_record().persistent_dirs,
["/root", "/usr", "/var", "/etc", "/opt"]
);
assert!( assert!(
resolved resolved
.effective_allow_prefixes() .effective_allow_prefixes()

View file

@ -558,6 +558,7 @@ fn every_schema_shape_round_trips_and_rejects_malformed_or_unknown_versions() {
br#"schema = "lazaret-trusted-host-config-v1" br#"schema = "lazaret-trusted-host-config-v1"
[profiles.trusted] [profiles.trusted]
image = "example.invalid/agent:v1" image = "example.invalid/agent:v1"
persistent_dirs = []
"#, "#,
) )
.unwrap(), .unwrap(),

View file

@ -11,7 +11,6 @@ default_allow = ["192.0.2.53/32", "198.51.100.53/32"]
[profiles.default] [profiles.default]
image = "docker.io/library/debian:trixie" image = "docker.io/library/debian:trixie"
isolate_gitignored = true isolate_gitignored = true
persistent_dirs = ["/root"]
allow_egress = ["203.0.113.10/32"] allow_egress = ["203.0.113.10/32"]
volumes = [ volumes = [
{ host = "{profile_dir}/agent-state", container = "/root/.local/state/agent", kind = "directory", access = "read-write", create = true }, { host = "{profile_dir}/agent-state", container = "/root/.local/state/agent", kind = "directory", access = "read-write", create = true },

View file

@ -295,6 +295,10 @@ later product mounts, and a layout cannot be constructed without `/workspace`.
Bundle construction, rather than trusted-host configuration syntax, owns the Bundle construction, rather than trusted-host configuration syntax, owns the
exact destination choices and transient-child classification. exact destination choices and transient-child classification.
An omitted profile `persistent_dirs` field selects `/root`, `/usr`, `/var`,
`/etc`, and `/opt`. An explicit list replaces that default in full, including an
empty list or paths used by an unorthodox distribution.
The loader resolves environment references only after structural, path, and The loader resolves environment references only after structural, path, and
network validation. A missing, non-UTF-8, or NUL-containing host value refuses network validation. A missing, non-UTF-8, or NUL-containing host value refuses
the selected profile. Resolved values live only in one move-owned, the selected profile. Resolved values live only in one move-owned,