Saturday, December 15, 2012

SSL certificates expiration / expiração de certificados SSL

This article is written in English and Portuguese
Este artigo está escrito em Inglês e Português


English version:

Introduction


In a recent customer engagement I was questioned about a topic that to be honest I never had to work with before. It relates to SSL connections to Informix and how should we handle the certificates, in particular the fact that they can/will expire... And what happens then.
Configuring SSL connections is a relatively simple process. It's well explained in the security manual and it's not my objective to explain it in detail here. Nevertheless, for the readers who haven't done it before here's a quick reference (if you really want to do it, read the manual!). Note that this example uses a self signed certificate, so this will be also copied to the client(s):

  • Server configuration
    1. Create a key database where the server certificates will be stored. This can be done with a command included in the IBM Global Security Kit which is bundled in the engine package:

      gsk8capicmd -keydb -create -db $INFORMIXDIR/ssl/$SERVER_NAME.kdb -pw $MYPASSWORD -type cms -stash

      The command above uses two variables that you need to set or replace by real values:
      SERVER_NAME is the value of the DBSERVERNAME parameter in $INFORMIXDIR/etc/$ONCONFIG
      MYPASSWORD is the password used to encrypt/access the keystore.

      We're creating the file of type "cms" in $INFORMIXDIR/ssl with the name of the engine and we're requesting the creation of a stash file which will allow the engine to get the password and access the keystore without having to specify the password again
      Naturally this file is security sensitive.
    2. Then we need to create a certificate file and store it in the keystore we created above. For testing purposes we're going to create a self sign certificate. Proper setups would use a CA (Certification Authority) to sign the certificates. The CA could be external or one implemented in your company (depending on the needs):

      gsk8capicmd -cert -create -db $INFORMIXDIR/ssl/$SERVER_NAME.kdb -pw ${MYPASSWORD} -label ${SERVER_NAME}_label -dn "CN=`hostname`" -size ${KEYSIZE} -default_cert yes -expire ${EXPIRE}

      The variables that need to be set or replaced by values are:

      SERVER_NAME and MYPASSWORD as explain above
      KEYSIZE to specify the key length (e.g 1024)
      EXPIRE to define the number of days that the certificate will be valid. Default should be 365 (one year)

      Note that we're defining a "label" for this certificate (${SERVER_NAME}_label) so that the engine can define which certificate it will use from the ones stored in the keystore. This label will be used in the engine configuration (see next step)
    3. Configure the label to define which certificate will be used by the engine. This is done by setting the SSL_KEYSTORE_LABEL:

      SSL_KEYSTORE_LABEL panther_label
    4. Create an entry in $INFORMIXSQLHOSTS for SSL (e.g panther_ssl) using the onsocssl protocol
    5. Configure one NETTYPE entry in $INFORMIXDIR/etc/$ONCONFIG:

      NETTYPE        onsocssl,1,20,NET
    6. Configure one (or more) virtual processor for SSL:

      VPCLASS ssl,num=1,noage
    7. Make sure that the new SSL port is configured in DBSERVERALIAS or DBSERVERNAME
    8. Extract the certificate so that it can be imported in the clients database (for self signed certificates):

      gsk8capicmd -cert -extract -db $INFORMIXDIR/ssl/${SERVER_NAME}.kdb -format ascii -label ${SERVER_NAME}_label -pw ${MYPASSWORD} -target ${SERVER_NAME}_label.cert
  •  On the client side
    1. Create the keystore on the client side:

      gsk8capicmd -keydb -create -db $INFORMIXDIR/etc/clientdb.kdb -pw ${MYPASSWORD} -type cms -stash
    2. Import the self signed certificate that we extracted in the server side last step:

      gsk8capicmd -cert -add -db $INFORMIXDIR/etc/clientdb.kdb -pw ${MYPASSWORD} -label ${SERVER_NAME}_label -file ${SERVER_NAME}_label.cert -format ascii

      note that the PATH specified after "-file" needs to point to the file extracted from the server keystore. You may need to FTP/SCP the file or use another copy method.
      In a proper setup you would import the CA certificate used to sign the certificates used in the servers to which the client wants to connect to
    3. Configure the client $INFORMIXSQLHOSTS file with the appropriate entry to define the SSL enabled port
    4. Create a file named $INFORMIXDIR/etc/conssl.cfg with the following content:

      SSL_KEYSTORE_FILE   /usr/informix/client_INFORMIXDIR/etc/clientdb.kdb   # Keystore file
      SSL_KEYSTORE_STH    /usr/informix/client_INFORMIXDIR/etc/clientdb.sth   # Keystore stash file

      This will tell the clients where the client side keystore and stash file are.
    5. Make sure the keystore and stash file have appropriate filesystem permissions (don't allow access to non-authorized people)

The problem

Once you have an SSL enabled environment you should be able to connect your clients (to which you need to make the certificate available). But then, you have the conditions to hit the problem. As you may have noticed, the certificates have an lifetime period. They're not valid before a date and after another date. By default the initial date is the time when you create them and the valid period is typically one year. What happens after that period? The customer already found that... No new connections will be possible (which is understandable since the certificate is expired), but the problem is that you cannot re-validate the certificate with the engine online. If you try to connect using an expired certificate you'll get the following error in the engine log:

15:27:18  listener-thread: err = -28014: oserr = 0: errstr = GSK_ERROR_BAD_DATE: Secure Sockets Layer error: GSK_ERROR_BAD_DATE.

The error message is:
-28014    Secure Sockets Layer error.

An error occurred during a Secure Socket Layer operation. For more information,
refer to the accompanying IBM Global Security Kit (GSKit) error message.

So, the problem summary is how to avoid getting into a situation where you have a running engine with an expired certificate.

How to avoid it


In generic terms, a way to avoid this is to keep track of the certificates expiration dates. Before they expire we should create new ones and schedule a stop of the server to make the change. Now, if you have a regular maintenance window this will be easier. But if you have a 24x7 system the stop becomes an issue. I'll split the solution into three parts:

  1. You should create a certificate that is secure enough for a specific time period. This period should be enough to accommodate enough time so that you can wait for a stop caused by another reason (OS maintenance/upgrade, a crash, a monthly/quarter/yearly stop). This will probably mean that you need to create a certificate that will last longer than the period you'd like to change it. Let's assume you establish as a policy that you'd like to exchange the certificates every 6 months. But you're only allowed to do a schedule stop once per year... So, to be safe you may want to create the certificate with 18 months before expiration. You should consider the correct key size to guarantee the security of the certificate for the interval desired. The following site may help with that:

    http://www.keylength.com/en/
  2. You need to setup everything that it's possible to do without stopping the engine, before the stop. The idea is to make the stop as short and simple as possible. So you can create a new certificate and store it in the same keystore, as long as you use a different label. This can be done before the stop while everything is working. This way, the only thing you'll have to do during the stop is to change the SSL_KEYSTORE_LABEL parameter.
  3. You must create a tracking system that will alert you with enough time, before the certificate expires. You have two ways to do this: You store each certificate ID (label for example) and it's expiration date in a database, and periodically run  a process that will alert you if any of the certificates will expire in a pre-defined period of time (e.g in the next 3 - choose your number -  months). The other option is to create an alarm that checks each server's certificate and generates some sort of alert (email, monitoring system etc.)
In order to help with point 3 above, I have a little script that can be changed/adapted to your particular needs:

1    #!/bin/sh
2   
3    SERVER_NAME=`onstat -c | grep DBSERVERNAME | grep -v "^[     ]*#" | awk '{print $2}'`
4    SERVER_LABEL=`onstat -c | grep SSL_KEYSTORE_LABEL | grep -v "^[     ]*#" | awk '{print $2}'`
5   
6    MYDATE=`gsk8capicmd -cert -details -locale en -db $INFORMIXDIR/ssl/$SERVER_NAME.kdb -stashed -label ${SERVER_LABEL} | grep After | awk '{print $4 " " $5 " " $6}'`
7   
8    NUM_DAYS=`dbaccess sysmaster <<EOF 2>/dev/null| grep "DAYS:" | awk -F':' '{print $2+0}'
9    select "DAYS:"||extend(TO_DATE("$MYDATE", "%B %d, %Y"), YEAR TO DAY) - extend(TODAY,YEAR TO DAY) FROM sysmaster:sysdual;
10    EOF`
11   
12   
13    if [ $NUM_DAYS -gt 0 ]
14    then
15        echo "Certificate is still valid for $NUM_DAYS days"
16    else
17        echo "Certificate has expired!"
18    fi

Let's leave aside the SHELL tricks and particularities. What the script it doing is pretty simple:
  1. Collects the DBSERVERNAME and SSL_KEYSTORE_LABLE $ONCONFIG parameters
  2. Calls gdk8capicmd command to display the certificate details. It extracts the line "Not After :". Note that I use the "-locale en" to define the date format.
  3. It uses Informix with an inline SQL script to calculate the number of days that the certificate still has before expiring


Versão Portuguesa:

Introducão

Durante uma visita recente a um cliente fui questionado sobre um tema com o qual muito honestamente nunca tinha lidado. Está relacionado com ligações SSL ao Informix e como deveremos gerir os certificados, em particular o facto de que podem expirar... e o que acontece então.
Configurar ligações SSL é um processo relativamente simples. Está bem explicado no manual de segurança e não é meu objetivo explicar detalhadamente aqui como o fazer. Em todo o caso, e pensando em todos os leitores que nunca o fizeram, aqui fica um guia rápido (se realmente o quiser fazer não deixe de seguir o manual). Note que este exemplo utiliza certificados auto-assinados, portanto estes são também copiados para os clientes:
  • Configuração do servidor
    1. Criar uma base de dados de chaves onde os certificados serão guardados. Isto pode ser feito com um comando incluído no IBM Global Security Kit, que é fornecido com o pacote do motor:

      gsk8capicmd -keydb -create -db $INFORMIXDIR/ssl/$NOME_SERVIDOR.kdb -pw $MINHA_PASSWORD -type cms -stash

      O comando acima utiliza duas variáveis que necessita de definir ou substituir pelo seu valor real:
      NOME_SERVIDOR é o valor do parâmetro DBSERVERNAME do $INFORMIXDIR/etc/$ONCONFIG
      MINHA_PASSWORD é a palavra chave usada para encriptar e aceder à base de dados de chaves/certificados (keystore)
      Estamos a criar o ficheiro do tipo "cms" em $INFORMIXDIR/ssl com o nome do motor e pedimos a criação de um ficheiro stash que permitirá ao motor obter a password para aceder à keystore sem que tenhamos de especificar a password novamente. Naturalmente este ficheiro é sensível do ponto de vista da segurança.
    2. Depois necessitamos de criar um ficheiro de certificado e guardá-lo na keystore que criámos. Para efeitos de teste criamos um certificado auto-assinado. Configurações reais provavelmente usariam uma CA (Certificate Authority) para assinar os certificados.
      A CA poderia ser external ou uma implementada na empresa (dependendo das necessidades)

      gsk8capicmd -cert -create -db $INFORMIXDIR/ssl/${NOME_SERVIDOR}.kdb -pw ${MINHA_PASSWORD} -label ${NOME_SERVIDOR}_label -dn "CN=`hostname`" -size ${TAMANHO_CHAVE} -default_cert yes -expire ${EXPIRA}

      As variáveis que necessitam de ser substituidas por valores são:

      NOME_SERVIDOR and MINHA_PASSWORD como explicado acima
      TAMANHO_CHAVE para especificar o tamanho da chave (ex: 1024)
      EXPIRA para definir o número de dias de duração do certificado. Por omissão deverá ser 365 (um ano)
      Note que estamos a definir uma "label" ou etiqueta para este certificado ( ${NOME_SERVIDOR}_label) para que o motor possa identificar qual o certificado que usará dos que estão guardados na keystore.
      Esta etiqueta será usada na configuração do motor (próximo passo)
    3. Configurar a etiqueta que define qual o certificado a usar pelo motor. Isto é feito definindo o parâmetro SSL_KEYSTORE_LABEL:

      SSL_KEYSTORE_LABEL panther_label
    4. Criar uma entrada no $INFORMIXSQLHOSTS para SSL (ex: panther_ssl) usando o protocolo onsocssl
    5. Configurar uma entrada NETTYPE no $INFORMIXDIR/etc/$ONCONFIG:
      NETTYPE socssl

      NETTYPE        onsocssl,1,20,NET
    6. Configurar um (ou mais) processadores virtuais para SSL:

      VPCLASS ssl,num=1,noage
    7. Garantir que a nova porta SSL está configurada como DBSERVERNAME ou uma entrada no DBSERVERALIAS
    8. Extrair o certificado de forma que possa ser importado para os repositórios dos clientes (para certificados auto-assinados):

      gsk8capicmd -cert -extract -db $INFORMIXDIR/ssl/${NOME_SERVIDOR}.kdb -format ascii -label ${NOME_SERVIDOR}_label -pw ${MINHA_PASSWORD} -target ${NOME_SERVIDOR}_label.cert
  • Do lado do cliente
    1. Criar a keystore do lado do cleinte:

      gsk8capicmd -keydb -create -db $INFORMIXDIR/etc/clientdb.kdb -pw ${MINHA_PASSWORD} -type cms -stash
    2. Importar o certificado auto-assinado que extraímos no último passo do lado do servidor:

      gsk8capicmd -cert -add -db $INFORMIXDIR/etc/clientdb.kdb -pw ${MINHA_PASSWORD} -label ${SERVER_NAME}_label -file ${NOME_SERVIDOR}_label.cert -format ascii

      note que o caminho especificado depois da opção "-file" tem de apontar para o ficheiro extraído da keystore do servidor. Poderá ter de o copiar por FTP/SCP ou usando qualquer outro método de transferência.
    3. Numa configuração mais rigorosa deveria importar o certificado da CA usada para assinar os certificados usados nos servidores a que o cliente se pretenda ligar
    4. Configurar o ficheiro $INFORMIXSQLHOSTS do cliente com a nova entrada que usa o porto SSL
    5. Criar um ficheiro com o nome $INFORMIXDIR/etc/conssl.cfg com o seguinte conteúdo:

      SSL_KEYSTORE_FILE   /usr/informix/client_INFORMIXDIR/etc/clientdb.kdb   # Keystore file
      SSL_KEYSTORE_STH    /usr/informix/client_INFORMIXDIR/etc/clientdb.sth   # Keystore stash file

      Isto indica aos clientes qual a keystore e o ficheiro de stash
    6. Garanta que o ficheiro da keystore e o stash têm as permissões adequadas (não permitir o acesso a pessoas não autorizadas)

O problema

Assim que tiver um ambiente com SSL ativado, deverá conseguir conectar os clientes (aos quais tem de disponibilizar o certificado). E então estará nas condições para encontrar o problema. Como terá notado, os certificados têm um período de vida. Não são válidos antes de uma data e não são válidos depois de outra data. Por omissão a data inicial é o momento em que cria o certificado e a duração típica é um ano. O que acontece findo esse período? O cliente já tinha descoberto... Novas conexões não serão estabelecidas (compreensível dado que o certificado está expirado), mas o problema é que não o pode revalidar com o motor a funcionar. Se tentar conectar-se com um certificado expirado terá a seguinte mensagem no online.log:


15:27:18  listener-thread: err = -28014: oserr = 0: errstr = GSK_ERROR_BAD_DATE: Secure Sockets Layer error: GSK_ERROR_BAD_DATE.

A mensagem de erro correspondente é:
-28014    Secure Sockets Layer error.

An error occurred during a Secure Socket Layer operation. For more information,
refer to the accompanying IBM Global Security Kit (GSKit) error message.

Em resumo, o problema é como evitar ficar numa situação em que se tenha um motor a funcionar com um certificado expirado

Como evitar

Em termos gerais, uma forma de evitar isto será manter um registo das datas de expiração. Antes de os certificados expirarem devemos criar novos e agendar uma paragem do servidor para efectuar a mudança. Se tiver uma janela de manutenção regular, isto será fácil. Mas se o seu sistema é 24x7 a paragem torna-se um problema. Vou partir a solução em três partes:

  1. Deve criar um certificado se seja suficientemente seguro por um determinado tempo. Este período deve ser suficiente para acomodar atrasos no agendamento da paragem do servidor de forma que possa esperar por outras razões de paragem (manutenção de SO, um crash, uma paragem mensal/trimestral ou anual). Isto provavelmente significa que terá de criar um certificado que dure mais tempo que aquele com que o deseja renovar. Por exemplo, se quiser mudar os certificados de seis em seis meses, mas só tem autorização para fazer uma paragem anual... Por isso, para ficar descansado poderá criar os certificados com um tempo de vida de 18 meses. Deve considerar o tamanho da chave necessário para garantir a segurança do certificado para o período desejado. O seguinte site poderá ajudar nisso:

    http://www.keylength.com/en/

  2. Necessita de preparar tudo o que seja possível sem parar o motor, antes da paragem programada. A ideia é que a paragem seja tão breve e simples quanto possível.. Isto incluí a criação do novo certificado e guardá-lo na mesma keystore, desde que utilize outra etiqueta (label). Isto pode ser feito antes da paragem enquanto o motor está a funcionar. Desta forma a única coisa que será necessário fazer durante a paragem é mudar o parâmetro SSL_KEYSTORE_LABEL.
  3. Deve criar um sistema que mantenha o rasto e que o alerte com antecedência sempre que um certificado estiver para expirar. Tem duas formas de o fazer: Pode guardar um identificador de certificado (por exemplo a etiqueta - label -) e a sua data de expiração numa base de dados, e periodicamente executar um processo que o alerte se algum dos certificados expirar num futuro próximo (ex: 3 meses). A outra opção é criar um alarm que verifique o certificado de cada um dos seus servidores e o alerte quando a expiração estiver próxima (por email, sistema de monitorização etc.)
Para ajudar no ponto 3 acima, criei um pequeno script que pode ser alterado/adaptado às suas necessidades específicas:

1    #!/bin/sh
2   
3    SERVER_NAME=`onstat -c | grep DBSERVERNAME | grep -v "^[     ]*#" | awk '{print $2}'`
4    SERVER_LABEL=`onstat -c | grep SSL_KEYSTORE_LABEL | grep -v "^[     ]*#" | awk '{print $2}'`
5   
6    MYDATE=`gsk8capicmd -cert -details -locale en -db $INFORMIXDIR/ssl/$SERVER_NAME.kdb -stashed -label ${SERVER_LABEL} | grep After | awk '{print $4 " " $5 " " $6}'`
7   
8    NUM_DAYS=`dbaccess sysmaster <<EOF 2>/dev/null| grep "DAYS:" | awk -F':' '{print $2+0}'
9    select "DAYS:"||extend(TO_DATE("$MYDATE", "%B %d, %Y"), YEAR TO DAY) - extend(TODAY,YEAR TO DAY) FROM sysmaster:sysdual;
10    EOF`
11   
12   
13    if [ $NUM_DAYS -gt 0 ]
14    then
15        echo "O certificado está válido por $NUM_DAYS dias"
16    else
17        echo "O certificado está expirado!"
18    fi

Deixando de lado os truques e particularidades da SHELL, o que o script está a fazer é bastante simples:
  1. Obtém os parâmetros DBSERVERNAME e SSL_KEYSTORE_LABLE do $ONCONFIG
  2. Chama o comando gdk8capicmd para obter os detalhes do certificado. Extrai a linha "Not After :". Note que uso a opção "-locale en" para definir o formato da data
  3. Usa o Informix com um script inline de SQL para calcular o número de dias até à data de expiração do certificado

2 comments:

Sdas01 said...

we have created SSL certificate which expairs in year 2031 ( 20 years)

Sdas01 said...

we have created SSL certificate which expairs in year 2031. This give us enough time to decide if we will continue to use SSL in future or switch to some other technology.