This is an automated email from the ASF dual-hosted git repository. aleks pushed a commit to branch fix-1.6.0 in repository https://gitbox.apache.org/repos/asf/fineract.git
commit db2354fa5c5421c0962f469bf98c7b51eb8a8df5 Author: Aleks <[email protected]> AuthorDate: Mon Jan 17 01:59:49 2022 +0100 FINERACT-1490: SSL configuration based on application.properties --- README.md | 13 +++ .../org/apache/fineract/ServerApplication.java | 3 - .../boot/EmbeddedTomcatWithSSLConfiguration.java | 112 --------------------- .../core/config/OAuth2SecurityConfig.java | 13 ++- .../infrastructure/core/config/SecurityConfig.java | 12 ++- .../src/main/resources/application.properties | 18 ++++ .../EmbeddedTomcatWithSSLConfigurationTest.java | 56 ----------- .../src/test/resources/application-test.properties | 8 ++ integration-tests/build.gradle | 1 + 9 files changed, 61 insertions(+), 175 deletions(-) diff --git a/README.md b/README.md index 767629fa2..eb8ca68de 100644 --- a/README.md +++ b/README.md @@ -186,6 +186,19 @@ Please check `application.properties` to see which connection pool settings can NOTE: we'll keep backwards compatibility until one of the next releases to ensure that things are working as expected. Environment variables prefixed `fineract_tenants_*` can still be used to configure the database connection, but we strongly encourage using `FINERACT_HIKARI_*` with more options. +SSL configuration +================= + +By default SSL is enabled, but all SSL related properties are now tunable. SSL can be turned off by setting the environment variable `FINERACT_SERVER_SSL_ENABLED` to false. If you do that then please make sure to also change the server port to `8080` via the variable `FINERACT_SERVER_PORT`, just for the sake of keeping the conventions. +You can choose now easily a different SSL keystore by setting `FINERACT_SERVER_SSL_KEY_STORE` with a path to a different (not embedded) keystore. The password can be set via `FINERACT_SERVER_SSL_KEY_STORE_PASSWORD`. See the `application.properties` file and the latest Spring Boot documentation (https://docs.spring.io/spring-boot/docs/current/reference/html/application-properties.html) for more details. + + +Tomcat configuration +==================== + +Please refer to the `application.properties` and the official Spring Boot documentation (https://docs.spring.io/spring-boot/docs/current/reference/html/application-properties.html) on how to do performance tuning for Tomcat. Note: you can set now the acceptable form POST size (default is 2MB) via environment variable `FINERACT_SERVER_TOMCAT_MAX_HTTP_FORM_POST_SIZE`. + + Instructions to run on Kubernetes ================================= diff --git a/fineract-provider/src/main/java/org/apache/fineract/ServerApplication.java b/fineract-provider/src/main/java/org/apache/fineract/ServerApplication.java index e2063518e..f567a6c2a 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/ServerApplication.java +++ b/fineract-provider/src/main/java/org/apache/fineract/ServerApplication.java @@ -20,12 +20,10 @@ package org.apache.fineract; import java.io.IOException; import org.apache.fineract.infrastructure.core.boot.AbstractApplicationConfiguration; -import org.apache.fineract.infrastructure.core.boot.EmbeddedTomcatWithSSLConfiguration; import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; import org.springframework.context.ConfigurableApplicationContext; -import org.springframework.context.annotation.Import; /** * Fineract main() application which launches Fineract in an embedded Tomcat HTTP (using Spring Boot). @@ -42,7 +40,6 @@ import org.springframework.context.annotation.Import; public class ServerApplication extends SpringBootServletInitializer { - @Import({ EmbeddedTomcatWithSSLConfiguration.class }) private static class Configuration extends AbstractApplicationConfiguration {} @Override diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/boot/EmbeddedTomcatWithSSLConfiguration.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/boot/EmbeddedTomcatWithSSLConfiguration.java deleted file mode 100644 index 8f5d26c05..000000000 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/boot/EmbeddedTomcatWithSSLConfiguration.java +++ /dev/null @@ -1,112 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.fineract.infrastructure.core.boot; - -import java.io.File; -import java.io.IOException; -import java.net.URL; -import org.apache.catalina.connector.Connector; -import org.apache.commons.io.FileUtils; -import org.apache.coyote.http11.Http11NioProtocol; -import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory; -import org.springframework.boot.web.servlet.server.ServletWebServerFactory; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.core.io.ClassPathResource; -import org.springframework.core.io.Resource; - -@Configuration -public class EmbeddedTomcatWithSSLConfiguration { - - // https://docs.spring.io/spring-boot/docs/2.2.6.RELEASE/reference/html/howto.html#howto-enable-multiple-connectors-in-tomcat - - @Bean - public ServletWebServerFactory servletContainer() { - TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory(); - tomcat.setContextPath(getContextPath()); - tomcat.addAdditionalTomcatConnectors(createSslConnector()); - return tomcat; - } - - private String getContextPath() { - return "/fineract-provider"; - } - - protected Connector createSslConnector() { - Connector connector = new Connector("org.apache.coyote.http11.Http11NioProtocol"); - Http11NioProtocol protocol = (Http11NioProtocol) connector.getProtocolHandler(); - try { - File keystore = getFile(getKeystore()); - connector.setScheme("https"); - connector.setSecure(true); - connector.setPort(getHTTPSPort()); - protocol.setSSLEnabled(true); - protocol.setKeystoreFile(keystore.getAbsolutePath()); - protocol.setKeystorePass(getKeystorePass()); - return connector; - } catch (IOException ex) { - throw new IllegalStateException("can't access keystore: [" + "keystore" + "] or truststore: [" + "keystore" + "]", ex); - } - } - - protected int getHTTPSPort() { - // TODO This shouldn't be hard-coded here, but configurable - return 8443; - } - - protected String getKeystorePass() { - return "openmf"; - } - - protected Resource getKeystore() { - return new ClassPathResource("/keystore.jks"); - } - - public File getFile(Resource resource) throws IOException { - try { - return resource.getFile(); - } catch (IOException e) { - // Uops.. OK, try again (below) - } - - try { - URL url = resource.getURL(); - /** - * // If this creates filenames that are too long on Win, // then could just use resource.getFilename(), // - * even though not unique, real risk prob. min.bon String tempDir = System.getProperty("java.io.tmpdir"); - * tempDir = tempDir + "/" + getClass().getSimpleName() + "/"; String path = url.getPath(); String uniqName - * = path.replace("file:/", "").replace('!', '_'); String tempFullPath = tempDir + uniqName; - **/ - // instead of File.createTempFile(prefix?, suffix?); - File targetFile = new File(resource.getFilename()); - long len = resource.contentLength(); - if (!targetFile.exists() || targetFile.length() != len) { // Only - // copy - // new - // files - FileUtils.copyURLToFile(url, targetFile); - } - return targetFile; - } catch (IOException e) { - // Uops.. erm, give up: - throw new IOException("Cannot obtain a File for Resource: " + resource.toString(), e); - } - - } -} diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/config/OAuth2SecurityConfig.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/config/OAuth2SecurityConfig.java index 32202a67b..6266d1eab 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/config/OAuth2SecurityConfig.java +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/config/OAuth2SecurityConfig.java @@ -30,6 +30,7 @@ import org.apache.fineract.infrastructure.security.service.TenantAwareJpaPlatfor import org.apache.fineract.infrastructure.security.vote.SelfServiceUserAccessVote; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.autoconfigure.web.ServerProperties; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -72,6 +73,9 @@ public class OAuth2SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private TenantAwareJpaPlatformUserDetailsService userDetailsService; + @Autowired + private ServerProperties serverProperties; + private static final JwtGrantedAuthoritiesConverter jwtGrantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter(); @Override @@ -97,8 +101,13 @@ public class OAuth2SecurityConfig extends WebSecurityConfigurerAdapter { .sessionCreationPolicy(SessionCreationPolicy.STATELESS) // .and() // .addFilterAfter(tenantAwareTenantIdentifierFilter, SecurityContextPersistenceFilter.class) // - .addFilterAfter(twoFactorAuthenticationFilter, BasicAuthenticationFilter.class) // - .requiresChannel(channel -> channel.antMatchers("/api/**").requiresSecure()); + .addFilterAfter(twoFactorAuthenticationFilter, BasicAuthenticationFilter.class); // + + if (serverProperties.getSsl().isEnabled()) { + http.requiresChannel(channel -> channel.antMatchers("/api/**").requiresSecure()); + } else { + http.requiresChannel(channel -> channel.antMatchers("/api/**").requiresInsecure()); + } } @Bean diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/config/SecurityConfig.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/config/SecurityConfig.java index 8fac76759..ed9dce568 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/config/SecurityConfig.java +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/config/SecurityConfig.java @@ -24,6 +24,7 @@ import org.apache.fineract.infrastructure.security.filter.TwoFactorAuthenticatio import org.apache.fineract.infrastructure.security.service.TenantAwareJpaPlatformUserDetailsService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.autoconfigure.web.ServerProperties; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -52,6 +53,9 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private TwoFactorAuthenticationFilter twoFactorAuthenticationFilter; + @Autowired + private ServerProperties serverProperties; + @Override protected void configure(HttpSecurity http) throws Exception { @@ -74,9 +78,13 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter { .sessionCreationPolicy(SessionCreationPolicy.STATELESS) // .and() // .addFilterAfter(tenantAwareBasicAuthenticationFilter(), SecurityContextPersistenceFilter.class) // - .addFilterAfter(twoFactorAuthenticationFilter, BasicAuthenticationFilter.class) // - .requiresChannel(channel -> channel.antMatchers("/api/**").requiresSecure()); + .addFilterAfter(twoFactorAuthenticationFilter, BasicAuthenticationFilter.class); // + if (serverProperties.getSsl().isEnabled()) { + http.requiresChannel(channel -> channel.antMatchers("/api/**").requiresSecure()); + } else { + http.requiresChannel(channel -> channel.antMatchers("/api/**").requiresInsecure()); + } } @Bean diff --git a/fineract-provider/src/main/resources/application.properties b/fineract-provider/src/main/resources/application.properties index 92b6f4eb0..bbb251dd0 100644 --- a/fineract-provider/src/main/resources/application.properties +++ b/fineract-provider/src/main/resources/application.properties @@ -46,6 +46,24 @@ management.endpoints.web.exposure.include=health,info # FINERACT-914 server.forward-headers-strategy=framework +server.port=${FINERACT_SERVER_PORT:8443} +server.servlet.context-path=${FINERACT_SERVER_SERVLET_CONTEXT_PATH:/fineract-provider} +server.compression.enabled=${FINERACT_SERVER_COMPRESSION_ENABLED:true} + +server.ssl.enabled=${FINERACT_SERVER_SSL_ENABLED:true} +server.ssl.protocol=TLS +#server.ssl.ciphers=${FINERACT_SERVER_SSL_CIPHERS:TLS_RSA_WITH_AES_128_CBC_SHA256} +#server.ssl.enabled-protocols=${FINERACT_SERVER_SSL_PROTOCOLS:TLSv1.2} +server.ssl.key-store=${FINERACT_SERVER_SSL_KEY_STORE:classpath:keystore.jks} +server.ssl.key-store-password=${FINERACT_SERVER_SSL_KEY_STORE_PASSWORD:openmf} + +server.tomcat.accept-count=${FINERACT_SERVER_TOMCAT_ACCEPT_COUNT:100} +server.tomcat.accesslog.enabled=${FINERACT_SERVER_TOMCAT_ACCESSLOG_ENABLED:false} +server.tomcat.max-connections=${FINERACT_SERVER_TOMCAT_MAX_CONNECTIONS:8192} +server.tomcat.max-http-form-post-size=${FINERACT_SERVER_TOMCAT_MAX_HTTP_FORM_POST_SIZE:2MB} +server.tomcat.max-keep-alive-requests=${FINERACT_SERVER_TOMCAT_MAX_KEEP_ALIVE_REQUESTS:100} +server.tomcat.threads.max=${FINERACT_SERVER_TOMCAT_THREADS_MAX:200} +server.tomcat.threads.min-spare=${FINERACT_SERVER_TOMCAT_THREADS_MIN_SPARE:10} # OAuth authorisation server endpoint spring.security.oauth2.resourceserver.jwt.issuer-uri=http://localhost:9000/auth/realms/fineract diff --git a/fineract-provider/src/test/java/org/apache/fineract/infrastructure/core/boot/tests/EmbeddedTomcatWithSSLConfigurationTest.java b/fineract-provider/src/test/java/org/apache/fineract/infrastructure/core/boot/tests/EmbeddedTomcatWithSSLConfigurationTest.java deleted file mode 100644 index b16f7d3be..000000000 --- a/fineract-provider/src/test/java/org/apache/fineract/infrastructure/core/boot/tests/EmbeddedTomcatWithSSLConfigurationTest.java +++ /dev/null @@ -1,56 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.fineract.infrastructure.core.boot.tests; - -import static org.junit.jupiter.api.Assertions.assertTrue; - -import java.io.File; -import java.io.IOException; - -import org.apache.fineract.infrastructure.core.boot.EmbeddedTomcatWithSSLConfiguration; -import org.junit.jupiter.api.Test; -import org.springframework.core.io.ClassPathResource; -import org.springframework.core.io.Resource; - -public class EmbeddedTomcatWithSSLConfigurationTest { - - @Test - public void testGetFileWithFileResource() throws IOException { - // Test class probably isn't in a JAR - checkClassResource(getClass()); - } - - @Test - public void testGetFileWithClasspathResource() throws IOException { - // Spring Resource class probably is in a JAR - File f1 = checkClassResource(Resource.class); - f1.delete(); - checkClassResource(Resource.class); - } - - protected File checkClassResource(Class<?> clazz) throws IOException { - String testClasspathResourcePath = clazz.getCanonicalName().replace('.', '/') + ".class"; - Resource r = new ClassPathResource(testClasspathResourcePath); - File f = new EmbeddedTomcatWithSSLConfiguration().getFile(r); - assertTrue(f.exists()); - f = new EmbeddedTomcatWithSSLConfiguration().getFile(r); - assertTrue(f.exists()); - return f; - } -} diff --git a/fineract-provider/src/test/resources/application-test.properties b/fineract-provider/src/test/resources/application-test.properties index 6a6f7654c..b77894796 100644 --- a/fineract-provider/src/test/resources/application-test.properties +++ b/fineract-provider/src/test/resources/application-test.properties @@ -46,6 +46,14 @@ management.endpoints.web.exposure.include=health,info # FINERACT-914 server.forward-headers-strategy=framework +server.port=8443 +server.servlet.context-path=/fineract-provider +server.compression.enabled=true + +server.ssl.enabled=true +server.ssl.protocol=TLS +server.ssl.key-store=keystore.jks +server.ssl.key-store-password=openmf spring.datasource.hikari.driverClassName=org.mariadb.jdbc.Driver spring.datasource.hikari.jdbcUrl=jdbc:mariadb://localhost:3306/fineract_tenants diff --git a/integration-tests/build.gradle b/integration-tests/build.gradle index 35908694c..4d4cfb827 100644 --- a/integration-tests/build.gradle +++ b/integration-tests/build.gradle @@ -38,6 +38,7 @@ cargo { } local { + logLevel = 'medium' installer { installConfiguration = configurations.tomcat downloadDir = file("$buildDir/download")
