Skip to content

Commit

Permalink
Merge pull request #2844 from habitat-sh/fnichol/studio-sudo-refactor
Browse files Browse the repository at this point in the history
Approved by: @nobody from Nowhere
Merged by: The Sentinels
  • Loading branch information
thesentinels authored Jul 28, 2017
2 parents 25cb200 + a16daa8 commit 8bf57ea
Show file tree
Hide file tree
Showing 4 changed files with 40 additions and 191 deletions.
56 changes: 0 additions & 56 deletions components/core/src/env.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,59 +77,3 @@ pub fn var_os<K: AsRef<OsStr>>(key: K) -> std::option::Option<OsString> {
None => None,
}
}

/// Fetches the environment variable `SUDO_USER` from the current process, but only if the value is
/// not `"root"`.
///
/// This function is special-purpose for a Habitat-centric interpretation of this value. If the
/// root user is running a command with `sudo`, then the environment will contain a
/// `SUDO_USER=root` value. However, Habitat considers root's home for caches, etc. to be under
/// the `/hab` directory (as opposed to root's `$HOME`).
///
/// # Examples
///
/// With no environment variable present:
///
/// ```
/// use std;
/// use habitat_core;
///
/// std::env::remove_var("SUDO_USER");
/// match habitat_core::env::sudo_user() {
/// Some(val) => panic!("The environment variable is set but should be unset!"),
/// None => println!("No SUDO_USER set in the environment"),
/// }
/// ```
///
/// With a non-root user set:
///
/// ```
/// use std;
/// use habitat_core;
///
/// std::env::set_var("SUDO_USER", "bob");
/// match habitat_core::env::sudo_user() {
/// Some(val) => assert_eq!(val, "bob"),
/// None => panic!("The environment variable is set and should be bob"),
/// }
/// ```
///
/// With the root user set:
///
/// ```
/// use std;
/// use habitat_core;
///
/// std::env::set_var("SUDO_USER", "root");
/// match habitat_core::env::sudo_user() {
/// Some(val) => panic!("The environment variable is set to root and should return with None!"),
/// None => println!("No non-root SUDO_USER set in the environment"),
/// }
/// ```
///
pub fn sudo_user() -> std::option::Option<String> {
match self::var("SUDO_USER") {
Ok(val) => if val != "root" { Some(val) } else { None },
Err(_) => None,
}
}
126 changes: 26 additions & 100 deletions components/hab/src/command/studio.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,127 +13,56 @@
// limitations under the License.

use std::env;
use std::fs;
use std::fs as stdfs;
use std::ffi::OsString;
use std::path::Path;
use std::path::PathBuf;

use common::ui::UI;
use hcore::crypto::CACHE_KEY_PATH_ENV_VAR;
use hcore::env as henv;
use hcore::fs::{CACHE_ARTIFACT_PATH, CACHE_KEY_PATH};
use hcore::os::{filesystem, users};
use hcore::fs;

use config;
use error::Result;

const ARTIFACT_PATH_ENVVAR: &'static str = "ARTIFACT_PATH";
const ORIGIN_ENVVAR: &'static str = "HAB_ORIGIN";
const STUDIO_CMD: &'static str = "hab-studio";
const STUDIO_CMD_ENVVAR: &'static str = "HAB_STUDIO_BINARY";
const STUDIO_PACKAGE_IDENT: &'static str = "core/hab-studio";

