nerves-project / nerves

Craft and deploy bulletproof embedded software in Elixir
http://nerves-project.org
Apache License 2.0
2.26k stars 193 forks source link

openssh-askpass hangs on Manjaro Linux #783

Open linusdm opened 2 years ago

linusdm commented 2 years ago

Environment

Current behavior

While trying out nerves on a fresh Manjaro installation (version 21.3.7) I'm unable to run mix burn due to a problem with ask-pass. As documented I have installed the openssh-askpass AUR package, and the askpass binary is available at /usr/bin/qt4-ssh-askpass. Since this is not in the default location (which would be /usr/bin/ssh-askpass) I set the SUDO_ASKPASS environment variable first to point to this location. With this setup the burn command hangs at the point where it should ask for a password (after having confirmed which SD card to burn to).

Output:

$ mix burn
==> nerves
==> sensor_hub

Nerves environment
  MIX_TARGET:   rpi3a
  MIX_ENV:      dev

Use 14.56 GiB memory card found at /dev/sdd? [Yn] 
--- HANGS HERE ---

I use a workaround by installing either the package lxqt-openssh-askpass or x11-ssh-askpass and pointing to the respective binary in SUDO_ASKPASS (/usr/bin/lxqt-openssh-askpass or /usr/lib/ssh/x11-ssh-askpass). This works fine and prompts the root password as expected.

Note that invoking the binary /usr/bin/qt4-ask-password directly works and prompts for a password, but an error is logged:

$ /usr/bin/qt4-ssh-askpass 
ErrorHandler::Throw - warning: QFSFileEngine::open: No file name specified file:  line: 0 function: 

I've heard of someone else having a similar problem on NixOS, although I can't reproduce that exact setup.

This might be just a known issue, but then I would suggest to update the docs to give some more guidance for whoever is in a similar boat.

Also, I'm not sure how to investigate this, as I can't find the exact location where the askpass binary is called. I only see where environment variable is updated, when it's not already set: https://github.com/nerves-project/nerves/blob/main/lib/mix/tasks/burn.ex#L130-L133

Expected behavior

Good integration with the suggested openssh-askpass AUR package for Arch based systems.

fhunleth commented 2 years ago

This might be just a known issue, but then I would suggest to update the docs to give some more guidance for whoever is in a similar boat.

This isn't a known issue to me, so I'm very thankful that you're bringing this up.

The reason that you couldn't find where the askpass binary is called is because it's called by sudo. The SUDO_ASKPASS tells sudo to use the specified program for reading passwords. If sudo doesn't see the environment variable, it will try to read the password itself if it detects an interactive terminal. If not, it fails. Unfortunately, Erlang disassociates child processes from the terminal, so sudo can't read the password the user. That's why the GUI based program is used.

I'll gladly merge whatever updates to the docs you find are needed to make the Nerves tooling work better with Manjaro work.

The ideal solution would be that sudo could ask on the terminal, but I never could figure out how to do that.

linusdm commented 2 years ago

Thank you for clarifying, that helped a lot. I didn't know anything about how sudo uses this askpass binary to collect a password.

Tinkering with it some more, I've noticed that the openssh-askpass binary just doesn't work well together with sudo. Simply trying something like SUDO_ASKPASS=/usr/bin/qt4-ssh-askpass sudo -A ls, also hangs, just like it does in the middle of running mix burn. So this is definitely an issue outside of nerves.

I guess I should've read the documentation better, because I just found out there is an explicit reference to an alternative askpass package.

I think you're right that the ideal solution would be to forgo the askpass dependency completely, and let the user fill in a sudo password in the terminal. If the SUDO_ASKPASS variable is not set (I disable these lines in the code), sudo complains like this: sudo: a terminal is required to read the password; either use the -S option to read from standard input or configure an askpass helper. Do you, or anyone else, have any suggestions on how this would be feasible with the current setup? I see a Port is used (and there is also a code-path that simply uses System.cmd/3) but I'm not sure what the possibilities are to forward keystrokes to sudo when a password is required.

fhunleth commented 2 years ago

This has been a longstanding problem with invoking sudo from Erlang. The best idea that I've had is to create a version of askpass that knows how to interact with a GenServer in the currently running Erlang VM which can get input from the user. I've asked around over the past years if anyone has better ideas. The other alternative was to run posix_spawnp in a NIF, but console I/O formatting was messed up and needed to be adjusted. No one has implemented either of these enough for us to use to my knowledge.

If you're interested in making progress in this area, I think we should start a proof-of-concept project where we can try different ways out and see what works the best. I think that it's doable.