]> git.ipfire.org Git - thirdparty/unbound.git/commitdiff
- For #278: fast_reload can reload tls-service-key, tls-service-pem
authorW.C.A. Wijngaards <wouter@nlnetlabs.nl>
Fri, 13 Mar 2026 15:25:42 +0000 (16:25 +0100)
committerW.C.A. Wijngaards <wouter@nlnetlabs.nl>
Fri, 13 Mar 2026 15:25:42 +0000 (16:25 +0100)
  and tls-cert-bundle changes. It checks the modification time of
  the tls-service-key and tls-service-pem files for update.

daemon/daemon.c
daemon/daemon.h
daemon/remote.c
daemon/remote.h
doc/Changelog
doc/unbound-control.rst
doc/unbound.conf.rst

index c8ec56afa6ed3022dfecbe53dd8e5b6c99ecd139..2ae7d6d05f2aff0813108c9f91f3e7ed3dec0dec 100644 (file)
@@ -221,12 +221,73 @@ setup_listen_sslctx(void** ctx, int is_dot, int is_doh,
 }
 #endif /* HAVE_SSL */
 
+#ifdef HAVE_SSL
+void* daemon_setup_listen_dot_sslctx(struct daemon* daemon,
+       struct config_file* cfg)
+{
+       void* ctx;
+       (void)setup_listen_sslctx(&ctx, 1, 0, cfg, daemon->chroot);
+       return ctx;
+}
+#endif /* HAVE_SSL */
+
+#ifdef HAVE_SSL
+#ifdef HAVE_NGHTTP2_NGHTTP2_H
+void* daemon_setup_listen_doh_sslctx(struct daemon* daemon,
+       struct config_file* cfg)
+{
+       void* ctx;
+       (void)setup_listen_sslctx(&ctx, 0, 1, cfg, daemon->chroot);
+       return ctx;
+}
+#endif /* HAVE_NGHTTP2_NGHTTP2_H */
+#endif /* HAVE_SSL */
+
+#ifdef HAVE_SSL
+#ifdef HAVE_NGTCP2
+void* daemon_setup_listen_quic_sslctx(struct daemon* daemon,
+       struct config_file* cfg)
+{
+       void* ctx;
+       char* chroot = daemon->chroot;
+       char* key = cfg->ssl_service_key;
+       char* pem = cfg->ssl_service_pem;
+       if(chroot && strncmp(key, chroot, strlen(chroot)) == 0)
+               key += strlen(chroot);
+       if(chroot && pem && strncmp(pem, chroot, strlen(chroot)) == 0)
+               pem += strlen(chroot);
+
+       if(!(ctx = quic_sslctx_create(key, pem, NULL))) {
+               fatal_exit("could not set up quic SSL_CTX");
+       }
+       return ctx;
+}
+#endif /* HAVE_NGTCP2 */
+#endif /* HAVE_SSL */
+
+#ifdef HAVE_SSL
+void* daemon_setup_connect_dot_sslctx(struct daemon* daemon,
+       struct config_file* cfg)
+{
+       void* ctx;
+       char* bundle, *chroot = daemon->chroot;
+       bundle = cfg->tls_cert_bundle;
+       if(chroot && bundle && strncmp(bundle, chroot, strlen(chroot)) == 0)
+               bundle += strlen(chroot);
+
+       if(!(ctx = connect_sslctx_create(NULL, NULL, bundle,
+               cfg->tls_win_cert)))
+               fatal_exit("could not set up connect SSL_CTX");
+       return ctx;
+}
+#endif /* HAVE_SSL */
+
 /* setups the needed ssl contexts, fatal_exit() on any failure */
 void
 daemon_setup_sslctxs(struct daemon* daemon, struct config_file* cfg)
 {
 #ifdef HAVE_SSL
-       char* bundle, *chroot = daemon->chroot;
+       char* chroot = daemon->chroot;
        if(cfg->ssl_service_key && cfg->ssl_service_key[0]) {
                char* key = cfg->ssl_service_key;
                char* pem = cfg->ssl_service_pem;
@@ -244,20 +305,18 @@ daemon_setup_sslctxs(struct daemon* daemon, struct config_file* cfg)
                                fatal_exit("could not set session ticket SSL_CTX");
                        }
                }
-               (void)setup_listen_sslctx(&daemon->listen_dot_sslctx, 1, 0,
-                       cfg, chroot);
+               daemon->listen_dot_sslctx = daemon_setup_listen_dot_sslctx(
+                       daemon, cfg);
 #ifdef HAVE_NGHTTP2_NGHTTP2_H
                if(cfg_has_https(cfg)) {
-                       (void)setup_listen_sslctx(&daemon->listen_doh_sslctx,
-                               0, 1, cfg, chroot);
+                       daemon->listen_doh_sslctx =
+                               daemon_setup_listen_doh_sslctx(daemon, cfg);
                }
 #endif
 #ifdef HAVE_NGTCP2
                if(cfg_has_quic(cfg)) {
-                       if(!(daemon->listen_quic_sslctx = quic_sslctx_create(
-                               key, pem, NULL))) {
-                               fatal_exit("could not set up quic SSL_CTX");
-                       }
+                       daemon->listen_quic_sslctx =
+                               daemon_setup_listen_quic_sslctx(daemon, cfg);
                }
 #endif /* HAVE_NGTCP2 */
 
@@ -279,12 +338,8 @@ daemon_setup_sslctxs(struct daemon* daemon, struct config_file* cfg)
                        log_err("Could not stat(%s): %s",
                                pem, strerror(errno));
        }
-       bundle = cfg->tls_cert_bundle;
-       if(chroot && bundle && strncmp(bundle, chroot, strlen(chroot)) == 0)
-               bundle += strlen(chroot);
-       if(!(daemon->connect_dot_sslctx = connect_sslctx_create(NULL, NULL,
-               bundle, cfg->tls_win_cert)))
-               fatal_exit("could not set up connect SSL_CTX");
+       daemon->connect_dot_sslctx = daemon_setup_connect_dot_sslctx(
+               daemon, cfg);
 #else /* HAVE_SSL */
        (void)daemon;(void)cfg;
 #endif /* HAVE_SSL */
@@ -315,20 +370,28 @@ daemon_delete_sslctxs(struct daemon* daemon)
 #endif
 }
 
-/** See if the SSL cert files have changed */
-static int
+int
 ssl_cert_changed(struct daemon* daemon, struct config_file* cfg)
 {
        time_t mtime = 0;
        long ns = 0;
+       char* chroot = daemon->chroot;
+       char* key = cfg->ssl_service_key;
+       char* pem = cfg->ssl_service_pem;
        log_assert(daemon->ssl_service_key && cfg->ssl_service_key);
+       if(chroot && strncmp(key, chroot, strlen(chroot)) == 0)
+               key += strlen(chroot);
+       if(chroot && pem && strncmp(pem, chroot, strlen(chroot)) == 0)
+               pem += strlen(chroot);
+
        if(strcmp(daemon->ssl_service_key, cfg->ssl_service_key) != 0)
                return 1;
-       if(strcmp(daemon->ssl_service_pem, cfg->ssl_service_pem) != 0)
+       if(daemon->ssl_service_pem && cfg->ssl_service_pem &&
+          strcmp(daemon->ssl_service_pem, cfg->ssl_service_pem) != 0)
                return 1;
-       if(!file_get_mtime(daemon->ssl_service_key, &mtime, &ns, NULL)) {
+       if(!file_get_mtime(key, &mtime, &ns, NULL)) {
                log_err("Could not stat(%s): %s",
-                       daemon->ssl_service_key, strerror(errno));
+                       key, strerror(errno));
                /* It has probably changed, but file read is likely going to
                 * fail. */
                return 0;
@@ -336,9 +399,9 @@ ssl_cert_changed(struct daemon* daemon, struct config_file* cfg)
        if(mtime != daemon->mtime_ssl_service_key ||
                ns != daemon->mtime_ns_ssl_service_key)
                return 1;
-       if(!file_get_mtime(daemon->ssl_service_pem, &mtime, &ns, NULL)) {
+       if(!file_get_mtime(pem, &mtime, &ns, NULL)) {
                log_err("Could not stat(%s): %s",
-                       daemon->ssl_service_pem, strerror(errno));
+                       pem, strerror(errno));
                /* It has probably changed, but file read is likely going to
                 * fail. */
                return 0;
index d38cd90d8d80337463f83a06a382a3da414a187d..2be8759a4daed87abf42cd48ab718b0b821e12c7 100644 (file)
@@ -244,4 +244,23 @@ int setup_acl_for_ports(struct acl_list* list, struct listen_port* port_list);
 /* setups the needed ssl contexts, fatal_exit() on any failure */
 void daemon_setup_sslctxs(struct daemon* daemon, struct config_file* cfg);
 
+/** See if the SSL cert files have changed */
+int ssl_cert_changed(struct daemon* daemon, struct config_file* cfg);
+
+/** Setup the listening DoT SSL_CTX, returns the ssl ctx. */
+void* daemon_setup_listen_dot_sslctx(struct daemon* daemon,
+       struct config_file* cfg);
+
+/** Setup the listening DoH SSL_CTX, returns the ssl ctx. */
+void* daemon_setup_listen_doh_sslctx(struct daemon* daemon,
+       struct config_file* cfg);
+
+/** Setup the listening Quic SSL_CTX, returns the ssl ctx */
+void* daemon_setup_listen_quic_sslctx(struct daemon* daemon,
+       struct config_file* cfg);
+
+/** Setup the connect DoT SSL_CTX, returns the ssl ctx */
+void* daemon_setup_connect_dot_sslctx(struct daemon* daemon,
+       struct config_file* cfg);
+
 #endif /* DAEMON_H */
index 292a7f6fad9f3071ae6848224971046943f7dbc1..eccbe745f24c451ba216364e6366897054da228b 100644 (file)
@@ -4633,6 +4633,26 @@ fr_init_time(struct timeval* time_start, struct timeval* time_read,
  * are kept in here. They can then be deleted.
  */
 struct fast_reload_construct {
+       /** ssl context for listening to dnstcp over ssl */
+       void* listen_dot_sslctx;
+       /** ssl context for connecting to dnstcp over ssl */
+       void* connect_dot_sslctx;
+       /** ssl context for listening to DoH */
+       void* listen_doh_sslctx;
+       /** ssl context for listening to quic */
+       void* listen_quic_sslctx;
+       /** the file name that the ssl context is made with, private key. */
+       char* ssl_service_key;
+       /** the file name that the ssl context is made with, certificate. */
+       char* ssl_service_pem;
+       /** modification time for ssl_service_key, in sec and ns. Like
+        * in a struct timespec, but without that for portability. */
+       time_t mtime_ssl_service_key;
+       long mtime_ns_ssl_service_key;
+       /** modification time for ssl_service_pem, in sec and ns. Like
+        * in a struct timespec, but without that for portability. */
+       time_t mtime_ssl_service_pem;
+       long mtime_ns_ssl_service_pem;
        /** construct for views */
        struct views* views;
        /** construct for auth zones */
@@ -4936,9 +4956,6 @@ fr_check_compat_cfg(struct fast_reload_thread* fr, struct config_file* newcfg)
        FR_CHECK_CHANGED_CFG("http_notls_downstream", http_notls_downstream, changed_str);
        FR_CHECK_CHANGED_CFG("https-port", https_port, changed_str);
        FR_CHECK_CHANGED_CFG("tls-port", ssl_port, changed_str);
-       FR_CHECK_CHANGED_CFG_STR("tls-service-key", ssl_service_key, changed_str);
-       FR_CHECK_CHANGED_CFG_STR("tls-service-pem", ssl_service_pem, changed_str);
-       FR_CHECK_CHANGED_CFG_STR("tls-cert-bundle", tls_cert_bundle, changed_str);
        FR_CHECK_CHANGED_CFG_STRLIST("proxy-protocol-port", proxy_protocol_port, changed_str);
        FR_CHECK_CHANGED_CFG_STRLIST("tls-additional-port", tls_additional_port, changed_str);
        FR_CHECK_CHANGED_CFG_STR("interface-automatic-ports", if_automatic_ports, changed_str);
@@ -5047,6 +5064,19 @@ fr_construct_clear(struct fast_reload_construct* ct)
        wait_limits_free(&ct->wait_limits_netblock);
        wait_limits_free(&ct->wait_limits_cookie_netblock);
        domain_limits_free(&ct->domain_limits);
+#ifdef HAVE_SSL
+       /* The SSL contexts can be SSL_CTX_free here. It is reference
+        * counted. So ongoing transfers with can continue.
+        * Once they are done, the context is freed. */
+       SSL_CTX_free((SSL_CTX*)ct->listen_dot_sslctx);
+       SSL_CTX_free((SSL_CTX*)ct->connect_dot_sslctx);
+       SSL_CTX_free((SSL_CTX*)ct->listen_doh_sslctx);
+#endif /* HAVE_SSL */
+#ifdef HAVE_NGTCP2
+       SSL_CTX_free((SSL_CTX*)ct->listen_quic_sslctx);
+#endif
+       free(ct->ssl_service_key);
+       free(ct->ssl_service_pem);
        /* Delete the log identity here so that the global value is not
         * reset by config_delete. */
        if(ct->oldcfg && ct->oldcfg->log_identity) {
@@ -5528,6 +5558,96 @@ auth_zones_check_changes(struct fast_reload_thread* fr,
        return 1;
 }
 
+/** Check if the sslctxs have changed. */
+static int
+fr_check_sslctx_change(struct fast_reload_thread* fr,
+       struct config_file* newcfg)
+{
+#ifdef HAVE_SSL
+       struct daemon* daemon = fr->worker->daemon;
+       if(newcfg->ssl_service_key && newcfg->ssl_service_key[0]) {
+               if(!daemon->ssl_service_key ||
+                       ssl_cert_changed(daemon, newcfg))
+                       return 1;
+       } else {
+               if(daemon->ssl_service_key)
+                       return 1; /* it is removed */
+       }
+       if((daemon->cfg->tls_cert_bundle && !newcfg->tls_cert_bundle) ||
+          (!daemon->cfg->tls_cert_bundle && newcfg->tls_cert_bundle) ||
+          (daemon->cfg->tls_cert_bundle && newcfg->tls_cert_bundle &&
+           strcmp(daemon->cfg->tls_cert_bundle, newcfg->tls_cert_bundle)!=0))
+               return 1; /* The tls-cert-bundle has changed and return
+                       true here makes it reload the connect_dot_sslctx. */
+#else
+       (void)fr; (void)newcfg;
+#endif /* HAVE_SSL */
+       return 0;
+}
+
+/** Create the SSL CTXs when they have changed. */
+static int
+ct_create_sslctxs(struct fast_reload_construct* ct,
+       struct config_file* newcfg, struct daemon* daemon)
+{
+#ifdef HAVE_SSL
+       char* chroot = daemon->chroot;
+       char* key = newcfg->ssl_service_key;
+       char* pem = newcfg->ssl_service_pem;
+
+       if(!(newcfg->ssl_service_key && newcfg->ssl_service_key[0])) {
+               /* Leave listen ctxs and file str at NULL */
+               ct->connect_dot_sslctx = daemon_setup_connect_dot_sslctx(
+                       daemon, newcfg);
+               return 1;
+       }
+
+       if(chroot && strncmp(key, chroot, strlen(chroot)) == 0)
+               key += strlen(chroot);
+       if(chroot && pem && strncmp(pem, chroot, strlen(chroot)) == 0)
+               pem += strlen(chroot);
+
+       ct->listen_dot_sslctx = daemon_setup_listen_dot_sslctx(daemon, newcfg);
+#ifdef HAVE_NGHTTP2_NGHTTP2_H
+       if(cfg_has_https(newcfg)) {
+               ct->listen_doh_sslctx = daemon_setup_listen_doh_sslctx(
+                       daemon, newcfg);
+       }
+#endif
+#ifdef HAVE_NGTCP2
+       if(cfg_has_quic(newcfg)) {
+               ct->listen_quic_sslctx = daemon_setup_listen_quic_sslctx(
+                       daemon, newcfg);
+       }
+#endif /* HAVE_NGTCP2 */
+       ct->connect_dot_sslctx = daemon_setup_connect_dot_sslctx(daemon,
+               newcfg);
+
+       /* Store mtime and names */
+       ct->ssl_service_key = strdup(newcfg->ssl_service_key);
+       if(!ct->ssl_service_key) {
+               log_err("ct_create_sslctxs: out of memory");
+               return 0;
+       }
+       ct->ssl_service_pem = strdup(newcfg->ssl_service_pem);
+       if(!ct->ssl_service_pem) {
+               log_err("ct_create_sslctxs: out of memory");
+               return 0;
+       }
+       if(!file_get_mtime(key, &ct->mtime_ssl_service_key,
+               &ct->mtime_ns_ssl_service_key, NULL))
+               log_err("Could not stat(%s): %s",
+                       key, strerror(errno));
+       if(!file_get_mtime(pem, &ct->mtime_ssl_service_pem,
+               &ct->mtime_ns_ssl_service_pem, NULL))
+               log_err("Could not stat(%s): %s",
+                       pem, strerror(errno));
+#else
+       (void)ct; (void)newcfg; (void)daemon;
+#endif /* HAVE_SSL */
+       return 1;
+}
+
 /** fast reload thread, construct from config the new items */
 static int
 fr_construct_from_config(struct fast_reload_thread* fr,
@@ -5535,6 +5655,13 @@ fr_construct_from_config(struct fast_reload_thread* fr,
 {
        int have_view_respip_cfg = 0;
 
+       fr->sslctxs_changed = fr_check_sslctx_change(fr, newcfg);
+       if(fr->sslctxs_changed) {
+               if(!ct_create_sslctxs(ct, newcfg, fr->worker->daemon)) {
+                       fr_construct_clear(ct);
+                       return 0;
+               }
+       }
        if(!(ct->views = views_create())) {
                fr_construct_clear(ct);
                return 0;
@@ -5812,6 +5939,44 @@ auth_zones_swap(struct auth_zones* az, struct auth_zones* data)
         * the xfer elements can continue to be their callbacks. */
 }
 
+/** Swap two void* */
+static void
+void_ptr_swap(void** a, void **b)
+{
+       void* tmp = *a;
+       *a = *b;
+       *b = tmp;
+}
+
+/** Swap two char* */
+static void
+char_ptr_swap(char** a, char **b)
+{
+       char* tmp = *a;
+       *a = *b;
+       *b = tmp;
+}
+
+/** Swap and set ssl ctx information */
+static void
+sslctxs_swap(struct daemon* daemon, struct fast_reload_construct* ct)
+{
+       void_ptr_swap(&daemon->listen_dot_sslctx, &ct->listen_dot_sslctx);
+       void_ptr_swap(&daemon->connect_dot_sslctx, &ct->connect_dot_sslctx);
+#ifdef HAVE_NGHTTP2_NGHTTP2_H
+       void_ptr_swap(&daemon->listen_doh_sslctx, &ct->listen_doh_sslctx);
+#endif
+#ifdef HAVE_NGTCP2
+       void_ptr_swap(&daemon->listen_quic_sslctx, &ct->listen_quic_sslctx);
+#endif /* HAVE_NGTCP2 */
+       char_ptr_swap(&daemon->ssl_service_key, &ct->ssl_service_key);
+       char_ptr_swap(&daemon->ssl_service_pem, &ct->ssl_service_pem);
+       daemon->mtime_ssl_service_key = ct->mtime_ssl_service_key;
+       daemon->mtime_ns_ssl_service_key = ct->mtime_ns_ssl_service_key;
+       daemon->mtime_ssl_service_pem = ct->mtime_ssl_service_pem;
+       daemon->mtime_ns_ssl_service_pem = ct->mtime_ns_ssl_service_pem;
+}
+
 #if defined(ATOMIC_POINTER_LOCK_FREE) && defined(HAVE_LINK_ATOMIC_STORE)
 /** Fast reload thread, if atomics are available, copy the config items
  * one by one with atomic store operations. */
@@ -6420,6 +6585,9 @@ fr_reload_config(struct fast_reload_thread* fr, struct config_file* newcfg,
        daemon->env->cachedb_enabled = cachedb_is_enabled(&daemon->mods,
                daemon->env);
 #endif
+       if(fr->sslctxs_changed) {
+               sslctxs_swap(daemon, ct);
+       }
 #ifdef USE_DNSTAP
        if(env->cfg->dnstap) {
                if(!fr->fr_nopause)
@@ -7616,6 +7784,40 @@ fr_worker_pickup_auth_changes(struct worker* worker,
        }
 }
 
+/** Fast reload, the worker picks up changes in listen_dnsport. */
+static void
+fr_worker_pickup_listen_dnsport(struct worker* worker)
+{
+       struct listen_dnsport* front = worker->front;
+       struct daemon* daemon = worker->daemon;
+       if(worker->daemon->fast_reload_thread->sslctxs_changed) {
+               struct listen_list* ll;
+               void* dot_sslctx = daemon->listen_dot_sslctx;
+               void* doh_sslctx = daemon->listen_doh_sslctx;
+               void* quic_sslctx = daemon->listen_quic_sslctx;
+               for(ll = front->cps; ll; ll = ll->next) {
+                       struct comm_point* cp = ll->com;
+                       if(cp->type == comm_tcp_accept &&
+                               cp->pp2_enabled /* true for http */) {
+                               if(cp->ssl)
+                                       cp->ssl = doh_sslctx;
+                       } else if(cp->type == comm_tcp_accept) {
+                               if(cp->ssl)
+                                       cp->ssl = dot_sslctx;
+#ifdef HAVE_NGTCP2
+                       } else if(cp->type == comm_doq) {
+                               if(cp->ssl) {
+                                       cp->ssl = quic_sslctx;
+                                       if(cp->doq_socket)
+                                               cp->doq_socket->ctx =
+                                                       (SSL_CTX*)quic_sslctx;
+                               }
+#endif
+                       }
+               }
+       }
+}
+
 /** Fast reload, the worker picks up changes in outside_network. */
 static void
 fr_worker_pickup_outside_network(struct worker* worker)
@@ -7631,6 +7833,8 @@ fr_worker_pickup_outside_network(struct worker* worker)
        outnet->tcp_reuse_timeout = cfg->tcp_reuse_timeout;
        outnet->tcp_auth_query_timeout = cfg->tcp_auth_query_timeout;
        outnet->delayclose = cfg->delay_close;
+       if(worker->daemon->fast_reload_thread->sslctxs_changed)
+               outnet->sslctx = worker->daemon->connect_dot_sslctx;
        if(outnet->delayclose) {
 #ifndef S_SPLINT_S
                outnet->delay_tv.tv_sec = cfg->delay_close/1000;
@@ -7702,6 +7906,7 @@ fast_reload_worker_pickup_changes(struct worker* worker)
        worker->env.cachedb_enabled = worker->daemon->env->cachedb_enabled;
 #endif
        fr_worker_pickup_outside_network(worker);
+       fr_worker_pickup_listen_dnsport(worker);
 #ifdef USE_DNSTAP
        fr_worker_pickup_dnstap_changes(worker);
 #endif
index 77c00a5976f06403e64035dddf37ccc164a09ead..e9c392fd819e1ba2dfcbd0c9df14f8db6200d3ac 100644 (file)
@@ -255,6 +255,8 @@ struct fast_reload_thread {
        struct fast_reload_auth_change* auth_zone_change_list;
        /** the old tree of auth zones, to lookup. */
        struct auth_zones* old_auth_zones;
+       /** If the ssl ctxs have changed. */
+       int sslctxs_changed;
 };
 
 /**
index 37b98f6702efebaa2a7fc6814bf7335cefbacc0f..f9b6df9677e9b6a9e64754aece0c1e6e5f5515cc 100644 (file)
@@ -8,6 +8,9 @@
          renew. Fix so that a reload checks if the files have changed, and
          if so, reload the contexts. Also for DoH, DoQ and outgoing DoT.
        - iana portlist updated.
+       - For #278: fast_reload can reload tls-service-key, tls-service-pem
+         and tls-cert-bundle changes. It checks the modification time of
+         the tls-service-key and tls-service-pem files for update.
 
 9 March 2026: Wouter
        - Fix compile failure in unbound-checkconf for older gcc compiler.
index 2f5520824dd946a6176b93a9de770ee24defdcd7..d95d6dd0b97515d88bf3581c6d450149d95f09e7 100644 (file)
@@ -170,6 +170,8 @@ There are several commands that the server understands.
     :ref:`tcp-auth-query-timeout<unbound.conf.tcp-auth-query-timeout>`,
     :ref:`delay-close<unbound.conf.delay-close>`.
     :ref:`iter-scrub-promiscuous<unbound.conf.iter-scrub-promiscuous>`.
+    :ref:`tls-service-key<unbound.conf.tls-service-key>`.
+    :ref:`tls-service-pem<unbound.conf.tls-service-pem>`.
 
     It does not work with
     :ref:`interface<unbound.conf.interface>` and
index f02087598b155fc883a60b60ff1f5eddb2484d72..8f7b24e83c2f4d63e2d0d8e86aa600c8ee6b2706 100644 (file)
@@ -1048,9 +1048,13 @@ These options are part of the ``server:`` section.
     certificate is in the :ref:`tls-service-pem<unbound.conf.tls-service-pem>`
     file and it must also be specified if
     :ref:`tls-service-key<unbound.conf.tls-service-key>` is specified.
-    Enabling or disabling this service requires a restart (a reload is not
-    enough), because the key is read while root permissions are held and before
-    chroot (if any).
+    If the key is stored with root permissions or outside of chroot, then
+    a change or enabling or disabling requires a restart (a reload is not
+    enough).
+    But if the key file (and tls-service-pem file) are accessible, then they
+    are read in on reload, and fast_reload.
+    The server checks the modification time of the file (and the filename)
+    to see if the file has changed for reload.
     The ports enabled implicitly or explicitly via
     :ref:`tls-port<unbound.conf.tls-port>` and
     :ref:`https-port<unbound.conf.https-port>` do not provide normal DNS TCP