Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions server/configs/application.properties
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,9 @@ mail.smtpUser=@@smtpUser@@
#mail.smtpStartTlsEnable=@@smtpStartTlsEnable@@
#mail.smtpSocketFactoryClass=@@smtpSocketFactoryClass@@
#mail.smtpAuth=@@smtpAuth@@
#mail.smtpConnectionTimeout=@@smtpConnectionTimeout@@
#mail.smtpTimeout=@@smtpTimeout@@
#mail.smtpWriteTimeout=@@smtpWriteTimeout@@

## Microsoft Graph configuration - if using this, comment out SMTP configuration
#mail.graph.tenantId=@@graphTenantId@@
Expand Down
3 changes: 3 additions & 0 deletions server/configs/webapps/embedded/config/application.properties
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,9 @@ mail.smtpUser=Anonymous
#mail.smtpStartTlsEnable=@@smtpStartTlsEnable@@
#mail.smtpSocketFactoryClass=@@smtpSocketFactoryClass@@
#mail.smtpAuth=@@smtpAuth@@
#mail.smtpConnectionTimeout=@@smtpConnectionTimeout@@
#mail.smtpTimeout=@@smtpTimeout@@
#mail.smtpWriteTimeout=@@smtpWriteTimeout@@

####################################################################################
## Microsoft Graph Configuration - if using this, comment out SMTP configuration
Expand Down
188 changes: 111 additions & 77 deletions server/embedded/src/org/labkey/embedded/LabKeyServer.java
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,7 @@ public class LabKeyServer
private static final String TERMINATE_ON_STARTUP_FAILURE = "terminateOnStartupFailure";
private static final String JARS_TO_SKIP = "tomcat.util.scan.StandardJarScanFilter.jarsToSkip";
private static final String JARS_TO_SCAN = "tomcat.util.scan.StandardJarScanFilter.jarsToScan";
private static final String SERVER_GUID = "serverGUID";
public static final String SERVER_GUID_PARAMETER_NAME = "org.labkey.mothership." + SERVER_GUID;
public static final String SERVER_SSL_KEYSTORE = "org.labkey.serverSslKeystore";
public static final String CUSTOM_LOG4J_CONFIG = "org.labkey.customLog4JConfig";
public static final String CORS_PREFIX = "cors.";

