Index | Thread | Search

From:
rivo nurges <rivo@elnit.ee>
Subject:
Re: [PATCH] relayd client certificate validation again
To:
Markus Läll <markus.l2ll@gmail.com>, Sören Tempel <soeren@soeren-tempel.net>
Cc:
tech@openbsd.org, brian@planetunix.net
Date:
Mon, 30 Sep 2024 21:07:12 +0300

Download raw body.

Thread
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 
> <soeren@soeren-tempel.net> 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
>     <https://marc.info/?t=154295268000001&r=1&w=2>
>
>     Markus Läll <markus.l2ll@gmail.com> 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 <rivo@elnit.ee> 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 <http://rl_conf.name>);
>     > >                                         return (-1);
>     > >                                 }
>     > > +                               if (rlay->rl_tls_client_ca_fd
>     !=3D -1 &&
>     > > +  config_setrelayfd(ps, id, n, 0,
>     > > +                                   rlay->rl_conf.id
>     <http://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 <http://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        <v.string>      STRING
>     > >   %token  <v.number>    NUMBER
>     > >   %type <v.string>      context hostname interface table value
>     path
>     > > @@ -188,6 +188,7 @@ typedef struct {
>     > >   %type <v.number>      opttls opttlsclient
>     > >   %type <v.number>      redirect_proto relay_proto match
>     > >   %type <v.number>      action ruleaf key_option
>     > > +%type  <v.number>      clientcaopt
>     > >   %type <v.port>        port
>     > >   %type <v.host>        host
>     > >   %type <v.addr>        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