vendredi, 19 novembre 2010

Using java.net.InetAddress.getByName() to validate an IP Address may be too permissive

java.net.InetAddress.getByName() static function can be used for validating a given IP Address.

But doing that way you need to pay attention to certain side effects border values :

String ip = "1.2.3.4"; // valid IP Address
InetAddress address = InetAddress.getByName( ip );
Assert.assertEquals( ip, address.getHostAddress() ); // Pass. OK

String ip = "1.2.3"; // invalid IP Address
InetAddress address = InetAddress.getByName( ip );
Assert.assertEquals( ip, address.getHostAddress() ); // Pass. KO !
If you have a look a little deeper, you will see that 1.2.3 is transformed in 1.2.0.3, which after transformation become a valid IP Address.

Having that in mind, a IP Address validation function can look like :
public static boolean validateIpAddress( String anIpAddress ) {
try {
InetAddress address = InetAddress.getByName( anIpAddress );
return address.getHostAddress().equals( anIpAddress );
} catch (final UnknownHostException e) {
return false;
}
}

samedi, 9 octobre 2010

Moving from ELCA to Verisign

I had a great time at ELCA Informatique SA, working for almost 3 years on Secutix, a complete ticketing system. But now it's time for me to move forward and see something else : different projects, different colleagues, different cultures, speak more English, nearer from home, and so on...

That's why I will move to Verisign, beginning the 2nd of November (the 1st is day off :)). Working on the heart of Internet (aka DNS) is some kind of child's dream which I will be able to realize ! I'm really excited to see how root name servers work, what's behind Registrars (aka registry), implementing DNSSEC or what means 1 billion transactions a day.

I really look forward to start there, and in the same time wish all the best to the Secutix team !

lundi, 2 août 2010

Dealing with old SSL certificats (algorithm check failed: MD2withRSA is disabled)

I faced the problem of SSLHandShakeException, or "algorithm check failed: MD2withRSA is disabled" when upgrading above java 1.6.0_17.

The source of problem is well known, the MD2withRSA has been removed from the JVM because it is no more secure (References : CVE-2009-2409 deprecate MD2 in SSL cert validation, Sun java 1.6u17 release notes).

Lot of posts around the problem give the solution to update the certificate to use another signature algorithm, for example SHA1withRSA.

But what to do when the certificate is not under our hands ?

I will try to explain the problem and how I solved it.

Analyse the certificate's chain

First of all is to analyse the chain of certificate to see which one is using the deprecated algorithm.

The following command will print all the certificates in the chain and store them under the names level0.pem, level1.pem and so on.

openssl s_client -showcerts -connect xxxxxxxxxxxxxxxxx:443 < /dev/null | awk -v c=-1 '/-----BEGIN CERTIFICATE-----/{inc=1;c++} inc {print > ("level" c ".pem")}/---END CERTIFICATE-----/{inc=0}'; for i in level?.pem; do openssl x509 -noout -serial -subject -issuer -in "$i"; done
The output given in my case :
  • Cert1, serial=xxxxxxxxxxxxxxxxxxxxx
    subject= xxxxxxxxxxxxxxxxxxxxxxxx
    issuer= /C=ZA/O=Thawte Consulting (Pty) Ltd./CN=Thawte SGC CA
  • Cert2, serial=30000002
    subject= /C=ZA/O=Thawte Consulting (Pty) Ltd./CN=Thawte SGC CA
    issuer= /C=US/O=VeriSign, Inc./OU=Class 3 Public Primary Certification Authority
  • Cert3, serial=70BAE41D10D92934B638CA7B03CCBABF
    subject= /C=US/O=VeriSign, Inc./OU=Class 3 Public Primary Certification Authority
    issuer= /C=US/O=VeriSign, Inc./OU=Class 3 Public Primary Certification Authority
We have a chain of 3 certificates, Cert1 signed by Cert2 signed by Cert3 signed by Cert3 (selfsigned).

Note : the first error here is that the webserver gives explicitly the entire chain of certificates. Thus the change of any root certificate provided by the jre will not be taken into account automatically.

