I am using an internal encryption library which encrypts my config file at the run time. Now in order to be able to use this library, my config file must be outside my JAR.
Therefore, I am using an external config file who's path is set in the environment variable.
When I try to run my springboot application through IntelliJ IDE it runs flawlessly. All I did was set the envrionment variable in Intellij'srun configuration. However, when I try to run it through gradle bootRun it complains
Web server failed to start. Port 8080 was already in use.
[main] WARN org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext - Exception encountered during context initialization - cancelling refresh attempt: org.springframework.context.ApplicationContextException: Failed to start bean 'webServerStartStop'; nested exception is org.springframework.boot.web.server.PortInUseException: Port 8080 is already in use
The command that I am using is ./gradlew bootRun
. I have removed the application.properties file from my resources folder and have it in some other path pointed by the environment variable. In my code, I am using the decryption library to read the config manually. I'm not supplying the config during the build but only reading the config path inside the code.
Sample config for DB:
@Configuration
class JpaConfig {
@Bean
public DataSource getDataSource() throws ProtectConfigInitException {
EncryptedProperties protectedConfig = new EncryptedProperties(System.getenv("CONFIG_FILE_PATH"));
String dbUrl = protectedConfig.getProperty("spring.datasource.url");
String dbDriverClassName = protectedConfig.getProperty("spring.datasource.driver-class-name");
String dbUsername = protectedConfig.getProperty("spring.datasource.username");
String dbPassword = protectedConfig.getProperty("spring.datasource.password");
return DataSourceBuilder.create()
.driverClassName(dbDriverClassName)
.url(dbUrl)
.username(dbUsername)
.password(dbPassword)
.build();
}
}
Sample SSL Config:
@Component
@Log
public class SSLConfig {
@Autowired
Environment env;
@PostConstruct
public void configureSSL() {
try {
EncryptedProperties protectedConfig = new EncryptedProperties(System.getenv("CONFIG_FILE_PATH"));
String keyStore = protectedConfig.getProperty("server.ssl.key-store");
String keyStorePassword = protectedConfig.getProperty("server.ssl.key-store-password");
String keyStoreType = protectedConfig.getProperty("server.ssl.key-store-type");
String keyStoreAlias = protectedConfig.getProperty("server.ssl.key-alias");
String sslKeyPassword = protectedConfig.getProperty("server.ssl.key-password");
System.setProperty("server.ssl.key-store", keyStore);
System.setProperty("server.ssl.key-store-password", keyStorePassword);
System.setProperty("server.ssl.key-store-type", keyStoreType);
System.setProperty("server.ssl.key-alias", keyStoreAlias);
System.setProperty("server.ssl.key-password", sslKeyPassword);
}catch(Exception e){
e.printStackTrace();
}
}
}
@Configuration
public class WebServerConfig {
@Bean
public ServletWebServerFactory getTomcatServletWebServerFactory() {
final var serverFactory = new TomcatServletWebServerFactory() {
@Override
protected void postProcessContext(Context context) {
final var securityConstraint = new SecurityConstraint();
securityConstraint.setUserConstraint("CONFIDENTIAL");
final var collection = new SecurityCollection();
collection.addPattern("/*");
securityConstraint.addCollection(collection);
context.addConstraint(securityConstraint);
}
};
serverFactory.addAdditionalTomcatConnectors(getHttpConnector());
return serverFactory;
}
private Connector getHttpConnector() {
final var connector = new Connector(TomcatServletWebServerFactory.DEFAULT_PROTOCOL);
connector.setScheme("http");
connector.setPort(8080);
connector.setSecure(false);
connector.setRedirectPort(8443);
return connector;
}
}
If I try to supply spring.config.location through commandline with the gradle boot run command, it complains of incorrect keystore password.
Here is the command I tried:
./gradlew bootRun -Dspring.config.location=/Users/myName/myProject/MyProjRoot/external.properties
Unable to start embedded Tomcat server
at org.springframework.context.support.DefaultLifecycleProcessor.doStart(DefaultLifecycleProcessor.java:185)
at org.springframework.context.support.DefaultLifecycleProcessor.access$200(DefaultLifecycleProcessor.java:53)
at org.springframework.context.support.DefaultLifecycleProcessor$LifecycleGroup.start(DefaultLifecycleProcessor.java:360)
at org.springframework.context.support.DefaultLifecycleProcessor.startBeans(DefaultLifecycleProcessor.java:158)
at org.springframework.context.support.DefaultLifecycleProcessor.onRefresh(DefaultLifecycleProcessor.java:122)
at org.springframework.context.support.AbstractApplicationContext.finishRefresh(AbstractApplicationContext.java:895)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:554)
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:143)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:758)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:750)
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:397)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:315)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1237)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1226)
at com.myApp.main(myAppApplication.java:20)
Caused by: org.springframework.boot.web.server.WebServerException: Unable to start embedded Tomcat server
at org.springframework.boot.web.embedded.tomcat.TomcatWebServer.start(TomcatWebServer.java:229)
at org.springframework.boot.web.servlet.context.WebServerStartStopLifecycle.start(WebServerStartStopLifecycle.java:43)
at org.springframework.context.support.DefaultLifecycleProcessor.doStart(DefaultLifecycleProcessor.java:182)
... 14 more
Caused by: java.lang.IllegalArgumentException: standardService.connector.startFailed
at org.apache.catalina.core.StandardService.addConnector(StandardService.java:231)
at org.springframework.boot.web.embedded.tomcat.TomcatWebServer.addPreviouslyRemovedConnectors(TomcatWebServer.java:282)
at org.springframework.boot.web.embedded.tomcat.TomcatWebServer.start(TomcatWebServer.java:213)
... 16 more
Caused by: org.apache.catalina.LifecycleException: Protocol handler start failed
at org.apache.catalina.connector.Connector.startInternal(Connector.java:1067)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183)
at org.apache.catalina.core.StandardService.addConnector(StandardService.java:227)
... 18 more
Caused by: java.lang.IllegalArgumentException: keystore password was incorrect
at org.apache.tomcat.util.net.AbstractJsseEndpoint.createSSLContext(AbstractJsseEndpoint.java:99)
at org.apache.tomcat.util.net.AbstractJsseEndpoint.initialiseSsl(AbstractJsseEndpoint.java:71)
at org.apache.tomcat.util.net.NioEndpoint.bind(NioEndpoint.java:216)
at org.apache.tomcat.util.net.AbstractEndpoint.bindWithCleanup(AbstractEndpoint.java:1141)
at org.apache.tomcat.util.net.AbstractEndpoint.start(AbstractEndpoint.java:1227)
at org.apache.coyote.AbstractProtocol.start(AbstractProtocol.java:592)
at org.apache.catalina.connector.Connector.startInternal(Connector.java:1064)
... 20 more
Caused by: java.io.IOException: keystore password was incorrect
at sun.security.pkcs12.PKCS12KeyStore.engineLoad(PKCS12KeyStore.java:2059)
at java.security.KeyStore.load(KeyStore.java:1445)
at org.apache.tomcat.util.security.KeyStoreUtil.load(KeyStoreUtil.java:67)
at org.apache.tomcat.util.net.SSLUtilBase.getStore(SSLUtilBase.java:216)
at org.apache.tomcat.util.net.SSLHostConfigCertificate.getCertificateKeystore(SSLHostConfigCertificate.java:207)
at org.apache.tomcat.util.net.SSLUtilBase.getKeyManagers(SSLUtilBase.java:282)
at org.apache.tomcat.util.net.SSLUtilBase.createSSLContext(SSLUtilBase.java:246)
at org.apache.tomcat.util.net.AbstractJsseEndpoint.createSSLContext(AbstractJsseEndpoint.java:97)
This is a very strange behavior. My suspicion is that when my application tries to access the keystore password at run time, it is already encrypted. By the time it reads SSLConfig.java it has already errored out. That's just a guess though.
I am initializing the encryption library that reads the file and encrypts it right before my spring boot application starts. However, spring boot application does not directly know that this is the config file. I'm supplying the config manually in the SSLConfig, JpaConfig above.
@SpringBootApplication
public class MyApplication {
@Autowired
Environment env;
public static void main(String[] args) {
try {
EncryptedProperties.init(System.getenv("CONFIG_FILE_PATH"));
}catch(Exception e){
throw new RuntimeException("Failed to encrypt properties" + e.getMessage());
}
SpringApplication.run(MyApplication.class, args);
}
What gradle command should I use such that it works fine through the command line as well, the way it works through IntelliJ? The reason I'm interested through command line is that I also want to be able to compile a jar which will be my deployable.