From 408fc345fb5d09702952ee530b2b85c9034bf964 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20=C3=85strand?= Date: Wed, 14 May 2025 12:36:09 +0200 Subject: [PATCH] Add separate parameter for kmip client key Previously this was required to be in the same file as the client certificate. Co-authored-by: Zsolt Parragi --- ci_scripts/setup-keyring-servers.sh | 1 - .../pg_tde/documentation/docs/functions.md | 11 +++-- .../docs/how-to/multi-tenant-setup.md | 7 +-- .../documentation/docs/wal-encryption.md | 7 +-- contrib/pg_tde/expected/kmip_test.out | 4 +- contrib/pg_tde/pg_tde--1.0-rc.sql | 48 ++++++++++++------- contrib/pg_tde/sql/kmip_test.sql | 4 +- contrib/pg_tde/src/catalog/tde_keyring.c | 9 ++-- .../src/catalog/tde_keyring_parse_opts.c | 7 +++ .../pg_tde/src/include/keyring/keyring_api.h | 1 + contrib/pg_tde/src/keyring/keyring_kmip.c | 2 +- .../pg_tde/src/pg_tde_change_key_provider.c | 4 +- 12 files changed, 67 insertions(+), 38 deletions(-) diff --git a/ci_scripts/setup-keyring-servers.sh b/ci_scripts/setup-keyring-servers.sh index c59ee21970c0b..81caf4003f89c 100755 --- a/ci_scripts/setup-keyring-servers.sh +++ b/ci_scripts/setup-keyring-servers.sh @@ -6,7 +6,6 @@ cd /tmp wget https://raw.githubusercontent.com/OpenKMIP/PyKMIP/refs/heads/master/bin/create_certificates.py python3 create_certificates.py -cat client_certificate_jane_doe.pem >> client_key_jane_doe.pem mkdir policies cd policies diff --git a/contrib/pg_tde/documentation/docs/functions.md b/contrib/pg_tde/documentation/docs/functions.md index f7bc4647b8990..3a13bf29a7961 100644 --- a/contrib/pg_tde/documentation/docs/functions.md +++ b/contrib/pg_tde/documentation/docs/functions.md @@ -106,15 +106,15 @@ The KMIP provider uses a remote KMIP server. Use these functions to add a KMIP provider: ```sql -SELECT pg_tde_add_database_key_provider_kmip('provider-name','kmip-addr', `port`, '/path_to/server_certificate.pem', '/path_to/client_key.pem'); -SELECT pg_tde_add_global_key_provider_kmip('provider-name','kmip-addr', `port`, '/path_to/server_certificate.pem', '/path_to/client_key.pem'); +SELECT pg_tde_add_database_key_provider_kmip('provider-name','kmip-addr', `port`, '/path_to/server_certificate.pem', '/path_to/client_cert.pem', '/path_to/client_key.pem'); +SELECT pg_tde_add_global_key_provider_kmip('provider-name','kmip-addr', `port`, '/path_to/server_certificate.pem', '/path_to/client_cert.pem', '/path_to/client_key.pem'); ``` These functions change the KMIP provider: ```sql -SELECT pg_tde_change_database_key_provider_kmip('provider-name','kmip-addr', `port`, '/path_to/server_certificate.pem', '/path_to/client_key.pem'); -SELECT pg_tde_change_global_key_provider_kmip('provider-name','kmip-addr', `port`, '/path_to/server_certificate.pem', '/path_to/client_key.pem'); +SELECT pg_tde_change_database_key_provider_kmip('provider-name','kmip-addr', `port`, '/path_to/server_certificate.pem', '/path_to/client_cert.pem', '/path_to/client_key.pem'); +SELECT pg_tde_change_global_key_provider_kmip('provider-name','kmip-addr', `port`, '/path_to/server_certificate.pem', '/path_to/client_cert.pem', '/path_to/client_key.pem'); ``` where: @@ -124,7 +124,8 @@ where: * `port` is the port to communicate with the KMIP server. Most KMIP servers use port 5696. * `server-certificate` is the path to the certificate file for the KMIP server. -* `client key` is the path to the client key. +* `client-cert` is the path to the client certificate. +* `client-key` is the path to the client key. The specified access parameters require permission to read and write keys at the server. diff --git a/contrib/pg_tde/documentation/docs/how-to/multi-tenant-setup.md b/contrib/pg_tde/documentation/docs/how-to/multi-tenant-setup.md index abcf3c4779144..88ccd323810dd 100644 --- a/contrib/pg_tde/documentation/docs/how-to/multi-tenant-setup.md +++ b/contrib/pg_tde/documentation/docs/how-to/multi-tenant-setup.md @@ -61,7 +61,7 @@ You must do these steps for every database where you have created the extension. For testing purposes, you can use the PyKMIP server which enables you to set up required certificates. To use a real KMIP server, make sure to obtain the valid certificates issued by the key management appliance. ```sql - SELECT pg_tde_add_database_key_provider_kmip('provider-name','kmip-addr', 5696, '/path_to/server_certificate.pem', '/path_to/client_key.pem'); + SELECT pg_tde_add_database_key_provider_kmip('provider-name','kmip-addr', 5696, '/path_to/server_certificate.pem', '/path_to/client_cert.pem', '/path_to/client_key.pem'); ``` where: @@ -70,12 +70,13 @@ You must do these steps for every database where you have created the extension. * `kmip-addr` is the IP address of a domain name of the KMIP server * `port` is the port to communicate with the KMIP server. Typically used port is 5696. * `server-certificate` is the path to the certificate file for the KMIP server. - * `client key` is the path to the client key. + * `client-cert` is the path to the client certificate. + * `client-key` is the path to the client key. :material-information: Warning: This example is for testing purposes only: ``` - SELECT pg_tde_add_database_key_provider_kmip('kmip','127.0.0.1', 5696, '/tmp/server_certificate.pem', '/tmp/client_key_jane_doe.pem'); + SELECT pg_tde_add_database_key_provider_kmip('kmip','127.0.0.1', 5696, '/tmp/server_certificate.pem', '/tmp/client_cert_jane_doe.pem', '/tmp/client_key_jane_doe.pem'); ``` === "With HashiCorp Vault" diff --git a/contrib/pg_tde/documentation/docs/wal-encryption.md b/contrib/pg_tde/documentation/docs/wal-encryption.md index c345eddd8057d..5f5448c04cd34 100644 --- a/contrib/pg_tde/documentation/docs/wal-encryption.md +++ b/contrib/pg_tde/documentation/docs/wal-encryption.md @@ -19,7 +19,7 @@ Before turning WAL encryption on, you must follow the steps below to create your For testing purposes, you can use the PyKMIP server which enables you to set up required certificates. To use a real KMIP server, make sure to obtain the valid certificates issued by the key management appliance. ```sql - SELECT pg_tde_add_global_key_provider_kmip('provider-name','kmip-addr', 5696, '/path_to/server_certificate.pem', '/path_to/client_key.pem'); + SELECT pg_tde_add_global_key_provider_kmip('provider-name','kmip-addr', 5696, '/path_to/server_certificate.pem', '/path_to/client_cert.pem', '/path_to/client_key.pem'); ``` where: @@ -28,12 +28,13 @@ Before turning WAL encryption on, you must follow the steps below to create your * `kmip-addr` is the IP address of a domain name of the KMIP server * `port` is the port to communicate with the KMIP server. Typically used port is 5696. * `server-certificate` is the path to the certificate file for the KMIP server. - * `client key` is the path to the client key. + * `client-cert` is the path to the client certificate. + * `client-key` is the path to the client key. :material-information: Warning: This example is for testing purposes only: ``` - SELECT pg_tde_add_key_using_global_key_provider_kmip('kmip','127.0.0.1', 5696, '/tmp/server_certificate.pem', '/tmp/client_key_jane_doe.pem'); + SELECT pg_tde_add_key_using_global_key_provider_kmip('kmip','127.0.0.1', 5696, '/tmp/server_certificate.pem', '/tmp/client_cert_jane_doe.pem', '/tmp/client_key_jane_doe.pem'); ``` === "With HashiCorp Vault" diff --git a/contrib/pg_tde/expected/kmip_test.out b/contrib/pg_tde/expected/kmip_test.out index 3d905bbaeedb3..ffb428896f1b7 100644 --- a/contrib/pg_tde/expected/kmip_test.out +++ b/contrib/pg_tde/expected/kmip_test.out @@ -1,5 +1,5 @@ CREATE EXTENSION pg_tde; -SELECT pg_tde_add_database_key_provider_kmip('kmip-prov','127.0.0.1', 5696, '/tmp/server_certificate.pem', '/tmp/client_key_jane_doe.pem'); +SELECT pg_tde_add_database_key_provider_kmip('kmip-prov','127.0.0.1', 5696, '/tmp/server_certificate.pem', '/tmp/client_certificate_jane_doe.pem', '/tmp/client_key_jane_doe.pem'); pg_tde_add_database_key_provider_kmip --------------------------------------- 1 @@ -35,6 +35,6 @@ 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'); +SELECT pg_tde_add_database_key_provider_kmip('will-not-work','127.0.0.1', 61, '/tmp/server_certificate.pem', '/tmp/client_certificate_jane_doe.pem', '/tmp/client_key_jane_doe.pem'); ERROR: SSL error: BIO_do_connect failed DROP EXTENSION pg_tde; diff --git a/contrib/pg_tde/pg_tde--1.0-rc.sql b/contrib/pg_tde/pg_tde--1.0-rc.sql index b8d3d4e5c0e3e..4a43da236e034 100644 --- a/contrib/pg_tde/pg_tde--1.0-rc.sql +++ b/contrib/pg_tde/pg_tde--1.0-rc.sql @@ -67,7 +67,8 @@ CREATE FUNCTION pg_tde_add_database_key_provider_kmip(provider_name TEXT, kmip_host TEXT, kmip_port INT, kmip_ca_path TEXT, - kmip_cert_path TEXT) + kmip_cert_path TEXT, + kmip_key_path TEXT) RETURNS INT LANGUAGE SQL BEGIN ATOMIC @@ -77,14 +78,16 @@ BEGIN ATOMIC json_object('host' VALUE COALESCE(kmip_host, ''), 'port' VALUE kmip_port, 'caPath' VALUE COALESCE(kmip_ca_path, ''), - 'certPath' VALUE COALESCE(kmip_cert_path, ''))); + 'certPath' VALUE COALESCE(kmip_cert_path, ''), + 'keyPath' VALUE COALESCE(kmip_key_path, ''))); END; CREATE FUNCTION pg_tde_add_database_key_provider_kmip(provider_name TEXT, kmip_host JSON, kmip_port JSON, kmip_ca_path JSON, - kmip_cert_path JSON) + kmip_cert_path JSON, + kmip_key_path JSON) RETURNS INT LANGUAGE SQL BEGIN ATOMIC @@ -94,7 +97,8 @@ BEGIN ATOMIC json_object('host' VALUE kmip_host, 'port' VALUE kmip_port, 'caPath' VALUE kmip_ca_path, - 'certPath' VALUE kmip_cert_path)); + 'certPath' VALUE kmip_cert_path, + 'keyPath' VALUE kmip_key_path)); END; CREATE FUNCTION pg_tde_list_all_database_key_providers @@ -179,7 +183,8 @@ CREATE FUNCTION pg_tde_add_global_key_provider_kmip(provider_name TEXT, kmip_host TEXT, kmip_port INT, kmip_ca_path TEXT, - kmip_cert_path TEXT) + kmip_cert_path TEXT, + kmip_key_path TEXT DEFAULT '') RETURNS INT LANGUAGE SQL BEGIN ATOMIC @@ -189,14 +194,16 @@ BEGIN ATOMIC json_object('host' VALUE COALESCE(kmip_host, ''), 'port' VALUE kmip_port, 'caPath' VALUE COALESCE(kmip_ca_path, ''), - 'certPath' VALUE COALESCE(kmip_cert_path, ''))); + 'certPath' VALUE COALESCE(kmip_cert_path, ''), + 'keyPath' VALUE COALESCE(kmip_key_path, ''))); END; CREATE FUNCTION pg_tde_add_global_key_provider_kmip(provider_name TEXT, kmip_host JSON, kmip_port JSON, kmip_ca_path JSON, - kmip_cert_path JSON) + kmip_cert_path JSON, + kmip_key_path JSON) RETURNS INT LANGUAGE SQL BEGIN ATOMIC @@ -206,7 +213,8 @@ BEGIN ATOMIC json_object('host' VALUE kmip_host, 'port' VALUE kmip_port, 'caPath' VALUE kmip_ca_path, - 'certPath' VALUE kmip_cert_path)); + 'certPath' VALUE kmip_cert_path, + 'keyPath' VALUE kmip_key_path)); END; -- Key Provider Management @@ -273,7 +281,8 @@ CREATE FUNCTION pg_tde_change_database_key_provider_kmip(provider_name TEXT, kmip_host TEXT, kmip_port INT, kmip_ca_path TEXT, - kmip_cert_path TEXT) + kmip_cert_path TEXT, + kmip_key_path TEXT) RETURNS INT LANGUAGE SQL BEGIN ATOMIC @@ -283,14 +292,16 @@ BEGIN ATOMIC json_object('host' VALUE COALESCE(kmip_host, ''), 'port' VALUE kmip_port, 'caPath' VALUE COALESCE(kmip_ca_path, ''), - 'certPath' VALUE COALESCE(kmip_cert_path, ''))); + 'certPath' VALUE COALESCE(kmip_cert_path, ''), + 'keyPath' VALUE COALESCE(kmip_key_path, ''))); END; CREATE FUNCTION pg_tde_change_database_key_provider_kmip(provider_name TEXT, kmip_host JSON, kmip_port JSON, kmip_ca_path JSON, - kmip_cert_path JSON) + kmip_cert_path JSON, + kmip_key_path JSON) RETURNS INT LANGUAGE SQL BEGIN ATOMIC @@ -300,7 +311,8 @@ BEGIN ATOMIC json_object('host' VALUE kmip_host, 'port' VALUE kmip_port, 'caPath' VALUE kmip_ca_path, - 'certPath' VALUE kmip_cert_path)); + 'certPath' VALUE kmip_cert_path, + 'keyPath' VALUE kmip_key_path)); END; -- Global Tablespace Key Provider Management @@ -367,7 +379,8 @@ CREATE FUNCTION pg_tde_change_global_key_provider_kmip(provider_name TEXT, kmip_host TEXT, kmip_port INT, kmip_ca_path TEXT, - kmip_cert_path TEXT) + kmip_cert_path TEXT, + kmip_key_path TEXT) RETURNS INT LANGUAGE SQL BEGIN ATOMIC @@ -377,14 +390,16 @@ BEGIN ATOMIC json_object('host' VALUE COALESCE(kmip_host, ''), 'port' VALUE kmip_port, 'caPath' VALUE COALESCE(kmip_ca_path, ''), - 'certPath' VALUE COALESCE(kmip_cert_path, ''))); + 'certPath' VALUE COALESCE(kmip_cert_path, ''), + 'keyPath' VALUE COALESCE(kmip_key_path, ''))); END; CREATE FUNCTION pg_tde_change_global_key_provider_kmip(provider_name TEXT, kmip_host JSON, kmip_port JSON, kmip_ca_path JSON, - kmip_cert_path JSON) + kmip_cert_path JSON, + kmip_key_path JSON) RETURNS INT LANGUAGE SQL BEGIN ATOMIC @@ -394,7 +409,8 @@ BEGIN ATOMIC json_object('host' VALUE kmip_host, 'port' VALUE kmip_port, 'caPath' VALUE kmip_ca_path, - 'certPath' VALUE kmip_cert_path)); + 'certPath' VALUE kmip_cert_path, + 'keyPath' VALUE kmip_key_path)); END; CREATE FUNCTION pg_tde_is_encrypted(relation REGCLASS) diff --git a/contrib/pg_tde/sql/kmip_test.sql b/contrib/pg_tde/sql/kmip_test.sql index d47467e5fda16..ec8f6102e75f4 100644 --- a/contrib/pg_tde/sql/kmip_test.sql +++ b/contrib/pg_tde/sql/kmip_test.sql @@ -1,6 +1,6 @@ CREATE EXTENSION pg_tde; -SELECT pg_tde_add_database_key_provider_kmip('kmip-prov','127.0.0.1', 5696, '/tmp/server_certificate.pem', '/tmp/client_key_jane_doe.pem'); +SELECT pg_tde_add_database_key_provider_kmip('kmip-prov','127.0.0.1', 5696, '/tmp/server_certificate.pem', '/tmp/client_certificate_jane_doe.pem', '/tmp/client_key_jane_doe.pem'); SELECT pg_tde_set_key_using_database_key_provider('kmip-key','kmip-prov'); CREATE TABLE test_enc( @@ -20,6 +20,6 @@ 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'); +SELECT pg_tde_add_database_key_provider_kmip('will-not-work','127.0.0.1', 61, '/tmp/server_certificate.pem', '/tmp/client_certificate_jane_doe.pem', '/tmp/client_key_jane_doe.pem'); DROP EXTENSION pg_tde; diff --git a/contrib/pg_tde/src/catalog/tde_keyring.c b/contrib/pg_tde/src/catalog/tde_keyring.c index 6eff577e9af07..eb70176eed15e 100644 --- a/contrib/pg_tde/src/catalog/tde_keyring.c +++ b/contrib/pg_tde/src/catalog/tde_keyring.c @@ -888,15 +888,17 @@ load_kmip_keyring_provider_options(char *keyring_options) if (kmip_keyring->kmip_host == NULL || kmip_keyring->kmip_host[0] == '\0' || kmip_keyring->kmip_port == NULL || kmip_keyring->kmip_port[0] == '\0' || kmip_keyring->kmip_ca_path == NULL || kmip_keyring->kmip_ca_path[0] == '\0' || - kmip_keyring->kmip_cert_path == NULL || kmip_keyring->kmip_cert_path[0] == '\0') + kmip_keyring->kmip_cert_path == NULL || kmip_keyring->kmip_cert_path[0] == '\0' || + kmip_keyring->kmip_key_path == NULL || kmip_keyring->kmip_key_path[0] == '\0') { ereport(WARNING, errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("missing in the keyring options:%s%s%s%s", + errmsg("missing in the keyring options:%s%s%s%s%s", (kmip_keyring->kmip_host != NULL && kmip_keyring->kmip_host[0] != '\0') ? "" : " host", (kmip_keyring->kmip_port != NULL && kmip_keyring->kmip_port[0] != '\0') ? "" : " port", (kmip_keyring->kmip_ca_path != NULL && kmip_keyring->kmip_ca_path[0] != '\0') ? "" : " caPath", - (kmip_keyring->kmip_cert_path != NULL && kmip_keyring->kmip_cert_path[0] != '\0') ? "" : " certPath")); + (kmip_keyring->kmip_cert_path != NULL && kmip_keyring->kmip_cert_path[0] != '\0') ? "" : " certPath", + (kmip_keyring->kmip_key_path != NULL && kmip_keyring->kmip_key_path[0] != '\0') ? "" : " keyPath")); return NULL; } @@ -925,6 +927,7 @@ debug_print_kerying(GenericKeyring *keyring) elog(DEBUG2, "KMIP Keyring Port: %s", ((KmipKeyring *) keyring)->kmip_port); elog(DEBUG2, "KMIP Keyring CA Path: %s", ((KmipKeyring *) keyring)->kmip_ca_path); elog(DEBUG2, "KMIP Keyring Cert Path: %s", ((KmipKeyring *) keyring)->kmip_cert_path); + elog(DEBUG2, "KMIP Keyring Key Path: %s", ((KmipKeyring *) keyring)->kmip_key_path); break; case UNKNOWN_KEY_PROVIDER: break; diff --git a/contrib/pg_tde/src/catalog/tde_keyring_parse_opts.c b/contrib/pg_tde/src/catalog/tde_keyring_parse_opts.c index a41234701805f..c9fd1c76cb4a7 100644 --- a/contrib/pg_tde/src/catalog/tde_keyring_parse_opts.c +++ b/contrib/pg_tde/src/catalog/tde_keyring_parse_opts.c @@ -72,6 +72,7 @@ typedef enum JsonKeyringField JK_KMIP_PORT, JK_KMIP_CA_PATH, JK_KMIP_CERT_PATH, + JK_KMIP_KEY_PATH, /* must be the last */ JK_FIELDS_TOTAL @@ -100,6 +101,7 @@ static const char *JK_FIELD_NAMES[JK_FIELDS_TOTAL] = { [JK_KMIP_PORT] = "port", [JK_KMIP_CA_PATH] = "caPath", [JK_KMIP_CERT_PATH] = "certPath", + [JK_KMIP_KEY_PATH] = "keyPath", }; typedef struct JsonKeyringState @@ -387,6 +389,8 @@ json_kring_object_field_start(void *state, char *fname, bool isnull) parse->top_level_field = JK_KMIP_CA_PATH; else if (strcmp(fname, JK_FIELD_NAMES[JK_KMIP_CERT_PATH]) == 0) parse->top_level_field = JK_KMIP_CERT_PATH; + else if (strcmp(fname, JK_FIELD_NAMES[JK_KMIP_KEY_PATH]) == 0) + parse->top_level_field = JK_KMIP_KEY_PATH; else { parse->top_level_field = JK_FIELD_UNKNOWN; @@ -518,6 +522,9 @@ json_kring_assign_scalar(JsonKeyringState *parse, JsonKeyringField field, char * case JK_KMIP_CERT_PATH: kmip->kmip_cert_path = value; break; + case JK_KMIP_KEY_PATH: + kmip->kmip_key_path = value; + break; default: elog(ERROR, "json keyring: unexpected scalar field %d", field); diff --git a/contrib/pg_tde/src/include/keyring/keyring_api.h b/contrib/pg_tde/src/include/keyring/keyring_api.h index 89be8282f8500..ef322709823c5 100644 --- a/contrib/pg_tde/src/include/keyring/keyring_api.h +++ b/contrib/pg_tde/src/include/keyring/keyring_api.h @@ -86,6 +86,7 @@ typedef struct KmipKeyring char *kmip_port; char *kmip_ca_path; char *kmip_cert_path; + char *kmip_key_path; } KmipKeyring; extern void RegisterKeyProviderType(const TDEKeyringRoutine *routine, ProviderType type); diff --git a/contrib/pg_tde/src/keyring/keyring_kmip.c b/contrib/pg_tde/src/keyring/keyring_kmip.c index 86b438a920886..3b2f4d72bd533 100644 --- a/contrib/pg_tde/src/keyring/keyring_kmip.c +++ b/contrib/pg_tde/src/keyring/keyring_kmip.c @@ -61,7 +61,7 @@ kmipSslConnect(KmipCtx *ctx, KmipKeyring *kmip_keyring, bool throw_error) return false; } - if (SSL_CTX_use_PrivateKey_file(ctx->ssl, kmip_keyring->kmip_cert_path, SSL_FILETYPE_PEM) != 1) + if (SSL_CTX_use_PrivateKey_file(ctx->ssl, kmip_keyring->kmip_key_path, SSL_FILETYPE_PEM) != 1) { SSL_CTX_free(ctx->ssl); ereport(level, errmsg("SSL error: Loading the client key failed")); diff --git a/contrib/pg_tde/src/pg_tde_change_key_provider.c b/contrib/pg_tde/src/pg_tde_change_key_provider.c index 489dbcdad339f..c7840ed8ac74d 100644 --- a/contrib/pg_tde/src/pg_tde_change_key_provider.c +++ b/contrib/pg_tde/src/pg_tde_change_key_provider.c @@ -203,7 +203,7 @@ main(int argc, char *argv[]) { provider_found = true; - if (argc - argstart != 7 && argc - argstart != 8) + if (argc - argstart != 8 && argc - argstart != 9) { help(); puts("\n"); @@ -211,7 +211,7 @@ main(int argc, char *argv[]) exit(1); } - if (!build_json(json, 5, "type", "kmip", "host", argv[4 + argstart], "port", argv[5 + argstart], "caPath", (argc - argstart > 7 ? argv[7 + argstart] : ""), "certPath", argv[6 + argstart])) + if (!build_json(json, 6, "type", "kmip", "host", argv[4 + argstart], "port", argv[5 + argstart], "caPath", (argc - argstart > 8 ? argv[8 + argstart] : ""), "certPath", argv[6 + argstart], "keyPath", argv[7 + argstart])) { exit(1); }