25 octobre 2014

pg_hba + SSL



Dans les deux précédentes parties (pg_hba part1 et pg_hba part2) nous avons vu comment accepter les connections à un serveur Postgresql en local sans mot de passe, puis avec mot de passe, puis via le réseau. Le point faible de l’accès via le réseau est qu’il n’était pas obligatoirement crypté. Pourtant dans les environnements où le serveur n’est pas dans un réseau totalement sous contrôle (comme dans les hébergements  cloud) le cryptage s’impose.  C’est donc ce que nous allons faire maintenant : configurer et activer  l’authentification et le cryptage (via SSL) de notre serveur Postgresql  ET de notre client pgAdmin.

1) pg_hba.conf

Il n’y a qu'un seul mot à changer dans le fichier pg_hba.conf !
Il suffit de remplacer « host » par « hostssl » et on obtient une ligne du type :

hostssl     all    all    xx.xx.xx.xx/nn     md5

NOTES:
-    On parle ici de SSL, pas du SSH. Un tunnel  SSH est une autre technique utilisée quand le serveur et/ou le client ne supportent pas le cryptage de manière native.
-    Une ligne avec « host » accepte à la fois les connexions cryptées et non cryptées. Notre but étant de forcer le cryptage on ne doit pas garder de ligne « host » (sauf si elle est associée à des ip dont le chemin entre les clients et le serveur est totalement sous contrôle)


2) postgresql.conf

Là aussi pas grand-chose à faire. Il faut juste avoir la ligne « ssl = on » (et pas de ligne ssl =off !)

NB: Quand tout sera finit il faudra redémarrer le service postgresql.


3) Clés SSL coté serveur

Postgresql a besoin d’au moins 2 fichiers (server.crt et server.key) dans son répertoire data (PGDATA) pour pouvoir accepter les connexions SSL.  Comme le certificat du serveur sera signé par nous-même il n’y a pas besoin d’autres fichiers.
Le fichier server.key contient la clé privée du serveur. Ce fichier est secret et doit appartenir au daemon postgres et n’être lisible que par lui. Sinon ça ne marche pas !
Le fichier server.crt n’est pas secret, il contient le certificat du serveur. Il sera envoyé au client pour prouver l’identité du serveur. Il contient le nom du host où s’exécute Postgresql.

(voir plus bas le script bash genSrvKeyCrt qui génère automatiquement ces deux fichiers)


4) Clés SLL coté client

pgAdmin (ou autre client de Postgresql acceptant SSL) a besoin d’au moins 3 fichiers (root.crt, user-name.key user-name.crt).
Là aussi, le fichier user-name.key est une clé privée qui doit rester secrète et n’être lisible que par le client.
Le fichier user-name.crt est le certificat de l’utilisateur coté Postgresql. Il sera envoyé au serveur pour prouver l’identité de l’utilisateur. Il contient le nom de l’utilisateur coté Postgresql qui n’est pas forcément le même que l’utilisateur Unix ou Windows)
Le fichier root.crt est en fait exactement le même fichier que server.crt que l’on a déjà vu coté serveur.

(voir plus bas le script bash genCliKeyCrt qui génère automatiquement ces deux fichiers)


5) scripts + openssl

