Skip to content

Commit

Permalink
fix: added MISE_SOPS_ROPS setting
Browse files Browse the repository at this point in the history
  • Loading branch information
jdx committed Dec 16, 2024
1 parent 3f0d91a commit 6ac208d
Show file tree
Hide file tree
Showing 4 changed files with 51 additions and 14 deletions.
3 changes: 3 additions & 0 deletions e2e/secrets/test_secrets
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,6 @@ mise x -- sops encrypt -i --age "$age_pub" .env.yaml
export MISE_SOPS_AGE_KEY=
assert "mise set _.file=.env.yaml"
assert_contains "mise env" "export SECRET=mysecret"

# sops
MISE_SOPS_ROPS=0 assert_contains "mise env" "export SECRET=mysecret"
6 changes: 6 additions & 0 deletions settings.toml
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,12 @@ a particular feature you'd like to try. Also, if something isn't working
right, try disabling it if you can.
"""

[sops.rops]
env = "MISE_SOPS_ROPS"
type = "Bool"
default = true
description = "Use rops to decrypt sops files. Disable to shell out to `sops` which will slow down mise but sops may offer features not available in rops."

[fetch_remote_versions_cache]
env = "MISE_FETCH_REMOTE_VERSIONS_CACHE"
type = "Duration"
Expand Down
4 changes: 2 additions & 2 deletions src/config/env_directive/file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ impl EnvResults {
if let Ok(raw) = file::read_to_string(p) {
let mut f: Env<serde_json::Value> = serde_json::from_str(&raw).wrap_err_with(errfn)?;
if !f.sops.is_empty() {
let raw = sops::decrypt::<_, JsonFileFormat>(&raw, parse_template)?;
let raw = sops::decrypt::<_, JsonFileFormat>(&raw, parse_template, "json")?;
f = serde_json::from_str(&raw).wrap_err_with(errfn)?;
}
f.env
Expand Down Expand Up @@ -87,7 +87,7 @@ impl EnvResults {
if let Ok(raw) = file::read_to_string(p) {
let mut f: Env<serde_yaml::Value> = serde_yaml::from_str(&raw).wrap_err_with(errfn)?;
if !f.sops.is_empty() {
let raw = sops::decrypt::<_, YamlFileFormat>(&raw, parse_template)?;
let raw = sops::decrypt::<_, YamlFileFormat>(&raw, parse_template, "yaml")?;
f = serde_yaml::from_str(&raw).wrap_err_with(errfn)?;
}
f.env
Expand Down
52 changes: 40 additions & 12 deletions src/sops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,16 @@ use rops::cryptography::hasher::SHA512;
use rops::file::state::EncryptedFile;
use rops::file::RopsFile;
use std::env;
use std::sync::{Mutex, OnceLock};

pub fn decrypt<PT, F>(input: &str, parse_template: PT) -> result::Result<String>
pub fn decrypt<PT, F>(input: &str, parse_template: PT, format: &str) -> result::Result<String>
where
PT: Fn(String) -> result::Result<String>,
F: rops::file::format::FileFormat,
{
static ONCE: std::sync::Once = std::sync::Once::new();
ONCE.call_once(|| {
static AGE_KEY: OnceLock<Option<String>> = OnceLock::new();
static MUTEX: Mutex<()> = Mutex::new(());
let age = AGE_KEY.get_or_init(|| {
let p = SETTINGS
.sops
.age_key_file
Expand All @@ -24,7 +26,7 @@ where
Ok(p) => p,
Err(e) => {
warn!("failed to parse sops age key file: {}", e);
return;
return None;
}
});
if p.exists() {
Expand All @@ -34,19 +36,45 @@ where
.lines()
.filter(|l| !l.starts_with('#'))
.collect::<String>();
env::set_var("ROPS_AGE", key);
if !key.is_empty() {
return Some(key);
}
}
}
if let Some(age_key) = &SETTINGS.sops.age_key {
if !age_key.is_empty() {
env::set_var("ROPS_AGE", age_key);
return Some(age_key.clone());
}
}
None
});
let f = input
.parse::<RopsFile<EncryptedFile<AES256GCM, SHA512>, F>>()
.wrap_err("failed to parse sops file")?;
Ok(f.decrypt::<F>()
.wrap_err("failed to decrypt sops file")?
.to_string())
let _lock = MUTEX.lock().unwrap(); // prevent multiple threads from using the same age key
let age_env_key = if SETTINGS.sops.rops {
"ROPS_AGE"
} else {
"SOPS_AGE_KEY"
};
let prev_age = env::var(age_env_key).ok();
if let Some(age) = &age {
env::set_var(age_env_key, age);
}
let output = if SETTINGS.sops.rops {
input
.parse::<RopsFile<EncryptedFile<AES256GCM, SHA512>, F>>()
.wrap_err("failed to parse sops file")?
.decrypt::<F>()
.wrap_err("failed to decrypt sops file")?
.to_string()
} else {
// TODO: this won't work right if sops is coming from mise since tools won't have been loaded
// TODO: this obviously won't work on windows
cmd!("sops", "--input-type", format, "--output-type", format, "-d", "/dev/stdin").stdin_bytes(input.as_bytes()).read()?
};

if let Some(age) = prev_age {
env::set_var(age_env_key, age);
} else {
env::remove_var(age_env_key);
}
Ok(output)
}

0 comments on commit 6ac208d

Please sign in to comment.