Skip to content

Commit b3def14

Browse files
committed
Add SSL args/config options
- Add --ssl-verify ("true", "false", or "/path/to.pem") - Add --ssl-no-revoke (from micromamba) - Add corresponding config fields for these Signed-off-by: Rick Elrod <rick.elrod@checkmk.com>
1 parent 0f80933 commit b3def14

7 files changed

Lines changed: 317 additions & 40 deletions

File tree

README.md

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,3 +35,38 @@ available:
3535
administrator), `csm.exe` will prompt for confirmation before continuing.
3636
Setting this configuration option to true, disables the check entirely. This
3737
option does nothing on non-Windows systems.
38+
39+
* **`env_create` section** - for options affecting the `csm env create`
40+
subcommand.
41+
42+
* `ssl_verify` - A boolean, which, if true (default) enables SSL/TLS
43+
certificate verification. If false, verification is disabled when calling
44+
`micromamba` and when using `pip`. Setting this to false has the effect of
45+
setting the following environment variables when `csm` calls `micromamba`:
46+
47+
- `CURL_CA_BUNDLE=""`
48+
- `PIP_CERT=""`
49+
- `PIP_INDEX_URL="https://pypi.org/simple"`
50+
- `PIP_TRUSTED_HOST="pypi.org files.pythonhosted.org pypi.pythonhosted.org"`
51+
- `REQUESTS_CA_BUNDLE=""`
52+
53+
This setting (and `ssl_bundle` below) can be overridden with the
54+
`--ssl-verify` command-line argument to `csm env create`.
55+
56+
* `ssl_bundle` - A string which configures the SSL/TLS certificate bundle to
57+
use when verifying certificates. Only taken into account when `ssl_verify`
58+
is true. Setting this has the effect of setting the following environment
59+
variables when `csm` calls `micromamba`:
60+
61+
- `CURL_CA_BUNDLE="<value>"`
62+
- `PIP_CERT="<value>"`
63+
- `REQUESTS_CA_BUNDLE="<value>"`
64+
65+
* `ssl_no_revoke` - A boolean which instructs `micromamba` to not check
66+
SSL/TLS certificate revokation. Setting this has the effect of setting the
67+
following environment variable when `csm` calls `micromamba`:
68+
69+
- `MAMBA_SSL_NO_REVOKE="true"`
70+
71+
This setting can be overridden with the `--ssl-no-revoke` command-line
72+
switch to `csm env create`.

src/csmrc.rs

Lines changed: 65 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,36 @@
33
use log::debug;
44
use serde::Deserialize;
55
use std::default::Default;
6+
use std::fs::File;
67
use std::io::{Error, ErrorKind};
78

9+
#[derive(Debug, Deserialize)]
10+
#[serde(default)]
11+
pub struct EnvCreateConfig {
12+
/// If true (default), verify SSL/TLS certificates. This is used for
13+
/// configuring passthrough to micromamba (and indirectly, pip).
14+
pub ssl_verify: bool,
15+
16+
/// The SSL/TLS certificate bundle to verify with. When None (default), uses
17+
/// the default, which when calling out to micromamba, corresponds to the
18+
/// system chain or "ca-certificates" from conda-forge if installed.
19+
pub ssl_bundle: Option<String>,
20+
21+
/// Disable SSL/TLS revokation checking (affects passthrough to micromamba).
22+
/// This is equivalent to passing --ssl-no-revoke to micromamba.
23+
pub ssl_no_revoke: bool,
24+
}
25+
26+
impl Default for EnvCreateConfig {
27+
fn default() -> Self {
28+
EnvCreateConfig {
29+
ssl_verify: true,
30+
ssl_bundle: None,
31+
ssl_no_revoke: false,
32+
}
33+
}
34+
}
35+
836
#[derive(Debug, Deserialize)]
937
#[serde(default)]
1038
pub struct Config {
@@ -30,6 +58,9 @@ pub struct Config {
3058
/// If true, skip checking for LongPaths support on Windows, and never try
3159
/// to set it.
3260
pub skip_longpaths_check: bool,
61+
62+
/// Options for the 'env create' subcommand
63+
pub env_create: EnvCreateConfig,
3364
}
3465

3566
#[allow(clippy::derivable_impls)]
@@ -42,32 +73,53 @@ impl Default for Config {
4273
download_micromamba: true,
4374
verbose: false,
4475
skip_longpaths_check: false,
76+
env_create: EnvCreateConfig::default(),
4577
}
4678
}
4779
}
4880

