como proteger uma porta aberta do PostgreSQL

29

Então, esta é a situação. Parece que precisamos ter uma porta TCP aberta 5432 para o mundo, onde um cliente tenha acesso ao banco de dados PostgreSQL.

Por razões óbvias, não podemos dizer apenas "não", apenas como último recurso.

Quais são os maiores problemas? Como posso defender nossa infraestrutura?

Enfim: por que não deveria ser aberto ao mundo? Eu acho que talvez seja mais seguro do que um servidor FTP sem manutenção de 20 anos.

PS VPN não está bem. Alguma criptografia talvez (se eu puder fornecer a ele um URL de conexão JDBC que funcione ).

Josip Rodin
fonte
4
Túneis SSH não são uma opção? Este artigo de instruções realmente usa o PostgreSQL como exemplo. Você pode fornecer ao cliente um cliente SSH pré-configurado para facilitar a conexão.
Lucifer Sam,
@LuciferSam Não. O banco de dados será usado por um aplicativo java desenvolvido internamente em cerca de 100 máquinas da empresa. Nossa única maneira de configurá-los é fornecer um URL de conexão jdbc para a administração localnet, qualquer outro é muito, muito problemático.
@milkman o que o aplicativo faz? talvez ele possa consultar um servidor RESTful? Obviamente, passando SQL para descansar não ganha nada, mas assumindo que é CRUD ..
tedder42
@ tedder42 Ele manipula o banco de dados do CMS dos usuários, que também é hospedado por nós. Não temos permissão para alterar sua fonte.

Respostas:

41

Exija SSL, mantenha o SELinux ativado, monitore os logs e use uma versão atual do PostgreSQL .

Lado do servidor

Exigir SSL

Em postgresql.confset ssl=one verifique se você possui o arquivo de chave e o arquivo de certificação instalados adequadamente (consulte a documentação e os comentários em postgresql.conf).

Pode ser necessário comprar um certificado de uma CA se você deseja que ele seja confiável por clientes sem configuração especial no cliente.

Em pg_hba.confuso, algo como:

hostssl theuser thedatabase 1.2.3.4/32 md5

... possivelmente com "all" para usuário e / ou banco de dados e possivelmente com um filtro de endereço IP de origem mais amplo.

Limite de usuários que podem efetuar login, negar login remoto de superusuário

Não permita "todos" para os usuários, se possível; você não deseja permitir logins de superusuário remotamente, se puder evitar a necessidade.

Limitar direitos dos usuários

Restrinja os direitos do (s) usuário (s) que podem efetuar login. Não dê a eles CREATEDBou CREATEUSERdireitos.

REVOKEà CONNECTdireita PUBLICem todos os seus bancos de dados e devolva-os apenas aos usuários / funções que devem poder acessar esse banco de dados. (Agrupe usuários em funções e conceda direitos a elas, em vez de diretamente a usuários individuais).

Verifique se os usuários com acesso remoto podem conectar-se apenas aos bancos de dados de que precisam e ter apenas direitos aos esquemas, tabelas e colunas dentro dos quais realmente precisam. Essa é uma boa prática para usuários locais também, é apenas uma segurança sensata.

Configuração do cliente

No PgJDBC, passe o parâmetrossl=true :

Para instruir o driver JDBC a tentar estabelecer uma conexão SSL, você deve adicionar o parâmetro da URL de conexão ssl = true.

... e instale o certificado do servidor no armazenamento confiável do cliente ou use um certificado de servidor confiável por uma das CAs no armazenamento confiável interno do Java, se você não quiser que o usuário precise instalar o certificado.

Ação em andamento

Agora, certifique-se de manter o PostgreSQL atualizado . O PostgreSQL teve apenas algumas falhas de segurança pré-autenticação, mas isso é mais que zero, portanto, fique atualizado. Seja como for, correções de bugs são coisas boas de se ter.

Adicione um firewall na frente se houver grandes blocos de rede / regiões das quais você sabe que nunca precisa acessar.

Conexões e desconexões de log (consulte postgresql.conf). Registre consultas, se possível. Execute um sistema de detecção de intrusão ou fail2ban ou similar na frente, se possível. Para fail2ban com postgres, há um guia prático aqui

Monitore os arquivos de log.

Paranoia de bônus

Passos extras para pensar ...

Exigir certificados de cliente

Se desejar, também é possível pg_hba.confexigir que o cliente apresente um certificado de cliente X.509 confiável pelo servidor. Ele não precisa usar a mesma autoridade de certificação que o certificado do servidor; você pode fazer isso com uma autoridade de certificação openssl homebrew. Um usuário JDBC precisa importar o certificado do cliente para seu Java Keystore keytoole, possivelmente, configurar algumas propriedades do sistema JSSE para apontar Java para seu keystore, para que não seja totalmente transparente.

Colocar a instância em quarentena

Se você quiser ser realmente paranóico, execute a instância do cliente em um contêiner / VM separado ou, pelo menos, em uma conta de usuário diferente, apenas com os bancos de dados necessários.

Dessa forma, se eles comprometem a instância do PostgreSQL, não avançam mais.

Use o SELinux

Eu não deveria ter que dizer isso, mas ...

