Authors: Umar Hayat. Small modification and Japanese document by Tatsuo Ishii.
Dicussion: https://www.pgpool.net/pipermail/pgpool-hackers/2020-March/003548.html
</listitem>
</varlistentry>
+ <varlistentry id="guc-ssl-passphrase-command" xreflabel="ssl_passphrase_command">
+ <term><varname>ssl_passphrase_command</varname> (<type>string</type>)
+ <indexterm>
+ <primary><varname>ssl_passphrase_command</varname>設定パラメータ</primary>
+ </indexterm>
+ </term>
+ <listitem>
+ <para>
+ 秘密鍵などのSSLファイルを復号する際に、パスフレーズの入手が必要な時に起動される外部コマンドを設定します。
+ デフォルトではこのパラメータは空文字で、この場合はパスフレーズが要求されてもSSLファイルはロードされません。
+ </para>
+ <para>
+ このコマンドは、パスフレーズを標準出力に書き出し、コード0で終了しなければなりません。
+ パラメータの値の<literal>%p</literal>はプロンプト文字列に置き換えられます。
+ (<literal>%</literal>を使いたい場合は<literal>%%</literal>としてください。)
+ プロンプト文字列はおそらく空白文字を含むので、適切に引用符付けするように注意してください。
+ 出力の最後に一個の改行があれば、削除されます。
+ </para>
+ <para>
+ このコマンドは実際にはパスフレーズ用にユーザにプロンプトを表示する必要はありません。
+ ファイルからパスフレーズが読めるなら、キーチェーン機構やその他から取得します。
+ 選択された仕組みが適切にセキュアかどうかを確認するのはユーザ次第です。
+ </para>
+ <para>
+ このパラメータはサーバ起動時にのみ設定可能です。
+ </para>
+ </listitem>
+ </varlistentry>
+
</variablelist>
</sect2>
</listitem>
</varlistentry>
+ <varlistentry id="guc-ssl-passphrase-command" xreflabel="ssl_passphrase_command">
+ <term><varname>ssl_passphrase_command</varname> (<type>string</type>)
+ <indexterm>
+ <primary><varname>ssl_passphrase_command</varname> configuration parameter</primary>
+ </indexterm>
+ </term>
+ <listitem>
+ <para>
+ Sets an external command to be invoked when a passphrase for decrypting
+ an SSL file such as a private key needs to be obtained. By default,
+ this parameter is empty, which means SSL file will not be loaded if passphrase is required.
+ </para>
+ <para>
+ The command must print the passphrase to the standard output and
+ exit with code 0. In the parameter value, %p is replaced by a prompt
+ string. (Write %% for a literal %.) Note that the prompt string will probably
+ contain whitespace, so be sure to quote adequately. A single newline is stripped
+ from the end of the output if present.
+ </para>
+ <para>
+ The command does not actually have to prompt the user for a passphrase.
+ It can read it from a file, obtain it from a keychain facility, or similar.
+ It is up to the user to make sure the chosen mechanism is adequately secure.
+ </para>
+ <para>
+ This parameter can only be set at server start.
+ </para>
+ </listitem>
+ </varlistentry>
+
</variablelist>
</sect2>
NULL, NULL, NULL, NULL
},
+ {
+ {"ssl_passphrase_command", CFGCXT_INIT, SSL_CONFIG,
+ "Path to the Diffie-Hellman parameters contained file",
+ CONFIG_VAR_TYPE_STRING, false, 0
+ },
+ &g_pool_config.ssl_passphrase_command,
+ "",
+ NULL, NULL, NULL, NULL
+ },
+
+
{
{"memqcache_oiddir", CFGCXT_INIT, CACHE_CONFIG,
"Tempory directory to record table oids.",
bool ssl_prefer_server_ciphers; /*Use SSL cipher preferences, rather than the client's*/
char *ssl_ecdh_curve; /* the curve to use in ECDH key exchange */
char *ssl_dh_params_file; /* path to the Diffie-Hellman parameters contained file */
+ char *ssl_passphrase_command; /* path to the Diffie-Hellman parameters contained file */
int64 relcache_expire; /* relation cache life time in seconds */
int relcache_size; /* number of relation cache life entry */
CHECK_TEMP_TABLE_OPTION check_temp_table; /* how to check temporary table */
ssl_dh_params_file = ''
# Name of the file containing Diffie-Hellman parameters used
# for so-called ephemeral DH family of SSL cipher.
+#ssl_passphrase_command=''
+ # Sets an external command to be invoked when a passphrase
+ # for decrypting an SSL file needs to be obtained
+ # (change requires restart)
#------------------------------------------------------------------------------
# POOLS
ssl_dh_params_file = ''
# Name of the file containing Diffie-Hellman parameters used
# for so-called ephemeral DH family of SSL cipher.
+#ssl_passphrase_command=''
+ # Sets an external command to be invoked when a passphrase
+ # for decrypting an SSL file needs to be obtained
+ # (change requires restart)
#------------------------------------------------------------------------------
# POOLS
ssl_dh_params_file = ''
# Name of the file containing Diffie-Hellman parameters used
# for so-called ephemeral DH family of SSL cipher.
+#ssl_passphrase_command=''
+ # Sets an external command to be invoked when a passphrase
+ # for decrypting an SSL file needs to be obtained
+ # (change requires restart)
#------------------------------------------------------------------------------
# POOLS
ssl_dh_params_file = ''
# Name of the file containing Diffie-Hellman parameters used
# for so-called ephemeral DH family of SSL cipher.
+#ssl_passphrase_command=''
+ # Sets an external command to be invoked when a passphrase
+ # for decrypting an SSL file needs to be obtained
+ # (change requires restart)
#------------------------------------------------------------------------------
# POOLS
ssl_dh_params_file = ''
# Name of the file containing Diffie-Hellman parameters used
# for so-called ephemeral DH family of SSL cipher.
+#ssl_passphrase_command=''
+ # Sets an external command to be invoked when a passphrase
+ # for decrypting an SSL file needs to be obtained
+ # (change requires restart)
#------------------------------------------------------------------------------
# POOLS
StrNCpy(status[i].desc, "path to the Diffie-Hellman parameters contained file", POOLCONFIG_MAXDESCLEN);
i++;
+ StrNCpy(status[i].name, "ssl_passphrase_command", POOLCONFIG_MAXNAMELEN);
+ snprintf(status[i].value, POOLCONFIG_MAXVALLEN, "%s", pool_config->ssl_passphrase_command);
+ StrNCpy(status[i].desc, "external command to be invoked when a passphrase for decrypting an SSL file such as a private key needs to be obtained", POOLCONFIG_MAXDESCLEN);
+ i++;
+
/* POOLS */
/* - Pool size - */
static SSL_CTX *SSL_frontend_context = NULL;
static bool SSL_initialized = false;
-static bool ssl_passwd_cb_called = false;
-static int ssl_passwd_cb(char *buf, int size, int rwflag, void *userdata);
+static bool dummy_ssl_passwd_cb_called = false;
+static int dummy_ssl_passwd_cb(char *buf, int size, int rwflag, void *userdata);
+static int ssl_external_passwd_cb(char *buf, int size, int rwflag, void *userdata);
static int verify_cb(int ok, X509_STORE_CTX *ctx);
static const char *SSLerrmessage(unsigned long ecode);
static void fetch_pool_ssl_cert(POOL_CONNECTION * cp);
static DH *load_dh_buffer(const char *, size_t);
static bool initialize_dh(SSL_CTX *context);
static bool initialize_ecdh(SSL_CTX *context);
+static int run_ssl_passphrase_command(const char *prompt, char *buf, int size);
#define SSL_RETURN_VOID_IF(cond, msg) \
do { \
* function that just returns an empty passphrase, guaranteeing failure.
*/
static int
-ssl_passwd_cb(char *buf, int size, int rwflag, void *userdata)
+dummy_ssl_passwd_cb(char *buf, int size, int rwflag, void *userdata)
{
/* Set flag to change the error message we'll report */
- ssl_passwd_cb_called = true;
+ dummy_ssl_passwd_cb_called = true;
/* And return empty string */
Assert(size > 0);
buf[0] = '\0';
return 0;
}
+/*
+ * Passphrase collection callback using ssl_passphrase_command
+ */
+static int
+ssl_external_passwd_cb(char *buf, int size, int rwflag, void *userdata)
+{
+ /* same prompt as OpenSSL uses internally */
+ const char *prompt = "Enter PEM pass phrase:";
+
+ Assert(rwflag == 0);
+
+ return run_ssl_passphrase_command(prompt, buf, size);
+}
+
/*
* Certificate verification callback
*
/*
* prompt for password for passphrase-protected files
*/
- SSL_CTX_set_default_passwd_cb(context, ssl_passwd_cb);
+ if(pool_config->ssl_passphrase_command && strlen(pool_config->ssl_passphrase_command))
+ SSL_CTX_set_default_passwd_cb(context, ssl_external_passwd_cb);
+ else
+ SSL_CTX_set_default_passwd_cb(context, dummy_ssl_passwd_cb);
/*
* Load and verify server's certificate and private key
/*
* OK, try to load the private key file.
*/
- ssl_passwd_cb_called = false;
+ dummy_ssl_passwd_cb_called = false;
if (SSL_CTX_use_PrivateKey_file(context,
pool_config->ssl_key,
SSL_FILETYPE_PEM) != 1)
{
- if (ssl_passwd_cb_called)
+ if (dummy_ssl_passwd_cb_called)
ereport(WARNING,
(errmsg("private key file \"%s\" cannot be reloaded because it requires a passphrase",
pool_config->ssl_key)));
return dh;
}
+/*
+ * Run ssl_passphrase_command
+ *
+ * The result will be put in buffer buf, which is of size size. The return
+ * value is the length of the actual result.
+ */
+int
+run_ssl_passphrase_command(const char *prompt, char *buf, int size)
+{
+ int loglevel = ERROR;
+ StringInfoData command;
+ char *p;
+ FILE *fh;
+ int pclose_rc;
+ size_t len = 0;
+
+ Assert(prompt);
+ Assert(size > 0);
+ buf[0] = '\0';
+
+ initStringInfo(&command);
+
+ for (p = pool_config->ssl_passphrase_command; *p; p++)
+ {
+ if (p[0] == '%')
+ {
+ switch (p[1])
+ {
+ case 'p':
+ appendStringInfoString(&command, prompt);
+ p++;
+ break;
+ case '%':
+ appendStringInfoChar(&command, '%');
+ p++;
+ break;
+ default:
+ appendStringInfoChar(&command, p[0]);
+ }
+ }
+ else
+ appendStringInfoChar(&command, p[0]);
+ }
+
+ fh = popen(command.data, "r");
+ if (fh == NULL)
+ {
+ ereport(loglevel,
+ (errmsg("could not execute command \"%s\": %m",
+ command.data)));
+ goto error;
+ }
+
+ if (!fgets(buf, size, fh))
+ {
+ if (ferror(fh))
+ {
+ ereport(loglevel,
+ (errmsg("could not read from command \"%s\": %m",
+ command.data)));
+ goto error;
+ }
+ }
+
+ pclose_rc = pclose(fh);
+ if (pclose_rc == -1)
+ {
+ ereport(loglevel,
+ (errmsg("could not close pipe to external command: %m")));
+ goto error;
+ }
+ else if (pclose_rc != 0)
+ {
+ ereport(loglevel,
+ (errmsg("command \"%s\" failed",
+ command.data)));
+ goto error;
+ }
+
+ /* strip trailing newline */
+ len = strlen(buf);
+ if (len > 0 && buf[len - 1] == '\n')
+ buf[--len] = '\0';
+
+error:
+ pfree(command.data);
+ return len;
+}
+
#else /* USE_SSL: wrap / no-op ssl functionality if
* it's not available */