From 2e2a47108efcf569bf6c5e453a6f994ba162c002 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 17 Sep 2021 10:38:34 +0200 Subject: [PATCH 01/10] Add TCP store Like UDSRemoteStore, but over a TCP connection. Example usage: $ nix path-info --store tcp://example.org:1234 ... --- src/libstore/tcp-store.cc | 107 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 107 insertions(+) create mode 100644 src/libstore/tcp-store.cc diff --git a/src/libstore/tcp-store.cc b/src/libstore/tcp-store.cc new file mode 100644 index 00000000000..1ce891427a3 --- /dev/null +++ b/src/libstore/tcp-store.cc @@ -0,0 +1,107 @@ +#include "remote-store.hh" +#include "finally.hh" + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace nix { + +struct TCPStoreConfig : virtual RemoteStoreConfig +{ + using RemoteStoreConfig::RemoteStoreConfig; + + const std::string name() override { return "TCP Store"; } +}; + +struct TCPStore : public virtual TCPStoreConfig, public virtual RemoteStore +{ + std::string host; + uint16_t port = 0; + + TCPStore(const std::string scheme, std::string path, const Params & params) + : StoreConfig(params) + , RemoteStoreConfig(params) + , TCPStoreConfig(params) + , Store(params) + , RemoteStore(params) + { + // FIXME: use parsed URL. + + auto p = path.find(':'); + if (p == std::string::npos) + throw UsageError("tcp:// stores require a port number (e.g. 'tcp://example.org:1234'), in '%s'", path); + + host = std::string(path, 0, p); + if (auto port2 = string2Int(std::string(path, p + 1))) + port = *port2; + else + throw UsageError("invalid TCP port number, in '%s'", path); + } + + std::string getUri() override + { return fmt("tcp://"); } + + static std::set uriSchemes() + { return {"tcp"}; } + + bool sameMachine() override + { return false; } + + ref openConnection() override + { + auto conn = make_ref(); + + struct addrinfo hints; + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = 0; + hints.ai_protocol = IPPROTO_TCP; + + struct addrinfo * result; + if (auto s = getaddrinfo(host.c_str(), fmt("%d", port).c_str(), &hints, &result)) + throw Error("DNS lookup of '%s' failed: %s", host, gai_strerror(s)); + + Finally cleanup([&]() { freeaddrinfo(result); }); + + std::string err; + + for (auto rp = result; rp; rp = rp->ai_next) { + AutoCloseFD fd = socket( + rp->ai_family, + rp->ai_socktype + #ifdef SOCK_CLOEXEC + | SOCK_CLOEXEC + #endif + , rp->ai_protocol); + if (!fd) { + err = strerror(errno); + continue; + } + closeOnExec(fd.get()); + + if (::connect(fd.get(), rp->ai_addr, rp->ai_addrlen) == -1) { + err = strerror(errno); + continue; + } + + conn->fd = std::move(fd); + conn->from.fd = conn->fd.get(); + conn->to.fd = conn->fd.get(); + conn->startTime = std::chrono::steady_clock::now(); + return conn; + } + + throw Error("could not connect to daemon at '%s': %s", host, err); + } +}; + +static RegisterStoreImplementation regTCPStore; + +} From 9973e1284d26b84aeb4e522fd28f5a79b539fc42 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 20 Sep 2021 16:05:10 +0200 Subject: [PATCH 02/10] nix daemon: Add --stdio flag --- src/nix/daemon.cc | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/nix/daemon.cc b/src/nix/daemon.cc index 2cf2a04c9cb..5dca53fbfc1 100644 --- a/src/nix/daemon.cc +++ b/src/nix/daemon.cc @@ -336,6 +336,17 @@ static RegisterLegacyCommand r_nix_daemon("nix-daemon", main_nix_daemon); struct CmdDaemon : StoreCommand { + bool stdio = false; + + CmdDaemon() + { + addFlag({ + .longName = "stdio", + .description = "Handle a single connection on stdin/stdout.", + .handler = {&stdio, true}, + }); + } + std::string description() override { return "daemon to perform store operations on behalf of non-root clients"; @@ -352,7 +363,7 @@ struct CmdDaemon : StoreCommand void run(ref store) override { - runDaemon(false); + runDaemon(stdio); } }; From da9bec691ba03ec1cf77b9eab3c260e5a0490b92 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 20 Sep 2021 16:05:18 +0200 Subject: [PATCH 03/10] nix daemon: Document how to listen on a TCP port --- src/nix/daemon.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/nix/daemon.md b/src/nix/daemon.md index e97016a94f4..29f091c9b06 100644 --- a/src/nix/daemon.md +++ b/src/nix/daemon.md @@ -8,6 +8,19 @@ R""( # nix daemon ``` +* The daemon does not have native support for listening on a TCP + socket, but you can do this using `socat`: + + ```console + # socat TCP-LISTEN:3456,reuseaddr,fork EXEC:'nix daemon --stdio' + ``` + + You can then connect to this daemon using the `tcp` store type: + + ```console + # nix store ping --store tcp://example.org:3456 + ``` + # Description This command runs the Nix daemon, which is a required component in From 72b4ca6b4282333e927fde67bbacc672f6f018aa Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 20 Sep 2021 17:37:05 +0200 Subject: [PATCH 04/10] Clean up PeerInfo handling --- src/nix/daemon.cc | 52 ++++++++++++++++++++++++----------------------- 1 file changed, 27 insertions(+), 25 deletions(-) diff --git a/src/nix/daemon.cc b/src/nix/daemon.cc index 5dca53fbfc1..80569d17faf 100644 --- a/src/nix/daemon.cc +++ b/src/nix/daemon.cc @@ -99,19 +99,16 @@ bool matchUser(const string & user, const string & group, const Strings & users) struct PeerInfo { - bool pidKnown; - pid_t pid; - bool uidKnown; - uid_t uid; - bool gidKnown; - gid_t gid; + std::optional pid; + std::optional uid; + std::optional gid; }; // Get the identity of the caller, if possible. static PeerInfo getPeerInfo(int remote) { - PeerInfo peer = { false, 0, false, 0, false, 0 }; + PeerInfo peer; #if defined(SO_PEERCRED) @@ -119,7 +116,9 @@ static PeerInfo getPeerInfo(int remote) socklen_t credLen = sizeof(cred); if (getsockopt(remote, SOL_SOCKET, SO_PEERCRED, &cred, &credLen) == -1) throw SysError("getting peer credentials"); - peer = { true, cred.pid, true, cred.uid, true, cred.gid }; + peer.pid = cred.pid; + peer.uid = cred.uid; + peer.gid = cred.gid; #elif defined(LOCAL_PEERCRED) @@ -131,7 +130,7 @@ static PeerInfo getPeerInfo(int remote) socklen_t credLen = sizeof(cred); if (getsockopt(remote, SOL_LOCAL, LOCAL_PEERCRED, &cred, &credLen) == -1) throw SysError("getting peer credentials"); - peer = { false, 0, true, cred.cr_uid, false, 0 }; + peer.uid = cred.uid; #endif @@ -197,11 +196,17 @@ static void daemonLoop() TrustedFlag trusted = NotTrusted; PeerInfo peer = getPeerInfo(remote.get()); - struct passwd * pw = peer.uidKnown ? getpwuid(peer.uid) : 0; - string user = pw ? pw->pw_name : std::to_string(peer.uid); + std::string user, group; - struct group * gr = peer.gidKnown ? getgrgid(peer.gid) : 0; - string group = gr ? gr->gr_name : std::to_string(peer.gid); + if (peer.uid) { + auto pw = getpwuid(*peer.uid); + user = pw ? pw->pw_name : std::to_string(*peer.uid); + } + + if (peer.gid) { + auto gr = getgrgid(*peer.gid); + auto group = gr ? gr->gr_name : std::to_string(*peer.gid); + } Strings trustedUsers = settings.trustedUsers; Strings allowedUsers = settings.allowedUsers; @@ -212,9 +217,11 @@ static void daemonLoop() if ((!trusted && !matchUser(user, group, allowedUsers)) || group == settings.buildUsersGroup) throw Error("user '%1%' is not allowed to connect to the Nix daemon", user); - printInfo(format((string) "accepted connection from pid %1%, user %2%" + (trusted ? " (trusted)" : "")) - % (peer.pidKnown ? std::to_string(peer.pid) : "") - % (peer.uidKnown ? user : "")); + printInfo( + "accepted connection from pid %s, user %s%s", + peer.pid ? std::to_string(*peer.pid) : "", + peer.uid ? user : "", + trusted ? " (trusted)" : ""); // Fork a child to handle the connection. ProcessOptions options; @@ -233,8 +240,8 @@ static void daemonLoop() setSigChldAction(false); // For debugging, stuff the pid into argv[1]. - if (peer.pidKnown && savedArgv[1]) { - string processName = std::to_string(peer.pid); + if (peer.pid && savedArgv[1]) { + string processName = std::to_string(*peer.pid); strncpy(savedArgv[1], processName.c_str(), strlen(savedArgv[1])); } @@ -242,13 +249,8 @@ static void daemonLoop() FdSource from(remote.get()); FdSink to(remote.get()); processConnection(openUncachedStore(), from, to, trusted, NotRecursive, [&](Store & store) { -#if 0 - /* Prevent users from doing something very dangerous. */ - if (geteuid() == 0 && - querySetting("build-users-group", "") == "") - throw Error("if you run 'nix-daemon' as root, then you MUST set 'build-users-group'!"); -#endif - store.createUser(user, peer.uid); + if (peer.uid) + store.createUser(user, *peer.uid); }); exit(0); From aaee026858df7fbabe1b922c73fb24cca77a2fe2 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 20 Sep 2021 18:15:49 +0200 Subject: [PATCH 05/10] nix daemon: Support multiple sockets In conjunction with systemd socket activation, this allows the daemon to listen to multiple sockets, e.g. [Socket] ListenStream=/nix/var/nix/daemon-socket/socket ListenStream=1234 --- src/nix/daemon.cc | 185 ++++++++++++++++++++++++++-------------------- 1 file changed, 104 insertions(+), 81 deletions(-) diff --git a/src/nix/daemon.cc b/src/nix/daemon.cc index 80569d17faf..e304dfb5931 100644 --- a/src/nix/daemon.cc +++ b/src/nix/daemon.cc @@ -15,17 +15,18 @@ #include #include -#include +#include +#include +#include +#include +#include #include -#include -#include -#include #include +#include +#include #include -#include -#include -#include -#include +#include +#include #if __APPLE__ || __FreeBSD__ #include @@ -155,106 +156,128 @@ static void daemonLoop() if (chdir("/") == -1) throw SysError("cannot change current directory"); - // Get rid of children automatically; don't let them become zombies. + // Get rid of children automatically; don't let them become zombies. setSigChldAction(true); - AutoCloseFD fdSocket; + std::vector listeningSockets; - // Handle socket-based activation by systemd. + // Handle socket-based activation by systemd. auto listenFds = getEnv("LISTEN_FDS"); if (listenFds) { - if (getEnv("LISTEN_PID") != std::to_string(getpid()) || listenFds != "1") + if (getEnv("LISTEN_PID") != std::to_string(getpid())) throw Error("unexpected systemd environment variables"); - fdSocket = SD_LISTEN_FDS_START; - closeOnExec(fdSocket.get()); + auto count = string2Int(*listenFds); + assert(count); + for (auto i = 0; i < count; ++i) { + AutoCloseFD fdSocket(SD_LISTEN_FDS_START + i); + closeOnExec(fdSocket.get()); + listeningSockets.push_back(std::move(fdSocket)); + } } - // Otherwise, create and bind to a Unix domain socket. + // Otherwise, create and bind to a Unix domain socket. else { createDirs(dirOf(settings.nixDaemonSocketFile)); - fdSocket = createUnixDomainSocket(settings.nixDaemonSocketFile, 0666); + listeningSockets.push_back(createUnixDomainSocket(settings.nixDaemonSocketFile, 0666)); } - // Loop accepting connections. + std::vector fds; + for (auto & i : listeningSockets) + fds.push_back({.fd = i.get(), .events = POLLIN}); + + // Loop accepting connections. while (1) { try { - // Accept a connection. - struct sockaddr_un remoteAddr; - socklen_t remoteAddrLen = sizeof(remoteAddr); - - AutoCloseFD remote = accept(fdSocket.get(), - (struct sockaddr *) &remoteAddr, &remoteAddrLen); checkInterrupt(); - if (!remote) { + + auto count = poll(fds.data(), fds.size(), -1); + if (count == -1) { if (errno == EINTR) continue; - throw SysError("accepting connection"); + throw SysError("poll"); } - closeOnExec(remote.get()); + for (auto & fd : fds) { + if (!fd.revents) continue; - TrustedFlag trusted = NotTrusted; - PeerInfo peer = getPeerInfo(remote.get()); + // Accept a connection. + struct sockaddr_un remoteAddr; + socklen_t remoteAddrLen = sizeof(remoteAddr); - std::string user, group; + AutoCloseFD remote = accept(fd.fd, + (struct sockaddr *) &remoteAddr, &remoteAddrLen); + checkInterrupt(); + if (!remote) { + if (errno == EINTR) continue; + throw SysError("accepting connection"); + } - if (peer.uid) { - auto pw = getpwuid(*peer.uid); - user = pw ? pw->pw_name : std::to_string(*peer.uid); - } + closeOnExec(remote.get()); - if (peer.gid) { - auto gr = getgrgid(*peer.gid); - auto group = gr ? gr->gr_name : std::to_string(*peer.gid); - } + TrustedFlag trusted = NotTrusted; + PeerInfo peer = getPeerInfo(remote.get()); + + std::string user, group; - Strings trustedUsers = settings.trustedUsers; - Strings allowedUsers = settings.allowedUsers; - - if (matchUser(user, group, trustedUsers)) - trusted = Trusted; - - if ((!trusted && !matchUser(user, group, allowedUsers)) || group == settings.buildUsersGroup) - throw Error("user '%1%' is not allowed to connect to the Nix daemon", user); - - printInfo( - "accepted connection from pid %s, user %s%s", - peer.pid ? std::to_string(*peer.pid) : "", - peer.uid ? user : "", - trusted ? " (trusted)" : ""); - - // Fork a child to handle the connection. - ProcessOptions options; - options.errorPrefix = "unexpected Nix daemon error: "; - options.dieWithParent = false; - options.runExitHandlers = true; - options.allowVfork = false; - startProcess([&]() { - fdSocket = -1; - - // Background the daemon. - if (setsid() == -1) - throw SysError("creating a new session"); - - // Restore normal handling of SIGCHLD. - setSigChldAction(false); - - // For debugging, stuff the pid into argv[1]. - if (peer.pid && savedArgv[1]) { - string processName = std::to_string(*peer.pid); - strncpy(savedArgv[1], processName.c_str(), strlen(savedArgv[1])); + if (peer.uid) { + auto pw = getpwuid(*peer.uid); + user = pw ? pw->pw_name : std::to_string(*peer.uid); } - // Handle the connection. - FdSource from(remote.get()); - FdSink to(remote.get()); - processConnection(openUncachedStore(), from, to, trusted, NotRecursive, [&](Store & store) { - if (peer.uid) - store.createUser(user, *peer.uid); - }); + if (peer.gid) { + auto gr = getgrgid(*peer.gid); + auto group = gr ? gr->gr_name : std::to_string(*peer.gid); + } + + Strings trustedUsers = settings.trustedUsers; + Strings allowedUsers = settings.allowedUsers; + + if (matchUser(user, group, trustedUsers)) + trusted = Trusted; + + if ((!trusted && !matchUser(user, group, allowedUsers)) || group == settings.buildUsersGroup) + throw Error("user '%1%' is not allowed to connect to the Nix daemon", user); + + printInfo( + "accepted connection from pid %s, user %s%s", + peer.pid ? std::to_string(*peer.pid) : "", + peer.uid ? user : "", + trusted ? " (trusted)" : ""); + + // Fork a child to handle the connection. + ProcessOptions options; + options.errorPrefix = "unexpected Nix daemon error: "; + options.dieWithParent = false; + options.runExitHandlers = true; + options.allowVfork = false; + startProcess([&]() { + listeningSockets.clear(); + + // Background the daemon. + if (setsid() == -1) + throw SysError("creating a new session"); + + // Restore normal handling of SIGCHLD. + setSigChldAction(false); + + // For debugging, stuff the pid into argv[1]. + if (peer.pid && savedArgv[1]) { + string processName = std::to_string(*peer.pid); + strncpy(savedArgv[1], processName.c_str(), strlen(savedArgv[1])); + } + + // Handle the connection. + FdSource from(remote.get()); + FdSink to(remote.get()); + processConnection(openUncachedStore(), from, to, trusted, NotRecursive, [&](Store & store) { + if (peer.uid) + store.createUser(user, *peer.uid); + }); + + exit(0); + }, options); - exit(0); - }, options); + } } catch (Interrupted & e) { return; From d4d527c95079382e78c88539ebf3f9f91bb4db81 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 20 Sep 2021 17:38:29 +0200 Subject: [PATCH 06/10] nix daemon: Apply auth to --stdio --- src/nix/daemon.cc | 130 ++++++++++++++++++++++++---------------------- src/nix/daemon.md | 2 +- 2 files changed, 70 insertions(+), 62 deletions(-) diff --git a/src/nix/daemon.cc b/src/nix/daemon.cc index e304dfb5931..0589af97ecd 100644 --- a/src/nix/daemon.cc +++ b/src/nix/daemon.cc @@ -115,11 +115,11 @@ static PeerInfo getPeerInfo(int remote) ucred cred; socklen_t credLen = sizeof(cred); - if (getsockopt(remote, SOL_SOCKET, SO_PEERCRED, &cred, &credLen) == -1) - throw SysError("getting peer credentials"); - peer.pid = cred.pid; - peer.uid = cred.uid; - peer.gid = cred.gid; + if (getsockopt(remote, SOL_SOCKET, SO_PEERCRED, &cred, &credLen) == 0) { + peer.pid = cred.pid; + peer.uid = cred.uid; + peer.gid = cred.gid; + } #elif defined(LOCAL_PEERCRED) @@ -129,9 +129,8 @@ static PeerInfo getPeerInfo(int remote) xucred cred; socklen_t credLen = sizeof(cred); - if (getsockopt(remote, SOL_LOCAL, LOCAL_PEERCRED, &cred, &credLen) == -1) - throw SysError("getting peer credentials"); - peer.uid = cred.uid; + if (getsockopt(remote, SOL_LOCAL, LOCAL_PEERCRED, &cred, &credLen) == 0) + peer.uid = cred.uid; #endif @@ -151,6 +150,52 @@ static ref openUncachedStore() } +static void authConnection(FdSource & from, FdSink & to) +{ + TrustedFlag trusted = NotTrusted; + PeerInfo peer = getPeerInfo(from.fd); + + std::string user, group; + + if (peer.uid) { + auto pw = getpwuid(*peer.uid); + user = pw ? pw->pw_name : std::to_string(*peer.uid); + } + + if (peer.gid) { + auto gr = getgrgid(*peer.gid); + group = gr ? gr->gr_name : std::to_string(*peer.gid); + } + + Strings trustedUsers = settings.trustedUsers; + Strings allowedUsers = settings.allowedUsers; + + if (matchUser(user, group, trustedUsers)) + trusted = Trusted; + + if ((!trusted && !matchUser(user, group, allowedUsers)) || group == settings.buildUsersGroup) + throw Error("user '%1%' is not allowed to connect to the Nix daemon", user); + + printInfo( + "accepted connection from pid %s, user %s%s", + peer.pid ? std::to_string(*peer.pid) : "", + peer.uid ? user : "", + trusted ? " (trusted)" : ""); + + // For debugging, stuff the pid into argv[1]. + if (peer.pid && savedArgv[1]) { + string processName = std::to_string(*peer.pid); + strncpy(savedArgv[1], processName.c_str(), strlen(savedArgv[1])); + } + + // Handle the connection. + processConnection(openUncachedStore(), from, to, trusted, NotRecursive, [&](Store & store) { + if (peer.uid) + store.createUser(user, *peer.uid); + }); +} + + static void daemonLoop() { if (chdir("/") == -1) @@ -201,11 +246,7 @@ static void daemonLoop() if (!fd.revents) continue; // Accept a connection. - struct sockaddr_un remoteAddr; - socklen_t remoteAddrLen = sizeof(remoteAddr); - - AutoCloseFD remote = accept(fd.fd, - (struct sockaddr *) &remoteAddr, &remoteAddrLen); + AutoCloseFD remote = accept(fd.fd, nullptr, nullptr); checkInterrupt(); if (!remote) { if (errno == EINTR) continue; @@ -214,36 +255,6 @@ static void daemonLoop() closeOnExec(remote.get()); - TrustedFlag trusted = NotTrusted; - PeerInfo peer = getPeerInfo(remote.get()); - - std::string user, group; - - if (peer.uid) { - auto pw = getpwuid(*peer.uid); - user = pw ? pw->pw_name : std::to_string(*peer.uid); - } - - if (peer.gid) { - auto gr = getgrgid(*peer.gid); - auto group = gr ? gr->gr_name : std::to_string(*peer.gid); - } - - Strings trustedUsers = settings.trustedUsers; - Strings allowedUsers = settings.allowedUsers; - - if (matchUser(user, group, trustedUsers)) - trusted = Trusted; - - if ((!trusted && !matchUser(user, group, allowedUsers)) || group == settings.buildUsersGroup) - throw Error("user '%1%' is not allowed to connect to the Nix daemon", user); - - printInfo( - "accepted connection from pid %s, user %s%s", - peer.pid ? std::to_string(*peer.pid) : "", - peer.uid ? user : "", - trusted ? " (trusted)" : ""); - // Fork a child to handle the connection. ProcessOptions options; options.errorPrefix = "unexpected Nix daemon error: "; @@ -260,19 +271,9 @@ static void daemonLoop() // Restore normal handling of SIGCHLD. setSigChldAction(false); - // For debugging, stuff the pid into argv[1]. - if (peer.pid && savedArgv[1]) { - string processName = std::to_string(*peer.pid); - strncpy(savedArgv[1], processName.c_str(), strlen(savedArgv[1])); - } - - // Handle the connection. FdSource from(remote.get()); FdSink to(remote.get()); - processConnection(openUncachedStore(), from, to, trusted, NotRecursive, [&](Store & store) { - if (peer.uid) - store.createUser(user, *peer.uid); - }); + authConnection(from, to); exit(0); }, options); @@ -290,7 +291,7 @@ static void daemonLoop() } } -static void runDaemon(bool stdio) +static void runDaemon(bool stdio, bool auth) { if (stdio) { if (auto store = openUncachedStore().dynamic_pointer_cast()) { @@ -324,10 +325,10 @@ static void runDaemon(bool stdio) } else { FdSource from(STDIN_FILENO); FdSink to(STDOUT_FILENO); - /* Auth hook is empty because in this mode we blindly trust the - standard streams. Limiting access to those is explicitly - not `nix-daemon`'s responsibility. */ - processConnection(openUncachedStore(), from, to, Trusted, NotRecursive, [&](Store & _){}); + if (auth) + authConnection(from, to); + else + processConnection(openUncachedStore(), from, to, Trusted, NotRecursive, [&](Store & _) {}); } } else daemonLoop(); @@ -351,7 +352,7 @@ static int main_nix_daemon(int argc, char * * argv) return true; }); - runDaemon(stdio); + runDaemon(stdio, false); return 0; } @@ -362,6 +363,7 @@ static RegisterLegacyCommand r_nix_daemon("nix-daemon", main_nix_daemon); struct CmdDaemon : StoreCommand { bool stdio = false; + bool auth = true; CmdDaemon() { @@ -370,6 +372,12 @@ struct CmdDaemon : StoreCommand .description = "Handle a single connection on stdin/stdout.", .handler = {&stdio, true}, }); + + addFlag({ + .longName = "no-auth", + .description = "Do not check whether the client is in `allowed-users`.", + .handler = {&auth, false}, + }); } std::string description() override @@ -388,7 +396,7 @@ struct CmdDaemon : StoreCommand void run(ref store) override { - runDaemon(stdio); + runDaemon(stdio, auth); } }; diff --git a/src/nix/daemon.md b/src/nix/daemon.md index 29f091c9b06..0993135e5ee 100644 --- a/src/nix/daemon.md +++ b/src/nix/daemon.md @@ -12,7 +12,7 @@ R""( socket, but you can do this using `socat`: ```console - # socat TCP-LISTEN:3456,reuseaddr,fork EXEC:'nix daemon --stdio' + # socat TCP-LISTEN:3456,reuseaddr,fork EXEC:'nix daemon --stdio',nofork ``` You can then connect to this daemon using the `tcp` store type: From 1347b33b90fdd41a7c6386bb22651b4fbdf815de Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 20 Sep 2021 19:14:06 +0200 Subject: [PATCH 07/10] Show IP client address --- src/nix/daemon.cc | 31 +++++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/src/nix/daemon.cc b/src/nix/daemon.cc index 0589af97ecd..97332bb5e27 100644 --- a/src/nix/daemon.cc +++ b/src/nix/daemon.cc @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -103,11 +104,13 @@ struct PeerInfo std::optional pid; std::optional uid; std::optional gid; + std::optional ip; + std::optional port; }; // Get the identity of the caller, if possible. -static PeerInfo getPeerInfo(int remote) +static PeerInfo getPeerInfo(int fd) { PeerInfo peer; @@ -115,7 +118,7 @@ static PeerInfo getPeerInfo(int remote) ucred cred; socklen_t credLen = sizeof(cred); - if (getsockopt(remote, SOL_SOCKET, SO_PEERCRED, &cred, &credLen) == 0) { + if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &cred, &credLen) == 0) { peer.pid = cred.pid; peer.uid = cred.uid; peer.gid = cred.gid; @@ -129,11 +132,24 @@ static PeerInfo getPeerInfo(int remote) xucred cred; socklen_t credLen = sizeof(cred); - if (getsockopt(remote, SOL_LOCAL, LOCAL_PEERCRED, &cred, &credLen) == 0) + if (getsockopt(fd, SOL_LOCAL, LOCAL_PEERCRED, &cred, &credLen) == 0) peer.uid = cred.uid; #endif + sockaddr_storage addr; + socklen_t addrlen = sizeof(addr); + if (getpeername(fd, (sockaddr *) &addr, &addrlen) == 0 + && (addr.ss_family == AF_INET || addr.ss_family == AF_INET6)) + { + char host[1024]; + char serv[128]; + if (getnameinfo((sockaddr *) &addr, addrlen, host, sizeof(host), serv, sizeof(serv), NI_NUMERICHOST | NI_NUMERICSERV) == 0) { + peer.ip = std::string(host); + peer.port = std::string(serv); + } + } + return peer; } @@ -177,9 +193,12 @@ static void authConnection(FdSource & from, FdSink & to) throw Error("user '%1%' is not allowed to connect to the Nix daemon", user); printInfo( - "accepted connection from pid %s, user %s%s", - peer.pid ? std::to_string(*peer.pid) : "", - peer.uid ? user : "", + "accepted connection from %s%s", + peer.ip + ? fmt("%s:%s", *peer.ip, *peer.port) + : peer.pid && peer.uid + ? fmt("pid %s, user %s", std::to_string(*peer.pid), user) + : "", trusted ? " (trusted)" : ""); // For debugging, stuff the pid into argv[1]. From 5a96c7acd4e28929a999ddfbef1e59bbc3efd567 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 20 Sep 2021 19:17:37 +0200 Subject: [PATCH 08/10] Document systemd socket activation --- src/nix/daemon.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/nix/daemon.md b/src/nix/daemon.md index 0993135e5ee..d4090471c38 100644 --- a/src/nix/daemon.md +++ b/src/nix/daemon.md @@ -31,4 +31,18 @@ management framework such as `systemd`. Note that this daemon does not fork into the background. +# Systemd socket activation + +`nix daemon` supports systemd socket-based activation using the +`nix-daemon.socket` unit in the Nix distribution. It supports +listening on multiple addresses; for example, the following stanza in +`nix-daemon.socket` makes the daemon listen both on a Unix domain +socket and on port 1234: + +``` +[Socket] +ListenStream=/nix/var/nix/daemon-socket/socket +ListenStream=1234 +``` + )"" From 18e3c8cdf66214c41dd06529ca7fb2614cfb2eb1 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 21 Sep 2021 11:22:37 +0200 Subject: [PATCH 09/10] Tweak src/nix/daemon.md Co-authored-by: John Ericson --- src/nix/daemon.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/nix/daemon.md b/src/nix/daemon.md index d4090471c38..798538d2cb8 100644 --- a/src/nix/daemon.md +++ b/src/nix/daemon.md @@ -8,8 +8,8 @@ R""( # nix daemon ``` -* The daemon does not have native support for listening on a TCP - socket, but you can do this using `socat`: +* The daemon does not have native support for opening a TCP socket to + listen to, but you can do this using `socat`: ```console # socat TCP-LISTEN:3456,reuseaddr,fork EXEC:'nix daemon --stdio',nofork From a9d0ac58a8fe4467301eade44f02280afbe24998 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Thu, 23 Sep 2021 11:23:31 +0200 Subject: [PATCH 10/10] Fix macOS --- src/nix/daemon.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/nix/daemon.cc b/src/nix/daemon.cc index 97332bb5e27..cfa16bbbcd0 100644 --- a/src/nix/daemon.cc +++ b/src/nix/daemon.cc @@ -133,7 +133,7 @@ static PeerInfo getPeerInfo(int fd) xucred cred; socklen_t credLen = sizeof(cred); if (getsockopt(fd, SOL_LOCAL, LOCAL_PEERCRED, &cred, &credLen) == 0) - peer.uid = cred.uid; + peer.uid = cred.cr_uid; #endif