PG-1419 Validate key provider access

This adds some validation to make sure we can access the key provider
when it's created to make the user experience a little nicer. The actual
access validation is very rudimentary for now but can easily be
expanded.
This commit is contained in:
Anders Åstrand 2025-04-16 13:29:01 +02:00 committed by AndersAstrand
parent 1b8513c21e
commit 157230de39
14 changed files with 110 additions and 5 deletions

View File

@ -206,8 +206,6 @@ To add a database specific provider:
pg_tde_add_database_key_provider_<TYPE>('provider_name', ... details ...)
```
Note that in these functions do not verify the parameters. For that, see `pg_tde_verify_key`.
### Changing providers
To change a value of a global provider:

View File

@ -160,4 +160,7 @@ SELECT id, provider_name FROM pg_tde_list_all_global_key_providers();
-1 | file-keyring
(1 row)
-- Creating a file key provider fails if we can't open or create the file
SELECT pg_tde_add_database_key_provider_file('will-not-work','/cant-create-file-in-root.per');
ERROR: Failed to open keyring file /cant-create-file-in-root.per: Permission denied
DROP EXTENSION pg_tde;

View File

@ -164,4 +164,7 @@ SELECT id, provider_name FROM pg_tde_list_all_global_key_providers();
-2 | file-keyring
(2 rows)
-- Creating a file key provider fails if we can't open or create the file
SELECT pg_tde_add_database_key_provider_file('will-not-work','/cant-create-file-in-root.per');
ERROR: Failed to open keyring file /cant-create-file-in-root.per: Permission denied
DROP EXTENSION pg_tde;

View File

@ -34,4 +34,7 @@ SELECT pg_tde_verify_key();
(1 row)
DROP TABLE test_enc;
-- Creating provider fails if we can't connect to kmip server
SELECT pg_tde_add_database_key_provider_kmip('will-not-work','127.0.0.1', 61, '/tmp/server_certificate.pem', '/tmp/client_key_jane_doe.pem');
ERROR: SSL error: BIO_do_connect failed
DROP EXTENSION pg_tde;

View File

@ -51,4 +51,7 @@ SELECT pg_tde_verify_key();
(1 row)
DROP TABLE test_enc;
-- Creating provider fails if we can't connect to vault
SELECT pg_tde_add_database_key_provider_vault_v2('will-not-work', :'root_token', 'http://127.0.0.1:61', 'secret', NULL);
ERROR: HTTP(S) request to keyring provider "will-not-work" failed
DROP EXTENSION pg_tde;

View File

@ -52,4 +52,7 @@ SELECT id, provider_name FROM pg_tde_list_all_global_key_providers();
SELECT pg_tde_delete_global_key_provider('file-keyring2');
SELECT id, provider_name FROM pg_tde_list_all_global_key_providers();
-- Creating a file key provider fails if we can't open or create the file
SELECT pg_tde_add_database_key_provider_file('will-not-work','/cant-create-file-in-root.per');
DROP EXTENSION pg_tde;

View File

@ -19,4 +19,7 @@ SELECT pg_tde_verify_key();
DROP TABLE test_enc;
-- Creating provider fails if we can't connect to kmip server
SELECT pg_tde_add_database_key_provider_kmip('will-not-work','127.0.0.1', 61, '/tmp/server_certificate.pem', '/tmp/client_key_jane_doe.pem');
DROP EXTENSION pg_tde;

View File

@ -31,4 +31,7 @@ SELECT pg_tde_verify_key();
DROP TABLE test_enc;
-- Creating provider fails if we can't connect to vault
SELECT pg_tde_add_database_key_provider_vault_v2('will-not-work', :'root_token', 'http://127.0.0.1:61', 'secret', NULL);
DROP EXTENSION pg_tde;

View File

@ -472,6 +472,8 @@ check_provider_record(KeyringProviderRecord *provider_record)
errmsg("Invalid provider options."));
}
KeyringValidate(provider);
pfree(provider);
}

View File

@ -61,6 +61,7 @@ typedef struct TDEKeyringRoutine
{
KeyInfo *(*keyring_get_key) (GenericKeyring *keyring, const char *key_name, KeyringReturnCodes *returnCode);
void (*keyring_store_key) (GenericKeyring *keyring, KeyInfo *key);
void (*keyring_validate) (GenericKeyring *keyring);
} TDEKeyringRoutine;
typedef struct FileKeyring
@ -91,5 +92,6 @@ extern void RegisterKeyProviderType(const TDEKeyringRoutine *routine, ProviderTy
extern KeyInfo *KeyringGetKey(GenericKeyring *keyring, const char *key_name, KeyringReturnCodes *returnCode);
extern KeyInfo *KeyringGenerateNewKeyAndStore(GenericKeyring *keyring, const char *key_name, unsigned key_len);
extern void KeyringValidate(GenericKeyring *keyring);
#endif /* KEYRING_API_H */

View File

@ -74,6 +74,7 @@ RegisterKeyProviderType(const TDEKeyringRoutine *routine, ProviderType type)
Assert(routine != NULL);
Assert(routine->keyring_get_key != NULL);
Assert(routine->keyring_store_key != NULL);
Assert(routine->keyring_validate != NULL);
kp = find_key_provider_type(type);
if (kp)
@ -148,3 +149,15 @@ KeyringGenerateNewKeyAndStore(GenericKeyring *keyring, const char *key_name, uns
return key;
}
void
KeyringValidate(GenericKeyring *keyring)
{
RegisteredKeyProviderType *kp = find_key_provider_type(keyring->type);
if (kp == NULL)
ereport(ERROR,
errmsg("Key provider of type %d not registered", keyring->type));
kp->routine->keyring_validate(keyring);
}

View File

@ -28,10 +28,12 @@
static KeyInfo *get_key_by_name(GenericKeyring *keyring, const char *key_name, KeyringReturnCodes *return_code);
static void set_key_by_name(GenericKeyring *keyring, KeyInfo *key);
static void validate(GenericKeyring *keyring);
const TDEKeyringRoutine keyringFileRoutine = {
.keyring_get_key = get_key_by_name,
.keyring_store_key = set_key_by_name
.keyring_store_key = set_key_by_name,
.keyring_validate = validate,
};
void
@ -143,3 +145,19 @@ set_key_by_name(GenericKeyring *keyring, KeyInfo *key)
}
close(fd);
}
static void
validate(GenericKeyring *keyring)
{
FileKeyring *file_keyring = (FileKeyring *) keyring;
int fd = BasicOpenFile(file_keyring->file_name, O_CREAT | O_RDWR | PG_BINARY);
if (fd < 0)
{
ereport(ERROR,
errcode_for_file_access(),
errmsg("Failed to open keyring file %s: %m", file_keyring->file_name));
}
close(fd);
}

View File

@ -26,10 +26,12 @@
static void set_key_by_name(GenericKeyring *keyring, KeyInfo *key);
static KeyInfo *get_key_by_name(GenericKeyring *keyring, const char *key_name, KeyringReturnCodes *return_code);
static void validate(GenericKeyring *keyring);
const TDEKeyringRoutine keyringKmipRoutine = {
.keyring_get_key = get_key_by_name,
.keyring_store_key = set_key_by_name
.keyring_store_key = set_key_by_name,
.keyring_validate = validate,
};
void
@ -204,3 +206,15 @@ get_key_by_name(GenericKeyring *keyring, const char *key_name, KeyringReturnCode
return key;
}
static void
validate(GenericKeyring *keyring)
{
KmipKeyring *kmip_keyring = (KmipKeyring *) keyring;
KmipCtx ctx;
kmipSslConnect(&ctx, kmip_keyring, true);
BIO_free_all(ctx.bio);
SSL_CTX_free(ctx.ssl);
}

View File

@ -70,10 +70,12 @@ static bool curl_perform(VaultV2Keyring *keyring, const char *url, CurlString *o
static void set_key_by_name(GenericKeyring *keyring, KeyInfo *key);
static KeyInfo *get_key_by_name(GenericKeyring *keyring, const char *key_name, KeyringReturnCodes *return_code);
static void validate(GenericKeyring *keyring);
const TDEKeyringRoutine keyringVaultV2Routine = {
.keyring_get_key = get_key_by_name,
.keyring_store_key = set_key_by_name
.keyring_store_key = set_key_by_name,
.keyring_validate = validate,
};
void
@ -300,6 +302,41 @@ cleanup:
return key;
}
static void
validate(GenericKeyring *keyring)
{
VaultV2Keyring *vault_keyring = (VaultV2Keyring *) keyring;
char url[VAULT_URL_MAX_LEN];
CurlString str;
long httpCode = 0;
/*
* Validate connection by listing available keys at the root level of the
* mount point
*/
snprintf(url, VAULT_URL_MAX_LEN, "%s/v1/%s/metadata/?list=true",
vault_keyring->vault_url, vault_keyring->vault_mount_path);
if (!curl_perform(vault_keyring, url, &str, &httpCode, NULL))
{
ereport(ERROR,
errmsg("HTTP(S) request to keyring provider \"%s\" failed",
vault_keyring->keyring.provider_name));
}
/* If the mount point doesn't have any secrets yet, we'll get a 404. */
if (httpCode != 200 && httpCode != 404)
{
ereport(ERROR,
errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("Listing secrets of \"%s\" at mountpoint \"%s\" failed",
vault_keyring->vault_url, vault_keyring->vault_mount_path));
}
if (str.ptr != NULL)
pfree(str.ptr);
}
/*
* JSON parser routines
*