Sudo in harmonic (#109)

* Perform sudo in harmonic

* Remove sudo in scripts

* A bit more tweaking...

* Yay unix

* Preserve environments we require

* Preserve GITHUB_PATH too

* Correct trace

* Remove unused vars

* Only pass --preserve-env if required
This commit is contained in:
Ana Hobden 2022-12-09 13:00:37 -08:00 committed by GitHub
parent 72792372ee
commit 148625c85f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 73 additions and 33 deletions

View file

@ -72,11 +72,11 @@ jobs:
- name: Set executable
run: chmod +x ./harmonic
- name: Initial install
run: sudo GITHUB_PATH=$GITHUB_PATH RUST_LOG=harmonic=trace RUST_BACKTRACE=full ./harmonic install linux-multi --extra-conf "access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}" --no-confirm
run: GITHUB_PATH=$GITHUB_PATH RUST_LOG=harmonic=trace RUST_BACKTRACE=full ./harmonic install linux-multi --extra-conf "access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}" --no-confirm
- name: Initial uninstall (without a `nix run` first)
run: sudo GITHUB_PATH=$GITHUB_PATH RUST_LOG=harmonic=trace RUST_BACKTRACE=full ./harmonic uninstall --no-confirm
run: GITHUB_PATH=$GITHUB_PATH RUST_LOG=harmonic=trace RUST_BACKTRACE=full ./harmonic uninstall --no-confirm
- name: Repeated install
run: sudo GITHUB_PATH=$GITHUB_PATH RUST_LOG=harmonic=trace RUST_BACKTRACE=full ./harmonic install linux-multi --extra-conf "access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}" --no-confirm
run: GITHUB_PATH=$GITHUB_PATH RUST_LOG=harmonic=trace RUST_BACKTRACE=full ./harmonic install linux-multi --extra-conf "access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}" --no-confirm
- name: echo $PATH
run: echo $PATH
- name: Test `nix`
@ -99,7 +99,7 @@ jobs:
if: success() || failure()
shell: fish --login {0}
- name: Repeated uninstall
run: sudo GITHUB_PATH=$GITHUB_PATH RUST_LOG=harmonic=trace RUST_BACKTRACE=full ./harmonic uninstall --no-confirm
run: GITHUB_PATH=$GITHUB_PATH RUST_LOG=harmonic=trace RUST_BACKTRACE=full ./harmonic uninstall --no-confirm
run-steam-deck:
name: Run Steam Deck (mock)
@ -119,11 +119,11 @@ jobs:
sudo chmod +x /bin/steamos-readonly
sudo useradd -m deck
- name: Initial install
run: sudo GITHUB_PATH=$GITHUB_PATH PATH=$PATH:$HOME/bin RUST_LOG=harmonic=trace RUST_BACKTRACE=full ./harmonic install steam-deck --persistence `pwd`/ci-test-nix --extra-conf "access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}" --no-confirm
run: GITHUB_PATH=$GITHUB_PATH PATH=$PATH:$HOME/bin RUST_LOG=harmonic=trace RUST_BACKTRACE=full ./harmonic install steam-deck --persistence `pwd`/ci-test-nix --extra-conf "access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}" --no-confirm
- name: Initial uninstall (without a `nix run` first)
run: sudo GITHUB_PATH=$GITHUB_PATH PATH=$PATH:$HOME/bin RUST_LOG=harmonic=trace RUST_BACKTRACE=full /nix/harmonic uninstall --no-confirm
run: GITHUB_PATH=$GITHUB_PATH PATH=$PATH:$HOME/bin RUST_LOG=harmonic=trace RUST_BACKTRACE=full /nix/harmonic uninstall --no-confirm
- name: Repeated install
run: sudo GITHUB_PATH=$GITHUB_PATH PATH=$PATH:$HOME/bin RUST_LOG=harmonic=trace RUST_BACKTRACE=full ./harmonic install steam-deck --persistence `pwd`/ci-test-nix --extra-conf "access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}" --no-confirm
run: GITHUB_PATH=$GITHUB_PATH PATH=$PATH:$HOME/bin RUST_LOG=harmonic=trace RUST_BACKTRACE=full ./harmonic install steam-deck --persistence `pwd`/ci-test-nix --extra-conf "access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}" --no-confirm
- name: echo $PATH
run: echo $PATH
- name: Test `nix`
@ -146,7 +146,7 @@ jobs:
if: success() || failure()
shell: fish --login {0}
- name: Repeated uninstall
run: sudo GITHUB_PATH=$GITHUB_PATH PATH=$PATH:$HOME/bin RUST_LOG=harmonic=trace RUST_BACKTRACE=full /nix/harmonic uninstall --no-confirm
run: GITHUB_PATH=$GITHUB_PATH PATH=$PATH:$HOME/bin RUST_LOG=harmonic=trace RUST_BACKTRACE=full /nix/harmonic uninstall --no-confirm
build-x86_64-darwin:
name: Build x86_64 Darwin
@ -179,11 +179,11 @@ jobs:
- name: Set executable
run: chmod +x ./harmonic
- name: Initial install
run: sudo GITHUB_PATH=$GITHUB_PATH RUST_LOG=harmonic=trace RUST_BACKTRACE=full ./harmonic install darwin-multi --extra-conf "access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}" --no-confirm
run: GITHUB_PATH=$GITHUB_PATH RUST_LOG=harmonic=trace RUST_BACKTRACE=full ./harmonic install darwin-multi --extra-conf "access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}" --no-confirm
- name: Initial uninstall (without a `nix run` first)
run: sudo GITHUB_PATH=$GITHUB_PATH RUST_LOG=harmonic=trace RUST_BACKTRACE=full /nix/harmonic uninstall --no-confirm
run: GITHUB_PATH=$GITHUB_PATH RUST_LOG=harmonic=trace RUST_BACKTRACE=full /nix/harmonic uninstall --no-confirm
- name: Repeated install
run: sudo GITHUB_PATH=$GITHUB_PATH RUST_LOG=harmonic=trace RUST_BACKTRACE=full ./harmonic install darwin-multi --extra-conf "access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}" --no-confirm
run: GITHUB_PATH=$GITHUB_PATH RUST_LOG=harmonic=trace RUST_BACKTRACE=full ./harmonic install darwin-multi --extra-conf "access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}" --no-confirm
- name: echo $PATH
run: echo $PATH
- name: Test `nix`
@ -206,5 +206,5 @@ jobs:
if: success() || failure()
shell: fish --login {0}
- name: Repeated uninstall
run: sudo GITHUB_PATH=$GITHUB_PATH RUST_LOG=harmonic=trace RUST_BACKTRACE=full /nix/harmonic uninstall --no-confirm
run: GITHUB_PATH=$GITHUB_PATH RUST_LOG=harmonic=trace RUST_BACKTRACE=full /nix/harmonic uninstall --no-confirm

View file

@ -93,11 +93,6 @@ main() {
exit 1
fi
local maybe_sudo=""
if [ "$(id -u)" -ne 0 ] && command -v sudo > /dev/null; then
maybe_sudo="sudo"
fi
if [ "$need_tty" = "yes" ] && [ ! -t 0 ]; then
# The installer is going to want to ask for confirmation by
# reading stdin. This script was piped into `sh` though and
@ -107,9 +102,9 @@ main() {
err "Unable to run interactively. Run with -y to accept defaults, --help for additional options"
fi
ignore "$maybe_sudo" "$_file" "$@" < /dev/tty
ignore "$_file" "$@" < /dev/tty
else
ignore "$maybe_sudo" "$_file" "$@"
ignore "$_file" "$@"
fi
local _retval=$?

View file

@ -7,7 +7,9 @@ mod interaction;
pub(crate) mod subcommand;
use clap::Parser;
use std::process::ExitCode;
use eyre::WrapErr;
use owo_colors::OwoColorize;
use std::{ffi::CString, process::ExitCode};
use tokio::sync::broadcast::{Receiver, Sender};
use self::subcommand::HarmonicSubcommand;
@ -77,5 +79,55 @@ pub(crate) async fn signal_channel() -> eyre::Result<(Sender<()>, Receiver<()>)>
}
pub fn is_root() -> bool {
nix::unistd::getuid() == nix::unistd::Uid::from_raw(0)
let euid = nix::unistd::Uid::effective();
tracing::trace!("Running as EUID {euid}");
euid.is_root()
}
pub fn ensure_root() -> eyre::Result<()> {
if !is_root() {
eprintln!(
"{}",
"Harmonic needs to run as `root`, attempting to escalate now via `sudo`..."
.yellow()
.dimmed()
);
let sudo_cstring = CString::new("sudo").wrap_err("Making C string of `sudo`")?;
let args = std::env::args();
let mut arg_vec_cstring = vec![];
arg_vec_cstring.push(sudo_cstring.clone());
let mut preserve_env_list = vec![];
for (key, _value) in std::env::vars() {
let preserve = match key.as_str() {
// Rust logging/backtrace bits we use
"RUST_LOG" | "RUST_BACKTRACE" => true,
// CI
"GITHUB_PATH" => true,
// Our own environments
key if key.starts_with("HARMONIC") => true,
_ => false,
};
if preserve {
preserve_env_list.push(key);
}
}
if !preserve_env_list.is_empty() {
arg_vec_cstring.push(
CString::new(format!("--preserve-env={}", preserve_env_list.join(",")))
.wrap_err("Building a `--preserve-env` argument for `sudo`")?,
);
}
for arg in args {
arg_vec_cstring.push(CString::new(arg).wrap_err("Making arg into C string")?);
}
tracing::trace!("Execvp'ing `{sudo_cstring:?}` with args `{arg_vec_cstring:?}`");
nix::unistd::execvp(&sudo_cstring, &arg_vec_cstring)
.wrap_err("Executing Harmonic as `root` via `sudo`")?;
}
Ok(())
}

View file

@ -5,7 +5,7 @@ use std::{
use crate::{
action::ActionState,
cli::{interaction, is_root, signal_channel, CommandExecute},
cli::{ensure_root, interaction, signal_channel, CommandExecute},
plan::RECEIPT_LOCATION,
planner::Planner,
BuiltinPlanner, InstallPlan,
@ -53,11 +53,7 @@ impl CommandExecute for Install {
explain,
} = self;
if !is_root() {
return Err(eyre!(
"`harmonic install` must be run as `root`, try `sudo harmonic install`"
));
}
ensure_root()?;
let existing_receipt: Option<InstallPlan> = match Path::new(RECEIPT_LOCATION).exists() {
true => {

View file

@ -5,7 +5,7 @@ use std::{
};
use crate::{
cli::{is_root, signal_channel},
cli::{ensure_root, signal_channel},
plan::RECEIPT_LOCATION,
InstallPlan,
};
@ -47,11 +47,7 @@ impl CommandExecute for Uninstall {
explain,
} = self;
if !is_root() {
return Err(eyre!(
"`harmonic install` must be run as `root`, try `sudo harmonic install`"
));
}
ensure_root()?;
// During install, `harmonic` will store a copy of itself in `/nix/harmonic`
// If the user opted to run that particular copy of Harmonic to do this uninstall,
@ -89,6 +85,7 @@ impl CommandExecute for Uninstall {
let temp_exe_cstring = CString::new(temp_exe.to_string_lossy().into_owned())
.wrap_err("Making C string of executable path")?;
tracing::trace!("Execv'ing `{temp_exe_cstring:?} {arg_vec_cstring:?}`");
nix::unistd::execv(&temp_exe_cstring, &arg_vec_cstring)
.wrap_err("Executing copied Harmonic")?;
}