(ce qui suit est moins long qu'il ne parait ...)

Les deux fichiers coté serveur (server.crt et server.key) sont à générer en premier, et  une fois pour toute. Le fichier server.crt coté serveur servant de root.crt coté client pour tous les utilisateurs, si on régénère les fichiers coté serveur il faut régénérer tous les certificats des utilisateurs.

Les deux fichiers coté client (user-name.key  et user-name.crt) sont générés à la demande pour chaque utilisateur accepté par le serveur Postgresql. Ces fichiers étant associés à l’utilisateur de Postgresql ils peuvent être partagés par plusieurs utilisateurs Unix accédant à Postgresql de la même manière.

Pour simplifier la création de ces clés et certificats on va utiliser deux scripts shell qui enchaineront les commandes openssl nécessaires pour générer tous ces fichiers.

On suppose que les deux scripts sont quelque part dans le PATH et que l'on travaille dans cette arborescence :

...
  |
  +--> server/
  |     +--> server.key
  |     +--> server.crt
  |
  +-- clients/
       +--> user-name.key
       +--> user-name.crt
       +--> root.crt

En copiant les deux scripts ci-dessous attention à ne pas insérer d'espaces après les \ en fin de ligne.
 

======================
==== genSrvKeyCrt ====
======================
#!/bin/bash

HOST=$(hostname)

# Coté serveur on a besoin de deux fichiers.
#  1) server.key  (clé privée du serveur)
#  2) server.crt  (certificat/identité du serveur)

# 1.1) Création de la clé privé du serveur
openssl genrsa             \
        -des3              \
        -out server.key    \
        -passout pass:1234 \
        1024

# 1.2) Supprime la passphrase (1234) de la clé privé
openssl rsa             \
        -in server.key  \
        -out server.key \
        -passin pass:1234

# 1.3) Vérification
openssl rsa -in server.key -check -noout

# 2.1) Création d'un certificat auto certifié d'une validité de 10 ans
openssl req             \
        -new            \
        -x509           \
        -days 3650      \
        -key server.key \
        -out server.crt \
        -subj "/OU=pgserver/CN=$HOST"

# 2.2) Vérification
openssl verify -CAfile server.crt server.crt

# Afficher le certificat (facultatif)
# openssl x509 -in server.crt -text -noout


### end ###

L’exécution donne ceci :

    > cd /path/to/server
    > GenSrvKeyCrt.sh
    Generating RSA private key, 1024 bit long modulus
    ..................++++++
    ...++++++
    e is 65537 (0x10001)
    writing RSA key
    RSA key ok
    server.crt: OK


Copier server.crt et server.key dans le répertoire PGDATA du serveur Postgresql.
Veiller à ce qu'ils appartiennent à 'postgres' et qu'ils ne soit lisibles que par 'postgres'.

> ls -l /path/to/postgres/data/server.*
-rw------- 1 postgres postgres 769 Oct 24 19:56 /home/postgres/data/server.crt
-rw------- 1 postgres postgres 887 Oct 24 19:56 /home/postgres/data/server.key


REDEMARRER le service postgresql avec : systemctl reload postgresql


======================
==== GenCliKeyCrt ====
======================
#!/bin/bash

if [ "$1" == "" ]; then
  echo
  echo "   Usage: $0 pgUserName"
  echo "Exemples: $0 jean"
  echo "          $0 postgres"
  echo
  exit
fi

PGUSER=$1

echo
echo "Création de la clé et du certificat pour $PGUSER"
echo

# Coté client on a besoin de 3 fichiers
#  1) root.crt   (l’autorité de certification (nous!))
#  2) .key (clé privée de l'utilisateur)
#  3) .crt (certificat/identité de l'utilisateur)


# 1) importer root.crt du répertoire frère 'server'
cp ../server/server.crt root.crt

# 2.1) Création de la clé privée user-name.key pour le client
openssl genrsa             \
        -des3              \
        -out $PGUSER.key   \
        -passout pass:1234 \
        1024

# 2.2) Supprime la passphrase (1234) de la clé privée
openssl rsa                \
        -in  $PGUSER.key   \
        -out $PGUSER.key   \
        -passin pass:1234

# 2.3) Vérification
openssl rsa -in $PGUSER.key -check -noout

 

# 3.1) Création du certificat user-name.crt pour le client
openssl req                \
        -new               \
        -key $PGUSER.key   \
        -out $PGUSER.csr   \
        -subj "/OU=pgclient/CN=$PGUSER"

# 3.2) Signature par le serveur du certificat du client
openssl x509               \
       -req                \
       -CAcreateserial     \
       -in    $PGUSER.csr  \
       -out   $PGUSER.crt  \
       -CA    root.crt     \
       -CAkey ../server/server.key



#fichier maintenant inutile

rm $PGUSER.csr

# 3.3) Vérifications
openssl verify -CAfile root.crt $PGUSER.crt
openssl verify -CAfile root.crt ../server/server.crt

# Affiche le certificat (facultatif)
# openssl x509 -in $PGUSER.crt -text -noout


### end ###

L’exécution donne ceci :

    > cd /path/to/client
    > GenCliKeyCrt.sh  bidule

    Création de la clé et du certificat pour bidule

    Generating RSA private key, 1024 bit long modulus
    ...........................................++++++
    ..++++++
    e is 65537 (0x10001)
    writing RSA key
    RSA key ok
    Signature ok
    subject=/OU=client/CN=bidule
    Getting CA Private Key
    bidule.crt: OK
    ../server/server.crt: OK



Copier les trois fichiers (bidule.csrbidule.keyroot.crt) sur la machine client.
Puis configurer la partie SSL de pgAdmin comme indiqué ci-dessous.


La partie "Properties" est configurée comme d’habitude avec le nom du user (bidule) et son password. Mais maintenant tout sera crypté et le serveur comme le client se seront authentifié car partageant le même root.crt.

Le premier utilisateur pour le quel on fait tout cela est en général 'postgres'  et non 'bidule' ;-)


