I am aware of the gazzilion pam / nginx questions here and on "Unix/Linux" but they are either unanswered or not related to my setup – so i try again 🙂
TL;dr
While the setup works perfectly with an Apache+mod_kerb_auth
combination, i fail to get automatic ticket forwarding working when using nginx+pam_krb5
– basic authentication against kerberos works though. The question is, what could be the cause and/or how to further debug it.
Below you find all the details, configs and environment
My setup:
- Windows 2012 DC in a lab (freshly installed and configured for KDC)
- Debian Stretch Linux as our "Service"
- On that Debian box i have:kerb-client MIT, Apache2+mod_kerb_auth and nignx+nginx_pam+pam
- a MacOS client
what is working:
- I have the usual setup with keytab-file, /etc/krb5.conf with all the realm/default_realm bits and pieces. (see below)
- I can kinit using the keytab file and my SPN:
HTTP/wiki.kwtest.local
(see below) - Using Apache2 + mod_kerb_auth on the same Debian Server, it works entirely including the ticket forwarding ( confirms that krb5.conf / keytab / KDC /infra setup works i guess). So i can use
kinit
+curl --negotiate
or Safari on my OSX and get access right away. - Adding the pam auth ( see below ) and the pam setup, i can authenticate entering the AD user credentials when accessing through safari ( curl gets 401 right away due to
-u
) … but ticket forwarding/verification does not work
Setup-Details:
Kerberos client configuration /etc/krb5.conf
[libdefaults]
default_realm = KWTEST.LOCAL
kdc_timesync = 1
# https://web.mit.edu/kerberos/krb5-1.12/doc/basic/ccache_def.html
# we do not want the keyring due to docker
ccache_type = 3
forwardable = true
proxiable = true
# no reverse lookup
rdns = false
[realms]
KWTEST.LOCAL = {
kdc = kdc.kwtest.local
admin_server = kdc.kwtest.local
}
[login]
krb4_convert = true
krb4_get_tickets = false
[logging]
default = FILE:/var/log/kdc.log:SYSLOG
kdc = FILE:/var/log/kdc.log:SYSLOG
# https://web.mit.edu/kerberos/krb5-1.12/doc/admin/conf_files/krb5_conf.html?highlight=appdefaults#appdefaults
[appdefaults]
forwardable = true
# https://manpages.debian.org/stretch/libpam-heimdal/pam_krb5.5.en.html
pam = {
ignore_k5login=true
debug=true
forwardable = true
proxiable = true
minimum_uid = 0
realm = KWTEST.LOCAL
keytab = /mnt/config/kerberos/drupalwiki.keytab
}
Pam configuration at /etc/pam.d/dw-kerb-nginx
cat /etc/pam.d/dw-kerb-nginx
auth required pam_krb5.so keytab=/mnt/config/kerberos/drupalwiki.keytab minimum_uid=20 forwardable=true realm=KWTEST.LOCAL trace=/var/log/pamtrace silent=false debug=true
account required pam_unix.so keytab=/mnt/config/kerberos/drupalwiki.keytab minimum_uid=20 forwardable=true realm=KWTEST.LOCAL trace=/var/log/pamtrace silent=false debug=true
Nginx configuration of that default server at /etc/nginx/sites-enabled/default
cat /etc/nginx/sites-enabled/default
server {
listen 80 default_server;
listen [::]:80 default_server;
root /var/www/html;
index index.html index.htm index.nginx-debian.html;
server_name _;
error_log /var/log/nginx/error.log debug;
location / {
auth_pam "Secure Zone";
auth_pam_service_name "dw-kerb-nginx";
try_files $uri $uri/ =404;
}
}
KVNO numbers match
#kdc KVNO
kvno HTTP/wiki.kwtest.local@KWTEST.LOCAL
HTTP/wiki.kwtest.local@KWTEST.LOCAL: kvno = 16
#client KVNO
klist -ke /mnt/config/kerberos/drupalwiki.keytab
Keytab name: FILE:/mnt/config/kerberos/drupalwiki.keytab
KVNO Principal
---- --------------------------------------------------------------------------
16 HTTP/wiki.kwtest.local@KWTEST.LOCAL (arcfour-hmac)
Service Keytab file from /mnt/config/kerberos/drupalwiki.keytab
lists the SPN
klist -ke /mnt/config/kerberos/drupalwiki.keytab
Keytab name: FILE:/mnt/config/kerberos/drupalwiki.keytab
KVNO Principal
---- --------------------------------------------------------------------------
16 HTTP/wiki.kwtest.local@KWTEST.LOCAL (arcfour-hmac)
Login with the keytab
file works using the SPN
kinit -k -t /mnt/config/kerberos/drupalwiki.keytab HTTP/wiki.kwtest.local@KWTEST.LOCAL
klist
Ticket cache: FILE:/tmp/krb5cc_0
Default principal: HTTP/wiki.kwtest.local@KWTEST.LOCAL
Valid starting Expires Service principal
01/07/2019 12:50:18 01/07/2019 22:50:18 krbtgt/KWTEST.LOCAL@KWTEST.LOCAL
renew until 01/08/2019 12:50:18
Question (things not working):
Ticket forwarding, so existing client tickets, are not validated, instead a basic-auth form is presented to enter the credentials – if i do so, i can authenticate. So basic krb authentication already works, just not ticket forwarding/validation.
Please be aware, that the exact same client (OSX Safari, OSX curl, Linux Curl), the exact same Debian Stretch server does work with Apache2+mod_ker_auth
. So it hardly can be a straight forward issues with the service keytab
file or /etc/krb5.conf
, otherwise apache2 would not work either, right?
How i tested
Testing from my OSX client using
# wikiuser is an user in the same AD/KDC ( not the user of the SPN)
kinit wikiuser@KWTEST.LOCAL
# ticket exists
klist
Credentials cache: API:34173A61-DFBB-4191-A39C-62EF67F1AC39
Principal: wikiuser@KWTEST.LOCAL
Issued Expires Principal
Jan 7 13:26:38 2019 Jan 7 23:26:20 2019 krbtgt/KWTEST.LOCAL@KWTEST.LOCAL
# the actual test
curl -u : --negotiate http://wiki.kwtest.local/index.html
<html>
<head><title>401 Authorization Required</title></head>
<body bgcolor="white">
<center><h1>401 Authorization Required</h1></center>
<hr><center>nginx/1.10.3</center>
</body>
</html>
Doing the same test, while stopping nginx and startin apache2
# on the Debian Service box
service nginx stop && service apache2 start
Now on my OSX box
kinit
curl -u : --negotiate http://wiki.kwtest.local/index.html
worked!%
What i have tried to fix it
- tried to get pam as verbose as possible using rsyslog and the debug=true / trace=/var/log/pamtrace settings, but i never got any output in there. PAM seems just failing log the auth attempt at all somehow. Only when i fill auth the basic form and actually authenticate successfully, i see this attempt in the logs – but the failed attempt using the existing tickets does not log anything at all
- playing around with all the
pam_krb5
parameters and settings likerealm
and such, but i cannot get it working nevertheless - used a linux client to test with curl, same result
Let me now if i forgot anything in the setup details or informations, i am perfectly aware that kerberos is complex and sensible to all those things, i hope to have included all the bits and pieces
Best Answer
The short answer to this question is, that is not possible with nginx_pam at all.
The technical term / protocol i ask for is "GSSAPI" or SPENGO based kerberos ticket authentication, so a user is automatically logged in if he has a existing kerberos ticket.
nginx_pam does not implement SPENGO/GSSAPI thus that will never work. What should happen is
on the first request the client asks for a resource (does not send any authentication header yet)
our nginx will pass that down through nginx_pam to pam, pam should then return "unauthorized" (no auth headers present). Nginx should now return a "401" with an header "WWW-Authorize: negotiate" to the browser (the browser then automatically sends a new request with the kerb ticket in the "Authorization" header
nginx should now unpack that ticket (based64 decode and such) and pass the ticket to pam (pam_krb)
pam will either return "ok" or "forbidden" ( not allowed for this user ).. the former is 200, the latter 403 for nginx passed to the client
Step 2 is not implemented by nginx_pam. Also, i am guessing ( not sure ), that pam rather only supports "authorized" or "not authorized", so boolean, not "unauthenticated", "authorized", "forbidden" .. .. so step 2 and 4 cannot be determinated (are the same)
Solution: I switched to https://github.com/stnoonan/spnego-http-auth-nginx-module for a proper GSSAPI based implementation.