Digging into the details of the third certificate (openssl x509 -in level2.pem -text) show that this certificate is signed with the old algorithm :
Certificate:
Data:
Version: 1 (0x0)
Serial Number:
70:ba:e4:1d:10:d9:29:34:b6:38:ca:7b:03:cc:ba:bf
Signature Algorithm: md2WithRSAEncryption
Issuer: C=US, O=VeriSign, Inc., OU=Class 3 Public Primary Certification Authority
Validity
Not Before: Jan 29 00:00:00 1996 GMT
Not After : Aug 1 23:59:59 2028 GMT
So not only the way of providing the full chain is wrong in point of view of the server configuration, but the last certificate was never updated and thus still use the deprecated signature algorigthm.

Correct the certificate's chain

Now we have identified the weak link of the chain, what's the next step ?

The key point here is to correct the certificate's chain in order to make it more compliant to the standards :

As the third certificate is a well know root certificate (named verisignclass3ca in the jre cacerts), a working version of it is provided in the cacert of java sun (debian oriented commande line, with "changeit" as password) :
keytool -keystore /etc/java-6-sun/security/cacerts -exportcert -alias "verisignclass3ca" | openssl x509 -inform der -text
will output :
Certificate:
Data:
Version: 1 (0x0)
Serial Number:
3c:91:31:cb:1f:f6:d0:1b:0e:9a:b8:d0:44:bf:12:be
Signature Algorithm: sha1WithRSAEncryption
Issuer: C=US, O=VeriSign, Inc., OU=Class 3 Public Primary Certification Authority
Validity
Not Before: Jan 29 00:00:00 1996 GMT
Not After : Aug 2 23:59:59 2028 GMT
which is the same as before, but with sha1WithRSAEncryption instead of md2WithRSAEncryption.

Thus we can simply remove it from the chain and validate normally the certificate's chain with only Cert1 and Cert2. The validation from Cert2 to "verisignclass3ca" will be automagically done.

To be able to do this, we need to create a custom X509TrustManager which will behave the following :
  1. if the certificate's serial is the one of Cert1 (serial=xxxxxxxxxxxxxxxxxxxxx), then remove the last certificate of the chain and revalidate.
  2. In all other cases, validate normally the certificate's chain.
Thus the custom X509TrustManager act as delegate, with one little variation (note : if someone know a easier way to instantiate the default TrustManagerFactory of TrustManager, please leave me a comment). Refer to my article about blind SSL factory for httpClients to see how to pass a custom TrustManager to a SSL factory.

My custom X509TrustManager class will finally look as the follow :
public class CustomX509TrustManager implements X509TrustManager {

X509TrustManager delegate = null;

public CustomX509TrustManager(KeyStore keystore) throws NoSuchAlgorithmException, KeyStoreException, CertificateException {
// Instantiate the default X509TrustManager
TrustManagerFactory factory = TrustManagerFactory
.getInstance(TrustManagerFactory.getDefaultAlgorithm());
factory.init(keystore);
TrustManager[] trustManagers = factory.getTrustManagers();
if (trustManagers != null && trustManagers.length > 0) {
for (int i = 0; i < trustManagers.length; i++) {
TrustManager trustManager = factory.getTrustManagers()[i];
if (trustManager instanceof X509TrustManager) {
delegate = (X509TrustManager) trustManager;
break;
}
}
}
if (delegate == null) {
throw new CertificateException("Cannot found any instance of X509TrustManager");
}
}

public X509Certificate[] getAcceptedIssuers() {
if (delegate == null) {
return null;
} else {
return delegate.getAcceptedIssuers();
}
}

public void checkClientTrusted(final X509Certificate[] c, final String a)
throws CertificateException {
if (delegate != null) {
delegate.checkClientTrusted(c, a);
} else {
throw new CertificateException("Unable to validate this certificate (delegate is null).");
}
}

public void checkServerTrusted(final X509Certificate[] c, final String a)
throws CertificateException {
if (delegate != null) {
// hardcoding test to be sure we are trying to validate the right certificate
if (c.length == 3 && c[0].getSerialNumber().toString(16).equals(BADCERTIFICATESIGNATURE)) {
c = new X509Certificate[] {c[0], c[1]};
}
delegate.checkServerTrusted(c, a);
} else {
throw new CertificateException("Unable to validate this certificate (delegate is null).");
}
}

private static final String BADCERTIFICATESIGNATURE = "xxxxxxxxxxxxxxxxxxxx";
}
With this implementation I'm sure that the my invalid certificate will continue to work, and it cannot be faked (as it could be with a completely blind TrustManager). But I can also expect that when the provider will update its certificate, everything will continue to work as expected because all other certificates are still validate the regular way.

