backintime
backintime copied to clipboard
Support ed25519 SSH keys for SSH tests
Experiment with allowing ed25519 SSH keys for SSH tests.
Note, I think this should work. However, I haven't been able to test it properly, because the SSH tests are unable to unlock my SSH key. This is strange, because I can ssh user@localhost without specifying a password, so I know that authorized_keys is setup correctly.
These changes do appear to correctly detect which kind of key I have, whether it is authorised, and pass it through to the tests.
The error looks like this:
/usr/bin/python3 -m unittest -b test/test_restore.py
WARNING: Failed to read process stat from /proc/21142/stat: [2] No such file or directory
.........E
Stderr:
WARNING: Failed to connect to Udev serviceHelper daemon via D-Bus: org.freedesktop.DBus.Error.ServiceUnknown
WARNING: D-Bus message: The name net.launchpad.backintime.serviceHelper was not provided by any .service files
WARNING: Udev-based profiles cannot be changed or checked due to Udev serviceHelper connection failure
/usr/lib/python3.12/tempfile.py:1075: ResourceWarning: Implicitly cleaning up <TemporaryDirectory '/tmp/tmpny1rrgcg'>
_warnings.warn(warn_message, ResourceWarning)
/usr/lib/python3.12/tempfile.py:1075: ResourceWarning: Implicitly cleaning up <TemporaryDirectory '/tmp/tmpgdzbjznm'>
_warnings.warn(warn_message, ResourceWarning)
ERROR: Failed to unlock SSH private key /home/dwales/.ssh/id_ed25519: WARNING: Failed to connect to Udev serviceHelper daemon via D-Bus: org.freedesktop.DBus.Error.ServiceUnknown
WARNING: D-Bus message: The name net.launchpad.backintime.serviceHelper was not provided by any .service files
WARNING: Udev-based profiles cannot be changed or checked due to Udev serviceHelper connection failure
/usr/lib/python3.12/getpass.py:91: GetPassWarning: Can not control echo on the terminal.
passwd = fallback_getpass(prompt, stream)
Warning: Password input may be echoed.
Enter password for SSH private key profile "Main profile": Traceback (most recent call last):
File "/usr/lib/python3.12/getpass.py", line 69, in unix_getpass
old = termios.tcgetattr(fd) # a copy to save
^^^^^^^^^^^^^^^^^^^^^
termios.error: (25, 'Inappropriate ioctl for device')
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/usr/share/backintime/common/askpass.py", line 45, in <module>
print(pw.password(None, profile_id, mode))
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/share/backintime/common/password.py", line 205, in password
password = self.passwordFromUser(parent, profile_id, mode, pw_id)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/share/backintime/common/password.py", line 291, in passwordFromUser
password = getpass.getpass(prompt + ' ')
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.12/getpass.py", line 91, in unix_getpass
passwd = fallback_getpass(prompt, stream)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.12/getpass.py", line 126, in fallback_getpass
return _raw_input(prompt, stream)
^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.12/getpass.py", line 148, in _raw_input
raise EOFError
EOFError
E
Stderr:
WARNING: Failed to connect to Udev serviceHelper daemon via D-Bus: org.freedesktop.DBus.Error.ServiceUnknown
WARNING: D-Bus message: The name net.launchpad.backintime.serviceHelper was not provided by any .service files
WARNING: Udev-based profiles cannot be changed or checked due to Udev serviceHelper connection failure
/usr/lib/python3.12/tempfile.py:1075: ResourceWarning: Implicitly cleaning up <TemporaryDirectory '/tmp/tmp67sd_znp'>
_warnings.warn(warn_message, ResourceWarning)
/usr/lib/python3.12/tempfile.py:1075: ResourceWarning: Implicitly cleaning up <TemporaryDirectory '/tmp/tmpn16ifmox'>
_warnings.warn(warn_message, ResourceWarning)
/usr/lib/python3.12/subprocess.py:1127: ResourceWarning: subprocess 21306 is still running
_warn("subprocess %s is still running" % self.pid,
ResourceWarning: Enable tracemalloc to get the object allocation traceback
ERROR: Failed to unlock SSH private key /home/dwales/.ssh/id_ed25519: WARNING: Failed to connect to Udev serviceHelper daemon via D-Bus: org.freedesktop.DBus.Error.ServiceUnknown
WARNING: D-Bus message: The name net.launchpad.backintime.serviceHelper was not provided by any .service files
WARNING: Udev-based profiles cannot be changed or checked due to Udev serviceHelper connection failure
/usr/lib/python3.12/getpass.py:91: GetPassWarning: Can not control echo on the terminal.
passwd = fallback_getpass(prompt, stream)
Warning: Password input may be echoed.
Enter password for SSH private key profile "Main profile": Traceback (most recent call last):
File "/usr/lib/python3.12/getpass.py", line 69, in unix_getpass
old = termios.tcgetattr(fd) # a copy to save
^^^^^^^^^^^^^^^^^^^^^
termios.error: (25, 'Inappropriate ioctl for device')
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/usr/share/backintime/common/askpass.py", line 45, in <module>
print(pw.password(None, profile_id, mode))
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/share/backintime/common/password.py", line 205, in password
password = self.passwordFromUser(parent, profile_id, mode, pw_id)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/share/backintime/common/password.py", line 291, in passwordFromUser
password = getpass.getpass(prompt + ' ')
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.12/getpass.py", line 91, in unix_getpass
passwd = fallback_getpass(prompt, stream)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.12/getpass.py", line 126, in fallback_getpass
return _raw_input(prompt, stream)
^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.12/getpass.py", line 148, in _raw_input
raise EOFError
EOFError
======================================================================
ERROR: test_restore (test.test_restore.TestRestoreSSH.test_restore)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/home/dwales/code/not-my-code/backintime/common/test/test_restore.py", line 189, in setUp
self.cfg.setCurrentHashId(mount.Mount(cfg = self.cfg).mount())
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/dwales/code/not-my-code/backintime/common/mount.py", line 221, in mount
backend = mounttools(cfg = self.config,
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/dwales/code/not-my-code/backintime/common/sshtools.py", line 180, in __init__
self.unlockSshAgent()
File "/home/dwales/code/not-my-code/backintime/common/sshtools.py", line 443, in unlockSshAgent
raise MountException(
exceptions.MountException: Could not unlock ssh private key. Wrong password or password not available for cron.
Stderr:
WARNING: Failed to connect to Udev serviceHelper daemon via D-Bus: org.freedesktop.DBus.Error.ServiceUnknown
WARNING: D-Bus message: The name net.launchpad.backintime.serviceHelper was not provided by any .service files
WARNING: Udev-based profiles cannot be changed or checked due to Udev serviceHelper connection failure
/usr/lib/python3.12/tempfile.py:1075: ResourceWarning: Implicitly cleaning up <TemporaryDirectory '/tmp/tmpny1rrgcg'>
_warnings.warn(warn_message, ResourceWarning)
/usr/lib/python3.12/tempfile.py:1075: ResourceWarning: Implicitly cleaning up <TemporaryDirectory '/tmp/tmpgdzbjznm'>
_warnings.warn(warn_message, ResourceWarning)
ERROR: Failed to unlock SSH private key /home/dwales/.ssh/id_ed25519: WARNING: Failed to connect to Udev serviceHelper daemon via D-Bus: org.freedesktop.DBus.Error.ServiceUnknown
WARNING: D-Bus message: The name net.launchpad.backintime.serviceHelper was not provided by any .service files
WARNING: Udev-based profiles cannot be changed or checked due to Udev serviceHelper connection failure
/usr/lib/python3.12/getpass.py:91: GetPassWarning: Can not control echo on the terminal.
passwd = fallback_getpass(prompt, stream)
Warning: Password input may be echoed.
Enter password for SSH private key profile "Main profile": Traceback (most recent call last):
File "/usr/lib/python3.12/getpass.py", line 69, in unix_getpass
old = termios.tcgetattr(fd) # a copy to save
^^^^^^^^^^^^^^^^^^^^^
termios.error: (25, 'Inappropriate ioctl for device')
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/usr/share/backintime/common/askpass.py", line 45, in <module>
print(pw.password(None, profile_id, mode))
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/share/backintime/common/password.py", line 205, in password
password = self.passwordFromUser(parent, profile_id, mode, pw_id)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/share/backintime/common/password.py", line 291, in passwordFromUser
password = getpass.getpass(prompt + ' ')
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.12/getpass.py", line 91, in unix_getpass
passwd = fallback_getpass(prompt, stream)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.12/getpass.py", line 126, in fallback_getpass
return _raw_input(prompt, stream)
^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.12/getpass.py", line 148, in _raw_input
raise EOFError
EOFError
======================================================================
ERROR: test_restore_file_with_spaces (test.test_restore.TestRestoreSSH.test_restore_file_with_spaces)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/home/dwales/code/not-my-code/backintime/common/test/test_restore.py", line 189, in setUp
self.cfg.setCurrentHashId(mount.Mount(cfg = self.cfg).mount())
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/dwales/code/not-my-code/backintime/common/mount.py", line 221, in mount
backend = mounttools(cfg = self.config,
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/dwales/code/not-my-code/backintime/common/sshtools.py", line 180, in __init__
self.unlockSshAgent()
File "/home/dwales/code/not-my-code/backintime/common/sshtools.py", line 443, in unlockSshAgent
raise MountException(
exceptions.MountException: Could not unlock ssh private key. Wrong password or password not available for cron.
Stderr:
WARNING: Failed to connect to Udev serviceHelper daemon via D-Bus: org.freedesktop.DBus.Error.ServiceUnknown
WARNING: D-Bus message: The name net.launchpad.backintime.serviceHelper was not provided by any .service files
WARNING: Udev-based profiles cannot be changed or checked due to Udev serviceHelper connection failure
/usr/lib/python3.12/tempfile.py:1075: ResourceWarning: Implicitly cleaning up <TemporaryDirectory '/tmp/tmp67sd_znp'>
_warnings.warn(warn_message, ResourceWarning)
/usr/lib/python3.12/tempfile.py:1075: ResourceWarning: Implicitly cleaning up <TemporaryDirectory '/tmp/tmpn16ifmox'>
_warnings.warn(warn_message, ResourceWarning)
/usr/lib/python3.12/subprocess.py:1127: ResourceWarning: subprocess 21306 is still running
_warn("subprocess %s is still running" % self.pid,
ResourceWarning: Enable tracemalloc to get the object allocation traceback
ERROR: Failed to unlock SSH private key /home/dwales/.ssh/id_ed25519: WARNING: Failed to connect to Udev serviceHelper daemon via D-Bus: org.freedesktop.DBus.Error.ServiceUnknown
WARNING: D-Bus message: The name net.launchpad.backintime.serviceHelper was not provided by any .service files
WARNING: Udev-based profiles cannot be changed or checked due to Udev serviceHelper connection failure
/usr/lib/python3.12/getpass.py:91: GetPassWarning: Can not control echo on the terminal.
passwd = fallback_getpass(prompt, stream)
Warning: Password input may be echoed.
Enter password for SSH private key profile "Main profile": Traceback (most recent call last):
File "/usr/lib/python3.12/getpass.py", line 69, in unix_getpass
old = termios.tcgetattr(fd) # a copy to save
^^^^^^^^^^^^^^^^^^^^^
termios.error: (25, 'Inappropriate ioctl for device')
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/usr/share/backintime/common/askpass.py", line 45, in <module>
print(pw.password(None, profile_id, mode))
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/share/backintime/common/password.py", line 205, in password
password = self.passwordFromUser(parent, profile_id, mode, pw_id)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/share/backintime/common/password.py", line 291, in passwordFromUser
password = getpass.getpass(prompt + ' ')
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.12/getpass.py", line 91, in unix_getpass
passwd = fallback_getpass(prompt, stream)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.12/getpass.py", line 126, in fallback_getpass
return _raw_input(prompt, stream)
^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.12/getpass.py", line 148, in _raw_input
raise EOFError
EOFErro
The interesting bits are:
/usr/lib/python3.12/getpass.py:91: GetPassWarning: Can not control echo on the terminal.
passwd = fallback_getpass(prompt, stream)
Warning: Password input may be echoed.
Enter password for SSH private key profile "Main profile": Traceback (most recent call last):
File "/usr/lib/python3.12/getpass.py", line 69, in unix_getpass
old = termios.tcgetattr(fd) # a copy to save
^^^^^^^^^^^^^^^^^^^^^
termios.error: (25, 'Inappropriate ioctl for device')
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/usr/share/backintime/common/askpass.py", line 45, in <module>
print(pw.password(None, profile_id, mode))
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/share/backintime/common/password.py", line 205, in password
password = self.passwordFromUser(parent, profile_id, mode, pw_id)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/share/backintime/common/password.py", line 291, in passwordFromUser
password = getpass.getpass(prompt + ' ')
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.12/getpass.py", line 91, in unix_getpass
passwd = fallback_getpass(prompt, stream)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.12/getpass.py", line 126, in fallback_getpass
return _raw_input(prompt, stream)
^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.12/getpass.py", line 148, in _raw_input
raise EOFError
EOFError
======================================================================
ERROR: test_restore (test.test_restore.TestRestoreSSH.test_restore)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/home/dwales/code/not-my-code/backintime/common/test/test_restore.py", line 189, in setUp
self.cfg.setCurrentHashId(mount.Mount(cfg = self.cfg).mount())
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/dwales/code/not-my-code/backintime/common/mount.py", line 221, in mount
backend = mounttools(cfg = self.config,
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/dwales/code/not-my-code/backintime/common/sshtools.py", line 180, in __init__
self.unlockSshAgent()
File "/home/dwales/code/not-my-code/backintime/common/sshtools.py", line 443, in unlockSshAgent
raise MountException(
exceptions.MountException: Could not unlock ssh private key. Wrong password or password not available for cron.
Very strangely, if I print out the result from sshtools.py:374, I get:
output='The agent has no identities.\n'
But if I run the following manually, it is able to access my key:
>>> proc = subprocess.Popen(['ssh-add', '-l'],
... stdout=subprocess.PIPE,
... universal_newlines=True)
...
>>> output = proc.communicate()[0]
>>> print(f"{output=}")
output='256 SHA256:5F... \n'
So, I suspect that the tests are running in an isolated way which prevents them from accessing my ssh-agent. This then triggers an issue with fetching my credentials with backintime-askpass.
EDIT: I can see that the tests run find in CI, so perhaps it's just an issue with my setup.
Thanks. I will look deeper to see why it's not able to access ssh-agent.
Testing some things:
| branch | key_type | password | tests | note |
|---|---|---|---|---|
| dev | rsa | no | ✅ | |
| dev | rsa | yes | ✅ | backintime-askpass got password |
| dev | ed25519 | no | ❌ | |
| dev | ed25519 | yes | ❌ | |
| pr | rsa | no | ✅ | |
| pr | rsa | yes | ✅ | |
| pr | ed25519 | no | ✅ | |
| pr | ed25519 | yes | ❌ ✅ | backintime-askpass did not appear, but then appeared randomly later, and it worked after that |
From what I can tell, this PR doesn't break anything. But there is an issue with unlocking the ed25519 key when secured with a password. It works find if it does not have a password.
I'm wondering if backintime-askpass is caching the password, from the earlier RSA key run? I'll keep thinking about this.
Edit: Strangely, when I logged into my computer today, the backintime-askpass dialog appeared. I entered the password for my SSH ed25519 key, then ran make test again on this branch, and all the tests pass.
Hypothesis:
- The tests can't access the SSH agent.
- The tests can request the SSH password with
backintime-askpass, but sometimes this gets cached, so if you enter it for one key, then switch to a different key, it won't work until the cache resets and you enter the password for the new key.
Further testing:
On branch dev:
- create new password protected RSA key with password
test- add to
~/.ssh/authorized_keys - clear keys from
ssh-agentwithssh-add -D - add new key with
ssh-add ~/.ssh/id_rsa - Run
make test - Got stuck for a very long time at one of the
test/test_restore.pySSH tests (10th?) - Then, after a very long time (>30 min?) the
backintime-askpassdialog appeared randomly, when the tests weren't even running. I entered the password, and after that the tests worked great.
- add to
- create new RSA key with different password
test2- Run all steps above
- Now it's getting stuck at the same spot in
test_restore.py. - I bet that sometime, I'll be randomly prompted to enter a new SSH password, then it will work fine. (I'll update when this happens)
Conclusion
There is a bug somewhere which doesn't detect that a new password is required when keys are changed. This already existed in the dev branch, and was not introduced by this PR.
There is a bug ... already existed in the
devbranch, and was not introduced by this PR.
That is a good message. 😄
I am really not experienced when it comes to ssh-agent. With "password" you mean the "passphrase" that you can set when generating a key pair? That passphrase I always leave empty. But when someone use a key with passphrase somehow the ssh-agent is involved? I need to learn the basics about this first. 🤣
We have several open Issue about ssh and password/keys. Maybe they are related? #1167, #949, #1224, #652
Some tests are failing (most of them skipped) because on "id_rsa" key is present. It doesn't matter if an "id_ed25519" key is present.
What do you think? How should the test suite behave? Should run with one of the key types only? Or should it run with each of them no matter which one is the first available?
EDIT: I think there is a problem in generic.py. But I don't understand what an "authorized key file" is and why it is relevant in context of SSH related unit tests.
if AUTHORIZED_KEYS_FILE.exists():
with AUTHORIZED_KEYS_FILE.open('rb') as auth:
auth_keys = auth.readlines()
for key in existing_public_keys:
with key.open('rb') as pub:
if pub.read() in auth_keys:
authorised_public_keys.append(key)
if authorised_public_keys:
KEY_IN_AUTH = True
PUBLIC_KEY_FILE = authorised_public_keys[0]
PRIV_KEY_FILE = PUBLIC_KEY_FILE.with_suffix('')
else:
KEY_IN_AUTH = False
# Later code expects these to be defined. They won't work, because
# they are not authorised!
# TODO: Refactor to avoid needing to define these.
PUBLIC_KEY_FILE = public_keys[0]
PRIV_KEY_FILE = PUBLIC_KEY_FILE.with_suffix('')
On my system (with existing "ed25519" key but without "id_rsa") the variable authorised_public_keys keeps empty. And because of that PUBLIC_KEY_FILE = public_keys[0] which results in id_rsa which is not present on my system.
The purpose of the file ~/.ssh/authorized_keys is to list the SSH public keys which are allowed to log into your machine.
The SSH tests use one of your public keys to SSH to localhost, so you need that public key to be listed in authorized_keys.
A quick way to do this is:
cat ~/.ssh/id_ed25519.pub >> ~/.ssh/authorized_keys
generic.py checks if you have either an RSA or ED25519 key in ~/.ssh. Then it checks if any of these keys are listed in authorized_keys. If both keys exist, and both are listed in authorized_keys, then it doesn't matter which one you use. This PR just picks the first one in the list.
If only one key type exists, or if both exist and only one is in authorized_keys, then you should pick the key that's in authorized_keys. (Keys not in authorized_keys will not be able to log in.)
If no keys exist, or if a key exists, but is not in authorized_keys, then you will not be able to log into localhost with SSH, so the SSH tests should be skipped.
Regarding PUBLIC_KEY_FILE = public_keys[0] in the last else:, yes, this doesn't make sense. The only reason I set this variable is because there is code later which uses it. This later code should be refactored to not use this variable if the key doesn't exist. (The later code could check KEY_IN_AUTH for example to determine whether to run. It doesn't need to run if the key doesn't exist, but currently it does.)
This is the later code which uses the variable: https://github.com/bit-team/backintime/blob/cf6a2d8b387e6990feabe778c1310d1aea42ee6c/common/test/generic.py#L110
I did some more thinking, and I think I identified the real bug. backintime-askpass doesn't always work.
To test this, try the following:
# clear key from ssh-agent
ssh-add -d ~/.ssh/id_rsa
# Add the key to ssh-agent, using the `backintime-askpass` tool to get the password
SSH_ASKPASS=backintime-askpass SSH_ASKPASS_REQUIRE=prefer ASKPASS_PROFILE_ID=1 ASKPASS_MODE=ssh ssh-add ~/.ssh/id_rsa
Environment variables determined by inspecting the env which is injected here: https://github.com/bit-team/backintime/blob/366ef003d7049ae460d43386f920285453d0ad9d/common/sshtools.py#L419
(I'm not sure if ASKPASS_PROFILE_ID or ASKPASS_MODE are required for this test. They are used in askpass.py.)
You will notice (if this bug is reproducible...) that the above command hangs for an extremely long time.
For comparison, install a standard askpass tool such as: ssh-askpass or ssh-askpass-gnome, then run:
# clear key from ssh-agent
ssh-add -d ~/.ssh/id_rsa
# Add the key to the ssh-agent using a standard `ssh-askpass` tool
SSH_ASKPASS=ssh-askpass SSH_ASKPASS_REQUIRE=prefer ASKPASS_PROFILE_ID=1 ASKPASS_MODE=ssh ssh-add ~/.ssh/id_rsa
You will notice that the dialog appears instantly, and you can enter your password, then the key is added to ssh-agent.
Now that I think about this some more, it makes me think that perhaps backintime-askpass is running (or crashed!) in the background somewhere, and it's preventing me from using it.
Looking at my running processes, I can see that are a couple of backintime-askpass related processes. Looking at htop, I can see that the following process appears to be stuck in a loop, but I never see the prompt:
/usr/bin/python3 -Es /usr/share/backintime/common/askpass.py Bad passphrase, try again for /home/dwales/.ssh/id_rsa:
Trying to kill these by process ID doesn't work, because they never exist when I kill them, so they must be constantly starting then stopping.
I can see in htop that there are two instances of ssh-add using a lot of CPU.
Killing one of these stops the rogue backintime-askpass processes too.
BUT
Now that I'm in a clean state, running the following takes me right back to continuously launching new backintime-askpass processes, then stopping them, then starting new ones:
ssh-add -d ~/.ssh/id_rsa
SSH_ASKPASS=backintime-askpass SSH_ASKPASS_REQUIRE=prefer ASKPASS_PROFILE_ID=1 ASKPASS_MODE=ssh ssh-add ~/.ssh/id_rsa
ps aux | grep askpass shows the following:
/bin/sh /usr/bin/backintime-askpass Bad passphrase, try again for /home/dwales/.ssh/id_rsa:
And every time I run this, the process IDs are different.
Theory
backintime-askpass has cached my (old) password somewhere, and is trying to use that rather than ask me again. It sends that to ssh-add, which tries to use it, discovers it doesn't work, then loops back to ask backintime-askpass again. But backintime-askpass just keeps giving it the same wrong answer, and never prompts me to re-enter it.
Hello David, thank you so much for this analysis. I do trust in your expertise here.
Is my understanding here correct that the askpass problem is separated from this PR? If it is the case I would suggest you open a new Issue with your askpass analysis. This way we can bring this PR to a clear end before starting a with another problem.
What do you think?
Best Christian
I've created a new issue.
OK, thanks for opening the Issue. Please note that I added some commits to your PR, so you might need to update your local repo.
Let's go back to the authorized keys topic. I understand it that way. When you setup a new key pair (ssh-keygen) and then copy the key to the remote machine (ssh-copy-id), in most cases you test this connection via $ ssh user@host. At the first connection I do get this yes-no-question about adding that key to the authorized file.
Am I right so far?
It seems the the unit tests (generic.py) does check if the key is in that authorized file. But do we need this? Can we just get rid of that part of the code?
OK, I think this is read to merge. What do you think?
Thanks, I'm happy to merge with your changes. I pulled them and the SSH tests run and pass with my ed25519 key. (I had to restart my computer to convince backintime-askpass to ask me for the password due to the bug linked above, but after that it worked...)
I've had another thought about this. 'Explicit is better than implicit'. Would it make sense for the tests to determine the preferred SSH private key from a config file, rather than just trying all of them? I'm concerned that we're going to run into problems unless we clearly define which key we are using, and which password goes with which key.
Conceptually I agree with you.
But BIT currently is IMHO not in the state to do such "fancy" things. Tests reading from config files or using the real file system at all are not unit tests but "system tests". I would like to concentrate on "unit tests" first because the coverage of real "unit tests" is in my estimation under 10% because most of the tests in BIT are system or integration tests. My plan is to separate the test suite into minimum two kinds of tests ("unit" and the others). This is one of our big todos described in the strategy outline (or #1489) also related to migration to modern python packaging standard (#1575) which need to be done first.
Thanks, understood.