comment sécuriser un port PostgreSQL ouvert

29

Voilà donc la situation. Il semble que nous devons avoir un port TCP 5432 ouvert sur le monde, où un client a accès à sa base de données PostgreSQL.

Pour des raisons évidentes, nous ne pouvons pas dire simplement «non», uniquement en dernier recours.

Quels sont les plus gros problèmes? Comment puis-je défendre notre infrastructure?

Quoi qu'il en soit: pourquoi ne devrait- il pas être ouvert au monde? Je pense qu'il est peut-être plus sécurisé qu'un serveur FTP non entretenu de 20 ans.

PS VPN n'est pas ok. Un cryptage peut-être (si je peux lui donner une URL de connexion JDBC qui fonctionne ).

Josip Rodin
la source
4
Les tunnels SSH ne sont pas une option? Cet article pratique utilise en fait PostgreSQL comme exemple. Vous pouvez fournir au client un client SSH préconfiguré pour faciliter la connexion.
Lucifer Sam
@LuciferSam Non. La base de données sera utilisée par une application Java développée en interne sur une centaine de machines de l'entreprise. Notre seule façon de les configurer est de donner une URL de connexion jdbc à leur administration localnet, toute autre est très-très problématique.
@milkman que fait l'application? peut-être pourrait-il plutôt interroger un serveur RESTful? Évidemment, passer SQL à REST ne rapporte rien, mais en supposant qu'il s'agit de CRUD ..
tedder42
@ tedder42 Il manipule la base de données des utilisateurs CMS, qui est également hébergée par nous. Nous n'avons pas la permission de changer sa source.

Réponses:

41

Exiger SSL, garder SELinux activé, surveiller les journaux et utiliser une version PostgreSQL actuelle .

Du côté serveur

SSL requis

Dans l' postgresql.confensemble ssl=onet assurez-vous que votre fichier de clés et votre certificat sont installés correctement (voir les documents et les commentaires dans postgresql.conf).

Vous devrez peut-être acheter un certificat auprès d'une autorité de certification si vous souhaitez qu'il soit approuvé par les clients sans configuration spéciale sur le client.

En pg_hba.confutilisation quelque chose comme:

hostssl theuser thedatabase 1.2.3.4/32 md5

... éventuellement avec "all" pour l'utilisateur et / ou la base de données, et éventuellement avec un filtre d'adresse IP source plus large.

Limiter les utilisateurs pouvant se connecter, refuser la connexion du superutilisateur distant

N'autorisez pas "tous" pour les utilisateurs si possible; vous ne voulez pas autoriser les connexions de superutilisateur à distance si vous pouvez en éviter le besoin.

Limiter les droits des utilisateurs

Restreindre les droits de l'utilisateur (s) qui peut se connecter. Ne pas leur donner CREATEDBou CREATEUSERdroits.

REVOKEle CONNECTdroit à partir PUBLICde toutes vos bases de données, puis restituez-le uniquement aux utilisateurs / rôles qui devraient pouvoir accéder à cette base de données. (Regroupez les utilisateurs en rôles et accordez des droits aux rôles, plutôt que directement aux utilisateurs individuels).

Assurez-vous que les utilisateurs avec accès à distance ne peuvent se connecter qu'aux bases de données dont ils ont besoin et ne disposent que des droits sur les schémas, les tables et les colonnes dont ils ont réellement besoin. C'est aussi une bonne pratique pour les utilisateurs locaux, c'est juste une sécurité raisonnable.

Configuration du client

Dans PgJDBC, passez le paramètressl=true :

Pour demander au pilote JDBC d'essayer d'établir une connexion SSL, vous devez ajouter le paramètre d'URL de connexion ssl = true.

... et installez le certificat de serveur dans le magasin de clés de confiance du client, ou utilisez un certificat de serveur approuvé par l'une des autorités de certification du magasin de clés de confiance intégré de Java si vous ne voulez pas que l'utilisateur doive installer le certificat.

Action en cours

Assurez-vous maintenant de maintenir PostgreSQL à jour . PostgreSQL n'a eu que quelques failles de sécurité avant l'authentification, mais c'est plus que zéro, alors restez à jour. Vous devriez de toute façon, les corrections de bugs sont de bonnes choses à avoir.

