From: Michael Tremer Date: Wed, 5 Oct 2022 15:31:37 +0000 (+0000) Subject: client: Perform Kerberos authentication against the hub X-Git-Tag: 0.9.28~281 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=6284f1f2ffc24a92a47acd54b16c477760d8fc4b;p=pakfire.git client: Perform Kerberos authentication against the hub Signed-off-by: Michael Tremer --- diff --git a/configure.ac b/configure.ac index f33a66b73..60a7245d5 100644 --- a/configure.ac +++ b/configure.ac @@ -245,6 +245,7 @@ AC_CHECK_FUNCS([ \ AM_PATH_PYTHON([3.6]) AX_PYTHON_MODULE([cpuinfo], [fatal]) +AX_PYTHON_MODULE([kerberos], [fatal]) AX_PYTHON_MODULE([psutil], [fatal]) AX_PYTHON_MODULE([setproctitle], [fatal]) AX_PYTHON_MODULE([systemd], [fatal]) diff --git a/contrib/config/client.conf b/contrib/config/client.conf index 6c59de98c..e9d4ae785 100644 --- a/contrib/config/client.conf +++ b/contrib/config/client.conf @@ -5,6 +5,5 @@ # The URL of the server to connect to. # server = https://pakfirehub.ipfire.org/ -# Your credentials to log in on the hub. -# username = ipfire -# password = 1234... +# Your credentials to log in on the hub +# keytab = /etc/pakfire/pakfire.keytab (defaults to /etc/krb5.keytab) diff --git a/src/pakfire/client.py b/src/pakfire/client.py index d248e7792..9eb4764d4 100644 --- a/src/pakfire/client.py +++ b/src/pakfire/client.py @@ -41,21 +41,17 @@ class Client(object): def connect_to_hub(self): huburl = self.config.get("client", "server", PAKFIRE_HUB) - # User Credentials - username = self.config.get("client", "username") - password = self.config.get("client", "password") + # keytab + keytab = self.config.get("client", "keytab") - if not (username and password): - raise RuntimeError("User credentials are not set") + # Create a connection to the hub + return hub.Hub(huburl, keytab=keytab) - # Create connection to the hub. - return hub.Hub(huburl, username, password) - - def check_connection(self): + async def check_connection(self): """ Tests the connection to the hub """ - return self.hub.test() + return await self.hub.test() # Builds diff --git a/src/pakfire/hub.py b/src/pakfire/hub.py index d995bc722..cbf116e48 100644 --- a/src/pakfire/hub.py +++ b/src/pakfire/hub.py @@ -23,6 +23,7 @@ import cpuinfo import functools import hashlib import json +import kerberos import logging import os.path import psutil @@ -39,6 +40,8 @@ from .i18n import _ # Setup logging log = logging.getLogger("pakfire.hub") +DEFAULT_KEYTAB = "/etc/krb5.keytab" + # Configure some useful defaults for all requests tornado.httpclient.AsyncHTTPClient.configure( None, defaults = { @@ -47,18 +50,21 @@ tornado.httpclient.AsyncHTTPClient.configure( ) class Hub(object): - def __init__(self, url, username, password): + def __init__(self, url, keytab=None): self.url = url - self.username = username - self.password = password + + # Store path to keytab + self.keytab = keytab or DEFAULT_KEYTAB # Initialise the HTTP client self.client = tornado.httpclient.AsyncHTTPClient() # XXX support proxies - async def _request(self, method, path, websocket=False, + async def _request(self, method, path, websocket=False, authenticate=True, body=None, body_producer=None, on_message_callback=None, **kwargs): + headers = {} + # Make absolute URL url = urllib.parse.urljoin(self.url, path) @@ -78,14 +84,21 @@ class Hub(object): if body is None: body = query_args + # Perform Kerberos authentication + if authenticate: + krb5_context = self._setup_krb5_context(url) + + # Set the Negotiate header + headers |= { + "Authorization" : + "Negotiate %s" % kerberos.authGSSClientResponse(krb5_context), + } + # Make the request req = tornado.httpclient.HTTPRequest( - method=method, url=url, body=body, + method=method, url=url, headers=headers, body=body, - # Authentication - auth_username=self.username, auth_password=self.password, - - # All all the rest + # Add all the rest body_producer=body_producer, ) @@ -108,18 +121,40 @@ class Hub(object): # Empty response return {} + def _setup_krb5_context(self, url): + """ + Creates the Kerberos context that can be used to perform client + authentication against the server, and mutual authentication for the server. + """ + # Parse the input URL + url = urllib.parse.urlparse(url) + + # Create a new client context + result, krb5_context = kerberos.authGSSClientInit("HTTP@%s" % url.hostname) + + if not result == kerberos.AUTH_GSS_COMPLETE: + raise RuntimeError("Could not create Kerberos Client context") + + # Next step... + try: + result = kerberos.authGSSClientStep(krb5_context, "") + + except kerberos.GSSError as e: + log.error("Kerberos authentication failed: %s" % e) + raise e + + if not result == kerberos.AUTH_GSS_CONTINUE: + raise RuntimeError("Cloud not continue Kerberos authentication") + + return krb5_context + # Test functions - def test(self): + async def test(self): """ Tests the connection """ - return self._request("/noop", decode="ascii") - - def test_error(self, code): - assert code >= 100 and code <= 999 - - self._request("/error/test/%s" % code) + return await self._request("GET", "/test") # Build actions diff --git a/src/scripts/pakfire-client.in b/src/scripts/pakfire-client.in index ffd3206d7..1fcf261b1 100644 --- a/src/scripts/pakfire-client.in +++ b/src/scripts/pakfire-client.in @@ -114,10 +114,13 @@ class Cli(object): tmp.cleanup() async def _check_connection(self, client, ns): - success = client.check_connection() + response = await client.check_connection() - if success: - print("%s: %s" % (_("Connection OK"), success)) + # Print the response from the service + try: + print("\n".join(response["message"])) + except KeyError: + pass async def _upload(self, client, ns): for file in ns.file: