mirror of
https://github.com/openSUSE/osc.git
synced 2025-02-13 14:05:06 +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', []):
|
for authreq in headers.get_all('www-authenticate', []):
|
||||||
scheme = authreq.split()[0].lower()
|
scheme = authreq.split()[0].lower()
|
||||||
authreqs[scheme] = authreq
|
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']
|
del headers['www-authenticate']
|
||||||
headers['www-authenticate'] = authreqs['signature']
|
headers['www-authenticate'] = authreqs['signature']
|
||||||
return self.signatureauthhandler.http_error_401(req, fp, code, msg, headers)
|
return self.signatureauthhandler.http_error_401(req, fp, code, msg, headers)
|
||||||
|
|
||||||
if 'basic' in authreqs:
|
if 'basic' in authreqs:
|
||||||
del headers['www-authenticate']
|
del headers['www-authenticate']
|
||||||
headers['www-authenticate'] = authreqs['basic']
|
headers['www-authenticate'] = authreqs['basic']
|
||||||
|
|
||||||
response = super(self.__class__, self).http_error_401(req, fp, code, msg, headers)
|
response = super(self.__class__, self).http_error_401(req, fp, code, msg, headers)
|
||||||
# workaround for http://bugs.python.org/issue9639
|
# workaround for http://bugs.python.org/issue9639
|
||||||
if hasattr(self, 'retried'):
|
if hasattr(self, 'retried'):
|
||||||
@ -565,7 +577,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 +596,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
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user