Archive

Archive for May, 2019

End to End Encryption with Beanstalk

May 23rd, 2019 No comments

Beanstalk is often configured to terminate SSL at the load balancer then make the connection to the web server/application instances using unencrypted HTTP. That’s usually okay as the AWS network is designed to keep such traffic private, but under certain conditions, such as those requiring PCI compliance, DoD/government rules, or simply out of an abundance of caution, there’s a desire to have all traffic encrypted – including that between the Beanstalk load balancer and servers.

There are two approaches for implementing end to end encryption on Beanstalk:

  • Use a layer 4 load balancer (Network or Classic Elastic Load Balancer.
    Using this approach, the load balancer never decrypts the traffic. The downside is that advanced reporting isn’t possible and layer 7 features, such as session affinity, cannot be implemented.
  • Use a layer 7 load balancer (Application or Classic Load Balancer).
    Using this approach, traffic is decrypted at the load balancer. The load balancer would then re-encrypt traffic to the servers. Session affinity and traffic reporting are available.

The preferred solution is to use the layer 7 approach with an Application Load Balancer. This preference is due to the additional features the layer 7 offers, because Network Load Balancers are more expensive, and because AWS is deprecating Classic Load Balancers.

The simplest way to accomplish this goal is to use a self signed certificate on the servers and then use HTTPS from the load balancer to the server. Application Load Balancers do not currently perform validation of certificates which is why the self signed approach works and why there’s no advantage to using a CA issued certificate.

The following approach will work on any Beanstalk supported platform that uses nginx as the proxy server. This configuration is based on AWS’s documentation, but trimmed for only Application Load Balancers and to include the nginx configuration and self-signed certificate generation.

In your Beanstalk application archive, add these files:

.ebextensions/nginx/conf.d/https.conf

# HTTPS server

server {
    listen       443;
    server_name  localhost;
    
    ssl                  on;
    # The certificate is generated in generate-certificate.sh
    ssl_certificate      /etc/pki/tls/certs/server.crt;
    ssl_certificate_key  /etc/pki/tls/certs/server.key;
    
    ssl_session_timeout  5m;
    
    ssl_protocols  TLSv1.2 TLSv1.3;
    ssl_prefer_server_ciphers   on;
    
    location / {
        proxy_pass  http://localhost:5000;
        proxy_set_header   Connection "";
        proxy_http_version 1.1;
        proxy_set_header        Host            $host;
        proxy_set_header        X-Real-IP       $remote_addr;
        proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header        X-Forwarded-Proto https;
    }
}

.ebextensions/nginx-https.conf

option_settings:
  aws:elasticbeanstalk:application:
    Application Healthcheck URL: HTTPS:443/
  aws:elasticbeanstalk:environment:process:default:
    Port: '443'
    Protocol: HTTPS
  aws:elbv2:listener:443:
    DefaultProcess: https
    ListenerEnabled: 'true'
    Protocol: HTTPS
  aws:elasticbeanstalk:environment:process:https:
    Port: '443'
    Protocol: HTTPS
packages:
  yum:
    bash: []
    openssl: []
files:
  "/tmp/generate_nginx_certificate.sh":
    mode: "000755"
    owner: root
    group: root
    content: |
      #!/usr/bin/env bash
      set -Eeuo pipefail # stop on all errors
      # These files are used by nginx, see nginx/conf.d/https.conf
      openssl genrsa 2048 > /etc/pki/tls/certs/server.key
      openssl req -new -x509 -nodes -sha1 -days 3650 -extensions v3_ca -key /etc/pki/tls/certs/server.key -subj "/CN=localhost" > /etc/pki/tls/certs/server.crt
commands:
  01generate_nginx_certificate:
    command: "/tmp/generate_nginx_certificate.sh"
Categories: Uncategorized Tags:

Change the Spring Session JDBC Serialization Method to Improve Performance

May 8th, 2019 No comments

Spring Session JDBC is a great way to allow an application to be stateless. By storing the session in the database, a request can be routed to any application server. This approach provides significant advantages such as automatic horizontal scaling, seamless failover, and no need for session affinity. By using JDBC, the database the application is already using provides the storage avoiding the need to setup and maintain other software, such as Memcache or Redis.

When Spring Session JDBC stores the session in the database, it has to serialize (convert from a Java object to a string) the session and also deserialize it (convert from a string back to a Java object). By default, it uses Java’s built in serialization.

There are numerous reasons not to use Java’s built in serialization (ObjectInputSteam / ObjectOutputStream). Oracle calls it a “Horrible Mistake” and plans to remove it in a future Java release. It’s also less performant and produces a larger serialized form than many alternatives.

Since Java serialization is (at least, for now) included in Java, it’s still commonly used, including by Spring Session JDBC. Switching to another serialization method can be a relatively quick and easy way to improve performance.

Any serialization can be used, including Jackson (which uses the JSON or XML format), Protocol Buffers, Avro, and more. However, all require work to define schemas for the data and additional configuration. In the interest of avoiding those efforts (which is especially important for legacy applications), a schemaless serializer (which is what Java’s built in serializer is) can be used such as FST (fast-serializer) or Kryo.

Switching the serializer used by Spring Session JDBC is done by defining a a bean named springSessionConversionService of type ConversionService. The following examples provide the code to use FST or Kryo.

Using FST with Spring Session JDBC

Add FST as dependency to the project. For example, using Maven:

<dependency>
    <groupId>de.ruedigermoeller</groupId>
    <artifactId>fst</artifactId>
    <version>2.56</version>
</dependency>

And these add these classes:

FstSessionConfig.java

import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.support.GenericConversionService;
import org.springframework.core.serializer.support.DeserializingConverter;
import org.springframework.core.serializer.support.SerializingConverter;

@Configuration
public class FstSessionConfig implements BeanClassLoaderAware {

	private ClassLoader classLoader;

	@Bean
	public ConversionService springSessionConversionService() {
		final FstDeserializerSerializer fstDeserializerSerializer = new FstDeserializerSerializer(classLoader);

		final GenericConversionService conversionService = new GenericConversionService();
		conversionService.addConverter(Object.class, byte[].class,
				new SerializingConverter(fstDeserializerSerializer));
		conversionService.addConverter(byte[].class, Object.class,
				new DeserializingConverter(fstDeserializerSerializer));
		return conversionService;
	}

	@Override
	public void setBeanClassLoader(final ClassLoader classLoader) {
		this.classLoader = classLoader;
	}
}

FstDeserializerSerializer.java

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import org.nustaq.serialization.FSTConfiguration;
import org.nustaq.serialization.FSTObjectOutput;
import org.springframework.core.NestedIOException;
import org.springframework.core.serializer.Deserializer;
import org.springframework.core.serializer.Serializer;

public class FstDeserializerSerializer implements Serializer<Object>, Deserializer<Object> {

	private final FSTConfiguration fstConfiguration;
	
	public FstDeserializerSerializer(final ClassLoader classLoader) {
		fstConfiguration = FSTConfiguration.createDefaultConfiguration();
		fstConfiguration.setClassLoader(classLoader);
	}

	@Override
	public Object deserialize(InputStream inputStream) throws IOException {
		try{
			return fstConfiguration.getObjectInput(inputStream).readObject();
		}
		catch (ClassNotFoundException ex) {
			throw new NestedIOException("Failed to deserialize object type", ex);
		}
	}

	@Override
	public void serialize(Object object, OutputStream outputStream) throws IOException {
		// Do not close fstObjectOutput - that would prevent reuse and cause an error
		// see https://github.com/RuedigerMoeller/fast-serialization/wiki/Serialization
		@SuppressWarnings("resource")
		final FSTObjectOutput fstObjectOutput = fstConfiguration.getObjectOutput(outputStream);
		fstObjectOutput.writeObject(object);
		fstObjectOutput.flush();
	}
}

Using Kryo with Spring Session JDBC

Add Kryo as dependency to the project. For example, using Maven:

<dependency>
	<groupId>com.esotericsoftware</groupId>
	<artifactId>kryo</artifactId>
	<version>5.0.0-RC4</version>
</dependency>

And these add these classes:

KryoSessionConfig.java

import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.support.GenericConversionService;
import org.springframework.core.serializer.support.DeserializingConverter;
import org.springframework.core.serializer.support.SerializingConverter;

@Configuration
public class KryoSessionConfig implements BeanClassLoaderAware {

	private ClassLoader classLoader;

	@Bean
	public ConversionService springSessionConversionService() {
		final KryoDeserializerSerializer kryoDeserializerSerializer = new KryoDeserializerSerializer(classLoader);

		final GenericConversionService conversionService = new GenericConversionService();
		conversionService.addConverter(Object.class, byte[].class,
				new SerializingConverter(kryoDeserializerSerializer));
		conversionService.addConverter(byte[].class, Object.class,
				new DeserializingConverter(kryoDeserializerSerializer));
		return conversionService;
	}

	@Override
	public void setBeanClassLoader(final ClassLoader classLoader) {
		this.classLoader = classLoader;
	}
}

KryoDeserializerSerializer.java

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import org.springframework.core.serializer.Deserializer;
import org.springframework.core.serializer.Serializer;

import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryo.io.Input;
import com.esotericsoftware.kryo.io.Output;
import com.esotericsoftware.kryo.util.Pool;

public class KryoDeserializerSerializer implements Serializer<Object>, Deserializer<Object> {
	private final ClassLoader classLoader;
	
	// Pool constructor arguments: thread safe, soft references, maximum capacity
	private final Pool<Kryo> kryoPool = new Pool<Kryo>(true, true) {
	   protected Kryo create () {
	      final Kryo kryo = new Kryo();
	      kryo.setClassLoader(classLoader);
	      kryo.setRegistrationRequired(false);
	      return kryo;
	   }
	};
	
	public KryoDeserializerSerializer(final ClassLoader classLoader) {
		this.classLoader = classLoader;
	}

	@Override
	public Object deserialize(InputStream inputStream) throws IOException {
		final Kryo kryo = kryoPool.obtain();
		try(final Input input = new Input(inputStream)){
			return kryo.readObjectOrNull(input, null);
		}finally {
			kryoPool.free(kryo);
		}
	}

	@Override
	public void serialize(Object object, OutputStream outputStream) throws IOException {
		final Kryo kryo = kryoPool.obtain();
		try(final Output output = new Output(outputStream)){
			kryo.writeObject(output, object);
		}finally {
			kryoPool.free(kryo);
		}
	}
}

How to Choose which Serializer to Use

The process of selecting which serializer to use for an application should be done only through testing, both for functionality and for performance.

For some applications, a serializer won’t work due to known limitations in the serializer or bugs in it. For example, FST doesn’t currently support the Java 8 time classes, so if your application stores session data using such a class, FST is not for you. With Kryo, I ran into a bug stopping me from using it (which will be fixed in version 5.0.0-RC5 and later).

Performance will also vary between serializers for each application. Factors that impact performance include exactly what is being serialized, how big that data is, how often it’s accessed, the version of Java, and how the system is configured. FST has published some benchmarks, but that information must be taken with a grain of salt as those benchmarks are only measuring very specific, isolated scenarios. That data does provide general guidance though – you can expect better performance when you switch from the Java serializer to FST, for example, but testing of the full application will need to be done to determine if the improvement is 0.1% or 10%.

Categories: Uncategorized Tags: