<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: hjb417</title>
    <description>The latest articles on DEV Community by hjb417 (@hjb417).</description>
    <link>https://dev.to/hjb417</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F257860%2Fdd881747-924a-4412-b713-4de0a6cd5435.png</url>
      <title>DEV Community: hjb417</title>
      <link>https://dev.to/hjb417</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/hjb417"/>
    <language>en</language>
    <item>
      <title>HOW TO: Enable Kerberos Delegation For Flask On Linux</title>
      <dc:creator>hjb417</dc:creator>
      <pubDate>Mon, 24 Feb 2020 11:25:20 +0000</pubDate>
      <link>https://dev.to/hjb417/how-to-enable-kerberos-delegation-for-flask-on-linux-53jb</link>
      <guid>https://dev.to/hjb417/how-to-enable-kerberos-delegation-for-flask-on-linux-53jb</guid>
      <description>&lt;p&gt;I recently encountered a scenario where I needed a Flask app running on Linux to pull data from a Microsoft SQL server running on Windows using the credentials of the caller and I was able to achieve this in a test environment using the below steps.&lt;/p&gt;

&lt;h1&gt;
  
  
  Sections:
&lt;/h1&gt;

&lt;ol&gt;
&lt;li&gt;Environment&lt;/li&gt;
&lt;li&gt;Steps&lt;/li&gt;
&lt;li&gt;Verification&lt;/li&gt;
&lt;li&gt;FAQ&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Environment:
&lt;/h1&gt;

&lt;ol&gt;
&lt;li&gt;Windows 2016 server

&lt;ul&gt;
&lt;li&gt;Name: DC&lt;/li&gt;
&lt;li&gt;Purpose: Active Directory Server&lt;/li&gt;
&lt;li&gt;Domain: SSO&lt;/li&gt;
&lt;li&gt;Services: Active Directory, DHCP, DNS&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Windows 2016 server

&lt;ul&gt;
&lt;li&gt;Name: IIS1&lt;/li&gt;
&lt;li&gt;Purpose: Hosts a website that will be called by Flaska&lt;/li&gt;
&lt;li&gt;Domain: SSO&lt;/li&gt;
&lt;li&gt;Services: IIS&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Windows 2016 server

&lt;ul&gt;
&lt;li&gt;Name: MSSQL1&lt;/li&gt;
&lt;li&gt;Purpose: Contains data that will be retrieved by Flask&lt;/li&gt;
&lt;li&gt;Domain: SSO&lt;/li&gt;
&lt;li&gt;Services: Microsoft SQL Server 2017&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Centos 7

