there are regression tests written using vtest from varnish https://github.com/haproxy/haproxy/tree/master/reg-tests
all important part of haproxy are supposed to be covered with reg-tests (test coverage is getting better and better, but not yet complete) вс, 5 июл. 2020 г. в 16:16, Gersner <gers...@gmail.com>: > Oh, wasn't aware of that. > Is there some automation to test this or should I manually verify this? > > > On Sun, Jul 5, 2020 at 2:13 PM Илья Шипицин <chipits...@gmail.com> wrote: > >> I recall some issues with LibreSSL and chaining trust. Like it was >> declared but never worked. >> we'll see that in runtime if there are such issues >> >> вс, 5 июл. 2020 г. в 16:06, Илья Шипицин <chipits...@gmail.com>: >> >>> nice, all ssl variants build well >>> https://travis-ci.com/github/chipitsine/haproxy/builds/174323866 >>> >>> вс, 5 июл. 2020 г. в 15:48, Gersner <gers...@gmail.com>: >>> >>>> >>>> >>>> On Sun, Jul 5, 2020 at 1:42 PM Илья Шипицин <chipits...@gmail.com> >>>> wrote: >>>> >>>>> do you have your patches on github fork ? >>>>> (I could not find your fork) >>>>> >>>> Yes. See branch >>>> https://github.com/Azure/haproxy/tree/wip/sgersner/ca-sign-extra >>>> >>>>> >>>>> вс, 5 июл. 2020 г. в 15:13, Gersner <gers...@gmail.com>: >>>>> >>>>>> >>>>>> >>>>>> On Sun, Jul 5, 2020 at 12:28 PM Илья Шипицин <chipits...@gmail.com> >>>>>> wrote: >>>>>> >>>>>>> does it clearly applies to current master ? either gmail scrambled >>>>>>> patch or it is not. >>>>>>> can you try please ? >>>>>>> >>>>>> Exporting the eml and running 'git am' it works cleanly. >>>>>> >>>>>> I've reproduced the exact same output when copy-pasting from gmail. >>>>>> It seems gmail converts the tabs to spaces and this fails the patch (Not >>>>>> sure why). >>>>>> Running patch with '-l' will resolve this, but it's probably safer to >>>>>> run git am on the email. >>>>>> >>>>>> >>>>>>> >>>>>>> $ patch -p1 < 1.patch >>>>>>> patching file doc/configuration.txt >>>>>>> patching file include/haproxy/listener-t.h >>>>>>> Hunk #1 FAILED at 163. >>>>>>> 1 out of 1 hunk FAILED -- saving rejects to file >>>>>>> include/haproxy/listener-t.h.rej >>>>>>> patching file src/cfgparse-ssl.c >>>>>>> Hunk #1 succeeded at 538 with fuzz 1. >>>>>>> Hunk #2 FAILED at 1720. >>>>>>> 1 out of 2 hunks FAILED -- saving rejects to file >>>>>>> src/cfgparse-ssl.c.rej >>>>>>> patching file src/ssl_sock.c >>>>>>> Hunk #1 FAILED at 1750. >>>>>>> Hunk #2 FAILED at 1864. >>>>>>> Hunk #3 FAILED at 1912. >>>>>>> Hunk #4 FAILED at 1943. >>>>>>> Hunk #5 FAILED at 1970. >>>>>>> Hunk #6 FAILED at 4823. >>>>>>> Hunk #7 FAILED at 4843. >>>>>>> 7 out of 7 hunks FAILED -- saving rejects to file src/ssl_sock.c.rej >>>>>>> >>>>>>> вс, 5 июл. 2020 г. в 11:46, <gers...@gmail.com>: >>>>>>> >>>>>>>> From: Shimi Gersner <sgers...@microsoft.com> >>>>>>>> >>>>>>>> haproxy supports generating SSL certificates based on SNI using a >>>>>>>> provided >>>>>>>> CA signing certificate. Because CA certificates may be signed by >>>>>>>> multiple >>>>>>>> CAs, in some scenarios, it is neccesary for the server to attach >>>>>>>> the trust chain >>>>>>>> in addition to the generated certificate. >>>>>>>> >>>>>>>> The following patch adds the ability to optionally serve all public >>>>>>>> certificates provided in the `ca-sign-file` PEM file. >>>>>>>> Certificate loading was ported to use `ca_sign_use_chain` structure, >>>>>>>> instead of directly reading public/private keys. >>>>>>>> --- >>>>>>>> doc/configuration.txt | 8 +++ >>>>>>>> include/haproxy/listener-t.h | 4 +- >>>>>>>> src/cfgparse-ssl.c | 13 +++++ >>>>>>>> src/ssl_sock.c | 98 >>>>>>>> ++++++++++++++++++++---------------- >>>>>>>> 4 files changed, 78 insertions(+), 45 deletions(-) >>>>>>>> >>>>>>>> diff --git a/doc/configuration.txt b/doc/configuration.txt >>>>>>>> index 6d472134e..1d3878bc1 100644 >>>>>>>> --- a/doc/configuration.txt >>>>>>>> +++ b/doc/configuration.txt >>>>>>>> @@ -12158,6 +12158,14 @@ ca-sign-pass <passphrase> >>>>>>>> the dynamic generation of certificates is enabled. See >>>>>>>> 'generate-certificates' for details. >>>>>>>> >>>>>>>> +ca-sign-use-chain >>>>>>>> + This setting is only available when support for OpenSSL was >>>>>>>> built in. It is >>>>>>>> + the CA private key passphrase. This setting is optional and used >>>>>>>> only when >>>>>>>> + the dynamic generation of certificates is enabled. See >>>>>>>> + 'generate-certificates' for details. >>>>>>>> + Enabling this flag will attach all public certificates encoded >>>>>>>> in `ca-sign-file` >>>>>>>> + to the served certificate to the client, enabling trust. >>>>>>>> + >>>>>>>> ca-verify-file <cafile> >>>>>>>> This setting designates a PEM file from which to load CA >>>>>>>> certificates used to >>>>>>>> verify client's certificate. It designates CA certificates which >>>>>>>> must not be >>>>>>>> diff --git a/include/haproxy/listener-t.h >>>>>>>> b/include/haproxy/listener-t.h >>>>>>>> index 224e32513..38ca2839f 100644 >>>>>>>> --- a/include/haproxy/listener-t.h >>>>>>>> +++ b/include/haproxy/listener-t.h >>>>>>>> @@ -163,8 +163,8 @@ struct bind_conf { >>>>>>>> char *ca_sign_file; /* CAFile used to generate and >>>>>>>> sign server certificates */ >>>>>>>> char *ca_sign_pass; /* CAKey passphrase */ >>>>>>>> >>>>>>>> - X509 *ca_sign_cert; /* CA certificate referenced by >>>>>>>> ca_file */ >>>>>>>> - EVP_PKEY *ca_sign_pkey; /* CA private key referenced by >>>>>>>> ca_key */ >>>>>>>> + int ca_sign_use_chain; /* Optionally attached the >>>>>>>> certificate chain to the served certificate */ >>>>>>>> + struct cert_key_and_chain * ca_sign_ckch; /* CA and >>>>>>>> possible certificate chain for ca generation */ >>>>>>>> #endif >>>>>>>> struct proxy *frontend; /* the frontend all these >>>>>>>> listeners belong to, or NULL */ >>>>>>>> const struct mux_proto_list *mux_proto; /* the mux to use >>>>>>>> for all incoming connections (specified by the "proto" keyword) */ >>>>>>>> diff --git a/src/cfgparse-ssl.c b/src/cfgparse-ssl.c >>>>>>>> index 144cef882..270c857f9 100644 >>>>>>>> --- a/src/cfgparse-ssl.c >>>>>>>> +++ b/src/cfgparse-ssl.c >>>>>>>> @@ -538,6 +538,18 @@ static int bind_parse_ca_sign_file(char >>>>>>>> **args, int cur_arg, struct proxy *px, s >>>>>>>> return 0; >>>>>>>> } >>>>>>>> >>>>>>>> +/* parse the "ca-sign-use-chain" bind keyword */ >>>>>>>> +static int bind_parse_ca_sign_use_chain(char **args, int cur_arg, >>>>>>>> struct proxy *px, struct bind_conf *conf, char **err) >>>>>>>> +{ >>>>>>>> +#if (defined SSL_CTRL_SET_TLSEXT_HOSTNAME && !defined >>>>>>>> SSL_NO_GENERATE_CERTIFICATES && defined SSL_CTX_set1_chain) >>>>>>>> + conf->ca_sign_use_chain = 1; >>>>>>>> +#else >>>>>>>> + memprintf(err, "%sthis version of openssl cannot attach >>>>>>>> certificate chain for SSL certificate generation.\n", >>>>>>>> + err && *err ? *err : ""); >>>>>>>> +#endif >>>>>>>> + return 0; >>>>>>>> +} >>>>>>>> + >>>>>>>> /* parse the "ca-sign-pass" bind keyword */ >>>>>>>> static int bind_parse_ca_sign_pass(char **args, int cur_arg, >>>>>>>> struct proxy *px, struct bind_conf *conf, char **err) >>>>>>>> { >>>>>>>> @@ -1708,6 +1720,7 @@ static struct bind_kw_list bind_kws = { >>>>>>>> "SSL", { }, { >>>>>>>> { "ca-ignore-err", bind_parse_ignore_err, 1 >>>>>>>> }, /* set error IDs to ignore on verify depth > 0 */ >>>>>>>> { "ca-sign-file", bind_parse_ca_sign_file, 1 >>>>>>>> }, /* set CAFile used to generate and sign server certs */ >>>>>>>> { "ca-sign-pass", bind_parse_ca_sign_pass, 1 >>>>>>>> }, /* set CAKey passphrase */ >>>>>>>> + { "ca-sign-use-chain", bind_parse_ca_sign_use_chain, 1 >>>>>>>> }, /* enable attaching ca chain to generated certificate */ >>>>>>>> { "ciphers", bind_parse_ciphers, 1 >>>>>>>> }, /* set SSL cipher suite */ >>>>>>>> #if (HA_OPENSSL_VERSION_NUMBER >= 0x10101000L) >>>>>>>> { "ciphersuites", bind_parse_ciphersuites, 1 >>>>>>>> }, /* set TLS 1.3 cipher suite */ >>>>>>>> diff --git a/src/ssl_sock.c b/src/ssl_sock.c >>>>>>>> index a32db1a28..54829eb98 100644 >>>>>>>> --- a/src/ssl_sock.c >>>>>>>> +++ b/src/ssl_sock.c >>>>>>>> @@ -1750,8 +1750,8 @@ static int ssl_sock_advertise_alpn_protos(SSL >>>>>>>> *s, const unsigned char **out, >>>>>>>> static SSL_CTX * >>>>>>>> ssl_sock_do_create_cert(const char *servername, struct bind_conf >>>>>>>> *bind_conf, SSL *ssl) >>>>>>>> { >>>>>>>> - X509 *cacert = bind_conf->ca_sign_cert; >>>>>>>> - EVP_PKEY *capkey = bind_conf->ca_sign_pkey; >>>>>>>> + X509 *cacert = bind_conf->ca_sign_ckch->cert; >>>>>>>> + EVP_PKEY *capkey = bind_conf->ca_sign_ckch->key; >>>>>>>> SSL_CTX *ssl_ctx = NULL; >>>>>>>> X509 *newcrt = NULL; >>>>>>>> EVP_PKEY *pkey = NULL; >>>>>>>> @@ -1864,6 +1864,16 @@ ssl_sock_do_create_cert(const char >>>>>>>> *servername, struct bind_conf *bind_conf, SSL >>>>>>>> if (!SSL_CTX_check_private_key(ssl_ctx)) >>>>>>>> goto mkcert_error; >>>>>>>> >>>>>>>> + /* Assign chain if any */ >>>>>>>> + if (bind_conf->ca_sign_use_chain && >>>>>>>> bind_conf->ca_sign_ckch->chain) { >>>>>>>> + if (!SSL_CTX_set1_chain(ssl_ctx, >>>>>>>> bind_conf->ca_sign_ckch->chain)) { >>>>>>>> + goto mkcert_error; >>>>>>>> + } >>>>>>>> + if (!SSL_CTX_add1_chain_cert(ssl_ctx, >>>>>>>> bind_conf->ca_sign_ckch->cert)) { >>>>>>>> + goto mkcert_error; >>>>>>>> + } >>>>>>>> + } >>>>>>>> + >>>>>>>> if (newcrt) X509_free(newcrt); >>>>>>>> >>>>>>>> #ifndef OPENSSL_NO_DH >>>>>>>> @@ -1912,7 +1922,7 @@ ssl_sock_assign_generated_cert(unsigned int >>>>>>>> key, struct bind_conf *bind_conf, SS >>>>>>>> >>>>>>>> if (ssl_ctx_lru_tree) { >>>>>>>> HA_RWLOCK_WRLOCK(SSL_GEN_CERTS_LOCK, >>>>>>>> &ssl_ctx_lru_rwlock); >>>>>>>> - lru = lru64_lookup(key, ssl_ctx_lru_tree, >>>>>>>> bind_conf->ca_sign_cert, 0); >>>>>>>> + lru = lru64_lookup(key, ssl_ctx_lru_tree, >>>>>>>> bind_conf->ca_sign_ckch->cert, 0); >>>>>>>> if (lru && lru->domain) { >>>>>>>> if (ssl) >>>>>>>> SSL_set_SSL_CTX(ssl, (SSL_CTX >>>>>>>> *)lru->data); >>>>>>>> @@ -1943,14 +1953,14 @@ ssl_sock_set_generated_cert(SSL_CTX >>>>>>>> *ssl_ctx, unsigned int key, struct bind_conf >>>>>>>> >>>>>>>> if (ssl_ctx_lru_tree) { >>>>>>>> HA_RWLOCK_WRLOCK(SSL_GEN_CERTS_LOCK, >>>>>>>> &ssl_ctx_lru_rwlock); >>>>>>>> - lru = lru64_get(key, ssl_ctx_lru_tree, >>>>>>>> bind_conf->ca_sign_cert, 0); >>>>>>>> + lru = lru64_get(key, ssl_ctx_lru_tree, >>>>>>>> bind_conf->ca_sign_ckch->cert, 0); >>>>>>>> if (!lru) { >>>>>>>> HA_RWLOCK_WRUNLOCK(SSL_GEN_CERTS_LOCK, >>>>>>>> &ssl_ctx_lru_rwlock); >>>>>>>> return -1; >>>>>>>> } >>>>>>>> if (lru->domain && lru->data) >>>>>>>> lru->free((SSL_CTX *)lru->data); >>>>>>>> - lru64_commit(lru, ssl_ctx, bind_conf->ca_sign_cert, >>>>>>>> 0, (void (*)(void *))SSL_CTX_free); >>>>>>>> + lru64_commit(lru, ssl_ctx, >>>>>>>> bind_conf->ca_sign_ckch->cert, 0, (void (*)(void *))SSL_CTX_free); >>>>>>>> HA_RWLOCK_WRUNLOCK(SSL_GEN_CERTS_LOCK, >>>>>>>> &ssl_ctx_lru_rwlock); >>>>>>>> return 0; >>>>>>>> } >>>>>>>> @@ -1970,7 +1980,7 @@ ssl_sock_generated_cert_key(const void *data, >>>>>>>> size_t len) >>>>>>>> static int >>>>>>>> ssl_sock_generate_certificate(const char *servername, struct >>>>>>>> bind_conf *bind_conf, SSL *ssl) >>>>>>>> { >>>>>>>> - X509 *cacert = bind_conf->ca_sign_cert; >>>>>>>> + X509 *cacert = bind_conf->ca_sign_ckch->cert; >>>>>>>> SSL_CTX *ssl_ctx = NULL; >>>>>>>> struct lru64 *lru = NULL; >>>>>>>> unsigned int key; >>>>>>>> @@ -4823,13 +4833,12 @@ int >>>>>>>> ssl_sock_load_ca(struct bind_conf *bind_conf) >>>>>>>> { >>>>>>>> struct proxy *px = bind_conf->frontend; >>>>>>>> - FILE *fp; >>>>>>>> - X509 *cacert = NULL; >>>>>>>> - EVP_PKEY *capkey = NULL; >>>>>>>> - int err = 0; >>>>>>>> + struct cert_key_and_chain *ckch = NULL; >>>>>>>> + int ret = 0; >>>>>>>> + char *err = NULL; >>>>>>>> >>>>>>>> if (!bind_conf->generate_certs) >>>>>>>> - return err; >>>>>>>> + return ret; >>>>>>>> >>>>>>>> #if (defined SSL_CTRL_SET_TLSEXT_HOSTNAME && !defined >>>>>>>> SSL_NO_GENERATE_CERTIFICATES) >>>>>>>> if (global_ssl.ctx_cache) { >>>>>>>> @@ -4843,52 +4852,55 @@ ssl_sock_load_ca(struct bind_conf >>>>>>>> *bind_conf) >>>>>>>> ha_alert("Proxy '%s': cannot enable certificate >>>>>>>> generation, " >>>>>>>> "no CA certificate File configured at >>>>>>>> [%s:%d].\n", >>>>>>>> px->id, bind_conf->file, bind_conf->line); >>>>>>>> - goto load_error; >>>>>>>> + goto failed; >>>>>>>> } >>>>>>>> >>>>>>>> - /* read in the CA certificate */ >>>>>>>> - if (!(fp = fopen(bind_conf->ca_sign_file, "r"))) { >>>>>>>> - ha_alert("Proxy '%s': Failed to read CA certificate >>>>>>>> file '%s' at [%s:%d].\n", >>>>>>>> - px->id, bind_conf->ca_sign_file, >>>>>>>> bind_conf->file, bind_conf->line); >>>>>>>> - goto load_error; >>>>>>>> + /* Allocate cert structure */ >>>>>>>> + ckch = calloc(1, sizeof(struct cert_key_and_chain)); >>>>>>>> + if (!ckch) { >>>>>>>> + ha_alert("Proxy '%s': Failed to read CA certificate >>>>>>>> file '%s' at [%s:%d]. Chain allocation failure\n", >>>>>>>> + px->id, bind_conf->ca_sign_file, >>>>>>>> bind_conf->file, bind_conf->line); >>>>>>>> } >>>>>>>> - if (!(cacert = PEM_read_X509(fp, NULL, NULL, NULL))) { >>>>>>>> - ha_alert("Proxy '%s': Failed to read CA certificate >>>>>>>> file '%s' at [%s:%d].\n", >>>>>>>> - px->id, bind_conf->ca_sign_file, >>>>>>>> bind_conf->file, bind_conf->line); >>>>>>>> - goto read_error; >>>>>>>> + >>>>>>>> + /* Try to parse file */ >>>>>>>> + if (ssl_sock_load_files_into_ckch(bind_conf->ca_sign_file, >>>>>>>> ckch, &err)) { >>>>>>>> + ha_alert("Proxy '%s': Failed to read CA certificate >>>>>>>> file '%s' at [%s:%d]. Chain loading failed: %s\n", >>>>>>>> + px->id, bind_conf->ca_sign_file, >>>>>>>> bind_conf->file, bind_conf->line, err); >>>>>>>> + if (err) free(err); >>>>>>>> + goto failed; >>>>>>>> } >>>>>>>> - rewind(fp); >>>>>>>> - if (!(capkey = PEM_read_PrivateKey(fp, NULL, NULL, >>>>>>>> bind_conf->ca_sign_pass))) { >>>>>>>> - ha_alert("Proxy '%s': Failed to read CA private key >>>>>>>> file '%s' at [%s:%d].\n", >>>>>>>> - px->id, bind_conf->ca_sign_file, >>>>>>>> bind_conf->file, bind_conf->line); >>>>>>>> - goto read_error; >>>>>>>> + >>>>>>>> + /* Fail if missing cert or pkey */ >>>>>>>> + if ((!ckch->cert) || (!ckch->key)) { >>>>>>>> + ha_alert("Proxy '%s': Failed to read CA certificate >>>>>>>> file '%s' at [%s:%d]. Chain missing certificate or private key\n", >>>>>>>> + px->id, bind_conf->ca_sign_file, >>>>>>>> bind_conf->file, bind_conf->line); >>>>>>>> + goto failed; >>>>>>>> } >>>>>>>> >>>>>>>> - fclose (fp); >>>>>>>> - bind_conf->ca_sign_cert = cacert; >>>>>>>> - bind_conf->ca_sign_pkey = capkey; >>>>>>>> - return err; >>>>>>>> + /* Final assignment to bind */ >>>>>>>> + bind_conf->ca_sign_ckch = ckch; >>>>>>>> + return ret; >>>>>>>> + >>>>>>>> + failed: >>>>>>>> + if (ckch) { >>>>>>>> + ssl_sock_free_cert_key_and_chain_contents(ckch); >>>>>>>> + free(ckch); >>>>>>>> + } >>>>>>>> >>>>>>>> - read_error: >>>>>>>> - fclose (fp); >>>>>>>> - if (capkey) EVP_PKEY_free(capkey); >>>>>>>> - if (cacert) X509_free(cacert); >>>>>>>> - load_error: >>>>>>>> bind_conf->generate_certs = 0; >>>>>>>> - err++; >>>>>>>> - return err; >>>>>>>> + ret++; >>>>>>>> + return ret; >>>>>>>> } >>>>>>>> >>>>>>>> /* Release CA cert and private key used to generate certificated */ >>>>>>>> void >>>>>>>> ssl_sock_free_ca(struct bind_conf *bind_conf) >>>>>>>> { >>>>>>>> - if (bind_conf->ca_sign_pkey) >>>>>>>> - EVP_PKEY_free(bind_conf->ca_sign_pkey); >>>>>>>> - if (bind_conf->ca_sign_cert) >>>>>>>> - X509_free(bind_conf->ca_sign_cert); >>>>>>>> - bind_conf->ca_sign_pkey = NULL; >>>>>>>> - bind_conf->ca_sign_cert = NULL; >>>>>>>> + if (bind_conf->ca_sign_ckch) { >>>>>>>> + >>>>>>>> ssl_sock_free_cert_key_and_chain_contents(bind_conf->ca_sign_ckch); >>>>>>>> + free(bind_conf->ca_sign_ckch); >>>>>>>> + bind_conf->ca_sign_ckch = NULL; >>>>>>>> + } >>>>>>>> } >>>>>>>> >>>>>>>> /* >>>>>>>> -- >>>>>>>> 2.27.0 >>>>>>>> >>>>>>>> >>>>>>>>