Index | Thread | Search

From:
Jan Schreiber <jes@posteo.de>
Subject:
httpd: support encrypted tls server keys
To:
tech@openbsd.org
Date:
Sat, 14 Feb 2026 14:34:42 +0000

Download raw body.

Thread
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 <v.string>      STRING
  %token  <v.number>     NUMBER
  %type  <v.port>        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.