I am trying to run the Flask-Kerberos example with a valid keytab file (it works with WSGI-Kerberos).
Here is the content of my ‘example.py’ file
from flask import Flask from flask import render_template from flask_kerberos import init_kerberos from flask_kerberos import requires_authentication from config import Config app = Flask(__name__) app.config.from_object(Config) @app.route("/") @requires_authentication def index(user): return render_template('index.html', user=user) if __name__ == '__main__': init_kerberos(app) app.run()
here is a ‘config.py’
import os import base64 from dotenv import load_dotenv basedir = os.path.abspath(os.path.dirname(__file__)) load_dotenv(os.path.join(basedir, '.flaskenv')) class Config(object): # Setup Secret Key for Application SECRET_KEY = os.environ.get('SECRET_KEY') or str(base64.b64encode('you-will-never-guess'.encode("utf-8"))) # Location of the keytab file KRB5_KTNAME = "K000007.keytab"
and here is a ‘.flaskenv’
FLASK_APP=example.py FLASK_RUN_HOST="0.0.0.0" FLASK_RUN_PORT=5000 FLASK_ENV=development
However, when start the Flask via flask run
I am getting the following error in CMD:
(venv) Server@User:~/.../flask_kerberos_example$ flask run * Serving Flask app "example.py" (lazy loading) * Environment: development * Debug mode: on * Running on http://0.0.0.0:5000/ (Press CTRL+C to quit) * Restarting with stat * Debugger is active! * Debugger PIN: 603-674-916 a.b.c.d - - [23/Jun/2021 08:47:51] "GET / HTTP/1.1" 401 - a.b.c.d - - [23/Jun/2021 08:47:51] "GET / HTTP/1.1" 500 - Traceback (most recent call last): File "/venv/lib/python3.7/site-packages/flask/app.py", line 2464, in __call__ return self.wsgi_app(environ, start_response) File "/venv/lib/python3.7/site-packages/flask/app.py", line 2450, in wsgi_app response = self.handle_exception(e) File "/venv/lib/python3.7/site-packages/flask/app.py", line 1867, in handle_exception reraise(exc_type, exc_value, tb) File "/venv/lib/python3.7/site-packages/flask/_compat.py", line 39, in reraise raise value File "/venv/lib/python3.7/site-packages/flask/app.py", line 2447, in wsgi_app response = self.full_dispatch_request() File "/venv/lib/python3.7/site-packages/flask/app.py", line 1952, in full_dispatch_request rv = self.handle_user_exception(e) File "/venv/lib/python3.7/site-packages/flask/app.py", line 1821, in handle_user_exception reraise(exc_type, exc_value, tb) File "/venv/lib/python3.7/site-packages/flask/_compat.py", line 39, in reraise raise value File "/venv/lib/python3.7/site-packages/flask/app.py", line 1950, in full_dispatch_request rv = self.dispatch_request() File "/venv/lib/python3.7/site-packages/flask/app.py", line 1936, in dispatch_request return self.view_functions[rule.endpoint](**req.view_args) File "/venv/lib/python3.7/site-packages/flask_kerberos.py", line 106, in decorated rc = _gssapi_authenticate(token) File "/venv/lib/python3.7/site-packages/flask_kerberos.py", line 70, in _gssapi_authenticate rc, state = kerberos.authGSSServerInit(_SERVICE_NAME) TypeError: argument 1 must be str, not None a.b.c.d - - [23/Jun/2021 08:47:51] "GET /?__debugger__=yes&cmd=resource&f=style.css HTTP/1.1" 200 - a.b.c.d - - [23/Jun/2021 08:47:51] "GET /?__debugger__=yes&cmd=resource&f=debugger.js HTTP/1.1" 200 - a.b.c.d - - [23/Jun/2021 08:47:51] "GET /?__debugger__=yes&cmd=resource&f=jquery.js HTTP/1.1" 200 - a.b.c.d - - [23/Jun/2021 08:47:51] "GET /?__debugger__=yes&cmd=resource&f=console.png HTTP/1.1" 200 - a.b.c.d - - [23/Jun/2021 08:47:51] "GET /?__debugger__=yes&cmd=resource&f=console.png HTTP/1.1" 200 -
And I have seen some related topics (were not helpful):
Advertisement
Answer
I started with brutally integrating the Flask-Kerberos code directly into my ‘example.py’ file and using some print()
s:
"""HERE GOES THE CONTENT OF flask_kerberos.py""" import kerberos from flask import Response from flask import _request_ctx_stack as stack from flask import make_response from flask import request from functools import wraps from socket import gethostname from os import environ _SERVICE_NAME = None #_SERVICE_NAME = 'HTTP/l.s.d' def init_kerberos(app, service='HTTP', hostname=gethostname()): ''' Configure the GSSAPI service name, and validate the presence of the appropriate principal in the kerberos keytab. :param app: a flask application :type app: flask.Flask :param service: GSSAPI service name :type service: str :param hostname: hostname the service runs under :type hostname: str ''' global _SERVICE_NAME _SERVICE_NAME = "%s@%s" % (service, hostname) print(_SERVICE_NAME) #HTTP@Server.l.s.d if 'KRB5_KTNAME' not in environ: app.logger.warn("Kerberos: set KRB5_KTNAME to your keytab file") else: try: principal = kerberos.getServerPrincipalDetails(service, hostname) except kerberos.KrbError as exc: app.logger.warn("Kerberos: %s" % exc.message[0]) else: app.logger.info("Kerberos: server is %s" % principal) def _unauthorized(): ''' Indicate that authentication is required ''' return Response('Unauthorized', 401, {'WWW-Authenticate': 'Negotiate'}) def _forbidden(): ''' Indicate a complete authentication failure ''' return Response('Forbidden', 403) def _gssapi_authenticate(token): ''' Performs GSSAPI Negotiate Authentication On success also stashes the server response token for mutual authentication at the top of request context with the name kerberos_token, along with the authenticated user principal with the name kerberos_user. @param token: GSSAPI Authentication Token @type token: str @returns gssapi return code or None on failure @rtype: int or None ''' state = None ctx = stack.top try: rc, state = kerberos.authGSSServerInit(_SERVICE_NAME) if rc != kerberos.AUTH_GSS_COMPLETE: return None rc = kerberos.authGSSServerStep(state, token) if rc == kerberos.AUTH_GSS_COMPLETE: ctx.kerberos_token = kerberos.authGSSServerResponse(state) ctx.kerberos_user = kerberos.authGSSServerUserName(state) return rc elif rc == kerberos.AUTH_GSS_CONTINUE: return kerberos.AUTH_GSS_CONTINUE else: return None except kerberos.GSSError: return None finally: if state: kerberos.authGSSServerClean(state) def requires_authentication(function): ''' Require that the wrapped view function only be called by users authenticated with Kerberos. The view function will have the authenticated users principal passed to it as its first argument. :param function: flask view function :type function: function :returns: decorated function :rtype: function ''' @wraps(function) def decorated(*args, **kwargs): header = request.headers.get("Authorization") if header: ctx = stack.top token = ''.join(header.split()[1:]) rc = _gssapi_authenticate(token) if rc == kerberos.AUTH_GSS_COMPLETE: response = function(ctx.kerberos_user, *args, **kwargs) response = make_response(response) if ctx.kerberos_token is not None: response.headers['WWW-Authenticate'] = ' '.join(['negotiate', ctx.kerberos_token]) return response elif rc != kerberos.AUTH_GSS_CONTINUE: return _forbidden() return _unauthorized() return decorated """END OF THE flask_kerberos.py""" from flask import Flask from flask import render_template from config import Config app = Flask(__name__) app.config.from_object(Config) @app.route("/") @requires_authentication def index(user): return render_template('index.html', user=user) if __name__ == '__main__': init_kerberos(app, hostname='Server.l.s.d') app.run()
As was mentioned in this answer:
The problem is exactly as the error message states – you’ve told the kerberos library to get a service principal from the keytab, but the keytab doesn’t contain an entry for that service principal.
So, I decided to check several variables and their values, i.e. _SERVICE_NAME
and getServerPrincipalDetails(service, hostname)
.
Firstly I set the _SERVICE_NAME='L.S.D'
and after I got the ‘Forbidden’ response in my Browser. And here is an output in the CMD:
(venv) Server@User:~/.../flask_kerberos_example$ flask run * Serving Flask app "example.py" (lazy loading) * Environment: development * Debug mode: on * Running on http://0.0.0.0:5000/ (Press CTRL+C to quit) * Restarting with stat * Debugger is active! * Debugger PIN: 417-811-792 a.b.c.d - - [06/Jul/2021 11:01:06] "GET / HTTP/1.1" 401 - a.b.c.d - - [06/Jul/2021 11:01:06] "GET / HTTP/1.1" 403 -
I run the the above code via Vim I received this message:
Traceback (most recent call last): File "example.py", line 34, in init_kerberos principal = kerberos.getServerPrincipalDetails(service, hostname) kerberos.KrbError: ('Cannot get sequence cursor from keytab', 2) During handling of the above exception, another exception occurred: Traceback (most recent call last): File "example.py", line 140, in <module> init_kerberos(app) File "example.py", line 36, in init_kerberos app.logger.warn("Kerberos: %s" % exc.message[0]) AttributeError: 'KrbError' object has no attribute 'message' shell returned 1
This issue brought me further to this issue on the GitHub. Where author stated:
Nevermind, regardless of how I interpret the code it works fine with service=”HTTP” and hostname=”my.host.name”.
Therefore, I tried to adjust service
and hostname
variables of the getServerPrincipalDetails(service, hostname)
function. The most convenient way for me to test it was:
import kerberos service = 'HTTP' hostname = 'Server.l.s.d' try: principal = kerberos.getServerPrincipalDetails(service, hostname) except kerberos.KrbError as exc: print("Kerberos: %s" % exc.message[0]) else: print("Kerberos: server is %s" % principal)
So, I ended up with the following variables and their values
_SERVICE_NAME = None service = 'HTTP' hostname = 'Server.l.s.d'
And after I got the response in Browser
Flask Kerberos Example
It worked, I think you are username@L.S.D.
and in CMD correspondingly
HTTP@Server.l.s.d * Serving Flask app "example" (lazy loading) * Environment: production WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead. * Debug mode: off * Running on http://0.0.0.0:5000/ (Press CTRL+C to quit) a.b.c.d - - [08/Jul/2021 13:02:18] "GET / HTTP/1.1" 401 - a.b.c.d - - [08/Jul/2021 13:02:18] "GET / HTTP/1.1" 200 - a.b.c.d - - [08/Jul/2021 13:02:18] "GET /static/style.css HTTP/1.1" 304 -
Unfortunatelly it still does not work via flask run
. It was asked as a new question here: Flask-Kerberos yields different results when running the code via “flask run” and with Vim