I am attempting to upgrade an existing web application to run on Tomcat 10.1 from Tomcat 9.0 where it works fine. My web application currently uses JDBCRealm but this appears to now be deprecated with Tomcat 10.1 and are now advised to use DataSourceRealm instead.
I have attempted to follow the instructions on the Apache website to use the DataSourceRealm instead and upgraded both my context.xml and server.xml however my authentication isn't now working.
Here is my context.xml file:
<Context path="/TASSU"> <Realm className="org.apache.catalina.realm.DataSourceRealm" connectionName="root" connectionPassword="sesame"
connectionURL="jdbc:mysql://localhost:3306/login_details" debug="99" driverName="com.mysql.jdbc.Driver"
roleNameCol="Rolename" userCredCol="Password" userNameCol="Username" userRoleTable="userrole" userTable="userpass"/>
And here is the realm information within my server.xml file:
<Realm className="org.apache.catalina.realm.LockOutRealm">
<Realm className="org.apache.catalina.realm.UserDatabaseRealm"
resourceName="UserDatabase"/>
<Realm className="org.apache.catalina.realm.DataSourceRealm" connectionName="root" connectionPassword="sesame"
connectionURL="jdbc:mysql://localhost:3306/login_details" debug="99" driverName="com.mysql.jdbc.Driver"
roleNameCol="Rolename" userCredCol="Password" userNameCol="Username" userRoleTable="userrole" userTable="userpass"/>
Here is the error that is being displayed in the logs:
25-Jan-2023 10:13:50.699 SEVERE [http-nio-8080-exec-23] org.apache.catalina.realm.DataSourceRealm.open Exception performing authentication
Can anyone see at first glance what the issue could be here? Have I set these two files up correctly?
Related
I am trying to use a valve from IBM for role based authentication into Tomcat 9 using HTML headers passed from IBM Secure Access Manager (Tivoli Access Manager). I have configured the valve within the Engine Block as shown below:
<Engine name="Catalina" defaultHost="localhost" >
<Realm className="org.apache.catalina.realm.LockOutRealm">
<Realm className="org.apache.catalina.realm.UserDatabaseRealm"
resourceName="UserDatabase" />
</Realm>
<!-- Here is my valve declaration -->
<Valve className="com.ibm.tivoli.integration.am.catalina.valves.AMTomcatValve"
debugTrace="true" addRoles="myrolename" groupsHeader="iv-groups" />
<Host name="localhost" appBase="/path/to/WARs" unpackWARs="true" autoDeploy="true" >
<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>
However, this causes Tomcat to fail to start. Here is the Stacktrace:
Server start FAILED
at java.lang.reflect.Method.invoke(Method.java:498)
at org.apache.catalina.startup.Bootstrap.load(Bootstrap.java:306)
at org.apache.catalina.startup.Bootstrap.main(Bootstrap.java:491)
Caused by: java.lang.ClassNotFoundException: org.apache.catalina.Authenticator
at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:338)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
... 35 more
I am not sure what is causing this error as the class org.apache.catalina.Authenticator clearly exists within my catalina.jar located within my $CATALINA_HOME/lib directory. I have placed my AMTomcatValve.jar into $CATALINA_HOME/lib and added it to the classpath that is given to the java command that starts up tomcat.
Also the Tomcat image starts when the valve tag is removed. It also starts when the valve is moved outside of the engine block (this removes it from the context however), but I am met with an "Unexpected Authentication Error" when trying to get into my app using the IBM Security Access Manger WebSeal.
Are there any suggestions on how to get Tomcat to find this class? I'd really like to avoid writing a custom login/authentication valve
My Tomcat web application logs are telling me the user1 isn't in the specified mapped LDAP role/group coming from AD. I believe my issues lie with the JNDI Realm definition. Can someone review it please and see where I may be going wrong, I've included the DN information from AD as well:
My "user1" account DN is
DistinguishedName : CN=user1,OU=Users,OU=Lab,DC=example,DC=com
The role/group "Users" I have specified in the web.xml config is
DistinguishedName : CN=Users,CN=Builtin,DC=example,DC=com
My Realm configuration is
<Realm
className="org.apache.catalina.realm.JNDIRealm"
debug="99"
connectionURL="ldap://example.com:389"
authentication="simple"
referrals="follow"
connectionName="cn=administrator,cn=users,dc=example,dc=com"
connectionPassword="##########"
userSearch="(sAMAccountName={0})"
userBase="cn=users,dc=example,dc=com"
userSubtree="true"
userRoleName="memberOf"
roleSearch="(member={0})"
roleName="cn"
roleSubtree="true"
roleBase="cn=users,cn=builtin,dc=example,dc=com"/>
Fixed myself. This was the correct configuration.
<Realm
className="org.apache.catalina.realm.JNDIRealm"
debug="99"
connectionURL="ldap://example.com:3268"
authentication="simple"
referrals="follow"
connectionName="cn=administrator,cn=users,dc=example,dc=com"
connectionPassword="########"
userSearch="(sAMAccountName={0})"
userBase="dc=example,dc=com"
userSubtree="true"
userRoleName="memberOf"
roleName="cn"
roleSubtree="true"/>
I am using tomcat security for my application:
<Realm className="org.apache.catalina.realm.UserDatabaseRealm" resourceName="UserDatabase"/>
<!--This is only applicable for Tomcat 8.5 and higher version with SQL server authentication-->
<Realm className="org.apache.catalina.realm.JDBCRealm"
driverName="com.microsoft.sqlserver.jdbc.SQLServerDriver" connectionURL="jdbc:sqlserver://xxx.xx.xxx.xxx:1433;databaseName=xxxxx" connectionName="xxxx" connectionPassword="xxxx" roleNameCol="role_name" userCredCol="user_pass" userNameCol="user_name" userRoleTable="user_roles" userTable="users">
<CredentialHandler algorithm="MD5" className="org.apache.catalina.realm.MessageDigestCredentialHandler"/>
</Realm>
</Realm>
Now I want to be able to dynamically set new roles to the logged-in user from within the application whatever the authentication datasource may be. I need this to be deployed in an application and the client won't let us have the authentication database details and would prefer to themselves set the realm details. So it should work for all server/database types and hence be generic to the extent as possible.
I have the war file of my application. I need to deploy this at the root level. The current URL is http://localhost:8080/war_name/application_name.
You have a couple of options:
Remove the out-of-the-box ROOT/ directory from tomcat and rename your war file to ROOT.war before deploying it.
Deploy your war as (from your example) war_name.war and configure the context root in conf/server.xml to use your war file :
<Context path="" docBase="war_name" debug="0" reloadable="true"></Context>
The first one is easier, but a little more kludgy. The second one is probably the more elegant way to do it.
on tomcat v.7 (vanilla installation)
in your conf/server.xml add the following bit towards the end of the file, just before the </Host> closing tag:
<Context path="" docBase="app_name">
<!-- Default set of monitored resources -->
<WatchedResource>WEB-INF/web.xml</WatchedResource>
</Context>
Note that docBase attribute. It's the important bit. You either make sure you've deployed app_name before you change your root web app, or just copy your unpacked webapp (app_name) into your tomcat's webapps folder. Startup, visit root, see your app_name there!
In tomcat 7 with these changes, i'm able to access myAPP at / and ROOT at /ROOT
<Context path="" docBase="myAPP">
<!-- Default set of monitored resources -->
<WatchedResource>WEB-INF/web.xml</WatchedResource>
</Context>
<Context path="ROOT" docBase="ROOT">
<!-- Default set of monitored resources -->
<WatchedResource>WEB-INF/web.xml</WatchedResource>
</Context>
Add above to the <Host> section in server.xml
I know that my answer is kind of overlapping with some of the other answer, but this is a complete solution that has some advantages. This works on Tomcat 8:
The main application is served from the root
The deployment of war files through the web interface is maintained.
The main application will run on port 80 while only the admins have access to the managment folders (I realize that *nix systems require superuser for binding to 80, but on windows this is not an issue).
This means that you only have to restart the tomcat once, and after updated war files can be deployed without a problem.
Step 1:
In the server.xml file, find the connector entry and replace it with:
<Connector
port="8080"
protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />
<Connector
port="80"
protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />
Step 2:
Define contexts within the <Host ...> tag:
<Context path="/" docBase="CAS">
<WatchedResource>WEB-INF/web.xml</WatchedResource>
</Context>
<Context path="/ROOT" docBase="ROOT">
<WatchedResource>WEB-INF/web.xml</WatchedResource>
</Context>
<Context path="/manager" docBase="manager" privileged="true">
<WatchedResource>WEB-INF/web.xml</WatchedResource>
</Context>
<Context path="/host-manager" docBase="host-manager" privileged="true">
<WatchedResource>WEB-INF/web.xml</WatchedResource>
</Context>
Note that I addressed all apps in the webapp folder. The first effectively switch the root and the main app from position. ROOT is now on http://example.com/ROOT and the the main application is on http://example.com/. The webapps that are password protected require the privileged="true" attribute.
When you deploy a CAS.war file that matches with the root (<Context path="/" docBase="CAS"> you have to reload that one in the admin panel as it does not refresh with the deployment.
Do not include the <Context path="/CAS" docBase="CAS"> in your contexts as it disables the manager option to deploy war files. This means that you can access the app in two ways: http://example.com/ and http://example.com/APP/
Step 3:
In order to prevent unwanted access to the root and manager folder, add a valve to those context tags like this:
<Context path="/manager" docBase="manager" privileged="true">
<WatchedResource>WEB-INF/web.xml</WatchedResource>
<Valve className="org.apache.catalina.valves.RemoteAddrValve"
addConnectorPort="true"
allow="143\.21\.2\.\d+;8080|127\.0\.0\.1;8080|::1;8080|0:0:0:0:0:0:0:1;8080"/>
</Context>
This essentially limits access to the admin web app folder to people from my own domain (fake IP address) and localhost when they use the default port 8080 and maintains the ability to dynamically deploy the war files through the web interface.
If you want to use this for multiple apps that are using different IP addresses, you can add the IP address to the connector (address="143.21.2.1").
If you want to run multiple web apps from the root, you can duplicate the Service tag (use a different name for the second) and change the docbase of the <Context path="/" docBase="CAS"> to for example <Context path="/" docBase="ICR">.
Remove $CATALINA_HOME/webapps/ROOT. Update $CATALINA_HOME/conf/server.xml, make sure that Host element look like the following text:
<Host name="localhost" appBase="webapps"
unpackWARs="true" autoDeploy="false" deployOnStartup="false">
<Context path="" docBase="myApp"></Context>
It works with Tomcat 8. autoDeploy and deployOnStartup need to set to false to prevent tomcat from deploying myApp twice.
The fastest way.
Make sure you don't have ROOT app deployed, undeploy if you have one
Rename your war to ROOT.war, deploy, thats all, no configuration changes needed
Adding to #Dima's answer, if you're using maven to build your package, you can tell it to set your WAR file name to ROOT in pom.xml:
<build>
<finalName>ROOT</finalName>
</build>
By default, tomcat will deploy ROOT.war webapp into root context (/).
Adding on to #Rob Hruska's sol, this setting in server.xml inside section works:
<Context path="" docBase="gateway" reloadable="true" override="true"> </Context>
Note: override="true" might be required in some cases.
open tomact manager url :- http://localhost:8080/manager/html/
then in applications you see a application having path as "/" is deployed simply Undeploy this.
Rename your application's war file as ROOT.war and just place at path :- C:\Program Files\Apache Software Foundation\Tomcat 8.5\webapps
start your Tomcat No extra configuration needed.
Now we can see our application home page or configured url at http://localhost:8080
In my server I am using this and root autodeploy works just fine:
<Host name="mysite" autoDeploy="true" appBase="webapps" unpackWARs="true" deployOnStartup="true">
<Alias>www.mysite.com</Alias>
<Valve className="org.apache.catalina.valves.RemoteIpValve" protocolHeader="X-Forwarded-Proto"/>
<Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
prefix="mysite_access_log." suffix=".txt"
pattern="%h %l %u %t "%r" %s %b"/>
<Context path="/mysite" docBase="mysite" reloadable="true"/>
</Host>
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>
Apache commons-digester is used to parse the configuration files, so I suspect this particular use case simply wasn't expected.
Tomcat's org.apache.catalina.startup.RealmRuleSet.addRuleInstances seems rigged to only go 2-levels deep for Realm configuration. Seems simple enough to add another layer in there.
I'd have to look at how the digester can be configured to see if arbitrary levels could be supported, or if some subset would have to be manually configured.
Feel free to head over to the Tomcat users' list to request such a change.
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).