Tomcat 7 nesting CombinedRealm, LockoutRealm and DataSourceRealm
java, realm, security, tomcat
Solution
The new answer is now:
Update to Tomcat 7.0.33 or later. Then it works perfectly.
Christopher Schultz was so friendly to forward my question here to the Tomcat user list. The great Tomcat developers have immediately addressed the issue and put this in the next release. Thanks a lot!
So you can now use a construction like the one in the question or like this with different order / "priorities":
...
<Engine name="Catalina" defaultHost="localhost">
<Realm className="org.apache.catalina.realm.CombinedRealm">
<!-- PRIMARY: tomcat-users.xml with critical system users
that should always work, DB independent and without lockout
NOTE: If the wrong password is given, the secondary path with
lockout is still attempted, so that a lockout on that path
will still occur and be logged. Still the primary path is not
locked for access by that happening. -->
<Realm className="org.apache.catalina.realm.UserDatabaseRealm"
resourceName="UserDatabase"/>
<!-- SECONDARY: DataSourceRealm with DB with lockout functionality -->
<!-- (three level nesting of realms requires Tomcat >= 7.0.33) -->
<Realm className="org.apache.catalina.realm.LockOutRealm"
failureCount="5" lockOutTime="60" > <!-- note that when an account is locked correct password
login is no longer possible (would otherwise defeat purpose of lockout),
but also lockoutTime is still reset in each correct attempt -->
<Realm className="org.apache.catalina.realm.DataSourceRealm"
dataSourceName="jdbc/authority"
userTable="user" userNameCol="username"
userCredCol="password" digest="SHA"
userRoleTable="user_role" roleNameCol="rolename" />
</Realm>
</Realm>
<Host >
...
</Host>
</Engine>
...
Of course you may also use other Realms and other combinations.
Note that one thing can be misleading in the logs: In this construction, if a wrong password is given for one of the critical users stored in the primary realm, the primary realm denies access, then the secondary realm via the lockout realm is tried and also denies access eventually locking out the username. This is logged by the lockout realm as a warning "An attempt was made to authenticate the locked user ...". Still with correct password, access keeps working via the primary realm, as it does not go via the lockout realm. I.e. all works as intended, just the log message could lead to confusion (of course this is impossible to avoid).
Problem
I'm trying to nest Realms as follows in Tomcat 7.0.32 (written here in pseudo-XML): ``` <CombinedRealm> <LockoutRealm> <DataSourceRealm/> </LockoutRealm> <UserDatabaseRealm/> </CombinedRealm> ``` This doesn't seem to work - is it possible to nest Realms in Tomcat by more than two levels? I get a warning in the logs: ``` No rules found matching 'Server/Service/Engine/Realm/Realm/Realm'. ``` The idea behind is that the web service has some critical users that must not be locked out (e.g. as a DOS) and some normal users, which may have weaker passwords, where the lockoutRealm should be active. I'm sure other people have been in this situation. If there is another way to achieve this (e.g. a whitelist for the LockoutRealm), please let me know. Single sign on is also needed. I guess extending the existing LockoutRealm code with a list of accounts never to lock out would be an option, but I'm not so keen on writing my own Realm, I would rather not add custom code on that level to Tomcat, as this will complicate setup for others and with every Tomcat update it might break etc. Thanks for any help! Here is the relevant part of server.xml of my test config: ``` <Engine name="Catalina" defaultHost="localhost"> <Realm className="org.apache.catalina.realm.CombinedRealm"> <!-- Lockout realm for the DB users --> <Realm className="org.apache.catalina.realm.LockOutRealm"> <!-- PRIMARY: DataSourceRealm with user DB --> <Realm className="org.apache.catalina.realm.DataSourceRealm" dataSourceName="jdbc/authority" userTable="user" userNameCol="username" userCredCol="password" digest="SHA" userRoleTable="user_role" roleNameCol="rolename" /> </Realm> <!-- FALLBACK: This Realm uses the UserDatabase configured in the global JNDI resources under the key "UserDatabase". Any edits that are performed against this UserDatabase are immediately available for use by the Realm. --> <Realm className="org.apache.catalina.realm.UserDatabaseRealm" resourceName="UserDatabase"/> </Realm> <Host name="localhost" appBase="webapps" unpackWARs="true" autoDeploy="true"> <!-- SingleSignOn valve, share authentication between web applications Documentation at: /docs/config/valve.html --> <Valve className="org.apache.catalina.authenticator.SingleSignOn" /> <!-- Access log processes all example. Documentation at: /docs/config/valve.html Note: The pattern used is equivalent to using pattern="common" --> <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs" prefix="localhost_access_log." suffix=".txt" pattern="%h %l %u %t "%r" %s %b" /> </Host> </Engine> ```