4981
impl Config {
82+
/// Validate the configuration to check for common errors.
83+
fn validate(self) -> Result<Self, std::io::Error> {
84+
// Specifying a bundle when ssl_verify is false makes no sense.
85+
// Do the safe thing and bail out - they might have meant to use the
86+
// bundle and we should not just disable verification in this case.
87+
if !self.env_create.ssl_verify && self.env_create.ssl_bundle.is_some() {
88+
return Err(Error::new(
89+
ErrorKind::InvalidData,
90+
"SSL/TLS verification was disabled, but a bundle was still specified",
91+
));
92+
}
93+
Ok(self)
94+
}
95+
5096
/// Read the user's ~/.csmrc if it exists, merging with the Default instance for
5197
/// Config. Return Err if a config file was found but failed to parse, otherwise
5298
/// Ok with the result of merging the config file values with the Default (and
5399
/// simply the Default if no config file exists).
54100
pub fn from_csmrc() -> Result<Self, std::io::Error> {
101+
fn parse_or_io_error(f: File) -> Result<Config, std::io::Error> {
102+
match serde_yaml_ng::from_reader::<File, Config>(f) {
103+
Ok(cfg) => cfg.validate(),
104+
Err(e) => Err(Error::new(ErrorKind::InvalidData, e)),
105+
}
106+
}
107+
55108
let Some(home) = std::env::home_dir() else {
109+
debug!("Could not determine home directory to read .csmrc, using defaults");
56110
return Ok(Self::default());
57111
};
112+
58113
let csmrc_path = home.join(".csmrc");
59-
match std::fs::read_to_string(csmrc_path) {
60-
Err(e) if e.kind() == ErrorKind::NotFound => {
61-
debug!("No .csmrc found, using defaults");
62-
Ok(Config::default())
63-
}
64-
Err(e) => Err(e),
65-
Ok(csmrc_data) => {
66-
let config = serde_yaml_ng::from_str(&csmrc_data)
67-
.map_err(|e| Error::new(ErrorKind::InvalidData, e));
68-
debug!("config: {:?}", config);
69-
config
70-
}
71-
}
114+
File::open(csmrc_path)
115+
.and_then(parse_or_io_error)
116+
.or_else(|e| {
117+
if e.kind() == ErrorKind::NotFound {
118+
debug!("No .csmrc found, using defaults");
119+
Ok(Config::default())
120+
} else {
121+
Err(e)
122+
}
123+
})
72124
}
73125
}

src/env/create.rs

Lines changed: 51 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use crate::csmrc::Config;
12
use crate::env::parsing::setup_file::RobotmkSetup;
23
use crate::env::{CommonArgs, dump_micromamba_captured_output_on_error, env_name};
34
use crate::micromamba::Micromamba;
@@ -15,30 +16,70 @@ pub struct Args {
1516
/// If specified, overrides the post-creation setup file [default: robotmk-setup.yaml]
1617
#[arg(long = "setup-file", value_name = "PATH")]
1718
pub setup_file: Option<PathBuf>,
19+
20+
/// Verify SSL/TLS certificates when communicating with external services.
21+
/// Can be "true" to use the system chain, "false" to disable verification,
22+
/// or a path to a custom chain (PEM file).
23+
#[arg(long = "ssl-verify")]
24+
ssl_verify: Option<String>,
25+
26+
/// Disable SSL/TLS revokation checking in micromamba
27+
#[arg(long = "ssl-no-revoke")]
28+
ssl_no_revoke: bool,
1829
}
1930