static final String MAX_TOTAL_CONNECTIONS_DEFAULT = "50";
static final String MAX_IDLE_DEFAULT = "10";
static final String MAX_WAIT_MILLIS_DEFAULT = "120000";
Expand Down Expand Up @@ -108,83 +104,88 @@ public static void main(String[] args)
""";

application.setDefaultProperties(new HashMap<>()
{{
{{
// GitHub Issue 796: JSON logging stopped after Tomcat/Spring update
// Propagate log4j configuration to Spring Boot config, which is necessary with Spring Boot 4.x
String log4JConfig = System.getProperty("log4j.configurationFile");
if (log4JConfig != null)
{
String[] log4JConfigParts = log4JConfig.split(",");
if (log4JConfigParts.length > 0)
{
if ("log4j2.xml".equals(log4JConfigParts[0]))
{
// Assume this is the one packaged with our embedded build and on the classpath
put("logging.config", "classpath:log4j2.xml");
}
else
{
put("logging.config", log4JConfigParts[0]);
}
if (log4JConfigParts.length > 1)
{
// Propagate log4j configuration to Spring Boot config, which is necessary with Spring Boot 4.x
String log4JConfig = System.getProperty("log4j.configurationFile");
if (log4JConfig != null)
{
String[] log4JConfigParts = log4JConfig.split(",");
if (log4JConfigParts.length > 0)
{
if ("log4j2.xml".equals(log4JConfigParts[0]))
{
// Assume this is the one packaged with our embedded build and on the classpath
put("logging.config", "classpath:log4j2.xml");
}
else
{
put("logging.config", log4JConfigParts[0]);
}
if (log4JConfigParts.length > 1)
{
put("logging.log4j2.config.override", String.join(",", Arrays.asList(log4JConfigParts).subList(1, log4JConfigParts.length)));
}
}
}
}
}
}

put("server.tomcat.basedir", ".");
put("server.tomcat.accesslog.directory", logHome);
put("server.tomcat.basedir", ".");
put("server.tomcat.accesslog.directory", logHome);

// Boost limits imposed by Tomcat v10.1.42
put("server.tomcat.max-part-count", 500);
put("server.tomcat.max-part-header-size", 1024); // GitHub Issue 161: LKS insert forms can't handle long file field names
put("server.tomcat.max-connections", 250);
// Boost limits imposed by Tomcat v10.1.42
put("server.tomcat.max-part-count", 500);
put("server.tomcat.max-part-header-size", 1024); // GitHub Issue 161: LKS insert forms can't handle long file field names
put("server.tomcat.max-connections", 250);
// Boost limit back to Tomcat 10 level
put("server.tomcat.max-parameter-count", 10_000);

// Enable HTTP compression for response content
put("server.compression.enabled", "true");
// Spring Boot compresses HTML, JSON and other types by default, but not TSV, CSV, or SVG.
// We have to duplicate the defaults and add those types
put("server.compression.mime-types", "text/html,text/xml,text/plain,text/css,text/javascript,application/javascript,application/json,application/xml,text/tab-separated-values,text/csv,image/svg+xml");

put("server.tomcat.accesslog.enabled", "true");
put("server.tomcat.accesslog.pattern", "%h %l %u %t \"%r\" %s %b %D %S %I \"%{Referer}i\" \"%{User-Agent}i\" %{LABKEY.username}s %{X-Forwarded-For}i");
put("jsonaccesslog.pattern", "%h %t %m %U %s %b %D %S \"%{Referer}i\" \"%{User-Agent}i\" %{LABKEY.username}s %{X-Forwarded-For}i");

// Issue 52415: Omit stack traces from Tomcat error pages by default, but propagate error messages
put("server.error.include-stacktrace", "never");
put("server.error.include-message", "always");

put("csp.enforce", enforceCsp);
put("csp.report", reportCsp);

// GitHub Issue 692: Stop using CBC in HTTPS ciphers
// These settings configure HTTPS. Admins must opt in with additional settings
// in application.properties, like the key store. Without those other settings,
// HTTP-only startup fails unless "server.ssl.enabled" is explicitly set to false here
put("server.ssl.enabled", "false");
put("server.ssl.protocol", "TLS");
put("server.ssl.enabled-protocols", "TLSv1.3,TLSv1.2");
// Use explicit JSSE cipher suite names to avoid CBC-mode suites
put("server.ssl.ciphers",
String.join(",",
// TLS 1.3
"TLS_AES_256_GCM_SHA384",
"TLS_AES_128_GCM_SHA256",
"TLS_CHACHA20_POLY1305_SHA256",
// TLS 1.2 (AEAD only, no CBC)
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256",
"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
"TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256"
)
);
put("server.ssl.use-cipher-suites-order", "true");
}}
put("server.tomcat.max-parameter-count", 10_000);

// Enable HTTP compression for response content
put("server.compression.enabled", "true");
// Spring Boot compresses HTML, JSON and other types by default, but not TSV, CSV, or SVG.
// We have to duplicate the defaults and add those types
put("server.compression.mime-types", "text/html,text/xml,text/plain,text/css,text/javascript,application/javascript,application/json,application/xml,text/tab-separated-values,text/csv,image/svg+xml");

put("server.tomcat.accesslog.enabled", "true");
put("server.tomcat.accesslog.pattern", "%h %l %u %t \"%r\" %s %b %D %S %I \"%{Referer}i\" \"%{User-Agent}i\" %{LABKEY.username}s %{X-Forwarded-For}i");
put("jsonaccesslog.pattern", "%h %t %m %U %s %b %D %S \"%{Referer}i\" \"%{User-Agent}i\" %{LABKEY.username}s %{X-Forwarded-For}i");

// Issue 52415: Omit stack traces from Tomcat error pages by default, but propagate error messages
put("server.error.include-stacktrace", "never");
put("server.error.include-message", "always");

put("csp.enforce", enforceCsp);
put("csp.report", reportCsp);

// GitHub Issue 692: Stop using CBC in HTTPS ciphers
// These settings configure HTTPS. Admins must opt in with additional settings
// in application.properties, like the key store. Without those other settings,
// HTTP-only startup fails unless "server.ssl.enabled" is explicitly set to false here
put("server.ssl.enabled", "false");
put("server.ssl.protocol", "TLS");
put("server.ssl.enabled-protocols", "TLSv1.3,TLSv1.2");
// Use explicit JSSE cipher suite names to avoid CBC-mode suites
put("server.ssl.ciphers",
String.join(",",
// TLS 1.3
"TLS_AES_256_GCM_SHA384",
"TLS_AES_128_GCM_SHA256",
"TLS_CHACHA20_POLY1305_SHA256",
// TLS 1.2 (AEAD only, no CBC)
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256",
"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
"TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256"
)
);
put("server.ssl.use-cipher-suites-order", "true");

// GitHub Issue #1416 - default values for SMTP timeouts
put("mail.smtp.connectiontimeout", 10 * 1000);
put("mail.smtp.timeout", 60 * 1000);
put("mail.smtp.writetimeout", 60 * 1000);
}}
);
application.setBannerMode(Banner.Mode.OFF);
application.run(args);
Expand Down Expand Up @@ -818,6 +819,9 @@ public static class MailProperties
private String smtpStartTlsEnable;
private String smtpSocketFactoryClass;
private String smtpAuth;
private String smtpConnectionTimeout = "10000"; // Default to 10 seconds
private String smtpTimeout = "60000"; // Default to 60 seconds
private String smtpWriteTimeout = "60000"; // Default to 60 seconds

public String getSmtpHost()
{
Expand Down Expand Up @@ -898,6 +902,36 @@ public void setSmtpAuth(String smtpAuth)
{
this.smtpAuth = smtpAuth;
}

public String getSmtpConnectionTimeout()
{
return smtpConnectionTimeout;
}

public void setSmtpConnectionTimeout(String smtpConnectionTimeout)
{
this.smtpConnectionTimeout = smtpConnectionTimeout;
}

public String getSmtpTimeout()
{
return smtpTimeout;
}

public void setSmtpTimeout(String smtpTimeout)
{
this.smtpTimeout = smtpTimeout;
}

public String getSmtpWriteTimeout()
{
return smtpWriteTimeout;
}

public void setSmtpWriteTimeout(String smtpWriteTimeout)
{
this.smtpWriteTimeout = smtpWriteTimeout;
}
}

@Configuration
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import org.apache.logging.log4j.Logger;
import org.apache.tomcat.util.descriptor.web.ContextResource;
import org.labkey.bootstrap.ConfigException;
import org.labkey.embedded.LabKeyServer.MailProperties;
import org.springframework.boot.tomcat.TomcatWebServer;
import org.springframework.boot.tomcat.servlet.TomcatServletWebServerFactory;
import org.springframework.boot.web.servlet.ServletContextInitializer;
Expand All @@ -39,14 +40,15 @@
import java.util.Map;
import java.util.Objects;

import static org.labkey.embedded.LabKeyServer.CORS_PREFIX;
import static org.labkey.embedded.LabKeyServer.CUSTOM_LOG4J_CONFIG;
import static org.labkey.embedded.LabKeyServer.SERVER_GUID_PARAMETER_NAME;
import static org.labkey.embedded.LabKeyServer.SERVER_SSL_KEYSTORE;

class LabKeyTomcatServletWebServerFactory extends TomcatServletWebServerFactory
{
private static final Logger LOG = LogManager.getLogger(LabKeyTomcatServletWebServerFactory.class);
private static final String SERVER_GUID = "serverGUID";
private static final String SERVER_GUID_PARAMETER_NAME = "org.labkey.mothership." + SERVER_GUID;
private static final String SERVER_SSL_KEYSTORE = "org.labkey.serverSslKeystore";
private static final String CUSTOM_LOG4J_CONFIG = "org.labkey.customLog4JConfig";
private static final String CORS_PREFIX = "cors.";

private final LabKeyServer _server;

public LabKeyTomcatServletWebServerFactory(LabKeyServer server)
Expand Down Expand Up @@ -423,39 +425,26 @@ private String getPropValue(Map<Integer, String> propValues, Integer resourceKey
private void addSmtpProperties(StandardContext context)
{
// Get session/mail properties
LabKeyServer.MailProperties mailProps = _server.smtpSource();
MailProperties mailProps = _server.smtpSource();

addSmtpProperty(context, "host", mailProps.getSmtpHost());
addSmtpProperty(context, "user", mailProps.getSmtpUser());
addSmtpProperty(context, "port", mailProps.getSmtpPort());
addSmtpProperty(context, "from", mailProps.getSmtpFrom());
addSmtpProperty(context, "password", mailProps.getSmtpPassword());
addSmtpProperty(context, "starttls.enable", mailProps.getSmtpStartTlsEnable());
addSmtpProperty(context, "socketFactory.class", mailProps.getSmtpSocketFactoryClass());
addSmtpProperty(context, "auth", mailProps.getSmtpAuth());
addSmtpProperty(context, "connectiontimeout", mailProps.getSmtpConnectionTimeout());
addSmtpProperty(context, "timeout", mailProps.getSmtpTimeout());
addSmtpProperty(context, "writetimeout", mailProps.getSmtpWriteTimeout());
}

if (mailProps.getSmtpHost() != null)
{
context.addParameter("mail.smtp.host", mailProps.getSmtpHost());
}
if (mailProps.getSmtpUser() != null)
{
context.addParameter("mail.smtp.user", mailProps.getSmtpUser());
}
if (mailProps.getSmtpPort() != null)
{
context.addParameter("mail.smtp.port", mailProps.getSmtpPort());
}
if (mailProps.getSmtpFrom() != null)
{
context.addParameter("mail.smtp.from", mailProps.getSmtpFrom());
}
if (mailProps.getSmtpPassword() != null)
{
context.addParameter("mail.smtp.password", mailProps.getSmtpPassword());
}
if (mailProps.getSmtpStartTlsEnable() != null)
{
context.addParameter("mail.smtp.starttls.enable", mailProps.getSmtpStartTlsEnable());
}
if (mailProps.getSmtpSocketFactoryClass() != null)
{
context.addParameter("mail.smtp.socketFactory.class", mailProps.getSmtpSocketFactoryClass());
}
if (mailProps.getSmtpAuth() != null)
private void addSmtpProperty(StandardContext context, String name, String value)
{
if (value != null)
{
context.addParameter("mail.smtp.auth", mailProps.getSmtpAuth());
context.addParameter("mail.smtp." + name, value);
}
}

Expand Down