Cómo asegurar un puerto PostgreSQL abierto

29

Entonces, esta es la situación. Parece que necesitamos tener un puerto TCP abierto 5432 para el mundo, donde un cliente tiene acceso a su base de datos PostgreSQL.

Por razones obvias, no podemos decir simplemente "no", solo como último recurso.

¿Cuáles son los mayores problemas? ¿Cómo puedo defender nuestra infraestructura?

De todos modos: ¿por qué no debería abrirse al mundo? Creo que quizás sea más seguro que un servidor FTP sin mantenimiento de 20 años.

PS VPN no está bien. Tal vez algún cifrado (si puedo darle una URL de conexión JDBC que funcione ).

Josip Rodin
fuente
44
¿Los túneles SSH no son una opción? Este artículo de procedimientos realmente utiliza PostgreSQL como ejemplo. Puede proporcionar al cliente un cliente SSH preconfigurado para facilitar la conexión.
Lucifer Sam
@LuciferSam No. El db será utilizado por una aplicación Java desarrollada internamente en alrededor de 100 máquinas de la compañía. Nuestra única forma de configurarlos es dar una url de conexión jdbc a su administración localnet, cualquier otra es muy problemática.
@milkman, ¿qué hace la aplicación? ¿tal vez podría consultar un servidor RESTful en su lugar? Obviamente, pasar SQL a REST no gana nada, pero suponiendo que sea
CRUDO
@ tedder42 Manipula la base de datos de los usuarios de CMS, que también alojamos nosotros. No tenemos permiso para cambiar su fuente.

Respuestas:

41

Requerir SSL, mantener SELinux activado, monitorear los registros y usar una versión actual de PostgreSQL .

Lado del servidor

Requerir SSL

En postgresql.confconjunto ssl=ony asegúrese de tener su archivo de claves y su archivo de certificados instalados adecuadamente (vea los documentos y los comentarios en postgresql.conf).

Es posible que deba comprar un certificado de una CA si desea que los clientes confíen en él sin una configuración especial en el cliente.

En pg_hba.confuso algo como:

hostssl theuser thedatabase 1.2.3.4/32 md5

... posiblemente con "todos" para usuario y / o base de datos, y posiblemente con un filtro de dirección IP de origen más amplio.

Limite a los usuarios que pueden iniciar sesión, denegar el inicio de sesión de superusuario remoto

No permita "todos" para los usuarios si es posible; no desea permitir inicios de sesión de superusuario de forma remota si puede evitar la necesidad de hacerlo.

Limitar los derechos de los usuarios.

Restrinja los derechos de los usuarios que pueden iniciar sesión. No les dé derechos CREATEDBni CREATEUSERderechos.

REVOKEel CONNECTderecho desde PUBLICtodas sus bases de datos, luego devuélvalas solo a los usuarios / roles que deberían poder acceder a esa base de datos. (Agrupe a los usuarios en roles y otorgue derechos a los roles, en lugar de directamente a los usuarios individuales).

Asegúrese de que los usuarios con acceso remoto solo puedan conectarse a las bases de datos que necesitan, y solo tengan derechos sobre los esquemas, tablas y columnas que realmente necesitan. Esta es una buena práctica para los usuarios locales también, es solo una seguridad sensata.

Configuración del cliente

En PgJDBC, pase el parámetrossl=true :

Para indicar al controlador JDBC que intente establecer una conexión SSL, debe agregar el parámetro URL de conexión ssl = true.

... e instale el certificado del servidor en el almacén de confianza del cliente, o use un certificado de servidor en el que confíe una de las CA en el almacén de confianza incorporado de Java si no desea que el usuario tenga que instalar el certificado.

Acción en curso

Ahora asegúrese de mantener PostgreSQL actualizado . PostgreSQL solo ha tenido un par de agujeros de seguridad previos a la autenticación, pero eso es más que cero, así que manténgase actualizado. De todos modos, deberías tener correcciones de errores.

Agregue un firewall al frente si hay grandes bloques de red / regiones de los que sabe que nunca necesita acceder.

Registro de conexiones y desconexiones (ver postgresql.conf). Registro de consultas si es práctico. Ejecute un sistema de detección de intrusos o fail2ban o similar al frente si es práctico. Para fail2ban con postgres, hay un procedimiento práctico aquí

Monitorear los archivos de registro.

Bonus paranoia

Pasos adicionales para pensar ...

Requerir certificados de cliente

Si lo desea, también puede utilizar pg_hba.confpara exigir que el cliente presente un certificado de cliente X.509 en el que confíe el servidor. No necesita usar la misma CA que el certificado del servidor, puede hacerlo con una CA homebrew openssl. Un usuario JDBC necesita importar el certificado del cliente en su Java Keystore keytooly posiblemente configurar algunas propiedades del sistema JSSE para apuntar Java a su almacén de claves, por lo que no es totalmente transparente.

Poner en cuarentena la instancia

Si desea ser realmente paranoico, ejecute la instancia para el cliente en un contenedor / VM separado, o al menos bajo una cuenta de usuario diferente, con solo las bases de datos que requieren.

De esa forma, si comprometen la instancia de PostgreSQL, no avanzarán más.

Use SELinux

No debería tener que decir esto, pero ...