Java and certificate's mess : blind SSL factory for commons httpClient

There are parts of Java which are not very "quick development" oriented. The side I want to illustrate here is dealing with SSL.

When the application is in early development stage, we don't want to bother with stuff like valid certificate. We don't want to get stuck for days because we haven't received valid development SSL certificate from Verisign or other "more trusted than me" monopoles. We just want to activate the "--no-check-certificate" à la wget, or "CURLOPT_SSL_VERIFYPEER" à la PHP.

Adding a new certificate in the cacert file is not so complicated, but it is not always possible on every computers (should developers have administrators rights on their computers ?), or break the debian pakage integrity and risk to be overridden at the next JRE update.

No, for development phase, a blind SSL factory is a good point for productivity. Simply DO NOT FORGET TO REMOVE IT in the integration or pre-production phase. Having valid and trusted SSL certificate in production is NOT an optional thing.

What is a blind SSL factory

A blind SSL factory is simply an SSL factory which will validate any certificate. Expirated, selfsigned, but also man-in-the-middled or forged certificates will be trusted.

Mike McKinney already explained how to create a blind SSL factory for LDAP connexions.

I will leverage his explanations to enable blind SSL factory for commons httpClient.

The start point is to create a TrustManager which will trust any certificate, and then give this TrustManager to the SSL factory. This will result in a blind SSL factory :)

Anonymous class that implements X509TrustManager :

class BlindX509TrustManager implements X509TrustManager
{
public X509Certificate[] getAcceptedIssuers()
{
return null;
}
public void checkClientTrusted(final X509Certificate[] c, final String a)
{
}
public void checkServerTrusted(final X509Certificate[] c, final String a)
{
}
}
Initializing the SSLContext to retrieve a SocketFactory :
SSLContext sc = SSLContext.getInstance("SSL");
sc.init(null, new TrustManager[] { new BlindX509TrustManager() }, new java.security.SecureRandom());
SocketFactory blindFactory = sc.getSocketFactory();
And finally the BlindSSLProtocolSocketFactory which implements the needed ProtocolSocketFactory of httpClient :
public class BlindSSLProtocolSocketFactory implements SecureProtocolSocketFactory
{
public Socket createSocket(String host, int port, InetAddress clientHost, int clientPort) throws IOException,
UnknownHostException
{
return blindFactory.createSocket(host, port, clientHost, clientPort);
}
public Socket createSocket(String host, int port, InetAddress localAddress, int localPort, HttpConnectionParams params)
throws IOException, UnknownHostException, ConnectTimeoutException
{
if (params == null)
{
throw new IllegalArgumentException("Parameters may not be null");
}
int timeout = params.getConnectionTimeout();
if (timeout == 0)
{
return createSocket(host, port, localAddress, localPort);
}
else
{
return ControllerThreadSocketFactory.createSocket(this, host, port, localAddress, localPort, timeout);
}
}
public Socket createSocket(String host, int port) throws IOException, UnknownHostException
{
return blindFactory.createSocket(host, port);
}
public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException, UnknownHostException
{
return ((SSLSocketFactory) blindFactory).createSocket(socket, host, port, autoClose);
}
public boolean equals(Object obj)
{
return obj != null && obj.getClass().equals(getClass());
}
public int hashCode()
{
return getClass().hashCode();
}
}
Everything tied up together, put into the classpath of the application, will override the https protocol with the BlindSSLProtocolSocketFactory and thus allow your code to connect any certificate !
public class BlindSSLProtocolSocketFactory implements SecureProtocolSocketFactory
{
private static final Log LOG = LogFactory.getLog(BlindSSLProtocolSocketFactory.class);
public BlindSSLProtocolSocketFactory()
{
blindFactory = createBlindSocketFactory();
}
public Socket createSocket(String host, int port, InetAddress clientHost, int clientPort) throws IOException,
UnknownHostException
{
return blindFactory.createSocket(host, port, clientHost, clientPort);
}

public Socket createSocket(String host, int port, InetAddress localAddress, int localPort, HttpConnectionParams params)
throws IOException, UnknownHostException, ConnectTimeoutException
{
if (params == null)
{
throw new IllegalArgumentException("Parameters may not be null");
}
int timeout = params.getConnectionTimeout();
if (timeout == 0)
{
return createSocket(host, port, localAddress, localPort);
}
else
{
return ControllerThreadSocketFactory.createSocket(this, host, port, localAddress, localPort, timeout);
}
}
public Socket createSocket(String host, int port) throws IOException, UnknownHostException
{
return blindFactory.createSocket(host, port);
}
public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException, UnknownHostException
{
return ((SSLSocketFactory) blindFactory).createSocket(socket, host, port, autoClose);
}
public boolean equals(Object obj)
{
return obj != null && obj.getClass().equals(getClass());
}
public int hashCode()
{
return getClass().hashCode();
}
private SocketFactory blindFactory = null;
private static SocketFactory createBlindSocketFactory()
{
try
{
SSLContext context = SSLContext.getInstance("SSL");
context.init(null, new TrustManager[] { new BlindX509TrustManager() }, null);
return context.getSocketFactory();
}
catch (Exception e)
{
LOG.error(e.getMessage(), e);
throw new HttpClientError(e.toString());
}
}
}
And to regirster the new protocol as the default https protocol :
Protocol.registerProtocol("https", new Protocol("https", (ProtocolSocketFactory) new BlindSSLProtocolSocketFactory(), 443));