Ajoutez un pare-feu devant s'il y a de grands réseaux / régions dont vous savez que vous n'avez jamais besoin d'accéder.

Consigner les connexions et les déconnexions (voir postgresql.conf). Enregistrez les requêtes si possible. Exécutez un système de détection d'intrusion ou fail2ban ou similaire en façade si possible. Pour fail2ban avec postgres, il existe un guide pratique ici

Surveillez les fichiers journaux.

Paranoïa bonus

Étapes supplémentaires à considérer ...

Exiger des certificats clients

Si vous le souhaitez, vous pouvez également utiliser pg_hba.confpour exiger que le client présente un certificat client X.509 approuvé par le serveur. Il n'a pas besoin d'utiliser la même autorité de certification que le certificat du serveur, vous pouvez le faire avec une autorité de certification homebrew openssl. Un utilisateur JDBC doit importer le certificat client dans son keystore Java avec keytoolet éventuellement configurer certaines propriétés du système JSSE pour pointer Java vers son keystore, donc ce n'est pas totalement transparent.

Mettre l'instance en quarantaine

Si vous voulez être vraiment paranoïaque, exécutez l'instance pour le client dans un conteneur / VM séparé, ou au moins sous un autre compte utilisateur, avec uniquement la ou les bases de données dont ils ont besoin.

De cette façon, s'ils compromettent l'instance PostgreSQL, ils n'iront pas plus loin.

Utilisez SELinux

Je ne devrais pas avoir à dire cela, mais ...

Exécutez une machine avec le support SELinux comme RHEL 6 ou 7, et ne désactivez pas SELinux ou ne le mettez pas en mode permissif . Gardez-le en mode d'application.

Utiliser un port autre que celui par défaut

La sécurité par l' obscurité seulement est la bêtise. Une sécurité qui utilise un peu d'obscurité une fois que vous avez fait les choses sensées ne fera probablement pas de mal.

Exécutez Pg sur un port autre que celui par défaut pour rendre la vie un peu plus difficile aux attaquants automatisés.

Mettez un proxy devant

Vous pouvez également exécuter PgBouncer ou PgPool-II devant PostgreSQL, agissant comme un pool de connexions et un proxy. De cette façon, vous pouvez laisser le proxy gérer SSL, pas le véritable hôte de base de données. Le proxy peut se trouver sur une machine virtuelle ou une machine distincte.

L'utilisation de proxys de regroupement de connexions est généralement une bonne idée avec PostgreSQL de toute façon, à moins que l'application cliente n'ait déjà un pool intégré. La plupart des serveurs d'applications Java, Rails, etc. ont un pool intégré. Même dans ce cas, un proxy de pooling côté serveur est au pire inoffensif.

Craig Ringer
la source
3
Si le client a un $ IP statique, autorisez-le uniquement via le pare-feu à $ port.
user9517 prend en charge GoFundMonica
Merci beaucoup! Pgjdbc a ce paramètre, mais je ne peux lui donner qu'une URL de connexion jdbc, et je ne sais pas si cela fonctionnera avec son application java (propriétaire, non débogable). Ok, sinon je vais poser une nouvelle question. Merci votre réponse détaillée!
1
@lesto En fait, je pense que l'exposition d'un VPN augmente massivement la surface d'attaque par rapport à un seul service restreint. Les gens oublient que le VPN devient alors un canal d'attaque pour tout malware sur la ou les machines distantes pour traverser toute la sécurité du périmètre et accéder aux entrailles du réseau. Je ne les trouve acceptables que s'ils se connectent à une DMZ qui les traite comme étant tout aussi toxiques que les hôtes Internet.
Craig Ringer
1
@CraigRinger je ne dis pas de supprimer le reste de la protection, mais d'encapsuler le service dans VPN
Lesto
1
@lesto Bien sûr, d'accord, un VPN peut être une couche supplémentaire utile s'il n'est pas traité comme une sauce de sécurité magique, comme le font malheureusement de nombreux administrateurs.
Craig Ringer
2

Une extension simple du plan d'action impressionnant de Craigs:

