O PostgreSQL há muito tempo suporta conexões SSL e também mecanismos de autenticação baseados em certificados. Embora nada nesse sentido pareça ser novo para o mundo PostgreSQL. No entanto, um pequeno problema irritante para a conexão do cliente (autenticação baseada em certificados do cliente) era um prompt "Digite a frase secreta do PEM:" para a chave do cliente criptografada.
Um novo recurso no PostgreSQL 13 complementa o parâmetro de servidor 'ssl_passphrase_command'. Enquanto o parâmetro ssl_passphrase_command permite que os administradores do servidor especifiquem uma senha para chaves de servidor criptografadas usadas para certificados de servidor; o parâmetro de conexão recém-introduzido 'sslpassword' fornece um controle semelhante para conexões de clientes.
Um olhar sobre a infraestrutura
Para fazer um exercício prático para esta análise de recursos, estabeleci um sistema bastante básico:
- Duas máquinas virtuais
- pgServer ( 172.25.130.189 )
- pgClient ( 172.25.130.178 )
- Certificados autoassinados no pgServer
- PostgreSQL 13 instalado em ambas as máquinas
- gcc para compilar um programa libpq de amostra
Configurando o servidor
Para analisar o recurso, vamos primeiro configurar uma instância do servidor PostgreSQL 13 com os certificados relevantes e a respectiva configuração na máquina virtual pgServer.
[[email protected]]$ echo ${HOME}
/var/lib/pgsql/
[[email protected]]$ mkdir ~/server_certs/
[[email protected]]$ openssl genrsa -des3 -passout pass:secretserverpass -out ~/server_certs/server.key
[[email protected]]$ openssl req -new -key ~/server_certs/server.key -days 365 -out ~/server_certs/server.crt -x509 -subj "/C=AU/ST=NSW/L=DY/O=MyOrg/OU=Dev/CN=pgServer"
Enter pass phrase for /var/lib/pgsql/server_certs/server.key:
[[email protected]]$ chmod 0600 /var/lib/pgsql/server_certs/server.key
[[email protected]]$ cp ~/server_certs/server.crt ~/server_certs/root.crt
Os comandos acima estão gerando um certificado autoassinado usando uma chave que é protegida por uma senha. As permissões do server.key são restritas conforme exigido pelo PostgreSQL. Configurar a instância do PostgreSQL para usar esses certificados não é mágica agora. Primeiro crie uma pasta DATA base usando:
[[email protected]]$ initdb
e cole os seguintes parâmetros de configuração no postgresql.conf gerado:
ssl=on
ssl_cert_file='/var/lib/pgsql/server_certs/server.crt'
ssl_key_file='/var/lib/pgsql/server_certs/server.key'
ssl_ca_file='/var/lib/pgsql/server_certs/root.crt'
ssl_passphrase_command = 'echo secretserverpass'
listen_addresses = '172.25.130.189'
E também certifique-se de que uma conexão SSL do nó pgClient seja aceita e possa usar o mecanismo de autenticação de certificado colando a seguinte linha no pg_hba.conf gerado:
hostssl all all 172.25.130.178/32 cert clientcert=1
Tudo o que é necessário agora é iniciar o servidor com a configuração acima usando o comando pg_ctl:
[[email protected]]$ pg_ctl start
Configurando o cliente
O próximo passo seria gerar certificados de cliente que são assinados pelos certificados de servidor acima mencionados:
[[email protected]]$ mkdir ~/client_certs/
[[email protected]]$ openssl genrsa -des3 -passout pass:secretclientpass -out ~/client_certs/postgresql.key
[[email protected]]$ openssl req -new -key ~/client_certs/postgresql.key -out ~/client_certs/postgresql.csr -subj "/C=AU/ST=NSW/L=DY/O=MyOrg/OU=Dev/CN=postgres"
Enter pass phrase for ~/client_certs/postgresql.key:
Na etapa acima, uma chave de cliente criptografada e um CSR para o certificado de cliente são gerados. As etapas a seguir completam um certificado de cliente assinando-o usando o certificado raiz do servidor e a chave do servidor.
[[email protected]]$ openssl x509 -req -in ~/client_certs/postgresql.csr -CA ~/server_certs/root.crt -CAkey ~/server_certs/server.key -out ~/client_certs/postgresql.crt -CAcreateserial
Signature ok
subject=/C=AU/ST=NSW/L=DY/O=MyOrg/OU=Dev/CN=postgres
Getting CA Private Key
Enter pass phrase for /var/lib/pgsql/server_certs/server.key:
Um aspecto importante a ser lembrado é o nome do CN nos certificados. Considere-o mais como uma identificação ou nome da entidade. No certificado de cliente acima, se o CN estiver definido como 'postgres', ele é destinado a uma função chamada postgres. Além disso, ao configurar o certificado do servidor, usamos CN=pgServer; pode ser importante quando usamos um modo de verificação completa da conexão SSL.
Hora de copiar os certificados para a máquina cliente para experimentar a conexão SSL:
[[email protected]]$ scp -r client_certs/* [email protected]:~/.postgresql
Por padrão em ambientes Linux/Unix, quando o psql é usado para fazer conexões SSL, ele procura certificados/chaves em ‘${HOME}/.postgresql’ do usuário atual. Todos esses arquivos também podem ser especificados nos parâmetros de conexão - No entanto, isso teria obscurecido o que queremos testar.
Na máquina pgClient, altere a permissão do postgresql.key para garantir que o PostgreSQL aceite o mesmo.
[[email protected]]$ chmod 0600 ~/.postgresql/postgresql.key
Testando o recurso
Parâmetro de conexão PSQL
Estamos praticamente prontos com a configuração do ambiente. Vamos tentar fazer uma conexão SSL:
[[email protected]]$ psql "host=172.25.130.189 port=5432 user=postgres dbname=postgres sslmode=prefer"
Enter PEM pass phrase:
Bem! Tudo começou apenas com o prompt acima. Se tivermos um programa em lote ou um script de automação, o prompt será um pouco complicado de manusear. Com a nova adição do parâmetro 'sslpassword' na string de conexão, agora é fácil especificá-lo conforme abaixo:
[[email protected]]$ psql "host=172.25.130.189 port=5432 user=postgres dbname=postgres sslmode=prefer sslpassword=secretclientpass"
A conexão deve ser bem-sucedida depois disso, sem nenhum prompt.
Libpq Hook para senha SSL
A história continua - há adição de uma função de gancho 'PQsetSSLKeyPassHook_OpenSSL' na interface Libpq. Isso pode ser usado por aplicativos cliente que podem não ter acesso à senha da chave e precisam gerar/buscar de uma interface externa usando alguma lógica complexa.
void PQsetSSLKeyPassHook_OpenSSL(PQsslKeyPassHook_OpenSSL_type hook);
Uma função de retorno de chamada do tipo PQsslKeyPassHook_OpenSSL_type pode ser registrada usando este gancho. O retorno de chamada será invocado pelo Libpq quando precisar obter uma senha. A assinatura de tal função de retorno de chamada deve ser:
int my_callback_function(char *buf, int size, PGconn *conn);
Abaixo está um exemplo de programa 'client_conn.c' - que demonstra a integração de tal gancho:
#include <stdlib.h>
#include <string.h>
#include "libpq-fe.h"
void do_exit(PGconn *conn) {
PQfinish(conn);
exit(1);
}
/**
* For PQsetSSLKeyPassHook_OpenSSL to provide password for SSL Key
**/
int ssl_password_provider(char *buf, int size, PGconn *conn)
{
const char * default_key_password = "secretclientpass";
strcpy(buf, default_key_password);
return strlen(default_key_password);
}
/**
* Sample program to make a connection and check server version
*/
int main()
{
PQsetSSLKeyPassHook_OpenSSL( ssl_password_provider );
PGconn *conn = PQconnectdb("host=172.25.130.189 port=5413 user=postgres dbname=postgres sslmode=prefer");
if (PQstatus(conn) == CONNECTION_BAD)
{
fprintf(stderr, "Connection to DB failed: %s\n", PQerrorMessage(conn));
do_exit(conn);
}
printf("Server version: %d\n", PQserverVersion(conn));
PQfinish(conn);
return 0;
}
Compile e execute o mesmo para verificar se realmente funciona:
[[email protected]]$ gcc -DUSE_OPENSSL -I/usr/pgsql-13/include/ -lpq -L/usr/pgsql-13/lib/ client_conn.c -o client_conn
[[email protected]]$ client_conn
[[email protected]]$ ./client_conn
Server version: 130000
Uma palavra final de cautela
O blog acima mostra uma pequena mas útil mudança nos parâmetros de conexão Libpq/psql para autenticação baseada em certificado no PostgreSQL. Mas, uma palavra de cautela - no exercício prático acima, usamos certificados autoassinados; pode não se encaixar muito bem em sua organização/ambiente de produção. Você pode tentar obter alguns certificados de terceiros para usar essa configuração de SSL.