1use std::fmt;
6
7use isoloom_spec::model::{MachineSecret, SecretSource, Spec};
8use isoloom_spec::syntax::Mode;
9
10#[derive(Debug, Clone, PartialEq, Eq)]
12pub enum ValueSource {
13 Secret(String),
15 Input(String),
17}
18
19#[derive(Debug, Clone, PartialEq, Eq)]
21pub struct SecretFile {
22 pub name: String,
25 pub source: ValueSource,
27 pub path: String,
29 pub uid: Option<u32>,
31 pub gid: Option<u32>,
33 pub mode: Option<u32>,
36}
37
38pub const DEFAULT_MODE: u32 = 0o444;
42
43pub const VM_DEFAULT_MODE: u32 = 0o400;
49
50impl SecretFile {
51 #[must_use]
53 pub fn of(spec: &Spec, s: &MachineSecret) -> SecretFile {
54 let m = s.mount();
55 let source = match spec.secrets.get(&m.source) {
56 Some(SecretSource::Input { input }) => ValueSource::Input(input.clone()),
57 _ => ValueSource::Secret(m.source.clone()),
58 };
59 SecretFile {
60 source,
61 name: m.source,
62 path: s.path(),
63 uid: m.uid,
64 gid: m.gid,
65 mode: m.mode.map(Mode::bits),
66 }
67 }
68}
69
70pub(crate) fn no_delivery(f: &mut fmt::Formatter<'_>, machine: &str) -> fmt::Result {
72 write!(
73 f,
74 "machine `{machine}` gets `secrets`, and this target has no machine `isoloom run` can hand them to"
75 )
76}
77
78pub(crate) fn no_owner(f: &mut fmt::Formatter<'_>, machine: &str) -> fmt::Result {
80 write!(
81 f,
82 "a secret of machine `{machine}` sets `uid` or `gid`, and this target places a secret's file as root's"
83 )
84}
85
86#[cfg(test)]
87mod tests {
88 use super::{SecretFile, ValueSource};
89 use crate::testing;
90
91 #[test]
92 fn a_file_from_an_input_is_given_at_each_start_and_one_from_a_secret_read_from_its_store() {
93 let spec = testing::spec(
94 "version: 1\nname: t\ninputs: [FLAG]\nsecrets: { db: { env: DB } }\nmachines:\n web: { app: { image: nginx }, files: [{ path: /flag, input: FLAG, mode: '0440' }, { path: /run/db, secret: db, uid: 70 }] }\n",
95 );
96 let files: Vec<SecretFile> = spec.machines["web"].secrets.iter().map(|s| SecretFile::of(&spec, s)).collect();
97 let at = |i: usize| files.get(i).map(|f| (f.name.as_str(), &f.source, f.path.as_str(), f.uid, f.mode));
98 assert_eq!(at(0), Some(("input-flag", &ValueSource::Input("FLAG".into()), "/flag", None, Some(0o440))));
99 assert_eq!(
100 at(1),
101 Some(("db", &ValueSource::Secret("db".into()), "/run/db", Some(70), Some(0o400))),
102 "root-only by default"
103 );
104 }
105}