20-
pub fn run(micromamba: Micromamba, args: Args) -> Result<(), ExitCode> {
31+
pub fn run(mut micromamba: Micromamba, args: Args, config: &Config) -> Result<(), ExitCode> {
2132
if let Some(path) = &args.setup_file
2233
&& !path.exists()
2334
{
2435
error!("Explicit --setup-file was given, but the path does not exist.");
2536
return Err(ExitCode::FAILURE);
2637
}
2738

39+
let (ssl_verify, ssl_bundle) = match &args.ssl_verify {
40+
Some(b) if b == "false" => (false, None),
41+
Some(b) if b == "true" => (true, None),
42+
Some(bundle) => (true, Some(bundle.clone())),
43+
None => (
44+
config.env_create.ssl_verify,
45+
config.env_create.ssl_bundle.clone(),
46+
),
47+
};
48+
49+
if ssl_verify && let Some(ssl_bundle) = ssl_bundle {
50+
micromamba.set_env_var("PIP_CERT", &ssl_bundle);
51+
micromamba.set_env_var("REQUESTS_CA_BUNDLE", &ssl_bundle);
52+
micromamba.set_env_var("CURL_CA_BUNDLE", &ssl_bundle);
53+
} else if !ssl_verify {
54+
micromamba.set_env_var("PIP_CERT", "");
55+
micromamba.set_env_var("REQUESTS_CA_BUNDLE", "");
56+
micromamba.set_env_var("CURL_CA_BUNDLE", "");
57+
micromamba.set_env_var(
58+
"PIP_TRUSTED_HOST",
59+
"pypi.org files.pythonhosted.org pypi.pythonhosted.org",
60+
);
61+
micromamba.set_env_var("PIP_INDEX_URL", "https://pypi.org/simple");
62+
}
63+
64+
let ssl_no_revoke = args.ssl_no_revoke || config.env_create.ssl_no_revoke;
65+
if ssl_no_revoke {
66+
micromamba.set_env_var("MAMBA_SSL_NO_REVOKE", "true");
67+
}
68+
2869
let env_name = env_name(args.common.name, &args.common.env_file)?;
2970
info!(
3071
"Creating environment '{}' - this may take some time...",
3172
env_name
3273
);
33-
let result = micromamba.stream_if_verbose(vec![
34-
"env",
35-
"create",
36-
"--file",
37-
&args.common.env_file.to_string_lossy(),
38-
"--name",
39-
&env_name,
40-
"--yes",
41-
]);
74+
75+
let env_file = args.common.env_file.to_string_lossy();
76+
let mut mm_args = vec![
77+
"env", "create", "--file", &env_file, "--name", &env_name, "--yes",
78+
];
79+
if !ssl_verify {
80+
mm_args.push("--ssl-verify=<false>");
81+
}
82+
let result = micromamba.stream_if_verbose(mm_args);
4283
let rc = result.exit_code();
4384
dump_micromamba_captured_output_on_error(&result, rc);
4485
if rc == ExitCode::SUCCESS {

src/env/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ pub fn run(config: Config, subcommand: Subcommand) -> Result<(), ExitCode> {
123123
let micromamba = Micromamba::new(&config);
124124

125125
match subcommand {
126-
Subcommand::Create(args) => create::run(micromamba, args),
126+
Subcommand::Create(args) => create::run(micromamba, args, &config),
127127
Subcommand::Delete(args) => delete::run(micromamba, args, &config),
128128
Subcommand::List => micromamba.stream(vec!["env", "list"]).into(),
129129
Subcommand::Info => micromamba.stream(vec!["info"]).into(),

src/micromamba/mod.rs

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ pub struct MicromambaInfo {
2222

2323
pub struct Micromamba<'a> {
2424
config: &'a Config,
25+
env_vars: HashMap<String, String>,
2526
}
2627

2728
fn block_on_child_exit(child: &mut std::process::Child) -> MicromambaResult {
@@ -85,21 +86,34 @@ fn exec_micromamba(cmd: &mut Command, stream_output: bool) -> MicromambaResult {
8586

8687
impl<'a> Micromamba<'a> {
8788
pub fn new(config: &'a Config) -> Self {
88-
Self { config }
89+
let mut micromamba = Self {
90+
config,
91+
env_vars: HashMap::new(),
92+
};
93+
if let Some(mamba_root_prefix) = &config.mamba_root_prefix {
94+
micromamba.set_env_var("MAMBA_ROOT_PREFIX", mamba_root_prefix);
95+
}
96+
micromamba
97+
}
98+
99+
/// Helper method to insert environment variables with &str for convenience
100+
pub fn set_env_var(&mut self, key: &str, value: &str) {
101+
self.env_vars.insert(key.to_string(), value.to_string());
89102
}
90103

91104
/// Return a [`Command`] ready to shell out to `micromamba` with the appropriate
92105
/// environment variables set based on configuration.
93106
fn micromamba_at(&self, path: &str, args: &Vec<&str>) -> Command {
94-
let mut env_vars: HashMap<&str, String> = HashMap::new();
95-
96-
if let Some(mamba_root_prefix) = &self.config.mamba_root_prefix {
97-
env_vars.insert("MAMBA_ROOT_PREFIX", mamba_root_prefix.to_string());
98-
}
107+
// On Windows, the env vars don't get shown by the Debug instance for
108+
// Command, so print them ourselves (always, to make testing easier).
109+
debug!(
110+
"Running command with these environment variables: {:?}",
111+
self.env_vars
112+
);
99113

100114
let mut cmd = Command::new(path);
101115
cmd.args(args);
102-
cmd.envs(env_vars);
116+
cmd.envs(&self.env_vars);
103117
if self.config.noop_mode {
104118
info!("Would run: {:?}", cmd);
105119
} else {

templates/mambarc

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,3 @@
1-
# Show the active environment in the shell prompt
2-
changeps1: True
3-
41
# proxy_servers:
52
# http: http://user:pass@corp.com:8080
6-
# https: https://user:pass@corp.com:8080
7-
8-
# Use this only if you are behind a proxy that does SSL inspection
9-
# ssl_verify: false
10-
# ssl_verify: mycorpcert.crt
11-
# ssl_no_revoke: true
3+
# https: https://user:pass@corp.com:8080

0 commit comments

Comments
 (0)