"openssl dhparam <size>", where size should be at least 2048, as 1024-bit DH
parameters should not be considered secure anymore.
+ssl-passphrase-cmd <cmd> <args> ...
+ This settings is only available when support for OpenSSL was built in. It
+ allows to define a full command line that will be called when an encrypted
+ certificate is loaded during init. The command could be a script or any other
+ program. It will be provided with the encrypted private key path as first
+ parameter and the user-defined "args" parameters then and should dump the
+ passphrase that allows to decode the encrypted private key on the standard
+ output.
+ For every new encrypted private key loaded during init, HAProxy will first
+ try every other already known passphrase to decode the private key and will
+ ultimately call the passphrase command again if none works.
+
ssl-propquery <query>
This setting is only available when support for OpenSSL was built in and when
OpenSSL's version is at least 3.0. It allows to define a default property
#endif
int renegotiate; /* Renegotiate mode (SSL_RENEGOTIATE_ flag) */
+ char **passphrase_cmd;
+ int passphrase_cmd_args_cnt;
};
/* The order here matters for picking a default context,
long long failed_ocsp_staple;
};
+struct passphrase_cb_data {
+ const char *path;
+ int passphrase_idx;
+};
+
#endif /* USE_OPENSSL */
#endif /* _HAPROXY_SSL_SOCK_T_H */
int ssl_load_global_issuer_from_BIO(BIO *in, char *fp, char **err);
int ssl_sock_load_cert(char *path, struct bind_conf *bind_conf, int is_default, char **err);
int ssl_sock_load_srv_cert(char *path, struct server *server, int create_if_none, char **err);
+int ssl_sock_passwd_cb(char *buf, int size, int rwflag, void *userdata);
void ssl_free_global_issuers(void);
int ssl_initialize_random(void);
int ssl_sock_load_cert_list_file(char *file, int dir, struct bind_conf *bind_conf, struct proxy *curproxy, char **err);
}
+/* parse 'ssl-passphrase-cmd' */
+static int ssl_parse_global_passphrase_cmd(char **args, int section_type, struct proxy *curpx,
+ const struct proxy *defpx, const char *file, int line,
+ char **err)
+{
+ int arg_cnt = 0;
+ int i;
+
+ if (!*args[1]) {
+ memprintf(err, "global statement '%s' expects a command line to a passphrase-providing tool (script/binary...) and its arguments.", args[0]);
+ return 1;
+ }
+
+ for (; *args[arg_cnt + 2]; ++arg_cnt)
+ ;
+
+ /* The first argument, by convention, should point to the filename
+ * associated with the file being executed. The array of pointers must
+ * be terminated by a null pointer.
+ * The certificate path will also be passed as first arg so we must
+ * leave enough space .
+ */
+ global_ssl.passphrase_cmd_args_cnt = arg_cnt + 1 + 1 + 1;
+
+ global_ssl.passphrase_cmd = calloc(global_ssl.passphrase_cmd_args_cnt, sizeof(*global_ssl.passphrase_cmd));
+ if (!global_ssl.passphrase_cmd) {
+ memprintf(err, "'%s' : Could not allocate memory", args[0]);
+ return ERR_ALERT | ERR_FATAL;
+ }
+
+ global_ssl.passphrase_cmd[0] = strdup(args[1]);
+
+ if (!global_ssl.passphrase_cmd[0]) {
+ memprintf(err, "'%s' : Could not allocate memory", args[0]);
+ goto err_alloc;
+ }
+
+ for (i = 0; i < arg_cnt; ++i) {
+ /* The first two slots have a special use, they will contain the
+ * command path and the certificate path. */
+ global_ssl.passphrase_cmd[i + 2] = strdup(args[i + 2]);
+ if (!global_ssl.passphrase_cmd[i + 2]) {
+ memprintf(err, "'%s' : Could not allocate memory (command line)", args[0]);
+ goto err_alloc;
+ }
+ }
+
+ return 0;
+
+err_alloc:
+ for (i = 0; i < arg_cnt; ++i) {
+ ha_free(&global_ssl.passphrase_cmd[i]);
+ }
+ ha_free(&global_ssl.passphrase_cmd);
+
+ return ERR_ALERT | ERR_FATAL;
+}
+
+
/***************************** Bind keyword Parsing ********************************************/
/* for ca-file and ca-verify-file */
{ CFG_LISTEN, "ssl-f-use", proxy_parse_ssl_f_use },
+ { CFG_GLOBAL, "ssl-passphrase-cmd", ssl_parse_global_passphrase_cmd },
+
{ 0, NULL, NULL },
}};
BIO *in = NULL;
int ret = 1;
EVP_PKEY *key = NULL;
+ struct passphrase_cb_data cb_data = { path, 0 };
if (buf) {
/* reading from a buffer */
}
/* Read Private Key */
- key = PEM_read_bio_PrivateKey(in, NULL, NULL, NULL);
+ key = PEM_read_bio_PrivateKey(in, NULL, ssl_sock_passwd_cb, &cb_data);
if (key == NULL) {
memprintf(err, "%sunable to load private key from file '%s'.\n",
err && *err ? *err : "", path);
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/types.h>
+#include <sys/wait.h>
#include <netdb.h>
#include <netinet/tcp.h>
.acme_scheduler = 1,
#endif
.renegotiate = SSL_RENEGOTIATE_DFLT,
+ .passphrase_cmd = NULL,
+ .passphrase_cmd_args_cnt = 0,
};
return cfgerr;
}
+/*
+ * Certificate password callback. The password will be provided by the external
+ * program defined in global section (see 'ssl-passphrase-cmd'). It will be
+ * called in a separate fork and it should dump the password on standard output.
+ */
+int ssl_sock_passwd_cb(char *buf, int size, int rwflag, void *userdata)
+{
+ int pass_len;
+ int read_len;
+ pid_t pid = -1;
+ int wstatus = 0;
+
+ int fd[2];
+
+ struct passphrase_cb_data *data = userdata;
+
+ if (!data)
+ return -1;
+
+ if (!global_ssl.passphrase_cmd) {
+ ha_alert("Trying to load a passphrase-protected private key without an 'ssl-passphrase-cmd' defined.");
+ return -1;
+ }
+
+ /* From execvp manpage : "The first argument, by convention, should
+ * point to the filename associated with the file being executed."
+ * The second argument will be the certificate key path.
+ */
+ ha_free(&global_ssl.passphrase_cmd[1]);
+ global_ssl.passphrase_cmd[1] = strdup(data->path);
+
+ if (!global_ssl.passphrase_cmd[1]) {
+ ha_alert("ssl_sock_passwd_cb: allocation failure\n");
+ return -1;
+ }
+
+ if (pipe(fd) < 0) {
+ ha_alert("ssl_sock_passwd_cb: pipe error");
+ return -1;
+ }
+
+ pid = fork();
+
+ switch(pid) {
+ case -1:
+ ha_alert("ssl_sock_passwd_cb: could not fork");
+ goto error;
+ case 0:
+ /* In child process, need to call external tool via execv to get
+ * passphrase */
+ close(0);
+ dup2(fd[1], 1);
+
+ execvp(global_ssl.passphrase_cmd[0], global_ssl.passphrase_cmd);
+ exit(1);
+ break;
+ default:
+ /* in parent */
+ /* Close write side of pipe, it won't be used by the parent */
+ close(fd[1]);
+
+ while (1) {
+ read_len = read(fd[0], buf, size);
+ if (read_len <= 0)
+ break;
+ pass_len = read_len;
+ }
+
+ /* Close read side of pipe */
+ close(fd[0]);
+ waitpid(pid, &wstatus, 0);
+ if (WEXITSTATUS(wstatus) != 0) {
+ ha_alert("ssl_sock_passwd_cb: external tool error (%d)\n", WEXITSTATUS(wstatus));
+ return -1;
+ }
+ }
+
+ return pass_len;
+
+error:
+ close(fd[0]);
+ close(fd[1]);
+ return -1;
+
+}
+
+
/* Create an initial CTX used to start the SSL connection before switchctx */
static int
ssl_sock_initial_ctx(struct bind_conf *bind_conf)
ha_free(&global_ssl.listen_default_client_sigalgs);
ha_free(&global_ssl.connect_default_client_sigalgs);
#endif
+
+ if (global_ssl.passphrase_cmd) {
+ int i = 0;
+ for (; i < global_ssl.passphrase_cmd_args_cnt; ++i) {
+ ha_free(&global_ssl.passphrase_cmd[i]);
+ }
+ ha_free(&global_ssl.passphrase_cmd);
+ }
}
static void __ssl_sock_init(void)