jm009
November 24, 2017, 4:35pm
1
I just want to report, that I managed to run Nextcloud 12 on Tomcat 9.0.1 beta with this patch
jFastCGI:master
ā jm009:master
opened 12:56PM - 24 Nov 17 UTC
Based on what i wrote here: https://github.com/jFastCGI/jfastcgi/issues/23
- ā¦ Whitelist of allowed client headers instead of blacklist.
- Initialize SCRIPT_NAME and PATH_INFO environment variables independently from servlet mapping, by searching the php script file in the URL with a regular expression.
- Remove some logging on info level from FastCGIHandler.java.
Sorry for having all these changes in one single commit.
I have Nextcloud running with this patch :-)
Should I have asked, before replacing the blacklist for allowed client headers by a whitelist?
Sorry for my doing before talking :-(
I saw the whitelist approach in the Tomcat CGIServlet and to me it looks like a good solution. I prefer to be overcautious when it is about publicly accessible web stuff.
And something else: I had downloaded jfastcgi-2.1.jar from https://sourceforge.net/projects/jfastcgi/. It contains different java package names (JRebel?)
Thank you for jFastCGI!
By the way, my Nextcloud calendar (in fact about 10 calendars) now opens in the web browser in less than 20 seconds, instead of more than 30 seconds before. The test server is not very fast and limited to one CPU core. Mozilla 57 and Chromium probably also will load the page much faster then my Debian 9.2 Stretch Firefox 52.5. So there is much room for improvement. But anyway, I would like to bang my head against the wall, to have to wait so long for some hundred calendar entries to be displayed on my screen... I don't want to complain about Nextcloud. Nextcloud is great. demo.nextcloud.com runs much faster. So I definitely have to look for a better test server ;-) It's just the fact, that gigahertz processors and megabit Internet connections are not a guarantee, to allow fast data editing ;-)
More on how I run nextcloud:
https://help.nextcloud.com/t/success-nextcloud-runs-with-jfastcgi-on-tomcat-9-0-1-beta/23935
to jFastCGI.
I use the SSO & SAML module. So the http basic authentication prompt is created by Tomcat. That means non logged in users donāt get in touch with PHP.
web.xml:
<?xml version="1.0" encoding="ISO-8859-1"?>
<web-app xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
version="2.4">
<servlet>
<servlet-name>php-fpm</servlet-name>
<description>
Forward php to php7.0-fpm on port 9000.
</description>
<servlet-class>org.jfastcgi.servlet.FastCGIServlet</servlet-class>
<init-param>
<param-name>server-address</param-name>
<param-value>127.0.0.1:9000</param-value>
</init-param>
<!-- init-param> ... somehow not used ??? :-(
<param-name>connection-factory</param-name>
<param-value>org.jfastcgi.client.SingleConnectionFactory</param-value>
</init-param -->
</servlet>
<servlet-mapping>
<servlet-name>php-fpm</servlet-name>
<url-pattern>/index.php</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>php-fpm</servlet-name>
<url-pattern>/index.php/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>php-fpm</servlet-name>
<url-pattern>/ocs/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>php-fpm</servlet-name>
<url-pattern>/remote.php/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>php-fpm</servlet-name>
<url-pattern>/updater/index.php</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>index.php</welcome-file>
</welcome-file-list>
<security-constraint>
<display-name>Disable access to everything</display-name>
<web-resource-collection>
<web-resource-name>disable-everything</web-resource-name>
<url-pattern>/*</url-pattern>
</web-resource-collection>
<auth-constraint />
</security-constraint>
<security-constraint>
<display-name>Require login for all access</display-name>
<web-resource-collection>
<web-resource-name>protected</web-resource-name>
<url-pattern>/index.php</url-pattern>
<url-pattern>/index.php/*</url-pattern>
<url-pattern>/ocs/*</url-pattern>
<url-pattern>/remote.php/*</url-pattern>
<url-pattern>/core/*</url-pattern>
<url-pattern>/apps/*</url-pattern>
<url-pattern>/settings/*</url-pattern>
<url-pattern>/updater/index.php</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>validUser</role-name>
</auth-constraint>
<!-- user-data-constraint>
<transport-guarantee>CONFIDENTIAL</transport-guarantee>
</user-data-constraint -->
</security-constraint>
<security-role>
<role-name>validUser</role-name>
</security-role>
<login-config>
<auth-method>BASIC</auth-method>
<realm-name>Nextcloud Login</realm-name>
</login-config>
</web-app>
context.xml:
<?xml version="1.0" encoding="UTF-8"?>
<Context docBase="/var/www/default/nextcloud##120003" privileged="true">
<!-- Realm className="org.apache.catalina.realm.MemoryRealm" pathname="conf/git_protected-users.xml" -->
<Realm className="org.apache.catalina.realm.JDBCRealm"
connectionName="nextcloud" connectionPassword="xxx"
connectionURL="jdbc:postgresql://xxx:5432/nextcloud?ssl=true"
driverName="org.postgresql.Driver"
userTable="oc_users" userNameCol="uid" userCredCol="password"
userRoleTable="oc_group_user" roleNameCol="gid"
allRolesMode="authOnly">
<CredentialHandler className="de.greinerinformatik.NextcloudCredentialHandler" />
</Realm>
</Context>
NextcloudCredentialHandler.java
package de.greinerinformatik;
import org.apache.catalina.CredentialHandler;
import org.mindrot.jbcrypt.BCrypt;
public class NextcloudCredentialHandler implements CredentialHandler {
@Override
public boolean matches(String inputCredentials, String storedCredentials) {
// substring(5) = "1|$2y...". 2y is used by PHP, because there was a bug in
// the 2a implementation of BCRYPT
return BCrypt.checkpw(inputCredentials, "$2a" + storedCredentials.substring(5));
}
@Override
public String mutate(String inputCredentials) {
String s = BCrypt.hashpw(inputCredentials, BCrypt.gensalt(10).substring(3));
if (!s.startsWith("$2a$10$")) {
throw new RuntimeException("Unexpected result of BCrypt.gensalt: '" + s + "'.");
}
return "1|$2y" + s.substring(3);
}
}
can you please help, I have a apache tomcat 8.5.24 running in raspberry pi, and I have a java web application running in it and what I want is next cloud should also run in the same server. I have installed php 7.0 by using apt-get php7.0 and I have mysql server and client installed in the raspberry pi. And I made tomcat to run port 80.
jm009
March 26, 2018, 10:29am
3
The next step would probably be to install jFastCGIā¦
One needs to have some knowledge about Java Webapp related things, to get all this up and runningā¦
jm009
July 20, 2022, 12:49pm
4
Here is an update for Nextcloud version 24.
web.xml does not need to be changed, but I did some changes anyway (do not require authentication for plugin ressources - .css, .svg and .js files):
<?xml version="1.0" encoding="ISO-8859-1"?>
<web-app xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
version="2.4">
<servlet>
<servlet-name>default</servlet-name>
<servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>
<init-param>
<param-name>debug</param-name>
<param-value>0</param-value>
</init-param>
<init-param>
<param-name>listings</param-name>
<param-value>false</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet>
<servlet-name>php-cgi</servlet-name>
<description>
Forward php to php8.1-fpm on port 9000.
</description>
<servlet-class>org.jfastcgi.servlet.FastCGIServlet</servlet-class>
<load-on-startup>1</load-on-startup>
<init-param>
<param-name>allowed-headers-regex</param-name>
<param-value>ACCEPT[-0-9A-Z]{0,100}|AUTHORIZATION|CACHE-CONTROL|COOKIE|DEPTH|HOST|IF-[-0-9A-Z]{2,100}|OCS-APIREQUEST|REFERER|REQUESTTOKEN|USER-AGENT|X-FORWARDED-FOR|X-UPDATER-AUTH|DESTINATION|OVERWRITE|X-REQUESTED-WITH</param-value>
</init-param>
<init-param>
<param-name>server-address</param-name>
<param-value>[::1]:9000</param-value>
</init-param>
<init-param>
<param-name>keep-alive</param-name>
<param-value>true</param-value>
</init-param>
<init-param> <!-- ... somehow not used ??? :-( -->
<param-name>connection-factory</param-name>
<!-- param-value>org.jfastcgi.client.SingleConnectionFactory</param-value -->
<param-value>org.jfastcgi.client.PooledConnectionFactory</param-value>
</init-param>
<init-param> <!-- ... somehow not used ??? :-( -->
<param-name>connection-factory</param-name>
<!-- param-value>org.jfastcgi.client.SingleConnectionFactory</param-value -->
<param-value>org.jfastcgi.client.PooledConnectionFactory</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>/</url-pattern>
<!-- url-pattern>*.svg</url-pattern -->
</servlet-mapping>
<servlet-mapping>
<servlet-name>php-cgi</servlet-name>
<url-pattern>/index.php</url-pattern>
<url-pattern>/phpinfo.php</url-pattern>
<url-pattern>/index.php/*</url-pattern>
<url-pattern>/ocs/*</url-pattern>
<url-pattern>/remote.php/*</url-pattern>
<url-pattern>/updater/index.php</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>index.php</welcome-file>
</welcome-file-list>
<security-constraint>
<display-name>Disable access to everything</display-name>
<web-resource-collection>
<web-resource-name>disable-everything</web-resource-name>
<url-pattern>/*</url-pattern>
</web-resource-collection>
<auth-constraint />
</security-constraint>
<security-constraint>
<display-name>Require login for all access</display-name>
<web-resource-collection>
<web-resource-name>protected</web-resource-name>
<url-pattern>/index.php</url-pattern>
<url-pattern>/phpinfo.php</url-pattern>
<url-pattern>/index.php/*</url-pattern>
<url-pattern>/ocs/*</url-pattern>
<url-pattern>/remote.php/*</url-pattern>
<url-pattern>/core/*</url-pattern>
<url-pattern>/dist/*</url-pattern>
<url-pattern>/apps/*</url-pattern>
<url-pattern>/settings/*</url-pattern>
<url-pattern>/updater/index.php</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>validUser</role-name>
</auth-constraint>
<!-- user-data-constraint>
<transport-guarantee>CONFIDENTIAL</transport-guarantee>
</user-data-constraint -->
</security-constraint>
<security-constraint>
<display-name>Allow static files</display-name>
<web-resource-collection>
<web-resource-name>notProtected</web-resource-name>
<url-pattern>/core/css/*</url-pattern>
<url-pattern>/core/doc/*</url-pattern>
<url-pattern>/core/fonts/*</url-pattern>
<url-pattern>/core/js/*</url-pattern>
<url-pattern>/core/img/*</url-pattern>
<url-pattern>/core/l10n/*</url-pattern>
<url-pattern>/dist/*</url-pattern>
<url-pattern>/apps/activity/img/*</url-pattern>
<url-pattern>/apps/accessibility/l10n/*</url-pattern>
<url-pattern>/apps/calendar/css/*</url-pattern>
<url-pattern>/apps/calendar/img/*</url-pattern>
<url-pattern>/apps/calendar/l10n/*</url-pattern>
<url-pattern>/apps/contacts/css/*</url-pattern>
<url-pattern>/apps/contacts/img/*</url-pattern>
<url-pattern>/apps/contacts/l10n/*</url-pattern>
<url-pattern>/apps/dashboard/css/*</url-pattern>
<url-pattern>/apps/dashboard/img/*</url-pattern>
<url-pattern>/apps/dashboard/l10n/*</url-pattern>
<url-pattern>/apps/files/css/*</url-pattern>
<url-pattern>/apps/files/img/*</url-pattern>
<url-pattern>/apps/files/l10n/*</url-pattern>
<url-pattern>/apps/files_rightclick/css/*</url-pattern>
<url-pattern>/apps/files_rightclick/l10n/*</url-pattern>
<url-pattern>/apps/files_sharing/l10n/*</url-pattern>
<url-pattern>/apps/files_pdfviewer/js/*</url-pattern>
<url-pattern>/apps/files_versions/l10n/*</url-pattern>
<url-pattern>/apps/firstrunwizard/img/*</url-pattern>
<url-pattern>/apps/firstrunwizard/js/*</url-pattern>
<url-pattern>/apps/images/css/*</url-pattern>
<url-pattern>/apps/images/img/*</url-pattern>
<url-pattern>/apps/images/l10n/*</url-pattern>
<url-pattern>/apps/notifications/css/*</url-pattern>
<url-pattern>/apps/notifications/img/*</url-pattern>
<url-pattern>/apps/notifications/js/*</url-pattern>
<url-pattern>/apps/notifications/l10n/*</url-pattern>
<url-pattern>/apps/photos/img/*</url-pattern>
<url-pattern>/apps/settings/img/*</url-pattern>
<url-pattern>/apps/tasks/img/tasks.svg</url-pattern>
<url-pattern>/apps/theming/css/*</url-pattern>
<url-pattern>/apps/theming/img/*</url-pattern>
<url-pattern>/apps/theming/js/*</url-pattern>
<url-pattern>/apps/theming/l10n/*</url-pattern>
<url-pattern>/apps/text/js/*</url-pattern>
<url-pattern>/apps/text/l10n/*</url-pattern>
<url-pattern>/apps/user_status/css/*</url-pattern>
<url-pattern>/apps/user_status/img/*</url-pattern>
<url-pattern>/apps/user_status/l10n/*</url-pattern>
<url-pattern>/apps/viewer/l10n/*</url-pattern>
<url-pattern>/apps/viewer/js/*</url-pattern>
</web-resource-collection>
</security-constraint>
<security-role>
<role-name>validUser</role-name>
</security-role>
<login-config>
<auth-method>BASIC</auth-method>
<realm-name>Nextcloud Login</realm-name>
</login-config>
<resource-ref>
<description>Nextcloud DB Connection</description>
<res-ref-name>jdbc/nextcloudlogin</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
</resource-ref>
</web-app>
JDBCRealm will become deprecated, so we change to DataSourceRealm with a sql DataSource - context.xml :
<?xml version="1.0" encoding="UTF-8"?>
<Context docBase="/var/www/nextcloud##240001" privileged="true">
<Resources allowLinking="true"/>
<Resource name="jdbc/nextcloudlogin" auth="Container" type="javax.sql.DataSource"
maxTotal="150" maxIdle="50" maxWaitMillis="15000"
username="XXX" password="YYY" driverClassName="org.postgresql.Driver"
url="jdbc:postgresql://[zzz]:5432/nextcloud?sslmode=require"/>
<Realm className="org.apache.catalina.realm.DataSourceRealm"
localDataSource="true"
dataSourceName="jdbc/nextcloudlogin"
userTable="oc_users" userNameCol="uid" userCredCol="password"
userRoleTable="oc_group_user" roleNameCol="gid">
<CredentialHandler className="de.greinerinformatik.NextcloudCredentialHandler" />
</Realm>
</Context>
Nextcloud 24 uses Argon2 as hash function for passwords - NextcloudCredentialHandler.java :
package de.greinerinformatik;
import org.apache.catalina.CredentialHandler;
import de.mkammerer.argon2.Argon2;
import de.mkammerer.argon2.Argon2Factory;
//import java.util.logging.Logger;
import java.util.Hashtable;
public class NextcloudCredentialHandler implements CredentialHandler {
// Argon2i is quite slow (on purpose!) so do some caching of already confirmed logins:
private static Hashtable<String, Long> successfulLoginCache = new Hashtable<String, Long>();
@Override
public boolean matches(String inputCredentials, String storedCredentials) {
Argon2 argon2 = Argon2Factory.create(Argon2Factory.Argon2Types.ARGON2id);
int firstIndexOfPipe = storedCredentials.indexOf('|');
if (firstIndexOfPipe < 0 || firstIndexOfPipe > 3) {
throw new RuntimeException("Unknown format of storedCredentials.");
}
String inputCredentialsNoTab = inputCredentials.replace("\\", "\\\\").replace("\t", "\\t");
String storedCredentialsNoTab = storedCredentials.replace("\\", "\\\\").replace("\t", "\\t");
String hashTableKey = inputCredentialsNoTab + "\t" + storedCredentialsNoTab;
Long lastCheckTime = successfulLoginCache.get(hashTableKey);
if (lastCheckTime != null && lastCheckTime > System.currentTimeMillis() - 180000) { // less than three minutes ago
return true;
}
boolean loginOk = argon2.verify(storedCredentials.substring(firstIndexOfPipe + 1), inputCredentials.toCharArray());
if (loginOk) {
successfulLoginCache.put(hashTableKey, System.currentTimeMillis());
return true;
}
else {
return false;
}
}
@Override
public String mutate(String inputCredentials) {
Argon2 argon2 = Argon2Factory.create(Argon2Factory.Argon2Types.ARGON2id);
int iterations = 4;
int memoryInKB = 65536;
int parallelism = 1; // Number of threads and compute lanes
return "3|" + argon2.hash(iterations, memoryInKB, parallelism, inputCredentials.toCharArray());
}
}