1
0
mirror of https://github.com/openSUSE/osc.git synced 2025-08-11 17:54:06 +02:00

ssh: recognize gpg keys (yubikey usage)

When using ssh keys from gpg, there are no private key files on
disk. The public keys are available from "ssh-add -L". Conveniently,
users store the public keys in some ".pub" file under ~/.ssh
(see e.g. https://serverfault.com/questions/906871/force-the-use-of-a-gpg-key-as-an-ssh-key-for-a-given-server;
this is also necessary to use IdentityFile= in ssh itself).

Thus public key files can't be ignored any more in list_ssh_dir_keys().
"ssh-keygen -Y sign" works nicely with a public key file if the agent
has access to the private key.
This commit is contained in:
Martin Wilck
2022-06-02 21:56:51 +02:00
committed by Daniel Mach
parent 715a30f3f3
commit 870d861b61

View File

@@ -565,7 +565,7 @@ def _build_opener(apiurl):
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
stdout, _ = proc.communicate() stdout, _ = proc.communicate()
if proc.returncode == 0 and stdout.strip(): if proc.returncode == 0 and stdout.strip():
return stdout.splitlines() return [self.get_fingerprint(line) for line in stdout.splitlines()]
else: else:
return [] return []
@@ -584,21 +584,43 @@ def _build_opener(apiurl):
return True return True
return False return False
def is_ssh_public_keyfile(self, keyfile_path):
if not os.path.isfile(keyfile_path):
return False
return keyfile_path.endswith(".pub")
@staticmethod
def get_fingerprint(line):
parts = line.strip().split(b" ")
if len(parts) < 2:
raise ValueError("Unable to retrieve ssh key fingerprint from line: {}".format(line))
return parts[1]
def list_ssh_dir_keys(self): def list_ssh_dir_keys(self):
sshdir = os.path.expanduser('~/.ssh') sshdir = os.path.expanduser('~/.ssh')
keys_in_home_ssh = {} keys_in_home_ssh = {}
for keyfile in os.listdir(sshdir): for keyfile in os.listdir(sshdir):
if keyfile.endswith(".pub"): if keyfile.startswith(("agent-", "authorized_keys", "config", "known_hosts")):
# skip files that definitely don't contain keys
continue continue
keyfile_path = os.path.join(sshdir, keyfile) keyfile_path = os.path.join(sshdir, keyfile)
if not self.is_ssh_private_keyfile(keyfile_path): # public key alone may be sufficient because the private key
# can get loaded into ssh-agent from gpg (yubikey works this way)
is_public = self.is_ssh_public_keyfile(keyfile_path)
# skip private detection if we think the key is a public one already
is_private = False if is_public else self.is_ssh_private_keyfile(keyfile_path)
if not is_public and not is_private:
continue continue
cmd = ["ssh-keygen", "-lf", keyfile_path] cmd = ["ssh-keygen", "-lf", keyfile_path]
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
stdout, _ = proc.communicate() stdout, _ = proc.communicate()
if proc.returncode == 0: if proc.returncode == 0:
fingerprint = stdout.strip() fingerprint = self.get_fingerprint(stdout)
if fingerprint: if fingerprint and (fingerprint not in keys_in_home_ssh or is_private):
# prefer path to a private key
keys_in_home_ssh[fingerprint] = keyfile_path keys_in_home_ssh[fingerprint] = keyfile_path
return keys_in_home_ssh return keys_in_home_ssh