Skip to content

Commit 0043fc1

Browse files
authored
Merge pull request #1148 from spkenv/preserve-env-var-config
Make environment variables to preserve configurable
2 parents d6add16 + b1854b7 commit 0043fc1

File tree

15 files changed

+174
-75
lines changed

15 files changed

+174
-75
lines changed

.github/workflows/rust.yml

+6
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,12 @@ jobs:
102102
run: |
103103
export PATH="$PWD/target/debug:$PATH"
104104
make packages.lint
105+
- name: Configure SPFS for Integration tests
106+
run: |
107+
cat << EOF > /etc/spfs.toml
108+
[environment]
109+
variable_names_to_preserve = ["TMPDIR"]
110+
EOF
105111
- name: SPFS Integration Tests - Regular User
106112
run: |
107113
# Run tests as a normal user to verify privilege escalation

.site/spi/run_integration_tests.sh

+5-1
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,12 @@ mkdir -p "$ORIGIN_REPO"
1111
# Pre-create a repo
1212
SPFS_REMOTE_origin_ADDRESS="file://${ORIGIN_REPO}?create=true" spfs ls-tags -r origin
1313
export SPFS_REMOTE_origin_ADDRESS="file://${ORIGIN_REPO}"
14+
cat << EOF > /etc/spfs.toml
15+
[environment]
16+
variable_names_to_preserve = ["TMPDIR"]
17+
EOF
1418
# Run tests as a normal user to verify privilege escalation
1519
useradd -m e2e
1620
su e2e -c /tests/run_tests.sh
1721
# Run tests that need root
18-
tests/run_privileged_tests.sh
22+
tests/run_privileged_tests.sh

Cargo.lock

+18-18
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/spfs-cli/cmd-enter/src/cmd_enter.rs

+41-2
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// SPDX-License-Identifier: Apache-2.0
33
// https://github.com/spkenv/spk
44

5+
use std::borrow::Cow;
56
use std::ffi::OsString;
67
#[cfg(feature = "sentry")]
78
use std::sync::atomic::Ordering;
@@ -13,6 +14,7 @@ use cli::configure_sentry;
1314
use miette::{Context, Result};
1415
#[cfg(unix)]
1516
use spfs::monitor::SPFS_MONITOR_FOREGROUND_LOGGING_VAR;
17+
use spfs::runtime::EnvKeyValue;
1618
use spfs::storage::fs::RenderSummary;
1719
use spfs_cli_common as cli;
1820
use spfs_cli_common::CommandName;
@@ -82,13 +84,34 @@ pub struct RemountArgs {
8284
enabled: bool,
8385
}
8486

