diff --git a/doc/lightningd-config.5 b/doc/lightningd-config.5 index 9d63265e7..8132dcefa 100644 --- a/doc/lightningd-config.5 +++ b/doc/lightningd-config.5 @@ -188,6 +188,14 @@ cause it to reopen this file (useful for log rotation)\. Set JSON-RPC socket (or /dev/tty), such as for \fBlightning-cli\fR(1)\. + \fBrpc-file-mode\fR=\fIMODE\fR +Set JSON-RPC socket file mode, as a 4-digit octal number\. +Default is 0600, meaning only the user that launched lightningd +can command it\. +Set to 0660 to allow users with the same group to access the RPC +as well\. + + \fBdaemon\fR Run in the background, suppress stdout and stderr\. diff --git a/doc/lightningd-config.5.md b/doc/lightningd-config.5.md index 739c1b74c..8bc5677e9 100644 --- a/doc/lightningd-config.5.md +++ b/doc/lightningd-config.5.md @@ -149,6 +149,13 @@ cause it to reopen this file (useful for log rotation). **rpc-file**=*PATH* Set JSON-RPC socket (or /dev/tty), such as for lightning-cli(1). + **rpc-file-mode**=*MODE* +Set JSON-RPC socket file mode, as a 4-digit octal number. +Default is 0600, meaning only the user that launched lightningd +can command it. +Set to 0660 to allow users with the same group to access the RPC +as well. + **daemon** Run in the background, suppress stdout and stderr. diff --git a/lightningd/jsonrpc.c b/lightningd/jsonrpc.c index e115dc77e..938452121 100644 --- a/lightningd/jsonrpc.c +++ b/lightningd/jsonrpc.c @@ -1074,7 +1074,7 @@ bool command_check_only(const struct command *cmd) void jsonrpc_listen(struct jsonrpc *jsonrpc, struct lightningd *ld) { struct sockaddr_un addr; - int fd, old_umask; + int fd, old_umask, new_umask; const char *rpc_filename = ld->rpc_filename; /* Should not initialize it twice. */ @@ -1103,8 +1103,9 @@ void jsonrpc_listen(struct jsonrpc *jsonrpc, struct lightningd *ld) errx(1, "rpc filename '%s' in use", rpc_filename); unlink(rpc_filename); - /* This file is only rw by us! */ - old_umask = umask(0177); + /* Set the umask according to the desired file mode. */ + new_umask = ld->rpc_filemode ^ 0777; + old_umask = umask(new_umask); if (bind(fd, (struct sockaddr *)&addr, sizeof(addr))) err(1, "Binding rpc socket to '%s'", rpc_filename); umask(old_umask); diff --git a/lightningd/lightningd.c b/lightningd/lightningd.c index f8247afc6..5b2aa4b9e 100644 --- a/lightningd/lightningd.c +++ b/lightningd/lightningd.c @@ -78,7 +78,6 @@ #include #include #include -#include #include #include @@ -254,6 +253,16 @@ static struct lightningd *new_lightningd(const tal_t *ctx) ld->initial_umask = umask(0); umask(ld->initial_umask); + /*~ This is the mode of the created JSON-RPC socket file, in + * traditional Unix octal. 0600 means only the user that ran + * lightningd can invoke RPC on it. Changing it to 0660 may + * be sensible if you run lightningd in its own system user, + * and just let specific users (add the group of the + * lightningd runner as an ancillary group) access its + * RPC. Can be overridden with `--rpc-file-mode`. + */ + ld->rpc_filemode = 0600; + return ld; } diff --git a/lightningd/lightningd.h b/lightningd/lightningd.h index 240fa2e37..641b74aaa 100644 --- a/lightningd/lightningd.h +++ b/lightningd/lightningd.h @@ -10,6 +10,7 @@ #include #include #include +#include #include #include @@ -91,6 +92,8 @@ struct lightningd { /* Location of the RPC socket. */ char *rpc_filename; + /* Mode of the RPC filename. */ + mode_t rpc_filemode; /* The root of the jsonrpc interface. Can be shut down * separately from the rest of the daemon to allow a clean diff --git a/lightningd/options.c b/lightningd/options.c index 63bf937ee..7117b1b10 100644 --- a/lightningd/options.c +++ b/lightningd/options.c @@ -105,6 +105,30 @@ static char *opt_set_s32(const char *arg, s32 *u) return NULL; } +static char *opt_set_mode(const char *arg, mode_t *m) +{ + char *endp; + long l; + + assert(arg != NULL); + + /* Ensure length, and starts with 0. */ + if (strlen(arg) != 4 || arg[0] != '0') + return tal_fmt(NULL, "'%s' is not a file mode", arg); + + /* strtol, manpage, yech. */ + errno = 0; + l = strtol(arg, &endp, 8); /* Octal. */ + if (errno || *endp) + return tal_fmt(NULL, "'%s' is not a file mode", arg); + *m = l; + /* Range check not needed, previous strlen checks ensures only + * 9-bit, which fits mode_t (unless your Unix is seriously borked). + */ + + return NULL; +} + static char *opt_add_addr_withtype(const char *arg, struct lightningd *ld, enum addr_listen_announce ala, @@ -225,6 +249,11 @@ static void opt_show_s32(char buf[OPT_SHOW_LEN], const s32 *u) snprintf(buf, OPT_SHOW_LEN, "%"PRIi32, *u); } +static void opt_show_mode(char buf[OPT_SHOW_LEN], const mode_t *m) +{ + snprintf(buf, OPT_SHOW_LEN, "\"%04o\"", (int) *m); +} + static char *opt_set_rgb(const char *arg, struct lightningd *ld) { assert(arg != NULL); @@ -842,6 +871,11 @@ static void register_opts(struct lightningd *ld) "Set the password to encrypt hsm_secret with. If no password is passed through command line, " "you will be prompted to enter it."); + opt_register_arg("--rpc-file-mode", &opt_set_mode, &opt_show_mode, + &ld->rpc_filemode, + "Set the file mode (permissions) for the " + "JSON-RPC socket"); + opt_register_logging(ld); opt_register_version();