From 6e47ded35c905821667c38460b693650a3f4d87d Mon Sep 17 00:00:00 2001 From: Patrick Uiterwijk Date: Sep 24 2018 19:08:01 +0000 Subject: Implement initial keyhelper This can be configured in sshd as the AuthorizedKeysCommand to dynamically get the keys from the Pagure database on the fly. The current version is very naive and just loops over all the users if user lookup isn't used. This should be improved in the future by making the user SSH keys be stored in the schema like DeployKey's, where each key has its own entry in a table and we can look them up by key ID. Signed-off-by: Patrick Uiterwijk --- diff --git a/doc/configuration.rst b/doc/configuration.rst index 6a4c885..b274f1e 100644 --- a/doc/configuration.rst +++ b/doc/configuration.rst @@ -1536,6 +1536,30 @@ not set to be integrated with repoSpanner. Defaults to: ``{}`` +SSH_KEYS_USERNAME_LOOKUP +~~~~~~~~~~~~~~~~~~~~~~~~ + +This configuration key is used by the keyhelper script to indicate that the +git username should be used and looked up. Use this if the username that is sent +to ssh is specific for a unique Pagure user (i.e. not using a single "git@" user +for all git operations). + + +SSH_KEYS_USERNAME_EXPECT +~~~~~~~~~~~~~~~~~~~~~~~~ + +This configuration key should contain the username that is used for git if a single +SSH user is used for all git ssh traffic (i.e. "git"). + + +SSH_KEYS_OPTIONS +~~~~~~~~~~~~~~~~ + +This configuration key provides the options added to keys as they are returned +to sshd, in the same format as AuthorizedKeysFile +(see "AUTHORIZED_KEYS FILE FORMAT" in sshd(8)). + + Deprecated configuration keys ----------------------------- diff --git a/files/keyhelper.py b/files/keyhelper.py new file mode 100644 index 0000000..c248a61 --- /dev/null +++ b/files/keyhelper.py @@ -0,0 +1,107 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" + (c) 2014-2018 - Copyright Red Hat Inc + + Authors: + Patrick Uiterwijk + +""" + +from __future__ import unicode_literals, print_function + +import sys +import os + +# Since this is run by sshd, we don't have a way to set environment +# variables ahead of time +if "PAGURE_CONFIG" not in os.environ and os.path.exists( + "/etc/pagure/pagure.cfg" +): + os.environ["PAGURE_CONFIG"] = "/etc/pagure/pagure.cfg" + +# Here starts the code +import pagure +import pagure.lib +from pagure.config import config as pagure_config +from pagure.lib.model import User, DeployKey + + +# Get the arguments +# Expect sshd config: +# AuthorizedKeysCommand: "%u" "%h" "%t" "%f" +# +# At this moment, we ignore the homedir and fingerprint, since looking +# up a key by fingerprint would require some model changes (ssh keys would +# need to be stored in a fashion like DeployKeys). +# But to not break installations in the future, we should ask installations +# to set up sshd in a way that it will work if we use them in the future. +if len(sys.argv) < 5: + print("Invalid call, too few arguments", file=sys.stderr) + sys.exit(1) + + +username, userhome, keytype, fingerprint = sys.argv[1:5] +username_lookup = pagure_config["SSH_KEYS_USERNAME_LOOKUP"] +expect_username = pagure_config["SSH_KEYS_USERNAME_EXPECT"] + + +if not username_lookup: + if not expect_username: + print("Pagure keyhelper configured incorrectly", file=sys.stderr) + sys.exit(1) + + if username != expect_username: + # Nothing to look up, this user is not git-related + sys.exit(0) + + +session = pagure.lib.create_session(pagure_config["DB_URL"]) +if not session: + print("Unable to get database access") + sys.exit(1) + + +# First try to figure out if this is a deploykey. +# We can look those up very quickly, since those are already +# indexed by key fingerprint. +query = session.query(DeployKey).filter( + DeployKey.ssh_search_key == fingerprint +) +for dkey in query.all(): + keyenv = { + "username": "deploykey_%s_%s" + % (werkzeug.secure_filename(dkey.project.fullname), dkey.id) + } + print( + "%s %s" + % (pagure_config["SSH_KEYS_OPTIONS"] % keyenv, dkey.public_ssh_key) + ) + sys.exit(0) + + +# Now look if it's a normal user +query = session.query(User) +if username_lookup: + query = query.filter(User.user == username) + +for user in query.all(): + for key in user.public_ssh_key.split("\n"): + # Make slightly more sane + key = key.strip() + # Check if this could even be a valid key + key = key.split(" ") + # Should be at the very least ["", "