1
0
mirror of https://github.com/openSUSE/osc.git synced 2025-02-04 02:26:16 +01:00

Merge pull request #1074 from dmach/pr1049-v2

RFC: ssh: recognize gpg keys (yubikey usage)
This commit is contained in:
Daniel Mach 2022-07-25 13:52:31 +02:00 committed by GitHub
commit f83e9a23d5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -540,14 +540,26 @@ def _build_opener(apiurl):
for authreq in headers.get_all('www-authenticate', []):
scheme = authreq.split()[0].lower()
authreqs[scheme] = authreq
if 'signature' in authreqs and self.signatureauthhandler and \
(self.signatureauthhandler.sshkey_known() or 'basic' not in authreqs):
if 'signature' in authreqs \
and self.signatureauthhandler \
and (
# sshkey explicitly set in the config file, use it instead of doing basic auth
self.signatureauthhandler.sshkey_known()
or (
# can't fall-back to basic auth, because server doesn't support it
'basic' not in authreqs
# can't fall-back to basic auth, because there's no password provided
or not self.passwd.find_user_password(None, apiurl)[1]
)):
del headers['www-authenticate']
headers['www-authenticate'] = authreqs['signature']
return self.signatureauthhandler.http_error_401(req, fp, code, msg, headers)
if 'basic' in authreqs:
del headers['www-authenticate']
headers['www-authenticate'] = authreqs['basic']
response = super(self.__class__, self).http_error_401(req, fp, code, msg, headers)
# workaround for http://bugs.python.org/issue9639
if hasattr(self, 'retried'):
@ -565,7 +577,7 @@ def _build_opener(apiurl):
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
stdout, _ = proc.communicate()
if proc.returncode == 0 and stdout.strip():
return stdout.splitlines()
return [self.get_fingerprint(line) for line in stdout.splitlines()]
else:
return []
@ -584,21 +596,43 @@ def _build_opener(apiurl):
return True
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):
sshdir = os.path.expanduser('~/.ssh')
keys_in_home_ssh = {}
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
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
cmd = ["ssh-keygen", "-lf", keyfile_path]
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
stdout, _ = proc.communicate()
if proc.returncode == 0:
fingerprint = stdout.strip()
if fingerprint:
fingerprint = self.get_fingerprint(stdout)
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
return keys_in_home_ssh