ssh: call ssh-keygen once for each key to import

ssh-keygen -l supports an input file that has multiple keys. As a
result, it will output multiple key fingerprints.

That being said, ssh-keygen will ignore empty newlines from the input
(and maybe other things?).
It makes it slightly challenging to associate each key with its
fingerprint because the number of lines in the input and output can
differ, e.g.:

             input               |               output
  -----------------------------------------------------------------------
  ssh-rsa AA[...] user@host     ◀-▶ 256 SHA256:[...] user@host (RSA)
  <empty line>                   ┌▶ 3072 SHA256:[...] user@host (ED25519)
  ssh-ed25519 AA[...] user@host ◀┘

To simplify this process, we will do one call to ssh-keygen -l for each
key from the input.

Signed-off-by: Olivier Gayot <olivier.gayot@canonical.com>
This commit is contained in:
Olivier Gayot 2022-11-21 23:46:16 +01:00
parent aa4a674352
commit 18f8735c49
1 changed files with 13 additions and 15 deletions

View File

@ -91,25 +91,23 @@ class SSHController(SubiquityController):
identities=None, error=e.stderr)
keys_material: str = cp.stdout.replace('\r', '').strip()
# ssh-keygen supports multiple keys at once.
# ssh-keygen supports multiple keys at once, but it is simpler to
# associate each key with its resulting fingerprint if we call
# ssh-keygen multiple times.
fingerprint_command = ('ssh-keygen', '-l', '-f', '-')
try:
cp = await arun_command(fingerprint_command, check=True,
input=keys_material)
except subprocess.CalledProcessError as e:
log.exception("ssh-import-id failed. stderr: %s", e.stderr)
return SSHFetchIdResponse(
tatus=SSHFetchIdStatus.FINGERPRINT_ERROR,
identities=None, error=e.stderr)
for key_material in (mat for mat in keys_material.splitlines() if mat):
try:
cp = await arun_command(fingerprint_command, check=True,
input=key_material)
except subprocess.CalledProcessError as e:
log.exception("ssh-import-id failed. stderr: %s", e.stderr)
return SSHFetchIdResponse(
tatus=SSHFetchIdStatus.FINGERPRINT_ERROR,
identities=None, error=e.stderr)
fingerprints: str = cp.stdout.replace(
fingerprint: str = cp.stdout.replace(
f'# ssh-import-id {user_id}', '').strip()
zipped = zip(
[mat for mat in keys_material.splitlines() if mat],
fingerprints.splitlines())
for key_material, fingerprint in zipped:
key_type, key, key_comment = key_material.split(' ', maxsplit=2)
identities.append(SSHIdentity(
key_type=key_type, key=key, key_comment=key_comment,