Peut-être que l'utilisateur n'utilise qu'un petit nombre relatif de fournisseurs de réseau (par exemple, son fournisseur de réseau mobile lors de ses déplacements, son réseau câblé de la maison et du lieu de travail sortant de l'IP du travail).

La plupart des fournisseurs de réseau ont de nombreuses adresses IP, mais pas vraiment de sous-réseaux. Ainsi, vous pouvez donner un filtre iptables, ce qui limite l'accès postgresql aux segments de réseau utilisés par votre client. Cela a considérablement réduit les possibilités d'attaque des sources de problèmes du réseau sélectionnées au hasard.

Un scénario de support simple:

  1. Votre client vous appelle "Je ne peux pas me connecter" .
  2. Vous découvrez avec une tcpdump -i eth0 -p tcp port 5432commande, d'où vient-il.
  3. Avec un, whois 1.2.3.4vous pouvez obtenir l'adresse IP utilisée par cette adresse IP. Par exemple, cela peut l'être 1.2.3.0/24.
  4. Avec un iptables -A INPUT -s 1.2.3.0/24 -p tcp --dport 5432 -j ACCEPT(ou un similaire), vous autorisez les connexions TCP avec son nouveau sous-réseau.

Il existe un très bon script perl nommé uifqui peut fournir des ensembles de règles iptables déclarables permanents et intuitifs. (Google pour "uif iptables").

peterh dit réintégrer Monica
la source
1
Idée intéressante, mais cela semble un peu fragile cependant.
nishantjr
@nishantjr Bien sûr, ce n'est pas une solution autonome, seulement une possibilité d'améliorer les choses.
Peterh dit réintégrer Monica le
Une approche plus pratique pourrait être de mettre sur liste blanche les différents FAI et / ou pays, pour des moyens de le faire, voir par exemple stackoverflow.com/questions/16617607/…
Josip Rodin
1

Voici une configuration Fail2ban assez simple pour PostgreSQL basée sur le HOWTO lié ci-dessus mais optimisée pour fonctionner réellement avec les packages Ubuntu, détecter une autre condition d'erreur et ignorer divers messages de débogage pour le rendre plus rapide:

/etc/fail2ban/filter.d/local-postgresql.conf:

[Definition]

failregex = <HOST>\(\d+\) FATAL:  password authentication failed for .+$
            <HOST>\(\d+\) FATAL:  no pg_hba.conf entry for host .+$

ignoreregex = duration:

/etc/fail2ban/jail.d/local-postgresql.conf:

[local-postgresql]
enabled  = true
filter   = local-postgresql
action   = iptables[name=PostgreSQL, port=5432, protocol=tcp]
           sendmail-whois[name=PostgreSQL, dest=root@localhost]
logpath  = /var/log/postgresql/postgresql-9.3-main.log
maxretry = 3
Josip Rodin
la source
1

Fail2ban est un outil puissant, mais ne croyez pas qu'un filtre fonctionnera tel quel. Testez tous les filtres à l'aide de l' outil failregex et n'oubliez pas d'échapper à toutes les citations (c'est-à-dire "admin" serait \ "admin \"). Par exemple, le test de la ligne de failregex de filtre suivante à partir de mon /etc/log/postgresql/postgresql-9.3-main.log n'a pas fonctionné pour moi.

fail2ban-regex '2016-09-20 14:30:09 PDT FATAL:  password authentication failed for user "admin"' '<HOST>\(\d+\) FATAL:  password authentication failed for .+$'

Ce qui précède m'a donné

Failregex: 0 au total

J'ai dû mettre à jour le failregex pour correspondre au format de journal.

fail2ban-regex '2016-09-20 14:30:09 PDT FATAL:  password authentication failed for user "admin"' 'FATAL:  password authentication failed for user \"<HOST>\"'

Cela m'a donné un résultat positif.

Failregex: 1 au total

Le test fail2ban-regex peut également être implémenté sur des fichiers journaux entiers.

fail2ban-regex /var/log/postgresql/postgresql-9.3-main.log /etc/fail2ban/filter.d/postgresql.local

Ce qui précède m'a donné le résultat positif suivant avec le failregex mis à jour.

Failregex: 169 au total

mètresales
la source