Let me explain my best how spnego authentication work and how can we implement SPNEGO authentication in a few lines in python.
In this test, I used MIT kerberos server (Active directory installation and configuration is more painful for me) refer my blog on how to setup and configure MIT kerberos in centos. Linux Ldap authentication with kerberos backend and openldap SASL Passthrough and kerberos (python module to implement the spengo authentication (pip install kerberos))
We need to create service principle in Kerberos server for the domain we are going to use www.sathish.com. since webserver use HTTP protocol, kerberos service principle would be HTTP.
So we need to create HTTP service principle with below name for www.sathish.com. ( TANU.COM is my kerberos domain.)
HTTP/www.sathish.com@TANU.COM
Run following commands in kerberos server to create the keytab.
kadmin.local -q "addprinc -randkey HTTP/www.sathish.com@TANU.COM"
kadmin.local -q "ktadd -k /www/http/secure/www.sathish.com.keytab HTTP/www.sathish.com
set below environment variable to point the keytab
export KRB5_KTNAME=/www/http/secure/www.sathish.com.keytab
(if we dont set above variable python code with throw below exception
rc, state = kerberos.authGSSServerInit('HTTP')
GSSError: (('Unspecified GSS failure. Minor code may provide more information', 851968), ('', 100002))
Now we are good to run our own code to implement the spnego kerberos authentication.
Webserver (simple python http server with custom code) running on CENTOS with url http:/www.sathish.com
Client machine(one Linux and one windows 10 desktop)
How SPNEGO work:
1) client request url using Internet explore ----> http://www.sathish.com
2) webserver get request and check "Authorization" header. if header not present in the request , webserver send the response with 401 and WWW-Authenticate','Negotiate' header back to client
header=s.headers.get('Authorization')
if not header:
s.send_response(401)
s.send_header('WWW-Authenticate','Negotiate')
3) Internet explore get the response from webserver and request URL with Authorization header with valid token.
4) Then webserver read the token from Authorization header and decrypt token using service key stored in the keytab for this HTTP Service principle HTTP/www.sathish.com@TANU.COM.
If the decryption is successful then user token is valid and we can mark user authentication is completed.
rc, state = kerberos.authGSSServerInit('HTTP')
if rc != kerberos.AUTH_GSS_COMPLETE:
return None
rc = kerberos.authGSSServerStep(state, token)
if rc == kerberos.AUTH_GSS_COMPLETE:
user = kerberos.authGSSServerUserName(state)
if token is valid, webserver can retrieve the username.
5) Post Authentication webserver response back to client.
Here is the full code for testing
Here is the curl with negotiate request output
[root@krbserver ~]# curl --negotiate -u : -b ~/cookiejar.txt -c ~/cookiejar.txt http://www.sathish.com/ -v
* About to connect() to www.sathish.com port 80 (#0)
* Trying 192.168.100.31...
* Connected to www.sathish.com (192.168.100.31) port 80 (#0)
> GET / HTTP/1.1
> User-Agent: curl/7.29.0
> Host: www.sathish.com
> Accept: */*
>
* HTTP 1.0, assume close after body
< HTTP/1.0 401 Unauthorized
< Server: BaseHTTP/0.3 Python/2.7.5
< Date: Sun, 13 May 2018 17:09:17 GMT
< WWW-Authenticate: Negotiate
* Closing connection 0
* Issue another request to this URL: 'http://www.sathish.com/'
* About to connect() to www.sathish.com port 80 (#1)
* Trying 192.168.100.31...
* Connected to www.sathish.com (192.168.100.31) port 80 (#1)
* Server auth using GSS-Negotiate with user ''
> GET / HTTP/1.0
> Authorization: Negotiate YIICWwYJKoZIhvcSAQICAQBuggJKMIICRqADAgEFoQMCAQ6iBwMFACAAAACjggFiYYIBXjCCAVqgAwIBBaEKGwhUQU5VLkNPTaIZMBegAwIBA6EQMA4bBEhUVFAbBmtyYmNuMaOCASowggEmoAMCARChAwIBA6KCARgEggEUoewKZoNfchhCOjox2e4y465Wnz2E94henxLTzQq70A270SbVPef5oIkqXXupDK9/JPhT2QPRUXEhWYoH41KwxfK4Q28H2OLpzkvCCk0wVLjNFrJZjhEm1mxV4eKUl4AAi/nSwYUCZGAEFrEUVwWQwP8apFCsGolvZXP9Skc0XViAZTM5JKL6mVgQcwwuvs+J6Cf6elzZPR+7BLrJRFHlUTvMS3C8H+WClAh9KDeSvSbxYvG4ZRhNgXqRrD259O5iGKySf126ToG00Zece4Fz0rHjSRJUnrV4H2Npku2sMSo7M9DnwBiDW8kQ84yQnpXbc1hOqiFIOWRc31ZuQ2O0OUormHm3mUZY+ABC2zA8BdIPLZvspIHKMIHHoAMCARCigb8EgbxC+6wp6oKhdTtVS6nFomRylJ1p8t1hn8nqs/rsNTHoia6csinnPP2ZR2oqydnaVq9cGMqA6q7U5Yz1iXlf0dGOWmOewucN8URLkYDC6sne2acqNkWCpOVyJJ9/AaC5QTFvVyR+Nj7aRW3LpSgJdrwrMybrfgYawthj0q44z7+FQi1T0K42sLmTvelkqvXFdVBn2/aqcaXCkkA9OI5IrZH0fqwt25nMm/mTkhsgH+ntuzjtm4VDViLV2DuZcA==
> User-Agent: curl/7.29.0
> Host: www.sathish.com
> Accept: */*
>
* HTTP 1.0, assume close after body
< HTTP/1.0 200 OK
< Server: BaseHTTP/0.3 Python/2.7.5
< Date: Sun, 13 May 2018 17:09:19 GMT
< Content-type: text/html
<
* Closing connection 1
<html><head><title>Title goes here.</title></head><body><p>This is a test.</p></body></html>[root@krbserver ~]#
In this test, I used MIT kerberos server (Active directory installation and configuration is more painful for me) refer my blog on how to setup and configure MIT kerberos in centos. Linux Ldap authentication with kerberos backend and openldap SASL Passthrough and kerberos (python module to implement the spengo authentication (pip install kerberos))
We need to create service principle in Kerberos server for the domain we are going to use www.sathish.com. since webserver use HTTP protocol, kerberos service principle would be HTTP.
So we need to create HTTP service principle with below name for www.sathish.com. ( TANU.COM is my kerberos domain.)
HTTP/www.sathish.com@TANU.COM
Run following commands in kerberos server to create the keytab.
kadmin.local -q "addprinc -randkey HTTP/www.sathish.com@TANU.COM"
kadmin.local -q "ktadd -k /www/http/secure/www.sathish.com.keytab HTTP/www.sathish.com
set below environment variable to point the keytab
export KRB5_KTNAME=/www/http/secure/www.sathish.com.keytab
(if we dont set above variable python code with throw below exception
rc, state = kerberos.authGSSServerInit('HTTP')
GSSError: (('Unspecified GSS failure. Minor code may provide more information', 851968), ('', 100002))
)
Now we are good to run our own code to implement the spnego kerberos authentication.
Webserver (simple python http server with custom code) running on CENTOS with url http:/www.sathish.com
Client machine(one Linux and one windows 10 desktop)
How SPNEGO work:
1) client request url using Internet explore ----> http://www.sathish.com
2) webserver get request and check "Authorization" header. if header not present in the request , webserver send the response with 401 and WWW-Authenticate','Negotiate' header back to client
header=s.headers.get('Authorization')
if not header:
s.send_response(401)
s.send_header('WWW-Authenticate','Negotiate')
4) Then webserver read the token from Authorization header and decrypt token using service key stored in the keytab for this HTTP Service principle HTTP/www.sathish.com@TANU.COM.
If the decryption is successful then user token is valid and we can mark user authentication is completed.
rc, state = kerberos.authGSSServerInit('HTTP')
if rc != kerberos.AUTH_GSS_COMPLETE:
return None
rc = kerberos.authGSSServerStep(state, token)
if rc == kerberos.AUTH_GSS_COMPLETE:
user = kerberos.authGSSServerUserName(state)
5) Post Authentication webserver response back to client.
Here is the full code for testing
import BaseHTTPServer
from SimpleHTTPServer import SimpleHTTPRequestHandler
import sys
import kerberos
import re
class AuthHandler(SimpleHTTPRequestHandler):
''' Main class to present webpages and authentication. '''
def do_HEAD(self):
print "send header"
self.send_response(200)
self.send_header('Content-type', 'text/html')
self.end_headers()
def do_AUTHHEAD(self):
print "send header"
self.send_response(401)
#self.send_header('WWW-Authenticate', 'Basic realm=\"Test\"')
self.send_header('WWW-Authenticate', 'Negotiate')
self.send_header('Content-type', 'text/html')
self.end_headers()
def do_GET(self):
''' Present frontpage with user authentication. '''
if self.headers.getheader('Authorization') == None:
self.do_AUTHHEAD()
self.wfile.write('no auth header received')
pass
#elif self.headers.getheader('Authorization') == 'Negotiate':
elif re.match(r'^Negotiate', self.headers.getheader('Authorization')):
#print self.headers.getheader('Authorization')
header=self.headers.getheader('Authorization')
token = ''.join(header.split()[1:])
print token
rc, state = kerberos.authGSSServerInit('HTTP')
if rc != kerberos.AUTH_GSS_COMPLETE:
return None
rc = kerberos.authGSSServerStep(state, token)
if rc == kerberos.AUTH_GSS_COMPLETE:
user = kerberos.authGSSServerUserName(state)
print user
SimpleHTTPRequestHandler.do_GET(self)
pass
else:
self.do_AUTHHEAD()
self.wfile.write(self.headers.getheader('Authorization'))
self.wfile.write('not authenticated')
pass
def test(HandlerClass = AuthHandler,
ServerClass = BaseHTTPServer.HTTPServer):
BaseHTTPServer.test(HandlerClass, ServerClass)
if __name__ == '__main__':
if len(sys.argv)<2: code="" port="" print="" simpleauthserver.py="" sys.exit="" test="" usage="">
2:>
Here is the curl with negotiate request output
[root@krbserver ~]# curl --negotiate -u : -b ~/cookiejar.txt -c ~/cookiejar.txt http://www.sathish.com/ -v
* About to connect() to www.sathish.com port 80 (#0)
* Trying 192.168.100.31...
* Connected to www.sathish.com (192.168.100.31) port 80 (#0)
> GET / HTTP/1.1
> User-Agent: curl/7.29.0
> Host: www.sathish.com
> Accept: */*
>
* HTTP 1.0, assume close after body
< HTTP/1.0 401 Unauthorized
< Server: BaseHTTP/0.3 Python/2.7.5
< Date: Sun, 13 May 2018 17:09:17 GMT
< WWW-Authenticate: Negotiate
* Closing connection 0
* Issue another request to this URL: 'http://www.sathish.com/'
* About to connect() to www.sathish.com port 80 (#1)
* Trying 192.168.100.31...
* Connected to www.sathish.com (192.168.100.31) port 80 (#1)
* Server auth using GSS-Negotiate with user ''
> GET / HTTP/1.0
> Authorization: Negotiate YIICWwYJKoZIhvcSAQICAQBuggJKMIICRqADAgEFoQMCAQ6iBwMFACAAAACjggFiYYIBXjCCAVqgAwIBBaEKGwhUQU5VLkNPTaIZMBegAwIBA6EQMA4bBEhUVFAbBmtyYmNuMaOCASowggEmoAMCARChAwIBA6KCARgEggEUoewKZoNfchhCOjox2e4y465Wnz2E94henxLTzQq70A270SbVPef5oIkqXXupDK9/JPhT2QPRUXEhWYoH41KwxfK4Q28H2OLpzkvCCk0wVLjNFrJZjhEm1mxV4eKUl4AAi/nSwYUCZGAEFrEUVwWQwP8apFCsGolvZXP9Skc0XViAZTM5JKL6mVgQcwwuvs+J6Cf6elzZPR+7BLrJRFHlUTvMS3C8H+WClAh9KDeSvSbxYvG4ZRhNgXqRrD259O5iGKySf126ToG00Zece4Fz0rHjSRJUnrV4H2Npku2sMSo7M9DnwBiDW8kQ84yQnpXbc1hOqiFIOWRc31ZuQ2O0OUormHm3mUZY+ABC2zA8BdIPLZvspIHKMIHHoAMCARCigb8EgbxC+6wp6oKhdTtVS6nFomRylJ1p8t1hn8nqs/rsNTHoia6csinnPP2ZR2oqydnaVq9cGMqA6q7U5Yz1iXlf0dGOWmOewucN8URLkYDC6sne2acqNkWCpOVyJJ9/AaC5QTFvVyR+Nj7aRW3LpSgJdrwrMybrfgYawthj0q44z7+FQi1T0K42sLmTvelkqvXFdVBn2/aqcaXCkkA9OI5IrZH0fqwt25nMm/mTkhsgH+ntuzjtm4VDViLV2DuZcA==
> User-Agent: curl/7.29.0
> Host: www.sathish.com
> Accept: */*
>
* HTTP 1.0, assume close after body
< HTTP/1.0 200 OK
< Server: BaseHTTP/0.3 Python/2.7.5
< Date: Sun, 13 May 2018 17:09:19 GMT
< Content-type: text/html
<
* Closing connection 1
<html><head><title>Title goes here.</title></head><body><p>This is a test.</p></body></html>[root@krbserver ~]#
No comments:
Post a Comment