From: Jan Schreiber Subject: httpd: support encrypted tls server keys To: tech@openbsd.org Date: Sat, 14 Feb 2026 14:34:42 +0000 Hello, this diff enables using encrypted tls server keys with httpd. The heavy lifting is already done in tls_load_file(3). I'm only passing the needed password and added the config settings plus a regression test. The additions to the man page are, with the exception of the additional word 'optional', the same as in the relayd man page. Jan diff --git usr.sbin/httpd/httpd.h usr.sbin/httpd/httpd.h index baf11d1b523..71fd829be6b 100644 --- usr.sbin/httpd/httpd.h +++ usr.sbin/httpd/httpd.h @@ -61,6 +61,7 @@  #define HTTPD_LOGVIS           VIS_NL|VIS_TAB|VIS_CSTYLE  #define HTTPD_TLS_CERT         "/etc/ssl/server.crt"  #define HTTPD_TLS_KEY          "/etc/ssl/private/server.key" +#define HTTPD_TLS_KEY_PASS     NULL  #define HTTPD_TLS_CONFIG_MAX   511  #define HTTPD_TLS_CIPHERS      "compat"  #define HTTPD_TLS_DHE_PARAMS   "none" @@ -508,6 +509,7 @@ struct server_config {         uint8_t                 *tls_key;         size_t                   tls_key_len;         char                    *tls_key_file; +       char                    *tls_key_pass;         uint32_t                 tls_protocols;         uint8_t                 *tls_ocsp_staple;         size_t                   tls_ocsp_staple_len; diff --git usr.sbin/httpd/parse.y usr.sbin/httpd/parse.y index 326b249662f..856f95dbf95 100644 --- usr.sbin/httpd/parse.y +++ usr.sbin/httpd/parse.y @@ -141,7 +141,7 @@ typedef struct {  %token TIMEOUT TLS TYPE TYPES HSTS MAXAGE SUBDOMAINS DEFAULT PRELOAD REQUEST  %token ERROR INCLUDE AUTHENTICATE WITH BLOCK DROP RETURN PASS REWRITE  %token CA CLIENT CRL OPTIONAL PARAM FORWARDED FOUND NOT -%token ERRDOCS GZIPSTATIC BANNER +%token ERRDOCS GZIPSTATIC BANNER PASSWORD  %token       STRING  %token       NUMBER  %type          port @@ -870,7 +870,7 @@ tlsopts             : CERTIFICATE STRING       {                                 fatal("out of memory");                         free($2);                 } -               | KEY STRING                    { +               | KEY STRING tlskeyopt          {                         free(srv_conf->tls_key_file);                         if ((srv_conf->tls_key_file = strdup($2)) == NULL)                                 fatal("out of memory"); @@ -947,7 +947,14 @@ tlsopts            : CERTIFICATE STRING       {                         srv_conf->tls_ticket_lifetime = 0;                 }                 ; - +tlskeyopt      : +               | tlskeyopt PASSWORD STRING     { +                       free(srv_conf->tls_key_pass); +                       if ((srv_conf->tls_key_pass = strdup($3)) == NULL) +                               fatal("out of memory"); +                       free($3); +               } +               ;  tlsclientopt   : /* empty */                 | tlsclientopt CRL STRING       {                         srv_conf->tls_flags = TLSFLAG_CRL; @@ -1496,6 +1503,7 @@ lookup(char *s)                 { "optional",           OPTIONAL },                 { "param",              PARAM },                 { "pass",               PASS }, +               { "password",           PASSWORD },                 { "port",               PORT },                 { "prefork",            PREFORK },                 { "preload",            PRELOAD }, diff --git usr.sbin/httpd/server.c usr.sbin/httpd/server.c index a38cf018d81..3bc22a62f6a 100644 --- usr.sbin/httpd/server.c +++ usr.sbin/httpd/server.c @@ -162,9 +162,8 @@ server_tls_load_keypair(struct server *srv)         log_debug("%s: using certificate %s", __func__,             srv->srv_conf.tls_cert_file); -       /* XXX allow to specify password for encrypted key */         if ((srv->srv_conf.tls_key = tls_load_file(srv->srv_conf.tls_key_file, -           &srv->srv_conf.tls_key_len, NULL)) == NULL) +           &srv->srv_conf.tls_key_len, srv->srv_conf.tls_key_pass)) == NULL)                 return (-1);         log_debug("%s: using private key %s", __func__,             srv->srv_conf.tls_key_file); diff --git regress/usr.sbin/httpd/tests/Httpd.pm regress/usr.sbin/httpd/tests/Httpd.pm index 53fed72040c..66f5cb7ba73 100644 --- regress/usr.sbin/httpd/tests/Httpd.pm +++ regress/usr.sbin/httpd/tests/Httpd.pm @@ -71,7 +71,12 @@ sub new {         if ($self->{listentls}) {             print $fh "\n";             print $fh "\ttls certificate \"".$args{chroot}."/server.crt\"\n"; -           print $fh "\ttls key \"".$args{chroot}."/server.key\""; +           my $tlskey = "\ttls key \"".$args{chroot}; +           if ($self->{tlskeypw}) { +               print $fh ${tlskey}."/server.enc.key\" password \"password1\""; +           } else { +               print $fh ${tlskey}."/server.key\""; +           }             $self->{verifytls}                 and print $fh "\n\ttls client ca \"".$args{chroot}."/ca.crt\"";         } diff --git regress/usr.sbin/httpd/tests/Makefile regress/usr.sbin/httpd/tests/Makefile index 3afb588ce93..f1981257cdf 100644 --- regress/usr.sbin/httpd/tests/Makefile +++ regress/usr.sbin/httpd/tests/Makefile @@ -82,7 +82,11 @@ server.crt: ca.crt server.req  client.crt: ca.crt client.req         openssl x509 -CAcreateserial -CAkey ca.key -CA ca.crt -req -in client.req -out $@ +server_enc.key: +       openssl pkcs8 -topk8 -in server.key -out server.enc.key -passout pass:password1 +  ${REGRESS_TARGETS:M*tls*} ${REGRESS_TARGETS:M*https*}: server.crt client.crt +${REGRESS_TARGETS:M*tls-pw*} ${REGRESS_TARGETS:M*https*}: server.crt client.crt server_enc.key  # make perl syntax check for all args files diff --git regress/usr.sbin/httpd/tests/args-tls-pw.pl regress/usr.sbin/httpd/tests/args-tls-pw.pl new file mode 100644 index 00000000000..f5d512d726e --- /dev/null +++ regress/usr.sbin/httpd/tests/args-tls-pw.pl @@ -0,0 +1,19 @@ +# test https connection + +use strict; +use warnings; + +our %args = ( +    client => { +       tls => 1, +       loggrep => 'Issuer.*/OU=ca/', +    }, +    httpd => { +       listentls => 1, +       tlskeypw => 1, +    }, +    len => 512, +    md5 => path_md5("512") +); + +1; diff --git usr.sbin/httpd/httpd.conf.5 usr.sbin/httpd/httpd.conf.5 index 3053709a359..745f7573bf6 100644 --- usr.sbin/httpd/httpd.conf.5 +++ usr.sbin/httpd/httpd.conf.5 @@ -709,7 +709,7 @@ in order of preference.  The special value of "default" will use the default curves; see  .Xr tls_config_set_ecdhecurves 3  for further details. -.It Ic key Ar file +.It Ic key Ar file Ic password Ar password  Specify the private key to use for this server.  The  .Ar file @@ -719,6 +719,9 @@ root directory of  .Nm httpd .  The default is  .Pa /etc/ssl/private/server.key . +The optional +.Ar password +argument will specify the password to decrypt the key.  .It Ic ocsp Ar file  Specify an OCSP response to be stapled during TLS handshakes  with this server.