Ejecute una máquina con soporte SELinux como RHEL 6 o 7, y no apague SELinux ni lo configure en modo permisivo . Manténgalo en modo obligatorio.

Use un puerto no predeterminado

La seguridad solo por oscuridad es estupidez. La seguridad que usa un poco de oscuridad una vez que haya hecho las cosas sensibles probablemente no le hará daño.

Ejecute Pg en un puerto no predeterminado para hacer la vida un poco más difícil para los atacantes automáticos.

Pon un proxy al frente

También puede ejecutar PgBouncer o PgPool-II frente a PostgreSQL, actuando como un grupo de conexiones y proxy. De esa manera, puede dejar que el proxy maneje SSL, no el host de la base de datos real. El proxy puede estar en una máquina virtual o máquina separada.

El uso de servidores proxy de agrupación de conexiones es generalmente una buena idea con PostgreSQL de todos modos, a menos que la aplicación cliente ya tenga un grupo integrado. La mayoría de los servidores de aplicaciones Java, Rails, etc. tienen agrupación integrada. Incluso entonces, un proxy de agrupación del lado del servidor es, en el peor de los casos, inofensivo.

Craig Ringer
fuente
3
Si el cliente tiene un $ IP estático, solo permita eso a través del firewall al puerto $ también.
user9517 es compatible con GoFundMonica el
¡Muchas gracias! Pgjdbc tiene este parámetro, pero solo puedo darle una url de conexión jdbc, y no estoy seguro de si funcionará con su aplicación java (propietaria, no borrable). Ok, si no, haré una nueva pregunta. Gracias tu respuesta detallada!
1
@lesto En realidad, creo que exponer una VPN aumenta enormemente la superficie de ataque en comparación con un solo servicio restringido. La gente olvida que la VPN se convierte en un canal de ataque para cualquier malware en las máquinas remotas para atravesar toda la seguridad del perímetro y llegar a las entrañas de la red. Solo los considero aceptables si se conectan a una DMZ que los considera tan tóxicos como los hosts de Internet.
Craig Ringer
1
@CraigRinger no estoy diciendo que elimine el resto de la protección, sino que encapsule el servicio en VPN
Lesto
1
@lesto Claro, de acuerdo, una VPN puede ser una capa adicional útil si no se trata como Magic Security Sauce, como desafortunadamente muchos administradores lo hacen.
Craig Ringer
2

Una extensión simple al impresionante plan de acción de Craigs:

Tal vez el usuario está utilizando solo un conjunto relativamente pequeño de proveedores de red (por ejemplo, su proveedor de red móvil mientras se mueve, su red de cable desde el hogar y el lugar de trabajo de IP saliente del trabajo).

La mayoría de los proveedores de red tienen muchas IP, pero no muchas subredes. Por lo tanto, puede proporcionar un filtro de iptables, que limita el acceso postgresql a los segmentos de red que utiliza su cliente. Esto redujo en gran medida las posibilidades de ataque de las fuentes de problemas de la red seleccionadas al azar.

Un escenario de soporte simple:

  1. Su cliente lo llama, "No puedo iniciar sesión" .
  2. Te enteras con una tcpdump -i eth0 -p tcp port 5432orden, de dónde viene.
  3. Con un whois 1.2.3.4puede obtener la dirección IP utilizada por esta ip. Por ejemplo, puede ser 1.2.3.0/24.
  4. Con un iptables -A INPUT -s 1.2.3.0/24 -p tcp --dport 5432 -j ACCEPT(o algo similar) permite las conexiones tcp con su nueva subred.

Hay un muy buen script perl llamado uifque puede proporcionar conjuntos de reglas de iptables declarables permanentes e intuitivos. (Google para "uif iptables").

Peter dice que reinstalar a Mónica
fuente
1
Idea interesante, pero eso suena un poco frágil.
nishantjr
@nishantjr Por supuesto, no es una solución independiente, solo una posibilidad de mejorar las cosas.
Peter dice reinstalar a Mónica el
Un enfoque más práctico podría ser incluir en la lista blanca a los ISP y / o países individuales, para conocer las formas de hacerlo, por ejemplo, stackoverflow.com/questions/16617607/…
Josip Rodin
1

Aquí hay una configuración bastante sencilla de Fail2ban para PostgreSQL basada en el CÓMO vinculado anteriormente, pero ajustada para funcionar realmente con paquetes de Ubuntu, detectar otra condición de error y omitir varios mensajes de depuración para hacerlo más rápido:

/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
fuente
1

Fail2ban es una herramienta poderosa, pero no confíe en que un filtro funcionará como está. Pruebe los filtros con la herramienta failregex y recuerde escapar de las comillas (es decir, "admin" sería \ "admin \"). Como ejemplo, probar la siguiente línea failregex del filtro desde mi /etc/log/postgresql/postgresql-9.3-main.log no funcionó para mí.

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

Lo anterior me dio

Failregex: 0 total

Tuve que actualizar el failregex para que coincida con el formato de registro.

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

Esto me dio un resultado positivo.

Failregex: 1 total

La prueba fail2ban-regex también se puede implementar en archivos de registro completos.

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

Lo anterior me dio el siguiente resultado positivo con el failregex actualizado.

Failregex: 169 en total

metros
fuente