TypeError:运行FlaskKerberos时,参数1必须为字符串,而不是None

2022-04-19 00:00:00 python flask kerberos

问题描述

我正在尝试使用有效的密钥表文件(it works with WSGI-Kerberos)运行Flask-Kerberos example。

这是我的‘example.py’文件的内容

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()

这里是‘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"

这里是‘.flaskenv’

FLASK_APP=example.py
FLASK_RUN_HOST="0.0.0.0"
FLASK_RUN_PORT=5000
FLASK_ENV=development

但是,当通过flask run启动烧瓶时,我在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 -

和我看过一些相关主题(没有帮助):

  • Flask_kerberos KrbError: ('Principal not found in keytab', -1)
  • SSO with Kerberos

解决方案

我开始时将Flask-Kerberos code直接集成到我的‘Example.py’文件中,并使用一些print()

"""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()

如this answer中所述:

问题完全如错误消息所述-您已经告诉 Kerberos库从密钥表中获取服务主体,但 密钥表不包含服务主体条目。

因此,我决定检查几个变量及其值,即_SERVICE_NAMEgetServerPrincipalDetails(service, hostname)

首先我设置_SERVICE_NAME='L.S.D',在我的浏览器中收到‘禁止’响应后。以下是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 -

我通过Vim运行上述代码我收到以下消息:

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

这个问题将我进一步带到了GitHub上的this issue。其中作者声明:

不要紧,不管我如何解释代码,它都可以很好地工作 服务=&Q;HTTP&Q;和主机名=&Q;my.host.name&Q;。

因此,我尝试调整getServerPrincipalDetails(service, hostname)函数的servicehostname变量。对我来说,测试它最方便的方式是:

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)

因此,我得到了以下变量及其值

_SERVICE_NAME = None
service = 'HTTP'
hostname = 'Server.l.s.d'

在浏览器中收到回复后

烧瓶Kerberos示例

成功了,我想您是用户名@L.S.D.

和相应的CMD

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 -
不幸的是,它仍然不能通过flask run工作。这是一个新问题:Flask-Kerberos yields different results when running the code via "flask run" and with Vim

相关文章