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
--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.
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
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
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.
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
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
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");
::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 {
# 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
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();
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<<Logger::Warning<<"Calling daemonize, going to background"<<endl;
L.toConsole(Logger::Critical);
signal(SIGUSR1,usr1Handler);
signal(SIGUSR2,usr2Handler);
signal(SIGPIPE,SIG_IGN);
- writePid();
- makeControlChannelSocket( ::arg().asNum("processes") > 1 ? forks : -1);
g_numThreads = ::arg().asNum("threads") + ::arg().mustDo("pdns-distributes-queries");
g_numWorkerThreads = ::arg().asNum("threads");
g_maxMThreads = ::arg().asNum("max-mthreads");
L<<Logger::Error<<"Unable to chroot to '"+::arg()["chroot"]+"': "<<strerror (errno)<<", exiting"<<endl;
exit(1);
}
+ else
+ L<<Logger::Error<<"Chrooted to '"<<::arg()["chroot"]<<"'"<<endl;
}
+ s_pidfname=::arg()["socket-dir"]+"/"+s_programname+".pid";
+ if(!s_pidfname.empty())
+ unlink(s_pidfname.c_str()); // remove possible old pid file
+ writePid();
+
+ makeControlChannelSocket( ::arg().asNum("processes") > 1 ? forks : -1);
+
Utility::dropUserPrivs(newuid);
makeThreadPipes();
::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")="";
::arg().parse(argc,argv);
+ if( !::arg()["chroot"].empty() && !::arg()["api-config-dir"].empty() && !::arg().mustDo("api-readonly") ) {
+ L<<Logger::Error<<"Using chroot and a writable API is not possible"<<endl;
+ exit(EXIT_FAILURE);
+ }
+
+ if (::arg()["socket-dir"].empty()) {
+ if (::arg()["chroot"].empty())
+ ::arg().set("socket-dir") = LOCALSTATEDIR;
+ else
+ ::arg().set("socket-dir") = "/";
+ }
+
::arg().set("delegation-only")=toLower(::arg()["delegation-only"]);
if(::arg().asNum("threads")==1)
}
if(cmd=="reload-acls") {
+ if(!::arg()["chroot"].empty()) {
+ L<<Logger::Error<<"Unable to reload ACL when chroot()'ed, requested via control channel"<<endl;
+ return "Unable to reload ACL when chroot()'ed, please restart\n";
+ }
+
try {
parseACLs();
}
}
if(cmd=="reload-zones") {
+ if(!::arg()["chroot"].empty()) {
+ L<<Logger::Error<<"Unable to reload zones and forwards when chroot()'ed, requested via control channel"<<endl;
+ return "Unable to reload zones and forwards when chroot()'ed, please restart\n";
+ }
return reloadAuthAndForwards();
}
{
arg().set("config-dir","Location of configuration directory (recursor.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("chroot","switch to chroot jail")="";
arg().set("process","When controlling multiple recursors, the target process number")="";
arg().set("timeout", "Number of seconds to wait for the recursor to respond")="5";
arg().set("config-name","Name of this virtual configuration - will rename the binary image")="";
cleanSlashes(configname);
- if(!::arg().preParseFile(configname.c_str(), "socket-dir", LOCALSTATEDIR))
+ if(!::arg().preParseFile(configname.c_str(), "socket-dir", ""))
cerr<<"Warning: unable to parse configuration file '"<<configname<<"'"<<endl;
+ if(!::arg().preParseFile(configname.c_str(), "chroot", ""))
+ cerr<<"Warning: unable to parse configuration file '"<<configname<<"'"<<endl;
+
arg().laxParse(argc,argv); // make sure the commandline wins
+
+ if (::arg()["socket-dir"].empty()) {
+ if (::arg()["chroot"].empty())
+ ::arg().set("socket-dir") = LOCALSTATEDIR;
+ else
+ ::arg().set("socket-dir") = ::arg()["chroot"] + "/";
+ } else if (!::arg()["chroot"].empty()) {
+ ::arg().set("socket-dir") = ::arg()["chroot"] + "/" + ::arg()["socket-dir"];
+ }
}
int main(int argc, char** argv)
if(!::arg().mustDo("write-pid"))
return;
- string fname=::arg()["socket-dir"]+"/"+s_programname+".pid";
+ string fname=::arg()["socket-dir"];
+ if (::arg()["socket-dir"].empty()) {
+ if (::arg()["chroot"].empty())
+ fname = LOCALSTATEDIR;
+ else
+ fname = ::arg()["chroot"] + "/";
+ } else if (!::arg()["socket-dir"].empty() && !::arg()["chroot"].empty()) {
+ fname = ::arg()["chroot"] + ::arg()["socket-dir"];
+ }
+
+ fname += + "/" + s_programname + ".pid";
ofstream of(fname.c_str());
if(of)
of<<getpid()<<endl;
{
::arg().set("config-dir","Location of configuration directory (pdns.conf)")=SYSCONFDIR;
::arg().set("config-name","Name of this virtual configuration - will rename the binary image")="";
- ::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("module-dir","Default directory for modules")=PKGLIBDIR;
::arg().set("chroot","If set, chroot to this directory for more security")="";
::arg().set("logging-facility","Log under a specific facility")="";