Win a copy of Five Lines of Code this week in the OO, Patterns, UML and Refactoring forum!
  • Post Reply Bookmark Topic Watch Topic
  • New Topic
programming forums Java Mobile Certification Databases Caching Books Engineering Micro Controllers OS Languages Paradigms IDEs Build Tools Frameworks Application Servers Open Source This Site Careers Other all forums
this forum made possible by our volunteer staff, including ...
Marshals:
  • Campbell Ritchie
  • Bear Bibeault
  • Ron McLeod
  • Jeanne Boyarsky
  • Paul Clapham
Sheriffs:
  • Tim Cooke
  • Liutauras Vilda
  • Junilu Lacar
Saloon Keepers:
  • Tim Moores
  • Stephan van Hulst
  • Tim Holloway
  • fred rosenberger
  • salvin francis
Bartenders:
  • Piet Souris
  • Frits Walraven
  • Carey Brown

Show REMOTE_USER in access log

 
Greenhorn
Posts: 19
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
With the Tomcat (8.0.15) access log valve, the default logging pattern is '%h %l %u %t "%r" %s %b'

I am using the SPNEGO filter to authenticate users. So I thought the %u would expand to the CGI variable REMOTE_USER but it just shows a blank.

I even tried using %{REMOTE_USER}i and %{REMOTE_USER}s but none of them end up showing the value in the access log.

How can this be done? Thanks

 
Saloon Keeper
Posts: 22254
151
Android Eclipse IDE Tomcat Server Redhat Java Linux
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Officially, the Access Log valve defines this:

%u - Remote user that was authenticated (if any), else '-'

If you are not using the J2EE standard login and are using a user-defined login, there will be no useful information there. If the user has not logged in, the user ID will report as "-".

The remote user ID has no definite relationship to the user's local network login ID. In fact, it will have no relationship at all unless you went to the effort of connecting the user's local LAN login to the J2EE standard security subsystem - which in Tomcat's case, is a Realm. So, in other words, I could be logged into my computer as "tim", but logged into the webapp as "snark@secret.com" and the Remote User would report me as "snark@secret.com", not as "tim".

If I'm reading the SPNEGO overview correctly, it's allowing the user's LAN login to serve as an identity authorization, but if SPNEGO is logging the user directly into the web application itself rather than into the Tomcat server - meaning that it isn't wired into a Tomcat Realm - then the Remote User can be expected to be blank.
 
Ivan Javanik
Greenhorn
Posts: 19
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Understood. So how can I wire up the SPNEGO filter to a Tomcat so that %u contains the CGI variable REMOTE_USER which in this case happens to be the currently logged in Windows user. In other words, the filter implements a form of SSO aka Windows Integrated Authentication and I want the access log to reflect that.
 
Tim Holloway
Saloon Keeper
Posts: 22254
151
Android Eclipse IDE Tomcat Server Redhat Java Linux
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
You don't do that in the SPNEGO filter. You have to install an actual Realm. Have you looked at this section of the Tomcat 8 docs? http://tomcat.apache.org/tomcat-8.0-doc/windows-auth-howto.html (note to earlier readers - I mis-pasted this URL originally. It has been fixed).

They say that SPNEGO can use any Realm you want, but there are special considerations if you use the JNDI (Active Directory) Realm.

I found this document, which seems to be using a Realm provided by SPNEGO itself:

http://jaaslounge.sourceforge.net/howto/SSO_Tomcat_Howto.pdf

If you intend for any of your webapps to be useable from outside of the site LAN/VLAN, you'll also need a non-SPNEGO Realm to handle those users, since they won't be part of the Windows Security Domain. Since Tomcat6 it has been possible to stack multiple Realms for an application, which is useful, since you can do things like define credentials in a database for those of us who log in Linux clients from outside the LAN while still getting the benefits of SPNEGO for in-house users.

Some further notes. In the Tomcat docs, they refer to the Kerberos (domain) REALM. This isn't the same thing as Tomcat's Realm modules. The domain realm is the Windows security domain that Windows users log into. Tomcat Realms are plug-in authentication and authorization modules for Tomcat itself.