Please note that if you want only access a selfsigned certificate you have better to use EasySSLProtocolSocketFactory from httpClient contibs. It still validate the selfsigned certificate, so expired or bad certificate will be denied.

dimanche, 25 avril 2010

Implementing unique constraints with Cassandra

I was talking about my "Doodle clone with Cassandra backend" in a previous post, I want to explain a little bit how I implement 2 kinds of constraints with Cassandra.

Unique name constrain

When subscribing to a poll, a unique constraint on the name of the subscriber is applied. There is a first check in Javascript in the GUI, but there is also a check of the uniqueness of the names on the server side.

Before going deeper, I just need to say a word about the structure used :

To store the subscribers, I use a SuperColumnFamily, with the ID of the poll as key. For a poll having key "1234", it gives me something like :

"1234" => {
"subscriberKey1" => { name = "subscriberName1", options = ... }
"subscriberKey2" => { name = "subscriberName2", options = ... }
...
}
The trick about the subscriberKey* is to generate a unique ID based on a timestamp in order to be sorted by Cassandra.
The key will be something like : currentTimestamp - ClusterNodeId - ThreadId. Adding the ThreadId into the key give me the guarantee that using the System.currentTimeMillis() will be unique, because the processing will unfortunately take more than a millisecond :)

Using this key, I will write the subscriber row with ConsistencyLevel.QUORUM, and then I will read all the rows till the one I have inserted (with ConsistencyLevel.QUORUM again).
At this point I use Cassandra magic (rows are ordered) to be sure that I have selected all the subscribers they subscribe before the current one.
I remains me to check manually the uniqueness of the name. If another row has the same name, I will simply remove the current subscriber.

Limiting the number of subscribers per option

Another constraint I have implemented is the limitation of the number of subscribers per option.

The principle is the same as the unique name constraint : I count the number of previously inserted subscriber.
One point to keep in mind is that we can read a row with a duplicated name, which will be deleted later so it should not be taken into account for the actual constraint. This mean that we need to check if we are not counting duplicated names in this constraint too...

But the good news about this constraint is that it show a way to implement counters in Cassandra. Maybe I will write another post about this topic. The bad news is that we need to check the duplicated names in several places.

