Monday, January 23, 2017

Declare secured connector on Tomcat for https connections

To access your tomcat threw https, you have to declare a secured connector. There are two parts to do that :


  1. modify your server.xml with the new connector configuration
  2. generate a java keystore the connector will refer to

Step 1 : Modify your serveur.xml like this


<Connector
           protocol="org.apache.coyote.http11.Http11NioProtocol"
           port="${ssl.port}" maxThreads="200"
           scheme="https" secure="true" SSLEnabled="true"
           keystoreFile="${java.home}/lib/security/tomcat_java.keystore" keystorePass="changeit"
           clientAuth="false" sslProtocol="TLSv1.1" ciphers="TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384,
TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
TLS_ECDHE_RSA_WITH_RC4_128_SHA,
TLS_RSA_WITH_AES_128_CBC_SHA256,
TLS_RSA_WITH_AES_128_CBC_SHA,
TLS_RSA_WITH_AES_256_CBC_SHA256,
TLS_RSA_WITH_AES_256_CBC_SHA,
SSL_RSA_WITH_RC4_128_SHA" />

Step 2 : generate the keystore which contains the certificate used to secure connections

To generate your keystore, you need openssl and keytool (%JAVA_HOME%/jre/bin/keytool). 

If you use an existing certificate in PEM format (cer or pem files) , you will need to convert it to PKCS#12 format (p12 file). To acheive that, you need :
  • your private key which was used to generate the certificate
  • your certificate
  • the root certificate form your Certificate Authority (Verisign, GoDaddy, Symantec, etc...)

Convert it with :

openssl pkcs12 -export -in [your_certificate].cer -inkey [your private key].key -out result-certificate.p12 -name tomcat -CAfile [Veridign certificate].cer -caname root

Then generate your keystore (NOTE -->  'tomcat' alias is important) :

keytool -importkeystore -deststorepass changeit -destkeypass changeit -destkeystore tomcat_java.keystore -srckeystore result-certificate.p12 -srcstoretype PKCS12 -srcstorepass changeit -alias tomcat

Restart Tomcat and check logs to see if connector is started.


PS : many thanks to John Willis. His post (http://www.johnwillis.com/2015/07/tomcat-errsslversionorciphermismatch.html) really helped me.


Acces Apache Tomcat on port 80 or 443 from Linux

Default Linux security rules don't allow to access network ports < 1024 to non rooted processes. A bad idea would be to start your Tomcat as ROOT. Very bad idea!!! But these's another solution. You can start your Tomcat with a port > 1024 and then redirect requests from standard ports (such as HTTP/80  or HTPS/443) to ports > 1024.

Let's imagine you bound Tomcat https connector to 8301. We'll add a rule to iptables like this :

Prequisites : switch to root (su -)

iptables -A PREROUTING -t nat -i eth1 -p tcp --dport 443 -j REDIRECT --to-port 8301
/etc/init.d/iptables save
/etc/init.d/iptables restart

Then, just check if rule is active with iptables -L

target     prot opt source               destination         
ACCEPT     tcp  --  anywhere             anywhere            tcp dpt:https 

Now, you can access your tomcat threw a classic https url :)

Friday, January 20, 2017

Use Spring profiles and yaml configuration without Spring Boot

That's a refinement you can do to updgrade legacy applications. Using yaml config files combines with Spring profiles is a great way to configure your apps. Let's see how to do that.

Firstly, let's consider that we weill inject something in a java class depending on the runtime  environment.

@Service
public class MyService {

@Value("${service.url}")
private String URL;

}

application.yaml should be like this :

service:
   url: http://alexdp.free.fr/violetumleditor

---
spring:
  profiles: production

service:
   url: http://violet.sourceforge.net


Thus, if I lauch my applicatin without JVM Spring profile params, URL will be http://alexdp.free.fr/violetumleditor. If I launch it with -Dspring.profiles.active=production, URL will be http://violet.sourceforge.net. Great! But this feature is natively supported only for Spring Boot based applications. So, let's active this on legacy apps with this XML spring config file fragment :

<bean id="yamlProperties"
class="org.springframework.beans.factory.config.YamlPropertiesFactoryBean">
<property name="resources">
<list>
<value>classpath:application.yml</value>
</list>
</property>
<property name="documentMatchers">
<bean
class="mypackage.SpringProfileDocumentMatcher" />
</property>
</bean>
<context:property-placeholder properties-ref="yamlProperties" />

Of course, you saw that we wrote our custom SpringProfileDocumentMatcher.

import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Properties;
import java.util.Set;

import org.springframework.beans.factory.config.YamlProcessor.DocumentMatcher;
import org.springframework.beans.factory.config.YamlProcessor.MatchStatus;
import org.springframework.context.EnvironmentAware;
import org.springframework.core.env.Environment;
import org.springframework.util.StringUtils;


/**
 * Allows to use Spring profiles without Spring Boot for legacy Spring based apps
 * 
 * @author Alexandre de Pellegrin
 *
 */
public class SpringProfileDocumentMatcher implements DocumentMatcher, EnvironmentAware {

private static final String[] DEFAULT_PROFILES = new String[] { "default" };

private String[] activeProfiles = new String[0];

public SpringProfileDocumentMatcher() {
}

public SpringProfileDocumentMatcher(String... profiles) {
addActiveProfiles(profiles);
}

public void addActiveProfiles(String... profiles) {
LinkedHashSet set = new LinkedHashSet(
Arrays.asList(this.activeProfiles));
Collections.addAll(set, profiles);
this.activeProfiles = set.toArray(new String[set.size()]);
}

@Override
public MatchStatus matches(Properties properties) {
String[] profiles = this.activeProfiles;
if (profiles.length == 0) {
profiles = DEFAULT_PROFILES;
}
return new ArrayDocumentMatcher("spring.profiles", profiles).matches(properties);
}

@Override
public void setEnvironment(Environment environment) {
if (environment != null) {
       addActiveProfiles(environment.getActiveProfiles());
   }
}


private class ArrayDocumentMatcher implements DocumentMatcher {

private final String key;

private final String[] patterns;

public ArrayDocumentMatcher(final String key, final String... patterns) {
this.key = key;
this.patterns = patterns;

}

@Override
public MatchStatus matches(Properties properties) {
if (!properties.containsKey(this.key)) {
return MatchStatus.ABSTAIN;
}
Set values = StringUtils.commaDelimitedListToSet(properties
.getProperty(this.key));
for (String pattern : this.patterns) {
for (String value : values) {
if (value.matches(pattern)) {
return MatchStatus.FOUND;
}
}
}
return MatchStatus.NOT_FOUND;
}
}
}


That's it!

Wednesday, January 18, 2017

Install Oracle Java Development Kit on Ubuntu

First of all, you need to download it from Oracle web site

http://www.oracle.com/technetwork/pt/java/javase/downloads/index.html

Then unzip it somewhere (be carefull that location is reachable from users who would use java)

tar xvzf jdk-*-linux-x64.tar.gz

The fun part would be here...

You should set your JAVA_HOME. Set it in your /etc/environment for every users or .profile for your current user

Now, we will create two symbolic links for java and javac command using update-alternatives command. There's two steps : install and set. The first create a symbolic link and register it to etc/alternatives with its alias. The second actives this alias. So, let's do this :


sudo update-alternatives --install /usr/bin/java java /home/..../jdk*/bin/java 1
sudo update-alternatives --install /usr/bin/javac javac /home/..../jdk*/bin/javac 1

sudo update-alternatives --set java /home/..../jdk*/bin/java
sudo update-alternatives --set javac /home/..../jdk*/bin/javac