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
import functools
import hashlib
import json
+import kerberos
import logging
import os.path
import psutil
# 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 = {
)
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)
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,
)
# 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