24 octobre 2014

Repository EPEL & REMI

Les packages de certaines applications ne sont pas disponibles sur les repositories configurés par défaut avec la distribution. Il faut alors aller les cherche sur d'autres repositories qu'il faut configurer soit même.

Par exemple, avec Centos / EL, le repository "EPEL" (Extra Packages for Enterprise Linux) ou les repositories de Remi Collet ne sont pas connus. Pour les installer il y a deux méthodes :  la facile et la moins facile !

Version facile (que pour EPEL)
On va essayer de voir si dans les repositories déjà configurés il y en a un qui contient la définition du repository EPEL.
On tape la commande :   yum search epel
On regarde dans ce qui s'affiche si le package existe et si oui, son nom exact.
Si le package existe on l'installe en général avec :  yum install epel-release
Si rien n’est trouvé passer à la version moins facile.

Version moins facile
Si la définition de EPEL n'est pas dans un package disponible il faut la télécharger puis l'installer à la main.
On va sur http://dl.fedoraproject.org/pub/epel
On choisit la version centos/el : 4, 5, 6, 7
On choisit l'architecture : i386, x86_64, ppc64
Si une liste de répertoires (a, b, c…) se présente on va dans le répertoire "e"
On cherche un package avec un nom du type  epel-release-X-Y.noarch.rpm  (le X et le Y étant variables)
On le télécharge sur la machine à configurer avec une commande du type (ici pour centos 7, 64 bits)
wget http://dl.fedoraproject.org/pub/epel/7/x86_64/e/epel-release-7-2.noarch.rpmEnfin on l'installe avec la commande : yum install epel-release-X-Y.rpm


Pour les repositories de Remy Collet il n'y a que la version moins facile qui marche, sachant qu'avec centos il faut déjà avoir installer EPEL. Le processus reste le même :
- trouver le rpm d'auto configuration (détails pour centos, fedora ici: http://rpms.famillecollet.com/)
- le télécharger (wget http://rpms.famillecollet.com/enterprise/remi-release-7.rpm)
- l'installer (yum install remi-release-7.rpm)


ATTENTION: Quand on a beaucoup de repositories configurés il peut y avoir des conflits. Quand on fait un yum update ou un yum install il faut bien vérifier le repository source.

Par défaut les repositories de Remi sont désactivés. Deux méthodes pour les activer :

- Editer le fichier /etc/yum.repos.d/remi.repo et y mettre le/les "enable=0" à "enable=1".


- Activer le bon repository au cas par cas. Par exemple :
yum --enablerepo=remi install nom-du-package 
yum --enablerepo=remi update  nom-du-package 
 
Pour voir les repositories actifs : yum repolist
Pour voir les repositories desactivés : yum repolist disabled




01 octobre 2014

mod_perl startup

Une fois que l'on a un couple {Apache2 + mod_perl2} adapté il faut faire une peu de configuration. Voici comment mettre en place mod_perl2  et verifier que tout marche.

Rappel: il faut au minimum mod_perl 2.0.9 pour Apache 2.4.x

1) Configurer Apache normalement pour qu'il affiche le fichier index.html de votre répertoire htdocs. Ceci est toujours utile de pouvoir servir des pages statiques sans faire appel à Perl.
NB: Inutile de configurer la partie cgi-bin, ou php ;-)