Limitation of the model

Complexity : adding more constraints grows the complexity in O(n^2), as every new constraints (duplicated names) should be implemented into every other constraints (limitation of subscribers per option).

Scalability : this model will scale very well for billions of polls with some tens of users. The inverse, some tens of polls with billions of users, needs a lot of resources for checking constraint, which will make the performance drop.

samedi, 24 avril 2010

Doodle clone with Cassandra backend

I follow Cassandra's activity for some months now, and I decided to try it. But what better than developing a small application to test real needs ?

That's what I did with my "Doodle clone with Cassandra backend".

What is Cassandra ?

According to the main page,

Cassandra is a highly scalable, eventually consistent, distributed, structured key-value store
Why a Doodle clone ?

I'm working during my free time on an event manager software, so it was a good pretext to analyze Doodle business :)
It appears to be quite simple, so it was a good starting point.

Which features to implement ?

I added the following feature to the doodle clone :
  • Easy (easier) to create polls
  • Login free, secret key based administration
  • No options limitation
  • Email feedback when someone subscribe the poll (if email provided)
  • No duplicate name of the subscribers
  • An optional limitation of the number of subscribers per options
Have a look !

Go to http://doodle.noisette.ch and try it, the result is really surprising.

Disclaimer

There is absolutely no affiliation with Doodle, neither any idea to become the next Doodle. The goal of this application is purely academic, and it is deployed as is, without any guarantee.

lundi, 5 avril 2010

Don't be abused by the term "commodity hardware"

NoSQL paradigm claims to build clusters of "commodity hardware". But don't be abused by this term : "commodity hardware" is not your living room's computer. Commodity hardware is two slots' computer, 16+GB RAM, (ten)giga ethernet, raid5 hard disks.


Commodity hardware is not 500$ computers. It's 5000$ computers. But building a 10 nodes' cluster for 50'000$ is still really cheap.

And remeber : clusters' primary bottlneck is I/O. Both disk and network. So there is no place for virtualisation here.

mercredi, 30 décembre 2009

Sequences scalability

Sequences are central part of most computer systems. They are used both in technical part (generate unique identifier for object) and in business part (count the remaining free tickets which can be sold).

When the load on the application goes higher, sequences become quickly central bottlenecks of systems. So the goal here is to first categorize different types of sequences and then gives some keys to see how sequences be distributed to increase their scalability, and hence the global scalability of the whole system.

Characteristics of sequences :

Order : the number generate should be used in a given order (ascendant or descendant), or the order do not matter. I will talk about ordered or unordered sequence.

Missing values : some values can be missing in the sequence. For example the sequence miss the value 4 should not alter the behavior of the application. I will talk about continued or discontinued sequence.

Finite sequence : all the values of the sequence are know and countable. Countable sequences are particular case of finite sequence where the sequence has a reasonable amount of values, and thus could be represented by one object per values. I will talk about finite or infinite sequence, and countable sequence.

Key to distribute sequences

Discontinued sequences are easy to scale : the application can pick numbers n by n in the sequence, reducing the amount of calls to the sequence by n. Good example for of discontinued sequences are objects identifier generators.

Unordered sequences are also easy to scale : a counter can be decomposed in n subcounter, picking at random one of the subcounter to increment or decrement will reduce the contention on one counter by n. A typical example is a quantity of object to sell. N subcounters are initialized with the sum is the total amount to sell. The object cannot be sold anymore when every subcounters are at 0.

Countable sequences can be scaled by representing every value of the sequence as a single object in the database. Updating an unused object is a easy task.

Finaly infinite, continued ordered sequence are very hard to scale because synchronization between all callers is required, so a single point of contention is mandatory.

Conclusion

Sequences are choke point in most systems, but as we saw above they can scale relatively simply with two simple tricks : read number n by n instead of 1 by 1, or decompose a counter in n subcounters.

dimanche, 15 novembre 2009

Call different services, use the first to reply

The needs


My needs were the following : I have an arbitrary amount of webservices to call, but I don't know, for a particular query, which one will return the answer. More than that, some are slower and others are faster, but again, I don't know which is the fastest for my particular query.

