From 2ed729d5ba7e4c0f837f80d4812878f07553450d Mon Sep 17 00:00:00 2001 From: Cynthia Coan Date: Sat, 26 Sep 2020 13:23:43 -0600 Subject: [PATCH] allow authentication for docker registries (#19) * allow authentication for docker registries * fix lint --- Cargo.lock | 7 +++++ Cargo.toml | 1 + docs/docs/schemas/executor-conf.md | 4 ++- src/config/types.rs | 50 +++++++++++++++--------------- src/executors/docker.rs | 38 +++++++++++++++++++++++ 5 files changed, 74 insertions(+), 26 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 354e317..c31dcbf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -292,6 +292,12 @@ dependencies = [ "rustc-demangle", ] +[[package]] +name = "base64" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3441f0f7b02788e948e47f457ca01f1d7e6d92c693bc132c22b087d3141c03ff" + [[package]] name = "bitflags" version = "1.2.1" @@ -501,6 +507,7 @@ dependencies = [ "async-std", "async-trait", "atty", + "base64", "cfg-if", "color-eyre", "colored", diff --git a/Cargo.toml b/Cargo.toml index c4354bb..3478cb8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,6 +17,7 @@ annotate-snippets = { version = "^0.9", features = ["color"] } async-std = { version = "^1.6", features = ["attributes"] } async-trait = "^0.1" atty = "^0.2" +base64 = "^0.12" colored = "^2.0" color-eyre = "^0.5.5" crossbeam-channel = "^0.4" diff --git a/docs/docs/schemas/executor-conf.md b/docs/docs/schemas/executor-conf.md index cf123f5..21df845 100644 --- a/docs/docs/schemas/executor-conf.md +++ b/docs/docs/schemas/executor-conf.md @@ -51,7 +51,9 @@ The Host Executor has no possible arguments. It ignores all possible options. | tcp_ports_to_expose | Comma Seperated String [OPTIONAL] | a comma seperated list of ports to export to the host machine. you won't need to set these if you're using two tasks in a pipeline, as each pipeline gets it's own docker network that allows services to natively communicate. | | udp_ports_to_expose | Comma Seperated String [OPTIONAL] | the same as `tcp_ports_to_export` just for udp instead. | | experimental_permission_helper | String'd Boolean [OPTIONAL] [EXPERIMENTAL] | [EXPERIMENTAL] will break in a later update, a flag that tells dev-loop to fix permissions on linux hosts for it's mounted volumes. | - +| run_until_ctrlc | String'd Boolean [OPTIONAL] | Used to indicate that a task will run until Ctrl-C is pressed. Effectively will not cause a failure when Ctrl-C is pressed. | +| docker_auth_username_env | String | The environment variable that contains the username for authentication. | +| docker_auth_password_env | String | The environment variable that contains the password for authentication. | - `provides`: List[ProvideConf] [OPTIONAL] A list of things this particular executor provides. See ProvideConf for more information. diff --git a/src/config/types.rs b/src/config/types.rs index 5dac762..2baba74 100644 --- a/src/config/types.rs +++ b/src/config/types.rs @@ -16,6 +16,31 @@ pub struct ProvideConf { version: Option, } +impl ProvideConf { + /// Create a new implementation of `ProvideConf` + #[cfg(test)] + #[must_use] + pub fn new(name: String, version: Option) -> Self { + Self { name, version } + } + + /// Get the name of the thing provided. + #[must_use] + pub fn get_name(&self) -> &str { + &self.name + } + + /// Get the version of the thing provided. + #[must_use] + pub fn get_version(&self) -> &str { + if self.version.is_none() { + "" + } else { + self.version.as_ref().unwrap() + } + } +} + /// All of the possible types of executors that dev-loop supports executing. #[derive(Debug, Deserialize, PartialEq, Serialize)] pub enum ExecutorType { @@ -43,31 +68,6 @@ pub struct ExecutorConf { provides: Option>, } -impl ProvideConf { - /// Create a new implementation of `ProvideConf` - #[cfg(test)] - #[must_use] - pub fn new(name: String, version: Option) -> Self { - Self { name, version } - } - - /// Get the name of the thing provided. - #[must_use] - pub fn get_name(&self) -> &str { - &self.name - } - - /// Get the version of the thing provided. - #[must_use] - pub fn get_version(&self) -> &str { - if self.version.is_none() { - "" - } else { - self.version.as_ref().unwrap() - } - } -} - impl ExecutorConf { /// Get the type of this executor. #[must_use] diff --git a/src/executors/docker.rs b/src/executors/docker.rs index ea726ac..777f8da 100644 --- a/src/executors/docker.rs +++ b/src/executors/docker.rs @@ -21,6 +21,7 @@ use color_eyre::{ use crossbeam_channel::Sender; use isahc::{ config::{Dialer, VersionNegotiation}, + http::{header::HeaderName, HeaderMap}, prelude::*, Error as HttpError, HttpClient, HttpClientBuilder, }; @@ -105,9 +106,45 @@ impl Executor { provides.insert(provided.get_name().to_owned(), version_opt); } + let mut default_headers = HeaderMap::new(); + if executor_args.contains_key("docker_auth_username_env") { + if executor_args.contains_key("docker_auth_password_env") { + let user_env_var = executor_args.get("docker_auth_username_env").unwrap(); + let pass_env_var = executor_args.get("docker_auth_password_env").unwrap(); + + if let Ok(username_ref) = std::env::var(user_env_var) { + if let Ok(password_ref) = std::env::var(pass_env_var) { + default_headers.insert( + "X-Registry-Auth".parse::()?, + base64::encode(format!( + "{opening_bracket}\"username\": \"{}\", \"password\": \"{}\"}}", + username_ref, + password_ref, + opening_bracket = "{", + )) + .parse()?, + ); + } else { + warn!( + "No password variable specified in: [{}], not authenticating", + pass_env_var + ); + } + } else { + warn!( + "No username variable specified in: [{}], not authenticating", + user_env_var + ); + } + } else { + warn!("`docker_auth_username_env` specified with no `docker_auth_password_env`, not authenticating."); + } + } + let client = if cfg!(target_os = "windows") { // TODO(xxx): set windows named pipe/url HttpClientBuilder::new() + .default_headers(&default_headers) .version_negotiation(VersionNegotiation::http11()) .build() } else { @@ -117,6 +154,7 @@ impl Executor { .unwrap_or_else(|| SOCKET_PATH.to_owned()) .parse::()?, ) + .default_headers(&default_headers) .version_negotiation(VersionNegotiation::http11()) .build() }?;