Whonix Linux Installer - Development Discussion

Fixed build running as root.

1 Like

CI still broken.

2023-03-31T08:47:09.4130706Z + sudo -- echo 'Successful root login'
2023-03-31T08:47:09.4131187Z installer-dist: [e[1me[32mNOTICEe[0m]: Executing: $ sudo -- echo Successful root login
2023-03-31T08:47:09.4191491Z 
2023-03-31T08:47:09.4191738Z We trust you have received the usual lecture from the local System
2023-03-31T08:47:09.4192125Z Administrator. It usually boils down to these three things:
2023-03-31T08:47:09.4192321Z 
2023-03-31T08:47:09.4192447Z     #1) Respect the privacy of others.
2023-03-31T08:47:09.4192690Z     #2) Think before you type.
2023-03-31T08:47:09.4192976Z     #3) With great power comes great responsibility.
2023-03-31T08:47:09.4193161Z 
2023-03-31T08:47:09.4193653Z sudo: a terminal is required to read the password; either use the -S option to read from standard input or configure an askpass helper
2023-03-31T08:47:09.4194198Z sudo: a password is required
2023-03-31T08:47:09.4200564Z + return 1
2023-03-31T08:47:09.4200940Z + die 1 'Failed to run test command as root.'
2023-03-31T08:47:09.4201477Z + log error 'Failed to run test command as root.'
2023-03-31T08:47:09.4201875Z + test 1 = 1

Needs setting up passwordless sudo.

(Based on Kicksecure /etc/sudoers.d/user-passwordless)

%sudo   ALL=(ALL:ALL) NOPASSWD:ALL

Probably needs to be setup by .github/workflows/builds.yml.

Strange, I don’t know why your merge did not get my last commits, maybe you forgot to fetch them before merging?

Anyway, was trying to trigger the build not only when the installer is changed, but also when the workflow has changed:

It eases testing and avoids making extraneous commits to the installer, but couldn’t get it to work last time, will try again.

1 Like

Fixed that problem with CI outputs.

1 Like

Uploaded new version just now.

Could you add a similar test please?

In short: run a command using root_cmd that is supposed to return no output (no stdout, no stderr, all empty). For example:

root_cmd test -d /usr 2>&1

But if there is any stdout or stderr, then that’s a system configuration issue unrelated to Whonix Linux Installer and installation should be aborted.

(Came up in in context of (Fixed) Build From Source Error: special device /dev/mapper/to does not exist)

There is already a root test and the installer sudo can be interactive, so we don’t force users to set a NOPASSWD sudo user.

Because it can return output, such as asking for a password, this test is not valid for user machines, but only on a controlled environment such as a build pipeline.

1 Like

Because it can return output, such as asking for a password, this test is not valid for user machines, but only on a controlled environment such as a build pipeline.

Let’s add a sudo test that checks if output by sudo is empty but that
only runs inside CI?

Added CI mode.

1 Like

I still think this is would be a very worthwhile feature:

The problem is, that this would only work for users that allow sudo to cache passwords.

If caching the sudo password is completely disabled, same in other words if timestamp_timeout=0 is set in sudo settings, then such a test does not seem possible in any sane way.

(Except inventing our own password prompt and caching which we should probably avoid. Specifically for the nice use case of timestamp_timeout=0)

The same in other words, if the user re-enters the sudo password for every sudo command, then this test does not seam possible in any sane way.

Other nice use cases could be where the user doesn’t (only) use a sudo password but configured some other mechanism (smartcards…).

Idea…

Can we separate in the sudo output into:

  • A) sudo output for password entry, and
  • B) sudo output returned for the actual command.

And that is for every other script that requires sudo at certain stages instead of running the script as root or with sudo.

I don’t think we should handle the password, it should be handled by sudo prompt or with sudo’s --stdin option to write the password to stdin and receive the prompt from stderr. That would also have to be deatl with su and doas.

I don’t know about sudo with smartcards, maybe the credentials can also be cached?

Yes we can separate it with the --stdin option, prompt will be sent to stderr. The output will be returned in the file descriptors the commands want it to return.

But, it doesn’t solve the problem with caching passwords, which I don’t think would be safe for us to handle it. Instead, it should be handled before-hand when the user is installing his own operating system. I understand some users might not do this, but then should we disrespect user will to not cache the password?

Yes.

Yes. I would guess these are independent sub components. The sudo caching period being independent from the authentication itself. Maybe done with the sudo askpass configuration file option. Most likely nothing we need to worry about.

Sounds good.

Yes, we should probably stay away from caching user passwords.

No. Respect user settings regarding cached credentials.

pseudo code:

sudo_output=$(some-clever-sudo-command $actual_command_to_run_with_sudo 2>&1)

sudo_output now contains stdout and stderr of '$actual_command_to_run_with_sudo` but

  • user was still able to see sudo’s request for authentication,
  • its result (if it failed).

Possible?

--stdin
Write the prompt to the standard error and read the password from the standard input instead of using the terminal device.

I don’t see how that helps? Ok, the sudo prompt goes to stderr. stdin will work either way. But then we cannot catch the stderr into a variable or file? (Which is needed to compare it later to see if it’s as expected.)

We can redirect them to different files.

Password comes from /dev/stdin.
sudo command >/this-is-stdout 2>/this-is-stderr
Shout stderr to the user as it is the prompt. Save stdout as it is the command output.

1 Like

Not sure if I understand but nice if you can do it.

In other words, I am basically looking to square a circle. Sudo may or may not have such a feature. Here’s the pseudo code that I am looking for:

sudo $actual_command_to_run_with_sudo \
  --sudo-to-user-communication-i.e.-authentication-request stdout(default) \ 
  --sudo-to-user-communication-in-case-of-authentication-failure stderr(default) \
  --stdout-of-actual_command_to_run_with_sudo /tmp/cmd-output.txt \
  --stderr-of-actual_command_to_run_with_sudo /tmp/cmd-output.txt

The problem with this is that it hides from the user:

  • sudo’s request to authenticate,
  • sudo’s notification in case of authentication failure.

Maybe you mean or we could something like this:

rm -f ./sudo-output.text 
touch ./sudo-output.text 

## launch sudo into the background
sudo command 1>./sudo-output.text  2>&1

sudo_pid=$!

tail -f ./sudo-output.text &
tail_pid=$!

wait $sudo_pid || sudo_error_handling

kill -9 $tail_pid

This is rather complex and all of this for the corner case of maybe handful of users who fully disabled sudo credentials caching. So I doubt we should go for a complex and therefore error-prone solution. Got another idea…

This should work anyhwere:

  • user’s system
  • CI

Here’s the pseudo code:

  ## This code we already have:

  log info "Testing root login"
  root_cmd echo "Successful root login" ||
    die 1 "Failed to run test command as root."

  ## Below we could add:

  user_using_sudo_credential_caching=""
  if sudo --non-interactive test -d /usr ; then
    user_using_sudo_credential_caching=yes
  fi

  if [ ! "$user_using_sudo_credential_caching" = "yes" ]; then
    log info "credential caching detected: no"
    return 0
  fi

  log info "credential caching detected: yes"

  sudo_output=$(root_cmd timeout --kill-after 5 5 test -d /usr 2>&1)

  if [ "$sudo_output" = "" ]; then
    log info "sudo output test success."
    return 0
  fi

  log error "sudo output: '$sudo_output'"

  die 105 "sudo output was expected to be empty but is actually non-empty. This is likely a system configuration issue."

bugs:

  • In function virtualbox_start_failed there is currently an issue:

The installer succeeded with download and import, but

But that function doesn’t really know that. If previously downloaded/imported, this is actually false.

I thought maybe I’ll get the information if the installer was downloading from should_download but that function might produce extraneous terminal output. Maybe should_download should be split into two functions, one for test only and one for output? Or set a variable there to indicate if an actual download took place so it can be checked later?

That function also doesn’t know if any import or re-import was done.

Another option would be to simplify the output of that function and just drop it but I think it’s quite useful as it’s supposed to be now?

  • --redownload (without additional options) is broken, doesn’t actually re-download if previously downloaded and already imported.

Version detection was broken due to Whonix version API changes.
Now fixed.

And new version uploaded just now.

function install_virtualbox_debian:

  if test "${has_vboxmanage}" = "1" && test "${has_linux_headers}" = "1"; then
    log notice "vboxmanage and ${linux_headers}"
    install_virtualbox_debian_common_end
    return 0
  fi

This could use a comment why it’s done like this.

It might happen that vboxmanage is installed but VirtualBox-Qt is not.

I guess that’s to cover case where a user installed VirtualBox from the oracle repository already?