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