87+
fn parse_env_key_value(s: &str) -> Result<EnvKeyValue> {
88+
let parts: Vec<&str> = s.splitn(2, '=').collect();
89+
if parts.len() != 2 {
90+
miette::bail!("Invalid environment key-value pair (missing '='): {s}");
91+
}
92+
if parts[0].is_empty() {
93+
miette::bail!("Invalid environment key-value pair (empty key): {s}");
94+
}
95+
if parts[0].contains(|c: char| c.is_whitespace()) {
96+
miette::bail!("Invalid environment key-value pair (key contains whitespace): {s}");
97+
}
98+
Ok(EnvKeyValue(parts[0].to_string(), parts[1].to_string()))
99+
}
100+
85101
#[derive(Debug, Args)]
86102
#[group(id = "enter_grp")]
87103
pub struct EnterArgs {
88104
/// The value to set $TMPDIR to in new environment
105+
///
106+
/// Deprecated: use --environment-override instead
89107
#[clap(long)]
90108
tmpdir: Option<String>,
91109

110+
/// Optional keys and values to set in the new environment, in the form
111+
/// KEY=VALUE. This option can be repeated.
112+
#[clap(long, value_parser = parse_env_key_value)]
113+
environment_override: Vec<EnvKeyValue>,
114+
92115
/// Put the rendering and syncing times into environment variables
93116
#[clap(long)]
94117
metrics_in_env: bool,
@@ -140,6 +163,7 @@ impl CmdEnter {
140163
// this function will eventually be required to discover the overlayfs
141164
// attributes. It can take many milliseconds to run so we prime the cache as
142165
// soon as possible in a separate thread
166+
143167
std::thread::spawn(spfs::runtime::overlayfs::overlayfs_available_options_prime_cache);
144168

145169
let mut runtime = self.load_runtime(config).await?;
@@ -233,7 +257,14 @@ impl CmdEnter {
233257
}
234258
};
235259

236-
owned.ensure_startup_scripts(self.enter.tmpdir.as_ref())?;
260+
let mut environment_overrides = Cow::Borrowed(&self.enter.environment_override);
261+
if let Some(tmpdir) = &self.enter.tmpdir {
262+
environment_overrides
263+
.to_mut()
264+
.push(EnvKeyValue("TMPDIR".to_string(), tmpdir.to_string()));
265+
}
266+
267+
owned.ensure_startup_scripts(environment_overrides.as_slice())?;
237268
std::env::set_var("SPFS_RUNTIME", owned.name());
238269

239270
Ok(Some(owned))
@@ -256,7 +287,15 @@ impl CmdEnter {
256287
let start_time = Instant::now();
257288
let render_summary = spfs::initialize_runtime(&mut owned).await?;
258289
self.report_render_summary(render_summary, start_time.elapsed().as_secs_f64());
259-
owned.ensure_startup_scripts(self.enter.tmpdir.as_ref())?;
290+
291+
let mut environment_overrides = Cow::Borrowed(&self.enter.environment_override);
292+
if let Some(tmpdir) = &self.enter.tmpdir {
293+
environment_overrides
294+
.to_mut()
295+
.push(EnvKeyValue("TMPDIR".to_string(), tmpdir.to_string()));
296+
}
297+
298+
owned.ensure_startup_scripts(environment_overrides.as_slice())?;
260299
std::env::set_var("SPFS_RUNTIME", owned.name());
261300

262301
Ok(Some(owned))

crates/spfs/Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ gitignore = "1.0"
5050
glob = { workspace = true }
5151
hyper = { version = "0.14.16", features = ["client"] }
5252
indicatif = { workspace = true }
53-
itertools = "0.10.3"
53+
itertools = { workspace = true }
5454
libc = { workspace = true }
5555
miette = { workspace = true }
5656
nix = { workspace = true, features = ["fs"] }

crates/spfs/src/bootstrap.rs

+15-9
Original file line numberDiff line numberDiff line change
@@ -355,15 +355,21 @@ where
355355

356356
let mut enter_args = Vec::new();
357357

358-
// Capture the current $TMPDIR value here before it is lost when running
359-
// privileged process spfs-enter.
360-
if let Some(tmpdir_value_for_child_process) = std::env::var_os("TMPDIR") {
361-
tracing::trace!(
362-
?tmpdir_value_for_child_process,
363-
"capture existing value for $TMPDIR (build_spfs_enter_command)"
364-
);
365-
366-
enter_args.extend(["--tmpdir".into(), tmpdir_value_for_child_process]);
358+
// Capture the configured environment variable values here before they are
359+
// possibly lost when running privileged process spfs-enter.
360+
let config = crate::get_config()?;
361+
for key in &config.environment.variable_names_to_preserve {
362+
if let Ok(value) = std::env::var(key) {
363+
tracing::trace!(
364+
?key,
365+
?value,
366+
"capture existing variable (build_spfs_enter_command)"
367+
);
368+
enter_args.extend([
369+
"--environment-override".into(),
370+
format!("{key}={value}").into(),
371+
]);
372+
}
367373
}
368374

369375
enter_args.extend([

crates/spfs/src/bootstrap_test.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ async fn test_shell_initialization_startup_scripts(
5757

5858
let tmp_startup_dir = tmpdir.path().join("startup.d");
5959
std::fs::create_dir(&tmp_startup_dir).unwrap();
60-
rt.ensure_startup_scripts(None).unwrap();
60+
rt.ensure_startup_scripts(&[]).unwrap();
6161
for startup_script in &[&rt.config.sh_startup_file, &rt.config.csh_startup_file] {
6262
let mut cmd = Command::new("sed");
6363
cmd.arg("-i");
@@ -131,7 +131,7 @@ async fn test_shell_initialization_no_startup_scripts(shell: &str, tmpdir: tempf
131131

132132
let tmp_startup_dir = tmpdir.path().join("startup.d");
133133
std::fs::create_dir(&tmp_startup_dir).unwrap();
134-
rt.ensure_startup_scripts(None).unwrap();
134+
rt.ensure_startup_scripts(&[]).unwrap();
135135
for startup_script in &[&rt.config.sh_startup_file, &rt.config.csh_startup_file] {
136136
let mut cmd = Command::new("sed");
137137
cmd.arg("-i");

crates/spfs/src/config.rs

+19
Original file line numberDiff line numberDiff line change
@@ -464,6 +464,24 @@ pub struct Sentry {
464464
pub email_domain: Option<String>,
465465
}
466466

467+
#[derive(Clone, Default, Debug, Deserialize, Serialize)]
468+
#[serde(default)]
469+
pub struct Environment {
470+
/// Environment variables names to preserve when creating an spfs
471+
/// environment.
472+
///
473+
/// Most environment variables are preserved by default but a few are
474+
/// cleared for security purposes. Known values include `TMPDIR` and
475+
/// `LD_LIBRARY_PATH`. Any variable listed here will be propagated into a
476+
/// new spfs runtime by capturing their values before running spfs-enter and
477+
/// then setting them back to the captured values from inside the spfs
478+
/// runtime startup script.
479+
///
480+
/// Any variables listed here that are not present in the environment will
481+
/// remain unset in the new spfs environment.
482+
pub variable_names_to_preserve: Vec<String>,
483+
}
484+
467485
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
468486
#[serde(default)]
469487
pub struct Config {
@@ -474,6 +492,7 @@ pub struct Config {
474492
pub fuse: Fuse,
475493
pub monitor: Monitor,
476494
pub sentry: Sentry,
495+
pub environment: Environment,
477496
}
478497

479498
impl Config {

crates/spfs/src/runtime/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ pub use storage::{
2727
Author,
2828
Config,
2929
Data,
30+
EnvKeyValue,
3031
KeyValuePair,
3132
KeyValuePairBuf,
3233
MountBackend,

0 commit comments

Comments
 (0)