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:
commit
f83e9a23d5
48
osc/conf.py
48
osc/conf.py
@ -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
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user