Also, from what I see in Google, getting the JNDI settings right can be a challenge. No surprise there. Anything having to do with wiring in LDAP for authentication is pretty much guaranteed to do that.
 
Ivan Javanik
Greenhorn
Posts: 19
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Yes, I do see that Tomat 8 now includes native support for Kerberos-based Windows domain authentication.

It seems to be implemented as a authentication valve and tightly integrated with Tomcat so when it is all configured can I assume that the %u token in the AccessLog valve will automatically contain the REMOTE_USER which is the Windows username of the client/browser?

Installing an actual Tomcat realm with the Sourceforge SPNEGO I am currently using seems a bit tricky, I don't know exactly how to go about doing it, any guidance will be appreciated.

Yes, the JAASLounge module seems like a alternative way to implement Kerberos SSO which is built with Tomcat realms in mind. But their Sourceforge project page seems to be pretty dormant for the past 4+ years, I am not very comfortable introducing "abandoned" software into our Production environment.

Yes, I realize that the nomenclature is a little confusing, a Tomcat realm is a different animal than the realm referred to in Kerberos documentation, the latter is really the AD domain. Thanks for the tip.

Another wrinkle is that the webapp I am working wit - refer to the other thread you are helping me with - uses the Tomcat auto deployment feature so there *is* no context.xml file to modify and the Tomcat built-in SPNEGO Authenticator valve appears to require adding some attributes to the Context element, setup a Authenticator valve and all that stuff. Not sure how to go about doing it, your guidance will be appreciated.

Ideally, from a support and maintenance perspective, I would like to use the Tomcat built-in SPNEGO authentication instead of using third-party libraries but it seems really tricky to implement and fairly new so there aren't too many people out there who have used it successfully.
 
Ivan Javanik
Greenhorn
Posts: 19
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
OK I have been doing some reading on Tomcat realms. I am very confused. Seems to me like a Realm performs *authentication* i.e. it looks up the username/password in some datasource (JDBC, AD, XML file, etc). So what is the difference, in concept, between a Authenticator and a Realm? The documentation for the SPNEGO authenticator in Tomcat 8 says that the "SPNEGO authenticator will work with any Realm" but I am not sure I understand what that statement means. What does the Authenticator do and what does the Realm do here? In the case of SPNEGO, the authenticator's job is to use Kerberos facilities to determin who is making the request and set REMOTE_USER accordingly. So what's left for the Realm component to do? I don't want the Realm to do anything else here, just say yup, all's well and let the request proceed so that the AccessLog valve gets the value of REMOTE_USER in the %u token.

What am I missing here?
 
Ivan Javanik
Greenhorn
Posts: 19
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
OK so I followed the instructions here but it's not quite working..

1. Turned off autoDeploy=true and created a file $CATALINA_HOME/conf/Catalina/localhost/app1.xml with

<?xml version="1.0" encoding="UTF-8"?>
<Context>
<Realm className="org.apache.catalina.realm.JAASRealm" />
</Context>


2. Added a fragment in $CATALINA_HOME/webapps/app1/WEB-INF/web.xml


<security-role>
<role-name>com.sun.security.jgss.krb5.accept</role-name>
</security-role>

