From: Pieter Lexis Date: Tue, 22 Dec 2015 11:39:53 +0000 (+0100) Subject: Fix chroot() issues X-Git-Tag: dnsdist-1.0.0-alpha1~6^2 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=f0f3f0b0dc95ebdb15c492922d4a8bf3b245d7c7;p=pdns Fix chroot() issues We now create the sockets and pid-files inside the chroot. The *_control tools also know about the chroot and act accordingly. Closes #191 Closes #148 --- diff --git a/docs/manpages/rec_control.1.md b/docs/manpages/rec_control.1.md index a9167ce5c..2d2ce201a 100644 --- a/docs/manpages/rec_control.1.md +++ b/docs/manpages/rec_control.1.md @@ -10,7 +10,7 @@ rec_control - control pdns_recursor DESCRIPTION ----------- -rec_control(1) allows the operator to control a running instance +**rec_control** allows the operator to control a running instance of the pdns_recursor. The commands that can be passed to the recursor are described on @@ -29,8 +29,11 @@ To dump the cache to disk, execute: --help : provide this helpful message. +--config-dir=*PATH* +: Directory where the recursor.conf lives. + --socket-dir=*PATH* -: Where the controlsocket will live. +: Where the controlsocket will live, please use **--config-dir** instead. --socket-pid=*PID* : When running in SMP mode, pid of **pdns_recursor** to control. diff --git a/docs/markdown/authoritative/settings.md b/docs/markdown/authoritative/settings.md index ebbeb0dab..d29394553 100644 --- a/docs/markdown/authoritative/settings.md +++ b/docs/markdown/authoritative/settings.md @@ -114,6 +114,13 @@ If sending carbon updates, this is the interval between them in seconds. See If set, chroot to this directory for more security. See ["Security settings & considerations"](../common/security.md). +Make sure that `/dev/log` is available from within the chroot. Logging will +silently fail over time otherwise (on logrotate). + +When setting `chroot`, all other paths in the config (except for +[`config-dir`](#config-dir) and [`module-dir`](#module-dir)) set in the configuration +are relative to the new root. + ## `config-dir` * Path diff --git a/docs/markdown/recursor/settings.md b/docs/markdown/recursor/settings.md index d2fad962d..d5ca00f92 100644 --- a/docs/markdown/recursor/settings.md +++ b/docs/markdown/recursor/settings.md @@ -123,6 +123,12 @@ If set, chroot to this directory for more security. See [Security](../common/sec Make sure that `/dev/log` is available from within the chroot. Logging will silently fail over time otherwise (on logrotate). +When using `chroot`, all other paths (except for [`config-dir`](#config-dir) set +in the configuration are relative to the new root. + +When using `chroot` and the API ([`webserver`](#webserver)), [`api-readonly`](#api-readonly) +must be set and [`api-config-dir`](#api-config-dir) unset. + ## `client-tcp-timeout` * Integer * Default: 2 @@ -607,6 +613,8 @@ Use only a single socket for outgoing queries. Where to store the control socket and pidfile. The default depends on `LOCALSTATEDIR` during compile-time (usually `/var/run` or `/run`). +When using [`chroot`](#chroot) the default becomes to `/`. + ## `socket-owner`, `socket-group`, `socket-mode` Owner, group and mode of the controlsocket. Owner and group can be specified by name, mode is in octal. diff --git a/pdns/common_startup.cc b/pdns/common_startup.cc index 06871f9d1..2381a3c3a 100644 --- a/pdns/common_startup.cc +++ b/pdns/common_startup.cc @@ -472,7 +472,7 @@ void mainthread() secPollParseResolveConf(); - if(!::arg()["chroot"].empty()) { + if(!::arg()["chroot"].empty()) { triggerLoadOfLibraries(); if(::arg().mustDo("master") || ::arg().mustDo("slave")) gethostbyname("a.root-servers.net"); // this forces all lookup libraries to be loaded diff --git a/pdns/dynlistener.cc b/pdns/dynlistener.cc index b160a056f..eab787f94 100644 --- a/pdns/dynlistener.cc +++ b/pdns/dynlistener.cc @@ -177,7 +177,16 @@ DynListener::DynListener(const string &progname) d_s=-1; if(!progname.empty()) { - string socketname=arg()["socket-dir"]+"/"; + string socketname = ::arg()["socket-dir"]; + if (::arg()["socket-dir"].empty()) { + if (::arg()["chroot"].empty()) + socketname = LOCALSTATEDIR; + else + socketname = ::arg()["chroot"]; + } else if (!::arg()["socket-dir"].empty() && !::arg()["chroot"].empty()) { + socketname = ::arg()["chroot"] + ::arg()["socket-dir"]; + } + socketname += "/"; cleanSlashes(socketname); if(!mkdir(socketname.c_str(),0700)) // make /var directory, if needed diff --git a/pdns/dynloader.cc b/pdns/dynloader.cc index 282b919be..f1785cbce 100644 --- a/pdns/dynloader.cc +++ b/pdns/dynloader.cc @@ -59,7 +59,7 @@ int main(int argc, char **argv) string s_programname="pdns"; ::arg().set("config-dir","Location of configuration directory (pdns.conf)")=SYSCONFDIR; - ::arg().set("socket-dir","Where the controlsocket will live")=LOCALSTATEDIR; + ::arg().set("socket-dir",string("Where the controlsocket will live, ")+LOCALSTATEDIR+" when unset and not chrooted" )=""; ::arg().set("remote-address","Remote address to query"); ::arg().set("remote-port","Remote port to query")="53000"; ::arg().set("secret","Secret needed to connect to remote PowerDNS"); @@ -94,8 +94,18 @@ int main(int argc, char **argv) ::arg().laxFile(configname.c_str()); ::arg().laxParse(argc,argv); // reparse so the commandline still wins } - - string socketname=::arg()["socket-dir"]+"/"+s_programname+".controlsocket"; + + string socketname=::arg()["socket-dir"]; + if (::arg()["socket-dir"].empty()) { + if (::arg()["chroot"].empty()) + socketname = LOCALSTATEDIR; + else + socketname = ::arg()["chroot"] + "/"; + } else if (!::arg()["socket-dir"].empty() && !::arg()["chroot"].empty()) { + socketname = ::arg()["chroot"] + ::arg()["socket-dir"]; + } + + socketname += "/" + s_programname + ".controlsocket"; cleanSlashes(socketname); try { diff --git a/pdns/pdns.conf-dist b/pdns/pdns.conf-dist index 69ccfa6f4..0cebcfeb9 100644 --- a/pdns/pdns.conf-dist +++ b/pdns/pdns.conf-dist @@ -460,9 +460,9 @@ # soa-retry-default=3600 ################################# -# socket-dir Where the controlsocket will live +# socket-dir Where the controlsocket will live, /var/run when unset and not chrooted # -# socket-dir=/var/run +# socket-dir= ################################# # tcp-control-address If set, PowerDNS can be controlled over TCP on this address diff --git a/pdns/pdns_recursor.cc b/pdns/pdns_recursor.cc index 87f147273..a002446cf 100644 --- a/pdns/pdns_recursor.cc +++ b/pdns/pdns_recursor.cc @@ -1774,6 +1774,13 @@ void handleRCC(int fd, FDMultiplexer::funcparam_t& var) RecursorControlParser::func_t* command; string answer=rcp.getAnswer(msg, &command); + + // If we are inside a chroot, we need to strip + if (!arg()["chroot"].empty()) { + int len = arg()["chroot"].length(); + remote = remote.substr(len); + } + try { s_rcc.send(answer, &remote); command(); @@ -2320,10 +2327,6 @@ int serviceMain(int argc, char*argv[]) break; } - s_pidfname=::arg()["socket-dir"]+"/"+s_programname+".pid"; - if(!s_pidfname.empty()) - unlink(s_pidfname.c_str()); // remove possible old pid file - if(::arg().mustDo("daemon")) { L< 1 ? forks : -1); g_numThreads = ::arg().asNum("threads") + ::arg().mustDo("pdns-distributes-queries"); g_numWorkerThreads = ::arg().asNum("threads"); g_maxMThreads = ::arg().asNum("max-mthreads"); @@ -2353,8 +2354,17 @@ int serviceMain(int argc, char*argv[]) L< 1 ? forks : -1); + Utility::dropUserPrivs(newuid); makeThreadPipes(); @@ -2580,7 +2590,7 @@ int main(int argc, char **argv) ::arg().set("socket-group","Group of socket")=""; ::arg().set("socket-mode", "Permissions for socket")=""; - ::arg().set("socket-dir","Where the controlsocket will live")=LOCALSTATEDIR; + ::arg().set("socket-dir",string("Where the controlsocket will live, ")+LOCALSTATEDIR+" when unset and not chrooted" )=""; ::arg().set("delegation-only","Which domains we only accept delegations from")=""; ::arg().set("query-local-address","Source IP address for sending queries")="0.0.0.0"; ::arg().set("query-local-address6","Source IPv6 address for sending queries. IF UNSET, IPv6 WILL NOT BE USED FOR OUTGOING QUERIES")=""; @@ -2655,6 +2665,18 @@ int main(int argc, char **argv) ::arg().parse(argc,argv); + if( !::arg()["chroot"].empty() && !::arg()["api-config-dir"].empty() && !::arg().mustDo("api-readonly") ) { + L<