&lt;ul&gt;
&lt;li&gt;Name: flask.sso.local&lt;/li&gt;
&lt;li&gt;Purpose: Host Flask app&lt;/li&gt;
&lt;li&gt;Domain: &lt;strong&gt;&amp;lt;NOT JOINED TO A DOMAIN&amp;gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Services: httpd&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Steps:
&lt;/h1&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Create a new user account in active directory (E.x: &lt;strong&gt;flask_svc_acct&lt;/strong&gt;)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Generate the keytab file on the Active Director server (&lt;strong&gt;NOTE&lt;/strong&gt;: &lt;em&gt;sa&lt;/em&gt; is the password I assigned to the account &lt;strong&gt;flask_svc_acct&lt;/strong&gt;)&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;em&gt;ktpass.exe -princ HTTP/&lt;a href="mailto:flask.sso.local@SSO.LOCAL"&gt;flask.sso.local@SSO.LOCAL&lt;/a&gt; -mapuser &lt;a href="mailto:flask_svc_acct@SSO.LOCAL"&gt;flask_svc_acct@SSO.LOCAL&lt;/a&gt; -pass sa -crypto ALL -ptype KRB5_NT_PRINCIPAL -out C:\Temp\http_flask_svc_acct.keytab&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Grant the account &lt;strong&gt;flask_svc_acct&lt;/strong&gt; unconstrained delegation&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Enable &lt;strong&gt;Trust this user for delegation to any service (Kerberos only)&lt;/strong&gt; in the &lt;em&gt;Delegation&lt;/em&gt; tab for the user &lt;strong&gt;flask_svc_acct&lt;/strong&gt; in Active Directory.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Copy the keytab file generated in the previous step to the linux box. (E.x: &lt;strong&gt;/etc&lt;/strong&gt;)&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I recommend using &lt;a href="https://mobaxterm.mobatek.net"&gt;MobaXterm&lt;/a&gt;. It lets you copy files from windows, ssh into linux boxes and has a GUI for editing text files on the linux box.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Install the packages needed to use python, kerberos and apache&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;yum install -y yum-utils&lt;/li&gt;
&lt;li&gt;yum install -y &lt;a href="https://centos7.iuscommunity.org/ius-release.rpm"&gt;https://centos7.iuscommunity.org/ius-release.rpm&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;yum groupinstall -y "Development Tools"&lt;/li&gt;
&lt;li&gt;yum install -y gcc gcc-c++ krb5-workstation krb5-libs krb5-auth-dialog krb5-devel httpd mod_auth_kerb unixODBC-devel openssl-devel bzip2-devel libffi-devel wget httpd-devel&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Installing the Microsoft ODBC Driver for SQL Server (&lt;strong&gt;NOTE: You can skip this step if you don't intend to connect to Microsoft SQL Server&lt;/strong&gt;)&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;curl &lt;a href="https://packages.microsoft.com/config/rhel/7/prod.repo"&gt;https://packages.microsoft.com/config/rhel/7/prod.repo&lt;/a&gt; &amp;gt; /etc/yum.repos.d/mssql-release.repo&lt;/li&gt;
&lt;li&gt;yum remove unixODBC-utf16 unixODBC-utf16-devel #to avoid conflicts&lt;/li&gt;
&lt;li&gt;ACCEPT_EULA=Y yum install -y msodbcsql17&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Grant apache read access to the keytab file&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;chown apache /etc/http_flask_svc_acct.keytab&lt;/li&gt;
&lt;li&gt;chmod 400 /etc/http_flask_svc_acct.keytab&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Install Python 3.8.1&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;cd /tmp&lt;/li&gt;
&lt;li&gt;wget &lt;a href="https://www.python.org/ftp/python/3.8.1/Python-3.8.1.tgz"&gt;https://www.python.org/ftp/python/3.8.1/Python-3.8.1.tgz&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;tar xvf Python-3.8.1.tgz&lt;/li&gt;
&lt;li&gt;cd Python-3.8.1&lt;/li&gt;
&lt;li&gt;./configure --enable-ipv6 --with-system-expat --with-system-ffi --enable-loadable-sqlite-extensions --enable-optimizations --enable-shared LDFLAGS="-Wl,-rpath /usr/local/lib"&lt;/li&gt;
&lt;li&gt;make altinstall&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Compile mod_wsgi&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;cd /tmp&lt;/li&gt;
&lt;li&gt;python3.8 -m venv mod_wsgi&lt;/li&gt;
&lt;li&gt;source mod_wsgi/bin/activate&lt;/li&gt;
&lt;li&gt;pip3.8 install mod_wsgi&lt;/li&gt;
&lt;li&gt;deactivate&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Copy the compiled mod_wsgi to apache's module directory&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;cp mod_wsgi/lib/python3.8/site-packages/mod_wsgi/server/mod_wsgi-py38.cpython-38-x86_64-linux-gnu.so /etc/httpd/modules/mod_wsgi.so&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create the file /etc/httpd/conf.modules.d/10-wsgi.conf with the content &lt;strong&gt;LoadModule wsgi_module modules/mod_wsgi.so&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;echo 'LoadModule wsgi_module modules/mod_wsgi.so' &amp;gt; /etc/httpd/conf.modules.d/10-wsgi.conf&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Update the firewall to allow Apache to receive traffic&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;firewall-cmd --permanent --add-service=http&lt;/li&gt;
&lt;li&gt;firewall-cmd --reload&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Disable Security-Enhanced Linux&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;setenforce 0&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Make /etc/krb5.conf look like&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Configuration snippets may be placed in this directory as well
includedir /etc/krb5.conf.d/

[logging]
 default = FILE:/var/log/krb5libs.log
 kdc = FILE:/var/log/krb5kdc.log
 admin_server = FILE:/var/log/kadmind.log

[libdefaults]
 dns_lookup_realm = false
 ticket_lifetime = 24h
 renew_lifetime = 7d
 forwardable = true
 rdns = false
 pkinit_anchors = /etc/pki/tls/certs/ca-bundle.crt
 default_realm = SSO.LOCAL
 default_ccache_name = KEYRING:persistent:%{uid}

[realms]
 SSO.LOCAL = {
 default_domain = sso.local
 }

[domain_realm]
 .sso.local = SSO.LOCAL
 sso.local = SSO.LOCAL
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Save the below content to &lt;strong&gt;/etc/httpd/conf.d/auth_kerb.conf&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#http://modauthkerb.sourceforge.net/configure.html
&amp;lt;Location /&amp;gt;
      AuthType Kerberos
      AuthName "Kerberos Login"
      Krb5KeyTab /etc/http_flask_svc_acct.keytab
      require valid-user
      KrbSaveCredentials on
      KrbConstrainedDelegation on
      KrbMethodNegotiate on
&amp;lt;/Location&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Change the value of &lt;em&gt;LogLevel&lt;/em&gt; in &lt;strong&gt;/etc/httpd/conf/httpd.conf&lt;/strong&gt; to &lt;em&gt;trace8&lt;/em&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create a subdirectory in /var/www to store and run our flask app (E.x.: &lt;strong&gt;/var/www/kerberos_test&lt;/strong&gt;)&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;mkdir /var/www/kerberos_test&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Go to the directory created in the prior step and create a python virtual environment for the flask app. I'll be using the virtual environment name &lt;strong&gt;venv&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;cd /var/www/kerberos_test&lt;/li&gt;
&lt;li&gt;python3.8 -m venv venv&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Install the modules &lt;strong&gt;flask requests requests-kerberos pyodbc&lt;/strong&gt; in the virtual environment&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;source venv/bin/activate&lt;/li&gt;
&lt;li&gt;pip3.8 install flask requests requests-kerberos pyodbc&lt;/li&gt;
&lt;li&gt;deactivate&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create the flask app (E.x.: &lt;strong&gt;/var/www/kerberos_test/wsgi.py&lt;/strong&gt;)&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import flask
import requests
import os
import pyodbc
import sys
import gc
from requests_kerberos import HTTPKerberosAuth
from contextlib import ExitStack

application = flask.Flask(__name__)

KRB5_THREADED_KEYRING_CCNAME = "KEYRING:thread:"

@application.before_first_request
def before_fist_req():
  os.environ["KRB5CCNAME"] = KRB5_THREADED_KEYRING_CCNAME

@application.before_request
def before_req():
  krb5_cc_copy(flask.request.environ["KRB5CCNAME"], KRB5_THREADED_KEYRING_CCNAME)

@application.route('/')
def hello_world():
  with ExitStack() as disposables:
    dest = "http://iis1.sso.local/wai.aspx"
    r = disposables.enter_context(requests.get(dest, auth=HTTPKerberosAuth(delegate=True)))
    conn_str = "Driver={ODBC Driver 17 for SQL Server};Server=mssql1.sso.local;Trusted_Connection=yes;"
    conn = disposables.enter_context(pyodbc.connect(conn_str))
    cursor = conn.cursor()
    query = "SELECT SYSTEM_USER, * FROM sys.dm_exec_sessions"
    cursor.execute(query)
    row = cursor.fetchone()

    return f"""
    &amp;lt;br&amp;gt;
    {sys.version}
    {flask.request.environ}
    &amp;lt;br&amp;gt;
    {dest}
    &amp;lt;br&amp;gt;
    headers:{r.headers}
    &amp;lt;br&amp;gt;
    text:{r.text}
    &amp;lt;br&amp;gt;
    {query}:{row}
    """

# Below code should be moved to separate module (E.x.: libkrb5_helper.py )

from contextlib import ExitStack
from ctypes import CDLL, byref, c_char_p, c_void_p, cast
libkrb5 = CDLL("libkrb5.so")

libkrb5.krb5_get_error_message.restype = c_void_p
libkrb5.krb5_free_error_message.argtypes = (c_void_p, c_void_p)

def krb5_raise_error(krb5_error_code, func, arguments):
  context = arguments[0]
  if krb5_error_code:    
    err_msg_ptr = libkrb5.krb5_get_error_message(context, krb5_error_code)
    try:
      err_msg = cast(err_msg_ptr, c_char_p).value.decode()
      raise Exception(err_msg)
    finally:
      libkrb5.krb5_free_error_message(context, err_msg_ptr)

libkrb5.krb5_cc_resolve.errcheck = krb5_raise_error
libkrb5.krb5_cc_get_principal.errcheck = krb5_raise_error
libkrb5.krb5_cc_initialize.errcheck = krb5_raise_error
libkrb5.krb5_cc_copy_creds.errcheck = krb5_raise_error
libkrb5.krb5_cc_destroy.errcheck = krb5_raise_error
libkrb5.krb5_cc_close.errcheck = krb5_raise_error

def krb5_cc_copy(src_cc_name, dest_cc_name):
  thread_ccache = c_void_p()
  src_cc = c_void_p()
  principal = c_void_p()
  krb5_context = c_void_p()
  with ExitStack() as krb_cleanup:    
    krb5_error_code = libkrb5.krb5_init_context(byref(krb5_context))
    if krb5_error_code:
      raise Exception(f"Call to krb5_init_context failed with error code [{krb5_error_code}].")
    krb_cleanup.callback(libkrb5.krb5_free_context, krb5_context)      

    libkrb5.krb5_cc_resolve(krb5_context, dest_cc_name.encode(), byref(thread_ccache))
    krb_cleanup.callback(libkrb5.krb5_cc_close, krb5_context, thread_ccache)

    libkrb5.krb5_cc_resolve(krb5_context, src_cc_name.encode(), byref(src_cc))
    krb_cleanup.callback(libkrb5.krb5_cc_close, krb5_context, src_cc)

    libkrb5.krb5_cc_get_principal(krb5_context, src_cc, byref(principal))
    krb_cleanup.callback(libkrb5.krb5_free_principal, krb5_context, principal)

    libkrb5.krb5_cc_initialize(krb5_context, thread_ccache, principal)
    libkrb5.krb5_cc_copy_creds(krb5_context, src_cc, thread_ccache)

&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add the following entry to &lt;strong&gt;/etc/httpd/conf/httpd.conf&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;VirtualHost *:80&amp;gt;
WSGIDaemonProcess kerberos_test python-home=/var/www/kerberos_test/venv
WSGIScriptAlias /kerberos_test /var/www/kerberos_test/wsgi.py
&amp;lt;Directory /var/www/kerberos_test&amp;gt;
 Require valid-user
&amp;lt;/Directory&amp;gt;
&amp;lt;Location /kerberos_test&amp;gt;
WSGIProcessGroup kerberos_test
&amp;lt;/Location&amp;gt;
&amp;lt;/VirtualHost&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Restart Apache&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;systemctl restart httpd&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Tail the log file&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;tail -f /etc/httpd/logs/error_log&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Change Apache's LogLevel back to warn once you've verified everything is working&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Verification:
&lt;/h1&gt;

&lt;p&gt;You can verify Kerberos authentication works by running the following code in &lt;a href="https://www.linqpad.net"&gt;LINQPad&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;wc&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Net&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WebClient&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;UseDefaultCredentials&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="n"&gt;wc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;DownloadString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"http://flask.sso.local/kerberos_test/"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;Dump&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;wc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UseDefaultCredentials&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;wc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;DownloadString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"http://flask.sso.local/kerberos_test/"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;Dump&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;And the output should look similar to below.&lt;br&gt;
I ran the c# code under the user account &lt;strong&gt;SSO\bob&lt;/strong&gt; so you'll see that the flask server was able to connect to both IIS and Microsoft SQL Server as &lt;strong&gt;SSO\bob&lt;/strong&gt; when I enabled &lt;em&gt;UseDefaultCredentials&lt;/em&gt; and the flask server returned status code 401 when I disabled &lt;em&gt;UseDefaultCredentials&lt;/em&gt; because I configured Apache to only allow domain users.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
  &amp;lt;br&amp;gt;
  3.8.1 (default, Mar 16 2020, 14:43:00) 
[GCC 4.8.5 20150623 (Red Hat 4.8.5-39)]
  {'UNIQUE_ID': 'AAAAAKVh3US3dOPcBnV2lAAAAAg', 'KRB5CCNAME': 'FILE:/run/httpd/krbcache/krb5cc_apache_tiJTTA', 'GATEWAY_INTERFACE': 'CGI/1.1', 'SERVER_PROTOCOL': 'HTTP/1.1', 'REQUEST_METHOD': 'GET', 'QUERY_STRING': '', 'REQUEST_URI': '/kerberos_test/', 'SCRIPT_NAME': '/kerberos_test', 'PATH_INFO': '/', 'PATH_TRANSLATED': '/var/www/html/', 'HTTP_HOST': 'flask.sso.local', 'SERVER_SIGNATURE': '', 'SERVER_SOFTWARE': 'Apache/2.4.6 (CentOS) mod_auth_kerb/5.4 mod_wsgi/4.7.1 Python/3.8', 'SERVER_NAME': 'flask.sso.local', 'SERVER_ADDR': '10.0.0.8', 'SERVER_PORT': '80', 'REMOTE_ADDR': '10.0.0.1', 'DOCUMENT_ROOT': '/var/www/html', 'REQUEST_SCHEME': 'http', 'CONTEXT_PREFIX': '', 'CONTEXT_DOCUMENT_ROOT': '/var/www/html', 'SERVER_ADMIN': 'root@localhost', 'SCRIPT_FILENAME': '/var/www/kerberos_test/wsgi.py', 'REMOTE_PORT': '57887', 'REMOTE_USER': 'bob@SSO.LOCAL', 'AUTH_TYPE': 'Negotiate', 'mod_wsgi.script_name': '/kerberos_test', 'mod_wsgi.path_info': '/', 'mod_wsgi.process_group': 'kerberos_test', 'mod_wsgi.application_group': 'flask.sso.local|/kerberos_test', 'mod_wsgi.callable_object': 'application', 'mod_wsgi.request_handler': 'wsgi-script', 'mod_wsgi.handler_script': '', 'mod_wsgi.script_reloading': '1', 'mod_wsgi.listener_host': '', 'mod_wsgi.listener_port': '80', 'mod_wsgi.enable_sendfile': '0', 'mod_wsgi.ignore_activity': '0', 'mod_wsgi.request_start': '1584910828940831', 'mod_wsgi.request_id': 'AAAAAKVh3US3dOPcBnV2lAAAAAg', 'mod_wsgi.connection_id': 'Dq71zrcrMRg', 'mod_wsgi.queue_start': '1584910828942817', 'mod_wsgi.daemon_connects': '1', 'mod_wsgi.daemon_restarts': '0', 'mod_wsgi.daemon_start': '1584910828942951', 'mod_wsgi.script_start': '1584910828943034', 'wsgi.version': (1, 0), 'wsgi.multithread': True, 'wsgi.multiprocess': False, 'wsgi.run_once': False, 'wsgi.url_scheme': 'http', 'wsgi.errors': &amp;lt;_io.TextIOWrapper name='&amp;lt;wsgi.errors&amp;gt;' encoding='utf-8'&amp;gt;, 'wsgi.input': &amp;lt;mod_wsgi.Input object at 0x7fcaa44d6ce0&amp;gt;, 'wsgi.input_terminated': True, 'wsgi.file_wrapper': &amp;lt;class 'mod_wsgi.FileWrapper'&amp;gt;, 'apache.version': (2, 4, 6), 'mod_wsgi.version': (4, 7, 1), 'mod_wsgi.total_requests': 18122, 'mod_wsgi.thread_id': 1, 'mod_wsgi.thread_requests': 9061, 'werkzeug.request': &amp;lt;Request 'http://flask.sso.local/kerberos_test/' [GET]&amp;gt;}
  &amp;lt;br&amp;gt;
  http://iis1.sso.local/wai.aspx
  &amp;lt;br&amp;gt;
  headers:{'Cache-Control': 'private', 'Content-Type': 'text/html; charset=utf-8', 'Content-Encoding': 'gzip', 'Vary': 'Accept-Encoding', 'Server': 'Microsoft-IIS/10.0', 'X-AspNet-Version': '4.0.30319', 'Persistent-Auth': 'true', 'X-Powered-By': 'ASP.NET', 'WWW-Authenticate': 'Negotiate YIGYBgkqhkiG9xIBAgICAG+BiDCBhaADAgEFoQMCAQ+ieTB3oAMCARKicARuRutiYUwRhiQtpfcCsipTRT70KVovTi/JbGwACalJeFqAaLvcd4zI0aPjr+nkVvAtbNLnCr7+umNmxxXjEeRBxtjv4S2/lXXw41zVfnDjkxZmxPSAgGFcYMN7mjnzomgNjTBlVqPR7xD2KifyElI=', 'Date': 'Sun, 22 Mar 2020 21:00:29 GMT', 'Content-Length': '199'}
  &amp;lt;br&amp;gt;
  text:
&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html&amp;gt;
&amp;lt;body&amp;gt;

&amp;lt;h1&amp;gt;
HELLO FROM IIS
&amp;lt;br&amp;gt;
SSO\bob&amp;lt;/h1&amp;gt;

&amp;lt;p&amp;gt;
SSO\bob&amp;lt;/p&amp;gt;

&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;

  &amp;lt;br&amp;gt;
  SELECT SYSTEM_USER, * FROM sys.dm_exec_sessions:('SSO\\bob', 52, datetime.datetime(2020, 3, 22, 17, 0, 29, 617000), 'flask.sso.local', 'httpd', 1140, 7, 'ODBC', b'\x01\x05\x00\x00\x00\x00\x00\x05\x15\x00\x00\x00\xb1$v\xb9\x047\xee\x1aNf\x97\xf5S\x04\x00\x00', 'SSO\\bob', 'SSO', 'bob', 'running', b'', 0, 3, 0, 0, 4, datetime.datetime(2020, 3, 22, 17, 0, 29, 617000), datetime.datetime(2020, 3, 22, 17, 0, 29, 613000), 0, 0, 0, True, -1, 'us_english', 'mdy', 7, True, False, True, False, True, True, True, True, 2, -1, 0, 0, 0, b'\x01\x05\x00\x00\x00\x00\x00\x05\x15\x00\x00\x00\xb1$v\xb9\x047\xee\x1aNf\x97\xf5S\x04\x00\x00', 'SSO\\bob', None, None, None, 1, 1, 1, 1)



6WebException4 
The remote server returned an error: (401) Unauthorized. 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;a&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  FAQ:
&lt;/h1&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Does the linux machine need to join the domain?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Why do you disable Security-Enhanced Linux (SELinux)?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I was getting the error
&lt;em&gt;ImportError: /var/www/kerberos_test/venv/lib/python3.8/site-packages/pyodbc.cpython-38-x86_64-linux-gnu.so: failed to map segment from shared object: Permission denied&lt;/em&gt;
in the log files when it was enabled. I found the work around in &lt;a href="https://stackoverflow.com/questions/20919771/centos-6-4-failed-to-map-segment-from-shared-object-permission-denied"&gt;Centos 6.4 - Failed to map segment from shared object: Permission denied
&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;How do I disable SELinux permanently?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;set &lt;strong&gt;SELINUX&lt;/strong&gt; to &lt;strong&gt;&lt;em&gt;permissive&lt;/em&gt;&lt;/strong&gt; in &lt;strong&gt;/etc/selinux/config&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Do I have to use unconstrained delegation to make this work?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No. It's recommended that you use constrained delegation&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;I get the error &lt;strong&gt;[auth_kerb:error] [pid 1562] [client 10.0.0.1:54363] gss_acquire_cred() failed: Unspecified GSS failure.  Minor code may provide more information (, No key table entry found matching HTTP/flask.sso.local@)&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;This can be for a number of reasons. Make sure the hostname is all lower case and contains the domain name. (E.x.: The hostname in my example must be flask.sso.local instead of flask, Flask, Flask.sso.local or FLASK.sso.local.&lt;/li&gt;
&lt;li&gt;Verify the kerberos settings are correct by running &lt;strong&gt;kinit&lt;/strong&gt; on the keytab file (E.x.: kinit -vkt /etc/http_flask_svc_acct.keytab) You should get something like &lt;strong&gt;kinit: Keytab contains no suitable keys for host/&lt;a href="mailto:flask.sso.local@SSO.LOCAL"&gt;flask.sso.local@SSO.LOCAL&lt;/a&gt; while getting initial credentials&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Use &lt;strong&gt;klist&lt;/strong&gt; to see the entries in the keytab file (E.x.: klist -kte /etc/http_flask_svc_acct.keytab) and make sure it has the expected encryption type and principal).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;I get the error &lt;strong&gt;pyodbc.Error: ('HY000', "[HY000] [Microsoft][ODBC Driver 17 for SQL Server]SSPI Provider: KDC can't fulfill requested option (851968) (SQLDriverConnect)")&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Try having the client connect with the username in all lowercase. (E.x.: If the user name is &lt;strong&gt;Bob&lt;/strong&gt;, try logging in as &lt;strong&gt;bob&lt;/strong&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;How do I use &lt;strong&gt;AES256-SHA1&lt;/strong&gt; encryption for keytabs?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Enable &lt;strong&gt;This account supports Kerberos AES 256 bit encyrption&lt;/strong&gt; on the service account and every account that will access the website in the &lt;code&gt;Account options&lt;/code&gt; in Active Directory&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;How do I have Apache start automatically after a reboot?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;systemctl enable httpd&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;How do I host multiple flask apps?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Make your entry for the &lt;em&gt;VirtualHost&lt;/em&gt; in &lt;strong&gt;/etc/httpd/conf/httpd.conf&lt;/strong&gt; look similar to the one below
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;VirtualHost *:80&amp;gt;
 WSGIDaemonProcess kerberos_test python-home=/var/www/kerberos_test/venv
 WSGIScriptAlias /kerberos_test /var/www/kerberos_test/wsgi.py
 &amp;lt;Directory /var/www/kerberos_test&amp;gt;
     Require valid-user
 &amp;lt;/Directory&amp;gt;
 &amp;lt;Location /kerberos_test&amp;gt;
   WSGIProcessGroup kerberos_test
 &amp;lt;/Location&amp;gt;

 WSGIDaemonProcess some_other_flask_app python-home=/var/www/some_other_flask_app/venv
 WSGIScriptAlias /some_other_flask_app /var/www/some_other_flask_app/wsgi.py
 &amp;lt;Directory /var/www/some_other_flask_app&amp;gt;
     Require valid-user
 &amp;lt;/Directory&amp;gt;
 &amp;lt;Location /some_other_flask_app&amp;gt;
   WSGIProcessGroup some_other_flask_app
 &amp;lt;/Location&amp;gt;
&amp;lt;/VirtualHost&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;br&gt;
`&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>kerberos</category>
      <category>s4u2proxy</category>
      <category>linux</category>
      <category>flask</category>
    </item>
  </channel>
</rss>
