diff --git a/configlexer.lex b/configlexer.lex index 4b2cf1bf..c85afdd1 100644 --- a/configlexer.lex +++ b/configlexer.lex @@ -267,6 +267,7 @@ rrl-ipv4-prefix-length{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_RRL_IPV4_ rrl-ipv6-prefix-length{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_RRL_IPV6_PREFIX_LENGTH;} rrl-whitelist-ratelimit{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_RRL_WHITELIST_RATELIMIT;} rrl-whitelist{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_RRL_WHITELIST;} +reload-config{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_RELOAD_CONFIG; } zonefiles-check{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_ZONEFILES_CHECK;} zonefiles-write{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_ZONEFILES_WRITE;} dnstap{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_DNSTAP;} diff --git a/configparser.y b/configparser.y index a2123cb0..2c35cb4e 100644 --- a/configparser.y +++ b/configparser.y @@ -115,6 +115,7 @@ struct component { %token VAR_MINIMAL_RESPONSES %token VAR_CONFINE_TO_ZONE %token VAR_REFUSE_ANY +%token VAR_RELOAD_CONFIG %token VAR_ZONEFILES_CHECK %token VAR_ZONEFILES_WRITE %token VAR_RRL_SIZE @@ -445,6 +446,8 @@ server_option: cfg_parser->opt->rrl_whitelist_ratelimit = (size_t)$2; #endif } + | VAR_RELOAD_CONFIG boolean + { cfg_parser->opt->reload_config = $2; } | VAR_ZONEFILES_CHECK boolean { cfg_parser->opt->zonefiles_check = $2; } | VAR_ZONEFILES_WRITE number diff --git a/nsd.conf.5.in b/nsd.conf.5.in index 7ffc0190..90359a7e 100644 --- a/nsd.conf.5.in +++ b/nsd.conf.5.in @@ -458,6 +458,9 @@ the response with one RRset. Popular and not large types, like A, AAAA and MX are preferred, and large types, like DNSKEY and RRSIG are picked with a lower preference than other types. This makes the response smaller. .TP +.B reload\-config:\fR +Reload configuration file and update TSIG keys and zones on SIGHUP. +Default is no. .B zonefiles\-check:\fR Make NSD check the mtime of zone files on start and sighup. If you disable it it starts faster (less disk activity in case of a lot of zones). @@ -468,6 +471,7 @@ regardless of this option. Write updated secondary zones to their zonefile every N seconds. If the zone or pattern's "zonefile" option is set to "" (empty string), no zonefile is written. The default is 3600 (1 hour). +.TP .\" rrlstart .TP .B rrl\-size:\fR diff --git a/nsd.conf.sample.in b/nsd.conf.sample.in index df9b8941..c50db2e4 100644 --- a/nsd.conf.sample.in +++ b/nsd.conf.sample.in @@ -216,6 +216,9 @@ server: # default is 3600. # zonefiles-write: 3600 + # Reload nsd.conf and update TSIG keys and zones on SIGHUP. + # reload-config: no + # RRLconfig # Response Rate Limiting, size of the hashtable. Default 1000000. # rrl-size: 1000000 diff --git a/options.c b/options.c index 9db0d398..29f0eb6d 100644 --- a/options.c +++ b/options.c @@ -137,6 +137,7 @@ nsd_options_create(region_type* region) opt->dnstap_log_auth_query_messages = 0; opt->dnstap_log_auth_response_messages = 0; #endif + opt->reload_config = 0; opt->zonefiles_check = 1; opt->zonefiles_write = ZONEFILES_WRITE_INTERVAL; opt->xfrd_reload_timeout = 1; diff --git a/options.h b/options.h index 02111890..f58a9183 100644 --- a/options.h +++ b/options.h @@ -116,6 +116,7 @@ struct nsd_options { const char* zonelistfile; const char* nsid; int xfrd_reload_timeout; + int reload_config; int zonefiles_check; int zonefiles_write; int log_time_ascii; diff --git a/remote.c b/remote.c index 752ddeb9..cad1c908 100644 --- a/remote.c +++ b/remote.c @@ -71,7 +71,6 @@ #else # include "mini_event.h" #endif -#include "remote.h" #include "util.h" #include "xfrd.h" #include "xfrd-catalog-zones.h" @@ -81,6 +80,7 @@ #include "options.h" #include "difffile.h" #include "ipc.h" +#include "remote.h" #ifdef HAVE_SYS_TYPES_H # include @@ -2038,6 +2038,46 @@ do_repattern(RES* ssl, xfrd_state_type* xfrd) region_destroy(region); } +static void print_cfg_err(void *unused, const char *message) +{ + (void)unused; + log_msg(LOG_ERR, "%s", message); +} + +/* mostly identical to do_repattern */ +void xfrd_reload_config(xfrd_state_type *xfrd) +{ + const char *chrootdir = xfrd->nsd->chrootdir; + const char *file = xfrd->nsd->options->configfile; + + if (chrootdir && !file_inside_chroot(file, chrootdir)) + { + log_msg(LOG_ERR, "%s is not relative to %s: %s", + xfrd->nsd->options->configfile, xfrd->nsd->chrootdir, + "chroot prevents reread of config"); + goto error_chroot; + } + + region_type *region = region_create(xalloc, free); + struct nsd_options *options = nsd_options_create(region); + + if (!parse_options_file( + options, file, print_cfg_err, NULL, xfrd->nsd->options)) + { + goto error_parse; + } + + repat_keys(xfrd, options); + repat_patterns(xfrd, options); /* adds/deletes zones too */ + repat_options(xfrd, options); + zonestat_inc_ifneeded(xfrd); + +error_parse: + region_destroy(region); +error_chroot: + return; +} + /** do the serverpid command: printout pid of server process */ static void do_serverpid(RES* ssl, xfrd_state_type* xfrd) diff --git a/remote.h b/remote.h index 771bf86d..c1d6a611 100644 --- a/remote.h +++ b/remote.h @@ -101,4 +101,6 @@ void daemon_remote_attach(struct daemon_remote* rc, struct xfrd_state* xfrd); */ int create_local_accept_sock(const char* path, int* noproto); +void xfrd_reload_config(struct xfrd_state *xfrd); + #endif /* DAEMON_REMOTE_H */ diff --git a/tpkg/reload-config.tdir/example.com.zone b/tpkg/reload-config.tdir/example.com.zone new file mode 100644 index 00000000..b61df49f --- /dev/null +++ b/tpkg/reload-config.tdir/example.com.zone @@ -0,0 +1,4 @@ +@ IN SOA ns1.example.com. hostmaster 2024080900 1800 900 604800 86400 + IN NS ns1.example.com. + IN NS ns2.example.com. + IN A 192.0.2.1 diff --git a/tpkg/reload-config.tdir/example.net.zone b/tpkg/reload-config.tdir/example.net.zone new file mode 100644 index 00000000..6d6d1007 --- /dev/null +++ b/tpkg/reload-config.tdir/example.net.zone @@ -0,0 +1,4 @@ +@ IN SOA ns1.example.net. hostmaster 2024090900 1800 900 604800 86400 + IN NS ns1.example.net. + IN NS ns2.example.net. + IN A 192.0.2.2 diff --git a/tpkg/reload-config.tdir/reload-config.1.conf b/tpkg/reload-config.tdir/reload-config.1.conf new file mode 100644 index 00000000..96000104 --- /dev/null +++ b/tpkg/reload-config.tdir/reload-config.1.conf @@ -0,0 +1,13 @@ +server: + xfrdfile: "xfrd.state" + logfile: "/dev/stderr" + zonesdir: "" + username: "" + chroot: "" + verbosity: 1 + ip-address: 127.0.0.1 + reload-config: yes + +zone: + name: example.com + zonefile: example.com.zone diff --git a/tpkg/reload-config.tdir/reload-config.2.conf b/tpkg/reload-config.tdir/reload-config.2.conf new file mode 100644 index 00000000..ce62fca0 --- /dev/null +++ b/tpkg/reload-config.tdir/reload-config.2.conf @@ -0,0 +1,17 @@ +server: + xfrdfile: "xfrd.state" + logfile: "/dev/stderr" + zonesdir: "" + username: "" + chroot: "" + verbosity: 1 + ip-address: 127.0.0.1 + reload-config: yes + +zone: + name: example.com + zonefile: example.com.zone + +zone: + name: example.net + zonefile: example.net.zone diff --git a/tpkg/reload-config.tdir/reload-config.dsc b/tpkg/reload-config.tdir/reload-config.dsc new file mode 100644 index 00000000..f5988091 --- /dev/null +++ b/tpkg/reload-config.tdir/reload-config.dsc @@ -0,0 +1,16 @@ +BaseName: reload-config +Version: 1.0 +Description: Test reread of nsd.conf on SIGHUP +CreationDate: Fri Aug 9 13:47:00 CEST 2024 +Maintainer: Jeroen Koekkoek +Category: +Component: +CmdDepends: +Depends: 0000_nsd-compile.tpkg +Help: +Pre: reload-config.pre +Post: reload-config.post +Test: reload-config.test +AuxFiles: +Passed: +Failure: diff --git a/tpkg/reload-config.tdir/reload-config.post b/tpkg/reload-config.tdir/reload-config.post new file mode 100644 index 00000000..33aceaa0 --- /dev/null +++ b/tpkg/reload-config.tdir/reload-config.post @@ -0,0 +1,19 @@ +# #-- reload-config.post --# +# source the master var file when it's there +[ -f ../.tpkg.var.master ] && source ../.tpkg.var.master +# source the test var file when it's there +[ -f .tpkg.var.test ] && source .tpkg.var.test +# +# do your teardown here +. ../common.sh + +cat nsd.log + +rm -f nsd.log +rm -f xfrd.state +rm -f nsd.zonelist + +if test -f $NSD_PID; then + # the test must have failed + kill_pid `cat $NSD_PID` +fi diff --git a/tpkg/reload-config.tdir/reload-config.pre b/tpkg/reload-config.tdir/reload-config.pre new file mode 100644 index 00000000..cf490829 --- /dev/null +++ b/tpkg/reload-config.tdir/reload-config.pre @@ -0,0 +1,16 @@ +# #-- reload-config.pre --# +# source the master var file when it's there +[ -f ../.tpkg.var.master ] && source ../.tpkg.var.master +# use .tpkg.var.test for in test variable passing +[ -f .tpkg.var.test ] && source .tpkg.var.test +. ../common.sh + +# start NSD +get_random_port 1 +NSD_PORT=$RND_PORT + +NSD_PID="nsd.pid.$$" + +# share the vars +echo "export NSD_PORT=$NSD_PORT" >> .tpkg.var.test +echo "export NSD_PID=$NSD_PID" >> .tpkg.var.test diff --git a/tpkg/reload-config.tdir/reload-config.test b/tpkg/reload-config.tdir/reload-config.test new file mode 100644 index 00000000..be583bd0 --- /dev/null +++ b/tpkg/reload-config.tdir/reload-config.test @@ -0,0 +1,37 @@ +# #-- reload-config.test --# +# source the master var file when it's there +[ -f ../.tpkg.var.master ] && source ../.tpkg.var.master +# use .tpkg.var.test for in test variable passing +[ -f .tpkg.var.test ] && source .tpkg.var.test +. ../common.sh + +NSD="../../nsd" + +cp reload-config.1.conf nsd.conf +$NSD -p $NSD_PORT -P $NSD_PID -V 5 -c nsd.conf > nsd.log 2>&1 +wait_nsd_up nsd.log + +dig @127.0.0.1 -p $NSD_PORT example.com. SOA | tee result +if ! grep hostmaster.example.com result; then + echo "zone example.com should be available" >&2 + exit 1 +fi + +dig @127.0.0.1 -p $NSD_PORT example.net. SOA | tee result +if grep hostmaster.example.net result; then + echo "zone example.net should not be available" >&2 + exit 1 +fi + +cp reload-config.2.conf nsd.conf +kill -SIGHUP $(head -n1 ${NSD_PID}) + +wait_logfile nsd.log "zone example.net read" + +dig @127.0.0.1 -p $NSD_PORT example.net. SOA | tee result +if ! grep hostmaster.example.net result; then + echo "zone example.net should be available" >&2 + exit 1 +fi + +exit 0 diff --git a/xfrd.c b/xfrd.c index fbbe4774..4e5fcf82 100644 --- a/xfrd.c +++ b/xfrd.c @@ -272,6 +272,9 @@ xfrd_sig_process(void) } else if(xfrd->nsd->signal_hint_reload_hup) { log_msg(LOG_WARNING, "SIGHUP received, reloading..."); xfrd->nsd->signal_hint_reload_hup = 0; + if(xfrd->nsd->options->reload_config) { + xfrd_reload_config(xfrd); + } if(xfrd->nsd->options->zonefiles_check) { task_new_check_zonefiles(xfrd->nsd->task[ xfrd->nsd->mytask], xfrd->last_task, NULL); @@ -316,6 +319,7 @@ xfrd_main(void) xfrd->shutdown = 0; while(!xfrd->shutdown) { + /* xfrd_sig_process takes care of reading zones on SIGHUP */ xfrd_process_catalog_producer_zones(); xfrd_process_catalog_consumer_zones(); /* process activated zones before blocking in select again */