So what I will do is to parallelize the queries, and use the value of the first which return my a good value.

Use an Executor

The first component to implement such a requirement is an Executor.
The Executor will run all my queries, in as many threads as I want, permitting to fine tune the behavior of the system.
As many queries will input the system and will be translated in many more threads, it is important to use a limited amount of threads to not crash the system.

Synchronization on result

A specific object will be used to synchronize the result : the first result will be returned to the caller, letting the running threads die, and no more threads will be run for this query.

Some basic primitive for concurrent programming (java.util.concurrent) will be used, such as CountDownLatch : the caller will block on the latch, and once a thread will find the answer, it will set the value into the result and count down the latch. The caller will be freed and the value will be available.

Watchdog if all the queries fail

One point to not forget is if every queries fail, the caller should be informed of this fail.
A watchdog will be used : it will block till all the threads executing the query will finish, and will then be run. Its only task is to free the caller. This one will continue the processing, but the result will not be available. This will be the responsibility of the caller to run user code to handle this special case.

Strong requirements

The requirements are the following :
  • Use as little synchronization as possible
  • Use as much concurrent primitives (java.util.concurrent) as possible
  • Do it as generic as possible
Java implementation

The interesting part of the implementation is given here below.
The Executor is simply an java.util.concurrent.Executor. I'm not good enough to implement a better solution than they do.
The result object is composed of the following attributes :
public class FutureResult {
private T result;
private boolean done;
private AtomicInteger threadCount = new AtomicInteger(0);
private CountDownLatch latch = new CountDownLatch(1);
}
A generic type for the value, a AtomicInteger to count how many other threads are working on this result, and a latch to make the caller wait.

The watchdog class will simply wait till all the threads are finish and free the caller if it is not already the case :
class WatchDogRunnable implements Runnable {
public void run() {
while (result.hasThreadsWorking()) {
synchronized (result) {
result.wait(2000);
}
}
latch.countDown();
}
}
And finally an abstract callable worker which can be easily extended to run the wanted tasks. The call function will increment the AtomicInteger, do the work if it is not already done, if a result is found, free the caller, decrement the AtomicInteger and return the value if it is set. Not that the worker implementation will implement the callInternal function.
public abstract class AbstractCallableWorker
implements Callable {
private FutureResult result;
public final T call() {
result.setOneMoreThread();
if (!result.isDone()) {
T callResult = callInternal();
if (callResult != null) {
setResult(callResult);
latch.countDown();
}
}
result.setOneLessWorkingThead();
return result.getResult();
}
protected abstract T callInternal();
}
Everything put together, you can implement as much worker as you want and call as many webservice as you want, with the only guaranty to get the caller continue with the fastest service to reply !

samedi, 14 novembre 2009

Clustering Jackrabbit 1.5.x with DataStore configuration

Jackrabbit is the reference implementation of JSR-170 : JCR, Java Content Repository.

One day we felt the needs to run a Jackrabbit repository in a clustered environment for reliability. So a JCR runs on two separated servers, backed by the an Oracle db and a NFS shared datastore.


It exists some difficulties to run such architecture, mainly because both JCR nodes (JCR1 and JCR2) do not know each others. So if we insert some content on one node, it takes some time before we can read it on the other node.

In Jackrabbit 1.5.x, Session.refresh do not work correctly, so we need to do some trick to enable cluster (synchronization) features.

First of all, we add a custom ServletFilter which handle the GET (read) request and transform it on a HEAD one (test if the content exists) using apache.commons.HttpClient.
If the return of the query says the content exists, then we run the normal request. If the content do not exists, we way for some arbitrary time and relaunch the HEAD request.

This filter will let enough time to the node to synchronize itself, and then perform the query in the right way.

Other possibilities like HEAD on the current node and then HEAD on a different node should also be possible, but this will require every nodes to know about every other nodes. No the best idea.

One another tricky point is to store all the content to the configured datastore. Be carefull to not rely on a JNDI persistence manager, but be sure to use a subclass of a bundle persistence manager, otherwise you will have some surprise with the size of the database.