PostgreSQL od dawna obsługuje połączenia SSL, a także mechanizmy uwierzytelniania oparte na certyfikatach. Chociaż nic w tym względzie nie wydaje się nowe dla świata PostgreSQL. Jednak małym dokuczliwym problemem związanym z połączeniem klienta (uwierzytelnianie oparte na certyfikatach klienta) był monit „Wprowadź frazę hasła PEM:” dla zaszyfrowanego klucza klienta.
Nowa funkcja w PostgreSQL 13 uzupełnia parametr serwera 'ssl_passphrase_command'. Podczas gdy parametr ssl_passphrase_command umożliwia administratorom serwerów określenie hasła do zaszyfrowanych kluczy serwera używanych do certyfikatów serwera; nowo wprowadzony parametr połączenia „sslpassword” zapewnia nieco podobną kontrolę nad połączeniami klientów.
Spojrzenie na infrastrukturę
Aby przejść przez praktyczne ćwiczenie dotyczące tej analizy funkcji, stworzyłem dość podstawowy system:
- Dwie maszyny wirtualne
- pgServer (172.25.130.189)
- pgClient (172.25.130.178)
- Certyfikaty z podpisem własnym na pgServer
- PostgreSQL 13 zainstalowany na obu komputerach
- gcc do kompilacji przykładowego programu libpq
Konfigurowanie serwera
Aby przeanalizować tę funkcję, skonfigurujmy najpierw instancję serwera PostgreSQL 13 z odpowiednimi certyfikatami i odpowiednią konfiguracją na maszynie wirtualnej 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
Powyższe polecenia generują samopodpisany certyfikat przy użyciu klucza chronionego hasłem. Uprawnienia do pliku server.key są ograniczone zgodnie z wymaganiami PostgreSQL. Konfigurowanie instancji PostgreSQL do korzystania z tych certyfikatów nie jest teraz niczym magicznym. Najpierw utwórz podstawowy folder DATA za pomocą:
[[email protected]]$ initdb
i wklej następujące parametry konfiguracyjne w wygenerowanym pliku postgresql.conf:
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'
A także upewnij się, że połączenie SSL z węzła pgClient jest akceptowane i może korzystać z mechanizmu uwierzytelniania certyfikatu, wklejając następujący wiersz w wygenerowanym pliku pg_hba.conf:
hostssl all all 172.25.130.178/32 cert clientcert=1
Teraz wystarczy uruchomić serwer z powyższą konfiguracją za pomocą polecenia pg_ctl:
[[email protected]]$ pg_ctl start
Konfigurowanie klienta
Następnym krokiem byłoby wygenerowanie certyfikatów klienta, które są podpisane przez wyżej wymienione certyfikaty serwera:
[[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:
W powyższym kroku generowany jest zaszyfrowany klucz klienta i CSR dla certyfikatu klienta. Poniższe kroki uzupełniają certyfikat klienta, podpisując go przy użyciu certyfikatu głównego serwera i klucza serwera.
[[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:
Jednym ważnym aspektem do zapamiętania jest nazwa CN w certyfikatach. Potraktuj to jako bardziej identyfikację lub nazwę podmiotu. W powyższym certyfikacie klienta, jeśli CN jest ustawiony na „postgres”, jest przeznaczony dla roli o nazwie postgres. Również podczas konfigurowania certyfikatu serwera użyliśmy CN=pgServer; może to mieć znaczenie, gdy używamy trybu weryfikacji pełnej połączenia SSL.
Czas na skopiowanie certyfikatów na komputer klienta w celu wypróbowania połączenia SSL:
[[email protected]]$ scp -r client_certs/* [email protected]:~/.postgresql
Domyślnie w środowiskach Linux/Unix, gdy psql jest używany do tworzenia połączeń SSL, wyszukuje certyfikat/klucze w „${HOME}/.postgresql” bieżącego użytkownika. Wszystkie te pliki można również określić w parametrach połączenia — jednak spowodowałoby to zachmurzenie rzeczy, którą chcemy przetestować.
Na komputerze pgClient zmień uprawnienia postgresql.key, aby upewnić się, że PostgreSQL akceptuje to samo.
[[email protected]]$ chmod 0600 ~/.postgresql/postgresql.key
Testowanie funkcji
Parametr połączenia PSQL
Właściwie skończyliśmy z konfiguracją środowiska. Spróbujmy nawiązać połączenie SSL:
[[email protected]]$ psql "host=172.25.130.189 port=5432 user=postgres dbname=postgres sslmode=prefer"
Enter PEM pass phrase:
Cóż! Wszystko zaczęło się tylko od powyższego monitu. Jeśli mamy program wsadowy lub skrypt automatyzacji, monit jest nieco trudny do obsłużenia. Dzięki nowemu dodaniu parametru „sslpassword” w ciągu połączenia można teraz łatwo to określić, jak poniżej:
[[email protected]]$ psql "host=172.25.130.189 port=5432 user=postgres dbname=postgres sslmode=prefer sslpassword=secretclientpass"
Po tym połączenie powinno się udać, bez żadnego monitu.
Podpięcie Libpq do hasła SSL
Historia toczy się dalej – dodano funkcję przechwytującą „PQsetSSLKeyPassHook_OpenSSL” w interfejsie Libpq. Może to być używane przez aplikacje klienckie, które mogą nie mieć dostępu do hasła klucza i muszą generować/pobierać z zewnętrznego interfejsu przy użyciu złożonej logiki.
void PQsetSSLKeyPassHook_OpenSSL(PQsslKeyPassHook_OpenSSL_type hook);
Funkcja oddzwaniania typu PQsslKeyPassHook_OpenSSL_type może zostać zarejestrowana za pomocą tego haka. Oddzwonienie zostanie wywołane przez Libpq, gdy będzie trzeba uzyskać hasło. Sygnatura takiej funkcji zwrotnej powinna wyglądać następująco:
int my_callback_function(char *buf, int size, PGconn *conn);
Poniżej znajduje się przykładowy program „client_conn.c” - który demonstruje integrację takiego hooka:
#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;
}
Skompiluj i uruchom to samo, aby sprawdzić, czy to naprawdę działa:
[[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
Ostatnie słowo przestrogi
Powyższy blog pokazuje małą, ale użyteczną zmianę w parametrach połączenia Libpq/psql dla uwierzytelniania opartego na certyfikacie w PostgreSQL. Ale słowo ostrzeżenia - w powyższym praktycznym ćwiczeniu używaliśmy certyfikatów z podpisem własnym; może nie pasować zbyt dobrze do Twojej organizacji/środowiska produkcyjnego. Możesz chcieć uzyskać certyfikaty innych firm, aby korzystać z takiej konfiguracji SSL.