2) Quand l’étape #1 fonctionne ajouter cette simple ligne à la fin du fichier de configuration principale de apache (httpd.conf)

    Include conf/mod_perl2.conf

Ainsi on ne touchera plus au fichier de configuration principal, on travaillera sur mod_perl2.conf, dont voici le contenu de départ :

LoadModule perl_module modules/mod_perl.so

# la ligne suivante n'est utile que si on a installé le module
# Apache2::Request qui utilise APR::Request qui utilise libapreq2
#   yum install libapreq2
#   cpanm       APR::Request
#   cpanm       Apache2::Request
# (le but étant de ne plus utiliser CGI.pm)
LoadModule apreq_module modules/mod_apreq2.so

PerlPostConfigRequire /path/to/myCode/startup.pl
PerlOptions -SetupEnv
 

<Location /hello>
   SetHandler perl-script
   PerlResponseHandler Hello
   PerlOptions +ParseHeaders

</Location>

a) On charge le module mod_perl (c'est lui qui importe Perl dans Apache)
b) On désigne un script Perl à exécuter au démarrage de Perl (voir plus bas)
c) On désactive l'importation de l'environnement dans le contexte de chaque requête (plus sûr et plus rapide)
d) On annonce que l'url /hello sera traitée par le module Perl Hello.pm
Ce module gèrera lui-même les headers et il sera exécuté en tant que "perl-script" (bien que ce soit forcement un module)


3) Le module Hello.pm est très simple à écrire et à comprendre :

package Hello;
use strict;
use warnings;

sub handler {  # Le nom de la fonction est imposé
    print "Content-type: text/plain\n\n";
    print "Hello at : ". localtime . "\n";
    return Apache2::Const::OK;
}
1;


On le stocke dans /path/to/myCode/Hello.pm



4) Le script startup.pl ressemble à ceci

use lib qw(/path/to/myCode);   # voir "Attention #2" plus bas

use ModPerl::Util ();
use ModPerl::Registry ();
 
use Apache2::RequestRec ();
use Apache2::RequestIO ();
use Apache2::RequestUtil ();
use Apache2::ServerRec ();
use Apache2::ServerUtil ();
use Apache2::Connection ();
use Apache2::Log ();

use Apache2::Const -compile => ':common';

use APR::Table ();
use APR::Const -compile => ':common';

1;


Le plus important dans ce fichier est la première ligne car elle indique où seront vos modules Perl chargés de réponde aux requêtes. Les autres "use" préchargent des modules d'usage courant ce qui evitera de répéter ces "use" dans chaque module Perl.


Si vous devez ouvrir un accès à une base de donnée ce sera aussi dans ce script en utilisant Apache::DBI Exemple pour Postgresql

use Apache::DBI;
Apache::DBI->connect_on_init("dbi:Pg:dbname=myDB", 

  "myUSR", 
  "myPASS",
  {AutoCommit => 1, RaiseError => 1, PrintError => 1} 

);
Apache::DBI->setPingTimeOut("dbi:Pg:dbname=myDB", 30);


ATTENTION #1: Pour accéder à la base de donnée dans un module (handler) il faudra utiliser le classique $dbh = DBI->connect(...) Mais il faudra que tous les paramètres de connect() soit exactement les mêmes que ceux passé à connect_on_init();


ATTENTION #2: Dans le contexte de mod_perl2 "use FindBin;" ne marche pas comme espéré.
En effet $FindBin::Bin donne le point de démarrage du serveur Apache, pas celui de startup.pl.

A la place il faudra faire ce genre de gymnastique:

use File::Basename;
our $PERLBASE;
BEGIN {
  $PERLBASE = dirname( __FILE__ );
}
use lib $PERLBASE;



Dans tous les modules (handlers) vous pourrez accéder simplement à $::PERLBASE