Execute uma máquina com suporte ao SELinux, como o RHEL 6 ou 7, e não desligue o SELinux ou configure-o para o modo permissivo . Mantenha-o no modo de imposição.

Use uma porta não padrão

Segurança apenas pela obscuridade é estupidez. A segurança que usa um pouco de obscuridade depois que você faz as coisas sensatas provavelmente não fará mal.

Execute a página em uma porta não padrão para tornar a vida um pouco mais difícil para invasores automatizados.

Coloque um proxy na frente

Você também pode executar o PgBouncer ou o PgPool-II na frente do PostgreSQL, atuando como um pool de conexão e proxy. Dessa forma, você pode deixar o proxy manipular o SSL, não o host real do banco de dados. O proxy pode estar em uma VM ou máquina separada.

O uso de proxies de pool de conexão geralmente é uma boa idéia com o PostgreSQL, a menos que o aplicativo cliente já tenha um pool embutido. A maioria dos servidores de aplicativos Java, Rails, etc. possui pool integrado. Mesmo assim, um proxy de pool no servidor é, na pior das hipóteses, inofensivo.

Craig Ringer
fonte
3
Se o cliente tiver um $ IP estático, permita apenas isso também através do firewall para $ port.
user9517 suporta GoFundMonica
Muito obrigado! O Pgjdbc possui esse parâmetro, mas posso fornecer a ele apenas um URL de conexão jdbc, e não tenho certeza se ele funcionará com o aplicativo java (proprietário, não removível). Ok, se não, vou fazer uma nova pergunta. Obrigado sua resposta detalhada!
11
@lesto Na verdade, acho que expor uma VPN aumenta enormemente a superfície de ataque em comparação com apenas um serviço restrito. As pessoas esquecem que a VPN se torna um canal de ataque para qualquer malware na (s) máquina (s) remota (s), perfurar toda a segurança do perímetro e chegar às entranhas da rede. Só os considero aceitáveis ​​se eles se conectam a uma DMZ que os trata como sendo tão tóxicos quanto os hosts da Internet.
Craig Ringer
11
@CraigRinger, não estou dizendo para remover o restante da proteção, mas para encapsular o serviço na VPN
Lesto
11
@lesto Certamente, uma VPN pode ser uma camada extra útil se não for tratada como Magic Security Sauce, como muitos administradores infelizmente fazem.
Craig Ringer
2

Uma simples extensão ao impressionante plano de ação de Craigs:

Talvez o usuário esteja usando apenas um conjunto relativamente pequeno de provedores de rede (por exemplo, seu provedor de rede móvel enquanto se move, sua rede a cabo da casa e do local de trabalho de saída do IP).

A maioria dos provedores de rede possui muitos IPs, mas não muitas sub-redes. Portanto, você pode fornecer um filtro iptables, que limita o acesso do postgresql aos segmentos de rede usados ​​pelo seu cliente. Isso reduziu bastante as possibilidades de ataque de fontes de problemas selecionadas aleatoriamente na rede.

Um cenário de suporte simples:

  1. Seu cliente chama você "Não consigo fazer login" .
  2. Você descobre com um tcpdump -i eth0 -p tcp port 5432comando, de onde ele está vindo.
  3. Com um whois 1.2.3.4você pode obter o endereço IP usado por este IP. Por exemplo, pode ser 1.2.3.0/24.
  4. Com um iptables -A INPUT -s 1.2.3.0/24 -p tcp --dport 5432 -j ACCEPT(ou algo parecido), você permite as conexões TCP com sua nova sub-rede.

Há um script perl muito bom chamado uifque pode fornecer conjuntos de regras declaráveis ​​permanentes e intuitivas para iptables. (Google para "uif iptables").

peterh diz restabelecer Monica
fonte
11
Idéia interessante, mas isso parece um pouco frágil.
Nishantjr 9/09/14
@nishantjr É claro que não é uma solução independente, apenas uma possibilidade de melhorar as coisas.
peterh diz restabelecer Monica
Uma abordagem mais prática pode ser a lista branca de ISPs e / ou países, para obter maneiras de fazer isso, consulte, por exemplo, stackoverflow.com/questions/16617607/…
Josip Rodin
1

Aqui está uma configuração bastante simples do Fail2ban para o PostgreSQL, com base no HOWTO vinculado acima, mas ajustado para realmente funcionar com pacotes Ubuntu, capturar outra condição de erro e pular várias mensagens de depuração para torná-lo mais 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
fonte
1

Fail2ban é uma ferramenta poderosa, mas não confie que um filtro funcione como está. Teste todos os filtros usando a ferramenta failregex e lembre-se de escapar de aspas (ou seja, "admin" seria \ "admin \"). Como exemplo, testar a seguinte linha de falha de erro de filtro do meu /etc/log/postgresql/postgresql-9.3-main.log não funcionou para mim.

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

O acima me deu

Failregex: 0 total

Eu tive que atualizar o failregex para corresponder ao formato do log.

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

Isso me deu um resultado positivo.

Regra de falha: 1 total

O teste fail2ban-regex também pode ser implementado em arquivos de log inteiros.

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

O exemplo acima me deu o seguinte resultado positivo com o failregex atualizado.

Failregex: 169 total

metros
fonte