<security-constraint>
<web-resource-collection>
<web-resource-name>APP1</web-resource-name>
<url-pattern>/*</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>com.sun.security.jgss.krb5.accept</role-name>
</auth-constraint>
</security-constraint>

<login-config>
<auth-method>SPNEGO</auth-method>
</login-config>


3. Made sure that the klist -k -t <full path to keytab file> works, shows the SPN in the keytab file

4. Made sure that kinit -V -k -t <key tab file> <principal as shown in output of above command> works, shows Authenticated to Kerberos v5

5. Created a jaas.conf file in $CATALINA_HOME/conf with

com.sun.security.jgss.krb5.initiate {
com.sun.security.auth.module.Krb5LoginModule required
doNotPrompt=true
principal="SPN NAME"
keyTab="full path to keytab file"
useKeyTab=true
storeKey=true;
debug=true;
};

com.sun.security.jgss.krb5.accept {
com.sun.security.auth.module.Krb5LoginModule required
doNotPrompt=true
useKeyTab=true
storeKey=true;
debug=true;
principal="SPN NAME"
keyTab="full path to keytab file"
};


6. Created a file in $CATALINA_HOME/conf/krb5.conf wth


[libdefaults]
default_realm = domain.COM
default_keytab_name = FILE:<path to keytab file>
default_tkt_enctypes = aes128-cts rc4-hmac des3-cbc-sha1 des-cbc-md5 des-cbc-crc
default_tgs_enctypes = aes128-cts rc4-hmac des3-cbc-sha1 des-cbc-md5 des-cbc-crc
permitted_enctypes = aes128-cts rc4-hmac des3-cbc-sha1 des-cbc-md5 des-cbc-crc
forwardable=true

[realms]
domain.COM = {
kdc = kdc.domain.com
}

[domain_realm]
domain.com=domain.com
.domain.com=domain.com


7. export JAVA_OPTS="-Djava.security.auth.login.config=<path to jaas.conf> -Djava.security.krb5.conf=<path to krb5.ini>" to make sure that it picks up the config files. Confirmed that it does in catalina.out

8. Restart Tomcat

After all this setup, /app1 still shows a error

28-Nov-2014 15:30:57.124 SEVERE [http-nio-8080-exec-2] org.apache.catalina.authenticator.SpnegoAuthenticator.authenticate Unable to login as the service principal
javax.security.auth.login.LoginException: No LoginModules configured for com.sun.security.jgss.krb5.accept
at javax.security.auth.login.LoginContext.init(LoginContext.java:264)

This is very frustrating. What am I missing here? The LoginModule is configured fine, why is it complaining?

Any help much appreciated. Thanks.
 
Tim Holloway
Saloon Keeper
Posts: 22254
151
Android Eclipse IDE Tomcat Server Redhat Java Linux
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
From the looks of it, the JAAS Login isn't being assigned properly. This could be because something wasn't typed in quite right or because the Tomcat user doesn't have proper read access rights on one or more of the config files.

You might want to bump up the JVM's JULI log level to DEBUG or TRACE and see if you can see details that might give you a clue.

Note that when I say "JAAS" Login, I mean literally the login module for JAAS itself, not the Tomcat Realm Login for JAAS. Your problem is at a lower level than that. Although do check the Tomcat JAASRealm docs. You don't have any atributes assigned, and although JAAS itself uses discrete config files, it's rare for a Tomcat configuraion element not to have some items of its own that need setting.

Incidentally, the original URL I gave for the Tomcat docs was missing the final "l" in ".html" so the link failed. I've corrected the earlier posting.
 
Ivan Javanik
Greenhorn
Posts: 19
  • Likes 1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Your comment about Tomcat configuration elements needing attributes was very helpful. I finally got this working.

Just to recap for the benefit of anyone reading this in the future...

1. autoDeploy is not relevant here. $CATALINA_HOME/conf/Catalina/localhost/app1.xml. As per the documentation, the Context element defined in the localhost folder takes precedence over the context implicitly defined by the auto-deployed WAR. So I created that file with the following



The default for the Valve > loginConfigName attribute is com.sun.security.jgss.krb5.accept but this needs to match the value for the JAAS Realm > appName attribute which was the reason for the error I was getting.

The reason for the allRolesMode="authOnly" is explained in the documentation, more on that below.

2. Added the following XML fragment to $CATALINA_HOME/webapps/app1/WEB-INF/web.xml



The reason for the role-name=* is that I am not really using any roles, I just want to use the JAAS realm to piggy-back off of the SPNEGO authenticator valve, no further role constraint is needed. See #1 above for how the special role "*" is interpreted by the realm.

5. Created a jaas.conf file in $CATALINA_HOME/conf with



The name used here APP1 has to match the loginConfigName and appName attributes in the Context element above. Strangely enough, the debug=true was giving me an error so I took it out.

6. Didn't really need the default_keytab_name in the krb5.conf file after all.

7. Didn't need JAVA_OPTS either. The default locations for jaas.conf and krb5.conf are $CATALINA_HOME/conf and $CATALINA_HOME, that seems to work fine. The JAAS Realm also seems to have a configFile attribute which can be used to specify the location of the jaas.conf file.

8. Restarted Tomcat.

Success!

The context for the APP1 webapp uses the SPNEGO authenticator and the JAAS realm to do authentication against the KDC and sets REMOTE_USER which appears in the %u in the Tomcat AccessLog valve.

The SPNEGO Authenticator valve has some attributes related to session caching that bear further investigation from a performance perspective, will look into them.

Thank you for your help.
 
Ivan Javanik
Greenhorn
Posts: 19
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Sorry, one more question....

The way this Oracle product works, they have provided some config options in the WAR file.

In other words,

This modifies some parameter inside the WAR file and so Tomcat silently and automatically redeploys the webapp by removing the webapps/app folder and replacing it with the contents of the WAR.

My carefully crafted solution above depends on the security-constraint element in the $CATALINA_HOME/webapps/app1/WEB-INF/web.xml file. I spent a couple of hours today trying to track down why the Kerberos/SPNEGO authenticator & JAAS Realm was not kicking in. I cranked up all the debug logging but there were no errors anywhere. REMOTE_USER was just not getting set. I just stumbled upon the fact that my web.xml had been replaced and since it didn't have the security-constraint, the authentication valve and realm settings were being ignored. Whew, I think this is a little too scary for a Production environment.

Question: Is there an override to app/WEB-INF/web.xml where I can place the security-constraint? Sort of like how conf/Catalina/localhost/app1.xml overrides the implicitly defined Context for the webapp. This way I can safely use Oracle-provided commands to modify the WAR without blowing away my web.xml.

Or maybe there is another approach to this that I am not thinking of?

Help? Thanks
 
Tim Holloway
Saloon Keeper
Posts: 22254
151
Android Eclipse IDE Tomcat Server Redhat Java Linux
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
This reminds me of how Jenkins works. An "Executable WAR" is a strange beast. What it actually is, of course, is an executable JAR which just happens to conform to WAR format so that the same file can be used for both the server and the WAR that the server hosts. In the case of Jenkins, I think there's an embedded copy of jetty in there. I'm rebuilding a Docker container for Jenkins today, so I'll get my memory refreshed.

J2EE defines a pair of Deployment Descriptors. The server-independent deployment descriptor is web.xml and is located inside the WAR's WEB-INF directory. The server-dependent deployment descriptor (which for Tomcat is the Context), is passed along to the deployment process and is external to the WAR. Although "external" is a relative term, since one of the places the Tomcat deployer looks for external deployment descriptors is in a META-INF/context.xml within the WAR.

But, like I said, web.xml is in a fixed location, and it doesn't support overlays, so your best bet is simply to extract the web.xml, make the necessary mods, then repack the "WAR".

You MIGHT be able to dynamically define constraints, but not, I think via the J2EE API. You'd almost certainly have to make externsive mods involving Tomcat's internals.

The worst of it is that after all that work, what you get is of limited utility. You can't use SPNEGO to secure anything coming in from outside the LAN (i.e., the open Internet), and I'm not sure that Linux or MacOS clients can do Windows LAN logins even in these advanced times.
 
Ivan Javanik
Greenhorn
Posts: 19
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Interesting, thanks. That's a bummer, wish the concept of a web.xml overlay similar to context.xml overlay was supported.

so your best bet is simply to extract the web.xml, make the necessary mods, then repack the "WAR".



Actually, now that I look at it closely, the executable WAR doesn't really do anything fancy, it just provides a series of prompts to add/modify key/value pairs in some non-Tomcat directory that it needs for the Oracle product to function correctly. I could just as well edit those files manually.

So if I don't need the executable WAR functionality, could I simply delete the WAR or rename it to war.orig so that there is no danger of the WAR inadvertently exploding and re-creating my webapp directory?

Looking at these two threads, I am not sure I understand what the effect of deleting a WAR file after the app has been auto-deployed is. In the first thread, you said that "It also won't update the exploded WAR directory if you replace the old WAR file with a new WAR file" but that is not what I see. If I simply "touch" the WAR or replace it, Tomcat automatically deletes and re-creates the webapp directory. In the second thread, the consensus is that I can what I suggested above i.e. stop Tomcat, delete/rename the WAR and restart Tomcat and forget that the webapp ever came from the WAR.

What say you?

Thanks
 
Tim Holloway
Saloon Keeper
Posts: 22254
151
Android Eclipse IDE Tomcat Server Redhat Java Linux
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
I started to mention - then forgot - an "executable WAR" usually means that you can execute it as a jar file that brings up a server, BUT you typically can also ignore that part and use it as a straight WAR just by deploying it in a webapp server yourself. Which is probably appropriate for you.

Up to Tomcat 6, if you dropped a WAR file into the Tomcat webapps directory, Tomcat would check that directory for a sub-directory with the same name as the WAR (minus the ".war" extension). If none was found AND the (default) auto-explode function was enabled, then the sub-directory would be created and the WAR file would be unzipped into it, then the webapp would be deployed. Thereafter any actions on the WAR file itself - including replacement - would be ignored. Tomcat would monitor the webapp sub-directory for changes and redeploy if anything in there was altered.

I'm fairly sure that's still how it works in Tomcat 7. Haven't checked on Tomcat 8 yet. So if you're deploying to an exploded WAR, the original WAR file can be safely deleted once it has been exploded. Whether you explode it manually or let Tomcat do it.

 
Ivan Javanik
Greenhorn
Posts: 19
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Yup, that's exactly what they have done. nohup java -jar app.war & can be used to bring up a standalone web server for use with their app, no Tomcat needed.

So if you're deploying to an exploded WAR, the original WAR file can be safely deleted once it has been exploded



That is not what I am seeing with Tomcat 8.0.15. I dropped a app.war file in webapps/, waited a few seconds, Tomcat exploded it into webapps/app/, the webapp at /app worked fine. I renamed webapps/app.war to webapps/app.warx, waited a few seconds and Tomcat deleted the webapps/app directory!

However if I shutdown Tomcat and then rename webapps/app.war to webapps/app.warx and then start it up again, it doesn't delete the webapps/app directory.

So I think that's my answer. Sound good?

Thanks for your help.

 
Tim Holloway
Saloon Keeper
Posts: 22254
151
Android Eclipse IDE Tomcat Server Redhat Java Linux
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
OK. I'll have to look into the Tomcat 8 behavior. Thanks for the info!
 
Ivan Javanik
Greenhorn
Posts: 19
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator

You can't use SPNEGO to secure anything coming in from outside the LAN (i.e., the open Internet), and I'm not sure that Linux or MacOS clients can do Windows LAN logins even in these advanced times.



This comment struck me. I have another Linux server running Apache httpd and mod_ntlm. When I access these pages from outside the LAN from my iPhone (of course, we have the connectivity in place using Mobile Iron, per-app VPN and all that stuff so Safari is able to get through the firewall), since it is unable to negotiate the Windows authentication, Safari pops up a Basic authentication dialog, I enter my Windows domain username and password and it takes me right in.

In contrast, when I try the exact same thing with this Tomcat/SPNEGO configuration, I don't get the Safari prompt, it straight away goes to the Tomcat HTTP 401 page with This request requires HTTP authentication

Question: Is there a way to configure Tomcat similar to mod_ntlm i.e. if SPNEGO is not avaiable, fall back to basic authentication and forward credentials to AD for verification?
 
Ivan Javanik
Greenhorn
Posts: 19
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
From what I read in the Apple documentation (quoted below), iOS does support the HTTP Negotiate with SPNEGO/Kerberos so I am not sure why it is not working with Tomcat.

Any ideas?

Single Sign-on
iOS supports authentication to enterprise networks through Single Sign-on (SSO).
SSO works with Kerberos-based networks to authenticate users to services they are
authorized to access. SSO can be used for a range of network activities, from secure
Safari sessions to third-party apps.
iOS SSO utilizes SPNEGO tokens and the HTTP Negotiate protocol to work with
Kerberos-based authentication gateways and Windows Integrated Authentication
systems that support Kerberos tickets. Certi?cated-based authentication is also
supported. SSO support is based on the open source Heimdal project.
The following encryption types are supported:
• AES128-CTS-HMAC-SHA1-96
• AES256-CTS-HMAC-SHA1-96
• DES3-CBC-SHA1
• ARCFOUR-HMAC-MD5
Safari supports SSO, and third-party apps that use standard iOS networking APIs can
also be con?gured to use it. To con?gure SSO, iOS supports a con?guration pro?le
payload that allows MDM servers to push down the necessary settings. This includes
setting the user principal name (that is, the Active Directory user account) and Kerberos
realm settings, as well as con?guring which apps and/or Safari web URLs should be
allowed to use SSO.


 
Tim Holloway
Saloon Keeper
Posts: 22254
151
Android Eclipse IDE Tomcat Server Redhat Java Linux
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
This is the kind of nasty problem that I really enjoy solving. However, since it requires actually doing serious work instead of just sitting here spouting pompous nonsense off the top of my head, it's not the kind of stuff I can do for free. I must leave you to the tender mercies of the Apple, MS, and SPNEGO docs, and may Google have mercy upon you.

Your question -1, I can do in pompous nonsense mode though, so here's the answer to that one:

As of Tomcat 6, a new combining Realm became available. I forget its exact name, but it's a Realm of Realms and it's described in the Tomcat Realm documentation. One of the most common uses for it, I'm sure is to allow mixed-source logins by vetting in-house logins against Active Directory using the JNDIRealm while allowing outsiders to be authenticated and authorized via database entries. You should be able to stack the JNDIRealm and the SPNEGO Realm the same way.
 
Ivan Javanik
Greenhorn
Posts: 19
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator

I'm sure is to allow mixed-source logins by vetting in-house logins against Active Directory using the JNDIRealm while allowing outsiders to be authenticated and authorized via database entries. You should be able to stack the JNDIRealm and the SPNEGO Realm the same way.



Well, it is not really database entries; all I want is for Tomcat to popup up the browser Basic authentication challenge if SPNEGO/JAAS fail. The ancient Apache mod_ntlm does this quite elegantly with the following simple directives in the Apache config and it works like a charm. Connections inside the firewall go right in without a prompt. From outside the firewall, it popups up the basic challenge, validates input against the domain controller and that's that.

Sigh, I guess I need to embark upon more Googling, just when I thought I saw light at the end of the tunnel. Wish me luck!

AuthType NTLM
AuthName "NTLM authentication"
NTLMAuth On
NTLMBasicAuth On
NTLMDomain ***
NTLMServer ***
NTLMBackup ***
require valid-user
 
Tim Holloway
Saloon Keeper
Posts: 22254
151
Android Eclipse IDE Tomcat Server Redhat Java Linux
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
The method that Tomcat will use to obtain credentials is independent of the Realm being used. If I stack a JDBCRealm, a JNDIRealm and a MemoryRealm, it doesn't matter which one ultimately validates the userid/password. If Tomcat needs the userId and password it will pop up a dialog or display a form, depending on whether web.xml is set for BASIC or FORM credentials. In other words, FIRST a need for credentials is detected, SECOND, Tomcat initiates the process to obtain credentials to the client, THIRD, the returned credentials are passed to the authenticate() method(s) for the Realm(s).

The only complication here is that as I recall, SPNEGO is itself configured as a credential source, and I'm suspecting that web.xml doesn't allow multiple credential sources, since normally they'd be pointless and/or dangerous.

If it helps, I once ran across a case where the wrong credential source was being used until I added a Valve to prevent it. So the 2 most likely places to look are in the Valve area of Tomcat and/or extensions to the SPNEGO configuration.

Incidentally, dialog-based logins are not really that popular in J2EE. They're supposed to be less secure (if you don't have TLS/SSL running, their internal encryption is something like ROT64). And you have to terminate the client application to log out.
 
Ivan Javanik
Greenhorn
Posts: 19
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Understood but as you said the SPNEGO authentication valve is a strange beast. Even if I change the auth-method in web.xml to FORM instead of SPNEGO, I still get authenticated for internal connections and go straight to the 401 page for external connections, no login form in sight. In fact, forget about the distinction between internal and external. Taking a step back, how can the SPNEGO valve be used in conjunction with a login form i.e. suppose my internal/inside the firewall browser does not support Windows Integrated Authentication, how would I go about presenting a login form with the special j_username/j_password/POST to j_security_check? I don't see any examples of a login form that validates the user input using Kerberos. What am I missing?

If this works i.e. I am able to use a form to gather credentials and validate using Kerberos/SPNEGO valve, I can go down the path of detecting internal vs external IP addresses, redirect to the appropriate login page and stuff like that. But I just don't see how I can use a login FORM with SPNEGO. Help? Thanks

 
Tim Holloway
Saloon Keeper
Posts: 22254
151
Android Eclipse IDE Tomcat Server Redhat Java Linux
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
At the risk of restating the obvious, there are 3 functional areas in Tomcat that are involved with container security and authentication (login):

1. The authentication/authorization sensor (not its real name)

2. The credentials acquisition function

3. The validation function

The AA sensor is the part of Tomcat that matches each incoming URL against the list of secured URLs defined in the target webapp's web.xml file. If the user is not already logged in, and a match is detected, then Tomcat shunts the incoming request to a temporary holding area and activates the credentials acquisition function.

The credentials acquisition function can be considered as a mini-webapp contained entirely within the Tomcat server itself. Depending on web.xml options, it will respond in place of the side-tracked original URL request, returning a challenge HTTP code (401, I think) or a login form. The user enters credentials and submits them back. They are recieved by Tomcat.

The validation function is a method defined by the Realm Interface. It's named validate() and accepts 2 parameters (user ID and password). It validates these credentials and either constructs a UserPrincipal object to track future URL requests OR it rejects the login, in which case the cycle returns to the credentials acquisition function. If it's a form-based login, the loginfail form is used this (and subsequent) times. This cycle will repeat until valid credentials are received or the user stops trying to log in.

Once validated, the ORIGINAL URL request is brought out of storage and processed as though the whole login sequence had never happened (except that now there's a UserPrincipal context with a remoteUser ID).



What SNPEGO has to do, therefore, is tap into the credentials acquisition and validation functions. It has to basically ignore the input parameters to the Realm validate() method and query the Kerberos/NTLM realm directly. And, since it's doing that, the credentials acquisition is essentially a no-operation function.

That's the infrastructure you're facing. How to get SPNEGO to step out of the way and employ a more traditional method is the challenge. As to the details, that's the kind of stuff I enjoy doing, but it's far too much work for me to be able to do for free. I'd probably end up looking at the Tomcat source code (not for the first time!)

If you haven't looked at the following page, I recommend doing so, but note that I'm not sure they're completely informed on how Tomcat's security works. For one thing, tomcat-users.xml is NOT a "default" credentials source, it's simply that the examples in the original server.xml use the MemoryRealm, and that's the file that that Realm defined to be its default source. I'm also not sure I believe the assertion that Tomcat7 is really phasing out Valves for servlet filters, since Valves deal with server-specific traffic functionality and servlet filters should be dealing with server-independent functions. But at least they should know SNPENGO.

Anyway, cavets aside, the URL in question is http://spnego.sourceforge.net/tomcat_valve.html
 
Ivan Javanik
Greenhorn
Posts: 19
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Thank you for the superb explanation of Tomcat container security and A&A, accurate and very concisely stated.

I did look at the Tomcat source code but their implementation of the SpnegoAuthenticator class omits support for WWW-Negotiate: Basic. In all other respects, it is functionally identical to Darwin Felix's SpnegoAuthenticator filter.

From my perspective, this is unfortunate. If you recall, I started this thread after I successfully implemented that filter but since it didn't invoke any Tomcat Realm, the request REMOTE_USER was not set. But other than that, the filter works beautifully. Inside the firewall, when accessed from browsers capable of negotiating Windows Integrated Authenticaiton, it silently negotiates credentials using Kerberos. When accesed from outside the firewall from a iOS device, by setting spnego.allow.basic=true it pops up a basic authentication challenge, I enter my Active Directory credentials and it shows the page. This is exactly what I am looking for.

To compensate for this small oversight in Tomcat's SPNEGO valve, I will need to go back to Darwin's SPNEGO servlet filter and implement it as a Valve using the link you refer to. This is not optimal for a few reasons a) the valve hard codes all the configuration parameters instead of reading a config file like the filter does. So any change to the parameters (e.g. pre-auth account password, logging level) will need a recompile b) an entry for the new valve needs to be added to the core Tomcat Authenticator.properties in lib/catalina.jar so break open the jar, add the entry to the file, re-pack it and c) the valve will need a new name since Tomcat 7+ now comes with SPNEGO

In the big scheme of things, I guess this is not too bad. The only downside is that the Tomcat access log doesn't include the remote user on the access requests but for internal connections it can be easily inferred by the IP address (because it maps to a person's workstation). For external connections, the IP address is for the DMZ server so unfortunately I can't associate it with a specific person, just that the request came from "out there".

I really wish that the Apache Tomcat folks would have added support for Basic authentication to their SPNEGO authenticator valve, it is such a small change really. From a Tomcat architectural perspective, another alternative would have been to allow multiple auth-method for a login-config element which are tried in the order they appear; so we could have SPNEGO, followed by FORM or BASIC. Oh well, you win some, you lose some.

I am not sure which way I will go yet but at least I understand my options. Thanks for your help.
 
Ivan Javanik
Greenhorn
Posts: 19
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Sorry, I see that 2 links in my previous post didn't go through...

Tomcat source code - http://svn.apache.org/repos/asf/tomcat/tc8.0.x/trunk/java/org/apache/catalina/authenticator/SpnegoAuthenticator.java
SPNEGO filter source code - http://sourceforge.net/p/spnego/code/HEAD/tree/trunk/src/java/net/sourceforge/spnego/SpnegoAuthenticator.java (specifically the doBasicAuth method)
 
Tim Holloway
Saloon Keeper
Posts: 22254
151
Android Eclipse IDE Tomcat Server Redhat Java Linux
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
A Valve, like most Tomcat components, is implemented as a JavaBean. The server.xml file is consumed by the Apache/Tomcat Digester to instantiate and initialize these JavaBeans.

So, you can configure a valve in 3 ways:

1. Via attributes on the Valve element itself.

2. Via sub-elements of the Valve

3. Via a config file, such as a properties file. Bonus points if, instead of hard-coding the properties filename, you make it a configurable property itself.

You can put the config file anywhere you want, in any format you want, although a good place is usually the Tomcat conf directory. Absolute paths are preferable.

There is one thing to watch out for, however. If you run Tomcat via the WTP Eclipse plugin, WTP will not copy the config file over to its hidden config clone. One more reason why I prefer the sysdeo plugin, instead.

If you've modified the stock Tomcat valve to meet your needs, shoot me a copy of the source. I'll check it over and see if I cannot at least publish it somewhere visible. Maybe I can even get it integrated officially into Tomcat.
 
Ivan Javanik
Greenhorn
Posts: 19
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
To my pleasant surprise, this turned out to be a non-issue (for me!). iOS7 added support for Kerberos as part of their Enterprise SSO push. Setting it up used to be a bit of a hassle but now there are MDM (mobile device management) vendors (e.g. Mobile Iron) that offer products like a per-app VPN tunnel that allow seamless, secure access to enterprise servers inside the firewall (using either their Web@Work app or the per-app VPN tunnel). Basically, it pushes out a configuration profile to the mobile device that enables it to successfully negotiate Kerberos authentication just like IE/Windows Integrated Authentication is able to do inside the enterprise. It is a new product so it is a little rough around the edges and takes some time & effort on the part of the MDM administrator to carefully setup but once it is setup, it works beautifully.
 
Consider Paul's rocket mass heater.
    Bookmark Topic Watch Topic
  • New Topic