pub fn start(ui: &mut UI, args: Vec<OsString>) -> Result<()> {
inner::rerun_with_sudo_if_needed(ui)?;

// If the `$HAB_ORIGIN` environment variable is not present, then see if a default is set in
// the CLI config. If so, set it as the `$HAB_ORIGIN` environment variable for the `hab-studio`
// or `docker` execv call.
if henv::var("HAB_ORIGIN").is_err() {
let config = config::load_with_sudo_user()?;
if henv::var(ORIGIN_ENVVAR).is_err() {
let config = config::load()?;
if let Some(default_origin) = config.origin {
debug!("Setting default origin {} via CLI config", &default_origin);
env::set_var("HAB_ORIGIN", default_origin);
}
}

// Check if we are running under a `sudo` invocation. If so, determine the non-root user that
// issued the command in order to set some Studio-related environment variables. This is done
// so that the `hab-studio` command will find the correct key cache, artifact cache, etc. and
// so that the correct directores will be volume mounted when used with Docker.
if let Some(sudo_user) = henv::sudo_user() {
if let Some(home) = users::get_home_for_user(&sudo_user) {
// If the `$HAB_CACHE_KEY_PATH` environment variable is not present, set it to the
// non-root user's key cache
if henv::var(CACHE_KEY_PATH_ENV_VAR).is_err() {
let cache_key_path = home.join(format!(".{}", CACHE_KEY_PATH));
debug!(
"Setting cache_key_path for SUDO_USER={} to: {}",
&sudo_user,
cache_key_path.display()
);
env::set_var(CACHE_KEY_PATH_ENV_VAR, cache_key_path);
}
// If the `$ARTIFACT_PATH` environment variable is not present, set it to the non-root
// user's key cache
if henv::var(ARTIFACT_PATH_ENVVAR).is_err() {
let cache_artifact_path = home.join(format!(".{}", CACHE_ARTIFACT_PATH));
try!(create_cache_artifact_path(
&cache_artifact_path,
Some(&sudo_user),
));
debug!(
"Setting cache_artifact_path for SUDO_USER={} to: {}",
&sudo_user,
cache_artifact_path.display()
);
env::set_var(ARTIFACT_PATH_ENVVAR, cache_artifact_path);
}
// Prevent any inner `hab` invocations from triggering similar logic: we will be
// operating in the context `hab-studio` which is running with root like privileges.
env::remove_var("SUDO_USER");
}
} else {
if let Some(home) = users::get_home_for_current_user() {
if henv::var(ARTIFACT_PATH_ENVVAR).is_err() {
let cache_artifact_path = home.join(format!(".{}", CACHE_ARTIFACT_PATH));
try!(create_cache_artifact_path(&cache_artifact_path, None));
debug!(
"Setting cache_artifact_path at: {}",
cache_artifact_path.display()
);
env::set_var(ARTIFACT_PATH_ENVVAR, cache_artifact_path);
}
if henv::var(CACHE_KEY_PATH_ENV_VAR).is_err() {
let path = fs::cache_key_path(None::<&str>);
debug!("Setting {}={}", CACHE_KEY_PATH_ENV_VAR, path.display());
env::set_var(CACHE_KEY_PATH_ENV_VAR, &path);
};

let artifact_path = match henv::var(ARTIFACT_PATH_ENVVAR) {
Ok(p) => PathBuf::from(p),
Err(_) => {
let path = fs::cache_artifact_path(None::<&str>);
debug!("Setting {}={}", ARTIFACT_PATH_ENVVAR, path.display());
env::set_var(ARTIFACT_PATH_ENVVAR, &path);
path
}
};
if !artifact_path.is_dir() {
debug!("Creating artifact_path at: {}", artifact_path.display());
stdfs::create_dir_all(&artifact_path)?;
}

inner::start(ui, args)
}

fn create_cache_artifact_path(path: &Path, sudo_user: Option<&str>) -> Result<()> {
if path.is_dir() {
Ok(())
} else {
match sudo_user {
Some(sudo_user) => {
debug!(
"Creating cache_artifact_path for SUDO_USER={} at: {}",
&sudo_user,
path.display()
)
}
None => debug!("Creating cache_artifact_path at: {}", path.display()),
};
try!(fs::create_dir_all(&path));
if let Some(sudo_user) = sudo_user {
if let (Some(uid), Some(gid)) =
(
users::get_uid_by_name(sudo_user),
users::get_primary_gid_for_user(sudo_user),
)
{
debug!(
"Setting permissions of {} for SUDO_USER={} to: {}:{}",
path.display(),
&sudo_user,
uid,
gid
);
try!(filesystem::chown(path.to_string_lossy().as_ref(), uid, gid));
}
}
Ok(())
}
}

#[cfg(target_os = "linux")]
mod inner {
use std::env;
Expand All @@ -155,6 +84,8 @@ mod inner {
const SUDO_CMD: &'static str = "sudo";

pub fn start(ui: &mut UI, args: Vec<OsString>) -> Result<()> {
rerun_with_sudo_if_needed(ui)?;

let command = match henv::var(super::STUDIO_CMD_ENVVAR) {
Ok(command) => PathBuf::from(command),
Err(_) => {
Expand All @@ -180,7 +111,7 @@ mod inner {
}
}

pub fn rerun_with_sudo_if_needed(ui: &mut UI) -> Result<()> {
fn rerun_with_sudo_if_needed(ui: &mut UI) -> Result<()> {
// If I have root permissions, early return, we are done.
if am_i_root() {
return Ok(());
Expand Down Expand Up @@ -314,11 +245,6 @@ mod inner {
run_container(docker_cmd, args, volumes.iter(), env_vars.iter())
}

pub fn rerun_with_sudo_if_needed(_ui: &mut UI) -> Result<()> {
// No sudo calls necessary here--we are calling `docker` commands instead
Ok(())
}

fn find_docker_cmd() -> Result<PathBuf> {
let docker_cmd = henv::var(DOCKER_CMD_ENVVAR).unwrap_or(DOCKER_CMD.to_string());

Expand Down
48 changes: 13 additions & 35 deletions components/hab/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,7 @@ use std::io::Write;
use std::path::PathBuf;

use hcore::config::ConfigFile;
use hcore::env as henv;
use hcore::fs::{am_i_root, FS_ROOT_PATH};
use hcore::os::users;
use toml;

use error::{Error, Result};
Expand All @@ -47,15 +45,18 @@ impl Default for Config {
}

pub fn load() -> Result<Config> {
common_load(false)
}

pub fn load_with_sudo_user() -> Result<Config> {
common_load(true)
let cli_config_path = cli_config_path();
if cli_config_path.exists() {
debug!("Loading CLI config from {}", cli_config_path.display());
Ok(Config::from_file(&cli_config_path)?)
} else {
debug!("No CLI config found, loading defaults");
Ok(Config::default())
}
}

pub fn save(config: &Config) -> Result<()> {
let config_path = cli_config_path(false);
let config_path = cli_config_path();
let parent_path = match config_path.parent() {
Some(p) => p,
None => {
Expand All @@ -72,34 +73,11 @@ pub fn save(config: &Config) -> Result<()> {
Ok(())
}

fn common_load(use_sudo_user: bool) -> Result<Config> {
let cli_config_path = cli_config_path(use_sudo_user);
if cli_config_path.exists() {
debug!("Loading CLI config from {}", cli_config_path.display());
Ok(Config::from_file(&cli_config_path)?)
} else {
debug!("No CLI config found, loading defaults");
Ok(Config::default())
}
}

fn cli_config_path(use_sudo_user: bool) -> PathBuf {
match am_i_root() {
true => {
if use_sudo_user {
if let Some(sudo_user) = henv::sudo_user() {
if let Some(home) = users::get_home_for_user(&sudo_user) {
return home.join(format!(".{}", CLI_CONFIG_PATH));
}
}
}
}
false => {
if let Some(home) = env::home_dir() {
return home.join(format!(".{}", CLI_CONFIG_PATH));
}
fn cli_config_path() -> PathBuf {
if !am_i_root() {
if let Some(home) = env::home_dir() {
return home.join(format!(".{}", CLI_CONFIG_PATH));
}
}

PathBuf::from(&*FS_ROOT_PATH).join(CLI_CONFIG_PATH)
}
1 change: 1 addition & 0 deletions components/studio/bin/hab-studio.sh
Original file line number Diff line number Diff line change
Expand Up @@ -448,6 +448,7 @@ new_studio() {
local studio_artifact_path
studio_artifact_path="${HAB_STUDIO_ROOT}${HAB_CACHE_ARTIFACT_PATH}"
if ! $bb mount | $bb grep -q "on $studio_artifact_path type"; then
$bb mkdir -p $v $ARTIFACT_PATH
$bb mkdir -p $v $studio_artifact_path
$bb mount $v --bind $ARTIFACT_PATH $studio_artifact_path
fi
Expand Down

0 comments on commit 8bf57ea

Please sign in to comment.