From: rivo nurges Subject: Re: [PATCH] relayd client certificate validation again To: Markus Läll , Sören Tempel Cc: tech@openbsd.org, brian@planetunix.net Date: Mon, 30 Sep 2024 21:07:12 +0300 Hi! I'm willing to revisit the patch too, if there is some interest getting it commited now. Rivo On 9/30/24 20:55, Markus Läll wrote: > Hi! > > When we (me and Rivo) got the patch ready and submitted it, there > simply didn't get any replies from the maintainers. IIRC, I did try to > direct attention to it more than once but with no success. (I also > understand that people do this for free, so assumed they were busy > with other things.) > > I still think the feature is useful, and also that adapting the patch > to the latest version of relayd wouldn't be that difficult. > > > > On Mon, Sep 30, 2024 at 7:57 PM Sören Tempel > wrote: > > Hello! > > Is there a reason why this patch never got a review? The feature (TLS > client certificate validation within relayd) seems to be requested > frequently [1]. I just came across this while looking into working on > such a patch myself. > > Is the feature not deemed useful, or is the patch not in a good shape? > If the latter is the case, I would be willing to revise the patch. > > I would also be fine with just implementing the validation part > without > adding the capability to forward the validated certificate, which > should > shrink the patchset a bit. > > Cheers > Sören > > [1]: https://marc.info/?t=154295268000001&r=1&w=2 > > > Markus Läll wrote: > > Hi, we've got the patch ready for client certificate validation, > cc'ing > > related people. > > > > The patch adds two features: > > 1. client certificate validation itself > > 2. passing on certificate and select fields in HTTP headers > > > > > > ## Brief description of client certificates (for whoever else is > reading) > > > > Client certificates, also known as mutual authentication, are > the reverse > > to TLS server certificates (like letsencrypt) where the server > > authenticates the user instead of the other way around. > > > > In principle client certificates work as follows: > > - the server has keypair and a CA certificate created from this > keypair > > - the user has a keypair and submits their public key for > certification (to > > being signed by the CA) > > - the server (relayd) has the CA certificate configured as > 'client ca > > "/path/to/ca.pem"' > > - the user provides their certificate when connecting, the provided > > certificate is validated against the CA certificate. > > > > How this is set up in practice is up to whoever implements the > > infrastructure. Client certificates can be installed to > operating systems' > > certificate stores (Windows, macOS) where browsers can use them, > or into > > browsers own certificate stores (Firefox has its own), or > specified on the > > command line (curl, wget) etc. > > > > > > ## Configuration > > > > To turn on client certificate validation add > > > > tls { client ca "/path/to/ca-cert.pem" } > > > > to relayd.conf. > > > > Add "optional" flag to make the certificate not required: > > > > tls { client ca "/path/to/cert.pem" optional } > > > > With "optional" relayd will succeed in the TLS handshake when no > client > > certificate is provided. But if a certificate *is* provided then > it *must* > > validate with the configured CA, otherwise the TLS handshake fails. > > > > > > ## Pass certificate on in HTTP header > > > > To pass on the certificate in an url-encoded PEM: > > > > match header set "ANY_HEADER_NAME" value "$CLIENT_CERT_CHAIN" > > > > With this configuration the downstream can inspect the > known-to-be-valid > > certificate further (e.g extract identity or other info from x509 > > extensions). > > > > There was discussion privately on is there any standard for putting > > certificates in HTTP headers, repeating the reply here as well: > > > > There appears to be no standard, but this is how other HTTP > servers do it: > > - nginx urlencodes the PEM file with $ssl_client_escaped_cert[1] > (this is > > what is done in this patch too). There is also the $ssl_client_cert > > variable which adds a tab to each next new line, but this way of > doing > > multiline HTTP headers is deprecated[2]. There is also > > $ssl_client_raw_cert, but the raw multiline PEM is invalid HTTP > header; > > - envoy also urlencodes the PEM[3]; > > - haproxy has only the binary DER, but base64 encoding it like > > %[ssl_c_der,base64]) should result in PEM with no newlines and > no headers; > > - apache has the %{SSL_CLIENT_CERT} with raw (multiline) PEM[4], > which is > > invalid in HTTP headers, but this can be processed with the escape > > function[5], something like "expr=3D %{escape:SSL_CLIENT_CERT}" > > > > [1] > > > https://nginx.org/en/docs/http/ngx_http_ssl_module.html#var_ssl_client_esca= > > ped_cert > > [2] https://tools.ietf.org/html/rfc7230#section-3.2.4 > > [3] > > > https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_conn_ma= > > n/headers#x-forwarded-client-cert > > [4] https://httpd.apache.org/docs/current/mod/mod_ssl.html#envvars > > [5] https://httpd.apache.org/docs/current/expr.html#functions > > > > In addition to extracting the entire certificate, subject and > issuer can be > > put to HTTP headers too for convenience: > > > > match header set "CS_SUBJECT" value "$CLIENT_CERT_SUBJECT" > > match header set "CS_ISSUER" value "$CLIENT_CERT_ISSUER" > > > > > > > > --=20 > > Markus L=C3=A4ll > > > > On Thu, Dec 16, 2021 at 11:23 PM rivo nurges wrote: > > > > > Hi! > > > > > > Here comes the support for relayd client certificate validation. > > > Full certificate chain, subject and issuer can be passed over > in http > > > headers. > > > It supports mandatory validation and optional validation(if > client chooses > > > to > > > provide certificate it will be validated). > > > > > > Part of my sample config. > > > > > > http protocol test { > > >    match header set "CS_SUBJECT" value "$CLIENT_CERT_SUBJECT" > > >    match header set "CS_ISSUER" value "$CLIENT_CERT_ISSUER" > > >    match header set "CS_CERT" value "$CLIENT_CERT_CHAIN" > > >    pass > > >    tls {client ca "/tmp/easyrsa3/pki/ca.crt" optional } > > > } > > > > > > This uses code from the patches submitted by Ashe Connor. > > > > > > Rivo > > > > > > diff refs/heads/master refs/heads/relay-clc3 > > > blob - a2f1c130d6b45e3082048218c11537dca485998a > > > blob + 5070a7d48f58403f53d818231e1676db749aa9d7 > > > --- usr.sbin/relayd/config.c > > > +++ usr.sbin/relayd/config.c > > > @@ -954,6 +954,15 @@ config_setrelay(struct relayd *env, > struct relay *rl= > > ay > > >  rlay->rl_conf.name ); > > >                                         return (-1); > > >                                 } > > > +                               if (rlay->rl_tls_client_ca_fd > !=3D -1 && > > > +  config_setrelayfd(ps, id, n, 0, > > > +                                   rlay->rl_conf.id > , > > > RELAY_FD_CLIENTCACERT, > > > +  rlay->rl_tls_client_ca_fd) =3D=3D -1)= > >  { > > > +  log_warn("%s: fd passing failed > > > for " > > > +                                           "`%s'", __func__, > > > +  rlay->rl_conf.name ); > > > +                                       return (-1); > > > +                               } > > >                                 /* Prevent fd exhaustion in > the parent. */ > > >                                 if (proc_flush_imsg(ps, id, n) > =3D=3D -1)= > >  { > > >  log_warn("%s: failed to flush " > > > @@ -987,6 +996,10 @@ config_setrelay(struct relayd *env, > struct relay *rl= > > ay > > >                 close(rlay->rl_s); > > >                 rlay->rl_s =3D -1; > > >         } > > > +       if (rlay->rl_tls_client_ca_fd !=3D -1) { > > > +               close(rlay->rl_tls_client_ca_fd); > > > +               rlay->rl_tls_client_ca_fd =3D -1; > > > +       } > > >         if (rlay->rl_tls_cacert_fd !=3D -1) { > > >                 close(rlay->rl_tls_cacert_fd); > > >                 rlay->rl_tls_cacert_fd =3D -1; > > > @@ -1012,6 +1025,10 @@ config_setrelay(struct relayd *env, > struct relay > > > *rlay > > >                         cert->cert_ocsp_fd =3D -1; > > >                 } > > >         } > > > +       if (rlay->rl_tls_client_ca_fd !=3D -1) { > > > +               close(rlay->rl_tls_client_ca_fd); > > > +               rlay->rl_tls_client_ca_fd =3D -1; > > > +       } > > > > > >         return (0); > > >   } > > > @@ -1034,6 +1051,7 @@ config_getrelay(struct relayd *env, > struct imsg > > > *imsg) > > >         rlay->rl_s =3D imsg->fd; > > >         rlay->rl_tls_ca_fd =3D -1; > > >         rlay->rl_tls_cacert_fd =3D -1; > > > +       rlay->rl_tls_client_ca_fd =3D -1; > > > > > >         if (ps->ps_what[privsep_process] & CONFIG_PROTOS) { > > >                 if (rlay->rl_conf.proto =3D=3D EMPTY_ID) > > > @@ -1163,6 +1181,9 @@ config_getrelayfd(struct relayd *env, > struct imsg > > > *ims > > >         case RELAY_FD_CAFILE: > > >                 rlay->rl_tls_cacert_fd =3D imsg->fd; > > >                 break; > > > +       case RELAY_FD_CLIENTCACERT: > > > +               rlay->rl_tls_client_ca_fd =3D imsg->fd; > > > +               break; > > >         } > > > > > >         DPRINTF("%s: %s %d received relay fd %d type %d for > relay %s", > > > __func__, > > > blob - 22beb857229a16e5b2c17a25a2944231d41e7e08 > > > blob + fe5e8ff4dfed10e8f09e3226bdfe33f8bc031c8e > > > --- usr.sbin/relayd/parse.y > > > +++ usr.sbin/relayd/parse.y > > > @@ -172,14 +172,14 @@ typedef struct { > > >   %token        CODE COOKIE DEMOTE DIGEST DISABLE ERROR EXPECT > PASS BLOCK > > > EXTERNAL > > >   %token        FILENAME FORWARD FROM HASH HEADER HEADERLEN > HOST HTTP ICMP > > > INCLUDE INET > > >   %token        INET6 INTERFACE INTERVAL IP KEYPAIR LABEL > LISTEN VALUE > > > LOADBALANCE LOG > > > -%token LOOKUP METHOD MODE NAT NO DESTINATION NODELAY NOTHING > ON PARENT > > > PATH > > > +%token LOOKUP METHOD MODE NAT NO DESTINATION NODELAY NOTHING > ON OPTIONAL > > > PARENT PATH > > >   %token        PFTAG PORT PREFORK PRIORITY PROTO QUERYSTR > REAL REDIRECT > > > RELAY REMOVE > > >   %token        REQUEST RESPONSE RETRY QUICK RETURN ROUNDROBIN > ROUTE SACK > > > SCRIPT SEND > > >   %token        SESSION SOCKET SPLICE SSL STICKYADDR STRIP > STYLE TABLE TAG > > > TAGGED TCP > > >   %token        TIMEOUT TLS TO ROUTER RTLABEL TRANSPARENT URL > WITH TTL > > > RTABLE > > >   %token        MATCH PARAMS RANDOM LEASTSTATES SRCHASH KEY > CERTIFICATE > > > PASSWORD ECDHE > > >   %token        EDH TICKETS CONNECTION CONNECTIONS CONTEXT > ERRORS STATE > > > CHANGES CHECKS > > > -%token WEBSOCKETS > > > +%token WEBSOCKETS CLIENT > > >   %token              STRING > > >   %token      NUMBER > > >   %type       context hostname interface table value > path > > > @@ -188,6 +188,7 @@ typedef struct { > > >   %type       opttls opttlsclient > > >   %type       redirect_proto relay_proto match > > >   %type       action ruleaf key_option > > > +%type        clientcaopt > > >   %type         port > > >   %type         host > > >   %type         address rulesrc ruledst addrprefix > > > @@ -244,6 +245,10 @@ opttlsclient       : /*empty*/    { $$ > =3D 0; } > > >                 | WITH ssltls   { $$ =3D 1; } > > >                 ; > > > > > > +clientcaopt    : /*empty*/     { $$ =3D 0; } > > > +               | OPTIONAL      { $$ =3D 1; } > > > +               ; > > > + > > >   http_type     : HTTP          { $$ =3D 0; } > > >                 | STRING        { > > >                         if (strcmp("https", $1) =3D=3D 0) { > > > @@ -1353,6 +1358,19 @@ tlsflags : SESSION TICKETS { > proto->tickets =3D 1;= > >  } > > >                         name->name =3D $2; > > >  TAILQ_INSERT_TAIL(&proto->tlscerts, name, entry); > > >                 } > > > +               | CLIENT CA STRING clientcaopt     { > > > +                       if (strlcpy(proto->tlsclientca, $3, > > > +  sizeof(proto->tlsclientca)) >=3D > > > +  sizeof(proto->tlsclientca)) { > > > +                               yyerror("tlsclientca truncated"); > > > +                               free($3); > > > +                               YYERROR; > > > +                               } > > > +                        if ($4) { > > > +                               proto->tlsflags |=3D > > > TLSFLAG_CLIENT_OPTIONAL; > > > +                        } > > > +                       free($3); > > > +               } > > >                 | NO flag                       { > proto->tlsflags &=3D > > > ~($2); } > > >                 | flag                          { > proto->tlsflags |=3D $1= > > ; } > > >                 ; > > > @@ -1824,6 +1842,7 @@ relay             : RELAY STRING  { > > >                         r->rl_conf.dstretry =3D 0; > > >                         r->rl_tls_ca_fd =3D -1; > > >                         r->rl_tls_cacert_fd =3D -1; > > > +                       r->rl_tls_client_ca_fd =3D -1; > > >  TAILQ_INIT(&r->rl_tables); > > >                         if (last_relay_id =3D=3D INT_MAX) { > > >                                 yyerror("too many relays > defined"); > > > @@ -2413,6 +2432,7 @@ lookup(char *s) > > >                 { "check",              CHECK }, > > >                 { "checks",             CHECKS }, > > >                 { "ciphers",            CIPHERS }, > > > +               { "client",             CLIENT }, > > >                 { "code",               CODE }, > > >                 { "connection",         CONNECTION }, > > >                 { "context",            CONTEXT }, > > > @@ -2458,6 +2478,7 @@ lookup(char *s) > > >                 { "nodelay",            NODELAY }, > > >                 { "nothing",            NOTHING }, > > >                 { "on",                 ON }, > > > +               { "optional",           OPTIONAL }, > > >                 { "params",             PARAMS }, > > >                 { "parent",             PARENT }, > > >                 { "pass",               PASS }, > > > @@ -3399,6 +3420,7 @@ relay_inherit(struct relay *ra, struct > relay *rb) > > >         if (!(rb->rl_conf.flags & F_TLS)) { > > >                 rb->rl_tls_cacert_fd =3D -1; > > >                 rb->rl_tls_ca_fd =3D -1; > > > +               rb->rl_tls_client_ca_fd =3D -1; > > >         } > > >         TAILQ_INIT(&rb->rl_tables); > > > > > > blob - da4a1aa0cc1158b22506c6d81e4d36b8810c025c > > > blob + 2d16b9d91e594a06d4b1b2bfc791c7f0c861fc57 > > > --- usr.sbin/relayd/relay.c > > > +++ usr.sbin/relayd/relay.c > > > @@ -2255,6 +2255,30 @@ relay_tls_ctx_create(struct relay *rlay) > > >                 } > > >                 rlay->rl_tls_cacert_fd =3D -1; > > > > > > +               if (rlay->rl_tls_client_ca_fd !=3D -1) { > > > +                       if ((buf =3D > relay_load_fd(rlay->rl_tls_client_ca= > > _fd, > > > +                           &len)) =3D=3D > > > +                           NULL) { > > > +                               log_warn( > > > +                                   "failed to read tls client CA > > > certificate"); > > > +                               goto err; > > > +                       } > > > + > > > +                       if (tls_config_set_ca_mem(tls_cfg, > buf, len) !=3D= > >  0) > > > { > > > +                               log_warnx( > > > +                                   "failed to set tls client > CA cert: %s= > > ", > > > +  tls_config_error(tls_cfg)); > > > +                               goto err; > > > +                       } > > > +                       purge_key(&buf, len); > > > + > > > +                       if (rlay->rl_proto->tlsflags & > > > TLSFLAG_CLIENT_OPTIONAL) > > > +  tls_config_verify_client_optional(tls_cfg= > > ); > > > +                       else > > > +  tls_config_verify_client(tls_cfg); > > > +               } > > > +               rlay->rl_tls_client_ca_fd =3D -1; > > > + > > >                 tls =3D tls_server(); > > >                 if (tls =3D=3D NULL) { > > >                         log_warnx("unable to allocate TLS > context"); > > > blob - d493c238813cfc692d83f65a88d4556b2fa35b0f > > > blob + 58ba35c16ea8d80b36796d977ad7920d3bed3a9c > > > --- usr.sbin/relayd/relay_http.c > > > +++ usr.sbin/relayd/relay_http.c > > > @@ -78,6 +78,7 @@ int relay_match_actions(struct > > > ctl_relay_event *, > > >                     struct relay_table **); > > >   void           relay_httpdesc_free(struct http_descriptor *); > > >   char *                 server_root_strip(char *, int); > > > +char           *url_encode(const char *); > > > > > >   static struct relayd  *env =3D NULL; > > > > > > @@ -1279,7 +1280,32 @@ relay_expand_http(struct > ctl_relay_event *cre, char > > > *v > > >                 if (expand_string(buf, len, "$TIMEOUT", ibuf) > !=3D 0) > > >                         return (NULL); > > >         } > > > - > > > +       if (strstr(val, "$CLIENT_CERT_") !=3D NULL && > > > tls_peer_cert_provided(cre->tls)) { > > > +               if (strstr(val, "$CLIENT_CERT_SUBJECT") !=3D > NULL) { > > > +                       if (expand_string(buf, len, > > > +                           "$CLIENT_CERT_SUBJECT", > > > tls_peer_cert_subject(cre->tls)) !=3D 0) > > > +                               return (NULL); > > > +               } > > > +               if (strstr(val, "$CLIENT_CERT_ISSUER") !=3D > NULL) { > > > +                       if (expand_string(buf, len, > > > +                           "$CLIENT_CERT_ISSUER", > > > tls_peer_cert_issuer(cre->tls)) !=3D 0) > > > +                               return (NULL); > > > +               } > > > +               if (strstr(val, "$CLIENT_CERT_CHAIN") !=3D NULL) { > > > +                       const char *pem; > > > +                       char *cbuf; > > > +                       size_t plen; > > > +                       pem =3D > tls_peer_cert_chain_pem(cre->tls, &plen); > > > +                       cbuf =3D malloc(plen); > > > +                       sprintf(cbuf, "%.*s", (int)plen - 1, pem); > > > +                       if (expand_string(buf, len, > > > +                           "$CLIENT_CERT_CHAIN", > url_encode(cbuf)) !=3D = > > 0) { > > > +                               free(cbuf); > > > +                               return (NULL); > > > +                       } else > > > +                               free(cbuf); > > > +               } > > > +       } > > >         return (buf); > > >   } > > > > > > @@ -2045,3 +2071,27 @@ server_root_strip(char *path, int n) > > >         return (path); > > >   } > > > > > > +char * > > > +url_encode(const char *src) > > > +{ > > > +       static char      hex[] =3D "0123456789ABCDEF"; > > > +       char            *dp, *dst; > > > +       unsigned char    c; > > > + > > > +       /* We need 3 times the memory if every letter is > encoded. */ > > > +       if ((dst =3D calloc(3, strlen(src) + 1)) =3D=3D NULL) > > > +               return (NULL); > > > + > > > +       for (dp =3D dst; *src !=3D 0; src++) { > > > +               c =3D (unsigned char) *src; > > > +               if (c =3D=3D ' ' || c =3D=3D '#' || c =3D=3D > '%' || c =3D= > > =3D '?' || c =3D=3D > > > '"' || > > > +                   c =3D=3D '&' || c =3D=3D '<' || c <=3D > 0x1f || c >=3D= > >  0x7f) { > > > +                       *dp++ =3D '%'; > > > +                       *dp++ =3D '%'; > > > +                       *dp++ =3D hex[c >> 4]; > > > +                       *dp++ =3D hex[c & 0x0f]; > > > +               } else > > > +                       *dp++ =3D *src; > > > +       } > > > +       return (dst); > > > +} > > > blob - 54e26e646fae5804e66d2d3cfeba68e06914ab2b > > > blob + cd99c21d7cdaf9fc5fdc33e5a0ad886afaa9b889 > > > --- usr.sbin/relayd/relayd.c > > > +++ usr.sbin/relayd/relayd.c > > > @@ -1360,6 +1360,15 @@ relay_load_certfiles(struct relayd > *env, struct > > > relay > > >         if ((rlay->rl_conf.flags & F_TLS) =3D=3D 0) > > >                 return (0); > > > > > > +       if (strlen(proto->tlsclientca) && > > > +           rlay->rl_tls_client_ca_fd =3D=3D -1) { > > > +               if ((rlay->rl_tls_client_ca_fd =3D > > > +                   open(proto->tlsclientca, O_RDONLY)) =3D=3D -1) > > > +                       return (-1); > > > +               log_debug("%s: using client ca %s", __func__, > > > +                   proto->tlsclientca); > > > +       } > > > + > > >         if (name =3D=3D NULL && > > >             print_host(&rlay->rl_conf.ss, hbuf, sizeof(hbuf)) > =3D=3D NULL) > > >                 goto fail; > > > blob - cecbae71f87e603b3e30d4c0114bf1c60a82b52a > > > blob + cfb7a314811730723449a5109d500014711db3ae > > > --- usr.sbin/relayd/relayd.conf.5 > > > +++ usr.sbin/relayd/relayd.conf.5 > > > @@ -948,6 +948,13 @@ will be used (strong crypto cipher suites > without an= > > on > > >   See the CIPHERS section of > > >   .Xr openssl 1 > > >   for information about SSL/TLS cipher suites and preference > lists. > > > +.It Ic client ca Ar path Op optional > > > +Require TLS client certificates whose authenticity can be > verified > > > +against the CA certificate(s) in the specified file in order to > > > +proceed beyond the TLS handshake. > > > +If the > > > +.Ic optional > > > +keyword is present, the certificate is verified only if > presented. > > >   .It Ic client-renegotiation > > >   Allow client-initiated renegotiation. > > >   To mitigate a potential DoS risk, > > > @@ -1361,6 +1368,12 @@ The value string may contain predefined > macros that > > > wi > > >   at runtime: > > >   .Pp > > >   .Bl -tag -width $SERVER_ADDR -offset indent -compact > > > +.It Ic $CLIENT_CERT_CHAIN > > > +The certificate chain of the client certificate. > > > +.It Ic $CLIENT_CERT_ISSUER > > > +The issuer of the client certificate. > > > +.It Ic $CLIENT_CERT_SUBJECT > > > +The subject of the client certificate. > > >   .It Ic $HOST > > >   The Host header's value of the relay. > > >   .It Ic $REMOTE_ADDR > > > blob - 2236d140f7e6b9477bac401cbcdd559db171680b > > > blob + 2a1166599bfd57b0682c4d4bacd15d340ff9b5ad > > > --- usr.sbin/relayd/relayd.h > > > +++ usr.sbin/relayd/relayd.h > > > @@ -139,11 +139,12 @@ struct ctl_relaytable { > > >   }; > > > > > >   enum fd_type { > > > -       RELAY_FD_CERT   =3D 1, > > > -       RELAY_FD_CACERT =3D 2, > > > -       RELAY_FD_CAFILE =3D 3, > > > -       RELAY_FD_KEY    =3D 4, > > > -       RELAY_FD_OCSP   =3D 5 > > > +       RELAY_FD_CERT           =3D 1, > > > +       RELAY_FD_CACERT         =3D 2, > > > +       RELAY_FD_CAFILE         =3D 3, > > > +       RELAY_FD_KEY            =3D 4, > > > +       RELAY_FD_OCSP           =3D 5, > > > +       RELAY_FD_CLIENTCACERT   =3D 6 > > >   }; > > > > > >   struct ctl_relayfd { > > > @@ -403,6 +404,7 @@ union hashkey { > > >   #define F_TLSINSPECT          0x04000000 > > >   #define F_HASHKEY             0x08000000 > > >   #define F_AGENTX_TRAPONLY     0x10000000 > > > +#define F_TLSVERIFY            0x20000000 > > > > > >   #define F_BITS > > >       \ > > >  "\10\01DISABLE\02BACKUP\03USED\04DOWN\05ADD\06DEL\07CHANGED"   \ > > > @@ -703,6 +705,7 @@ TAILQ_HEAD(relay_rules, relay_rule); > > >   #define TLSFLAG_VERSION    0x1f > > >   #define TLSFLAG_CIPHER_SERVER_PREF            0x20 > > >   #define TLSFLAG_CLIENT_RENEG                  0x40 > > > +#define        TLSFLAG_CLIENT_OPTIONAL    0x80 > > >   #define TLSFLAG_DEFAULT    \ > > >  (TLSFLAG_TLSV1_2|TLSFLAG_TLSV1_3|TLSFLAG_CIPHER_SERVER_PREF) > > > > > > @@ -746,6 +749,7 @@ struct protocol { > > >         char  tlscacert[PATH_MAX]; > > >         char                     tlscakey[PATH_MAX]; > > >         char                    *tlscapass; > > > +       char  tlsclientca[PATH_MAX]; > > >         struct keynamelist       tlscerts; > > >         char  name[MAX_NAME_SIZE]; > > >         int                      tickets; > > > @@ -835,6 +839,7 @@ struct relay { > > > > > >         int                      rl_tls_ca_fd; > > >         int                      rl_tls_cacert_fd; > > > +       int rl_tls_client_ca_fd; > > >         EVP_PKEY                *rl_tls_pkey; > > >         X509                    *rl_tls_cacertx509; > > >         char                    *rl_tls_cakey; > > > > -- > Markus Läll