From cc247e9a3d47174639a56f87a0de484d775e2ae1 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Mon, 2 Nov 2026 12:16:47 +0000 Subject: [PATCH 1/2] libnvme/config: don't make nvme-fabrics.conf world-readable The emitter wrote every file with mode 0644. nvme-fabrics.conf can hold KX-HMAC-CHAP secrets and TLS PSKs, so nvme config create and nvme config convert made them readable by every user. As the file is replaced, a mode of 0600 set by the admin was reset to 0644 as well. Create a new file with mode 0600, and keep the mode of an existing one. Signed-off-by: Daniel Wagner --- libnvme/src/nvme/config-emit.c | 10 ++++- libnvme/tests/config-emit.c | 68 ++++++++++++++++++++++++++++++++++ 2 files changed, 77 insertions(+), 1 deletion(-) diff --git a/libnvme/src/nvme/config-emit.c b/libnvme/src/nvme/config-emit.c index 2724419a74..345d3c4d91 100644 --- a/libnvme/src/nvme/config-emit.c +++ b/libnvme/src/nvme/config-emit.c @@ -333,6 +333,8 @@ static int emit_tmpfile(struct libnvme_global_ctx *ctx, const char *final, const struct emit_persona *persona, char **tmp_out) { struct libnvmf_conf_file *raw; + mode_t mode = 0600; + struct stat st; char *tmp; FILE *fp; int fd, ret; @@ -345,7 +347,13 @@ static int emit_tmpfile(struct libnvme_global_ctx *ctx, const char *final, ret = fd; goto err_free; } - if (fchmod(fd, 0644) < 0) { + /* + * The file can hold KX-HMAC-CHAP secrets and TLS PSKs: keep the mode + * the admin gave an existing file, and make a new one root-only. + */ + if (stat(final, &st) == 0) + mode = st.st_mode & 07777; + if (fchmod(fd, mode) < 0) { ret = -errno; close(fd); goto err_unlink; diff --git a/libnvme/tests/config-emit.c b/libnvme/tests/config-emit.c index 9d3b57a10e..a97641b4b6 100644 --- a/libnvme/tests/config-emit.c +++ b/libnvme/tests/config-emit.c @@ -27,6 +27,8 @@ #include static const char *g_hostid = "46ba5037-7ce5-41fa-9452-48477bf00080"; +static const char *g_secret = + "DHHC-1:00:ia6zGodOr4SEG0Zzaw398rpY0wqipUWj4jWjUh4HWUz6aQ2n:"; struct fixture { char dir[32]; @@ -414,6 +416,70 @@ static bool test_force_overwrite(struct libnvme_global_ctx *ctx, return pass; } +static bool install_one(struct libnvme_global_ctx *ctx, const char *path, + bool force) +{ + struct libnvmf_config_emitter *e; + int ret; + + e = libnvmf_config_emit_new(ctx); + shr_assert(e); + shr_assert(add(e, false, "10.0.0.9", + "nqn.2014-08.org.nvmexpress:mode-vol", NULL, NULL, NULL, + "kxchap-secret", g_secret) == 0); + ret = libnvmf_config_emit_install(e, path, force); + libnvmf_config_emit_free(e); + + return ret == 0; +} + +static bool check_mode(const char *path, mode_t want, const char *what) +{ + struct stat st; + + shr_assert(stat(path, &st) == 0); + if ((st.st_mode & 07777) != want) { + printf(" - %s: mode %04o, expected %04o [FAIL]\n", what, + (unsigned int)(st.st_mode & 07777), (unsigned int)want); + return false; + } + printf(" - %s: mode %04o [PASS]\n", what, (unsigned int)want); + + return true; +} + +/* + * The file can hold secrets: a new file is readable by its owner only, an + * existing one keeps its mode. + */ +static bool test_file_mode(struct libnvme_global_ctx *ctx, struct fixture *fx) +{ + bool pass = true; + FILE *f; + + printf("test_file_mode:\n"); + + if (!install_one(ctx, fx->main_path, false)) { + printf(" - install failed [FAIL]\n"); + return false; + } + pass &= check_mode(fx->main_path, 0600, "new file"); + + fixture_wipe(fx); + f = fopen(fx->main_path, "w"); + shr_assert(f); + shr_assert(fclose(f) == 0); + shr_assert(chmod(fx->main_path, 0640) == 0); + + if (!install_one(ctx, fx->main_path, true)) { + printf(" - force install failed [FAIL]\n"); + return false; + } + pass &= check_mode(fx->main_path, 0640, "overwritten file"); + + return pass; +} + static bool test_all_dropins(struct libnvme_global_ctx *ctx, struct fixture *fx) { struct libnvmf_config_emitter *e; @@ -594,6 +660,8 @@ int main(void) fixture_wipe(&fx); pass &= test_force_overwrite(ctx, &fx); fixture_wipe(&fx); + pass &= test_file_mode(ctx, &fx); + fixture_wipe(&fx); pass &= test_all_dropins(ctx, &fx); fixture_wipe(&fx); pass &= test_add_validation(ctx); From 79361ed10a12513b61d4bbad5786b24b103dae24 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Mon, 2 Nov 2026 12:17:55 +0000 Subject: [PATCH 2/2] doc: describe the INI configuration in the fabrics man pages The man pages of discover, connect-all and connect still describe the configuration of nvme-cli 2.x: discovery.conf, and --config naming a JSON file. Since 3.0, they read nvme-fabrics.conf, and nvme connect reads a file only with --config. nvme-config-convert(1) says that a converted file is renamed, but it is kept and a symlink marks it. Describe what the commands do, and the mode of the file nvme config create writes. Signed-off-by: Daniel Wagner --- Documentation/nvme-config-convert.txt | 11 +++---- Documentation/nvme-config-create.txt | 7 +++-- Documentation/nvme-connect-all.txt | 41 ++++++++++++++------------- Documentation/nvme-connect.txt | 6 +++- Documentation/nvme-discover.txt | 39 ++++++++++++------------- 5 files changed, 57 insertions(+), 47 deletions(-) diff --git a/Documentation/nvme-config-convert.txt b/Documentation/nvme-config-convert.txt index 162247786e..c3d007a318 100644 --- a/Documentation/nvme-config-convert.txt +++ b/Documentation/nvme-config-convert.txt @@ -34,10 +34,11 @@ The converted configuration is written to the file specified by --output. If --output is not specified, the default location, @SYSCONFDIR@/nvme/nvme-fabrics.conf, is used. Unless --force is specified, the command refuses to overwrite an existing configuration. On success, -the converted legacy file(s) are renamed to '.converted' to prevent -conversion on subsequent runs. Running the command again is safe: a source -file that is missing only because a prior run already converted and -renamed it is treated as already done, not as an error. +each converted legacy file is kept for a rollback, and a symlink +'.converted' to it marks it as converted. Running the command +again is safe: a legacy file with such a marker is treated as already +done, not as an error. nvme-config-status(1) shows the state of the +legacy files. Malformed entries (for example, invalid addressing or an unreadable port) are skipped with a log message. Conversion continues unless a memory @@ -79,7 +80,7 @@ EXAMPLES + Note: unlike config.json, discovery.conf has no --config-style override — its path is always @SYSCONFDIR@/nvme/discovery.conf. If that file exists on -the system, this command still converts it and renames it to +the system, this command still converts it and marks it with the symlink 'discovery.conf.converted', even though --config points elsewhere. SEE ALSO diff --git a/Documentation/nvme-config-create.txt b/Documentation/nvme-config-create.txt index a6cad071b1..9f44b2f0c1 100644 --- a/Documentation/nvme-config-create.txt +++ b/Documentation/nvme-config-create.txt @@ -18,8 +18,8 @@ SYNOPSIS DESCRIPTION ----------- Build one NVMe-oF connection entry from command-line options and add it to -the INI configuration used by 'nvme connect', 'nvme discover', and -'nvme connect-all'. The format is documented at +the INI configuration used by 'nvme discover' and 'nvme connect-all', +and by 'nvme connect --config'. The format is documented at https://github.com/linux-nvme/nvme-cli/blob/master/libnvme/design/CONFIG.md If the target file exists, its connections are kept and the new entry is @@ -34,6 +34,9 @@ is then optional. A connection with an explicit --hostnqn or --hostid is written to a drop-in under '.d/'. One without either goes into the main file. +The file can hold KX-HMAC-CHAP secrets and TLS PSKs. A new file is +created with mode 0600. An existing file keeps its mode. + OPTIONS ------- --discovery:: diff --git a/Documentation/nvme-connect-all.txt b/Documentation/nvme-connect-all.txt index 8902b9d6d4..81b17d2b99 100644 --- a/Documentation/nvme-connect-all.txt +++ b/Documentation/nvme-connect-all.txt @@ -27,10 +27,12 @@ DESCRIPTION Send one or more Discovery requests to a NVMe over Fabrics Discovery Controller, and create controllers for the returned discovery records. -If no parameters are given, then 'nvme connect-all' will attempt to -find a @SYSCONFDIR@/nvme/discovery.conf file to use to supply a list of -connect-all commands to run. If no @SYSCONFDIR@/nvme/discovery.conf file -exists, the command will quit with an error. +If no Discovery Controller is given, 'nvme connect-all' reads the +NVMe-oF configuration, @SYSCONFDIR@/nvme/nvme-fabrics.conf and its +drop-in files (see nvme-fabrics.conf(5)), and connects to every Discovery +Controller in it. A missing configuration is the same as an empty one. +A legacy @SYSCONFDIR@/nvme/discovery.conf or config.json is converted +first; see nvme-config-convert(1). Otherwise a specific Discovery Controller should be specified using the --transport, --traddr and if necessary the --trsvcid and a Discovery @@ -56,11 +58,11 @@ OPTIONS -J :: --config=:: - Use the specified JSON configuration file instead of the - default @SYSCONFDIR@/nvme/config.json file or 'none' to not read in - an existing configuration file. The JSON configuration file - format is documented in - https://github.com/linux-nvme/nvme-cli/blob/master/libnvme/doc/config-schema.json + Use the specified INI configuration file instead of the default + @SYSCONFDIR@/nvme/nvme-fabrics.conf, or 'none' to not read a + configuration file. The file is read only when no Discovery + Controller is given on the command line. A legacy JSON file is + converted first. The format is described in nvme-fabrics.conf(5). -p:: --persistent[=]:: @@ -137,22 +139,21 @@ the RDMA network. Port 4420 is used by default: * Issue a 'nvme connect-all' command with a user-defined path for the NBFT table: + ----------- -# nvme connet-all --nbft-path=/sys/firmware/acpi/tables/NBFT1 +# nvme connect-all --nbft-path=/sys/firmware/acpi/tables/NBFT1 ------------ + -* Issue a 'nvme connect-all' command using a @SYSCONFDIR@/nvme/discovery.conf file: +* Issue a 'nvme connect-all' command for every Discovery Controller in +@SYSCONFDIR@/nvme/nvme-fabrics.conf. Query the two ports of the +Discovery Controller, because some resources may be accessible on one +port only: + ----------- -# Machine default 'nvme discover' commands. Query the -# Discovery Controller's two ports (some resources may only -# be accessible on a single port). Note an official -# nqn (Host) name defined in the NVMe specification is being used -# in this example. --t rdma -a 192.168.69.33 -s 4420 -q nqn.2014-08.com.example:nvme:nvm-subsystem-sn-d78432 --t rdma -a 192.168.1.4 -s 4420 -q nqn.2014-08.com.example:nvme:nvm-subsystem-sn-d78432 - -At the prompt type "nvme connect-all". +# @SYSCONFDIR@/nvme/nvme-fabrics.conf +[Discovery Controller] +controller = transport=rdma;traddr=192.168.69.33;trsvcid=4420 +controller = transport=rdma;traddr=192.168.1.4;trsvcid=4420 +# nvme connect-all ------------ SEE ALSO diff --git a/Documentation/nvme-connect.txt b/Documentation/nvme-connect.txt index c243ac4082..f03d2d772a 100644 --- a/Documentation/nvme-connect.txt +++ b/Documentation/nvme-connect.txt @@ -24,7 +24,11 @@ specified by the --nqn option. OPTIONS ------- --config=:: - Use the specified JSON configuration file instead of the default. + Connect every connection of the specified INI configuration file + (see nvme-fabrics.conf(5)) directly, without discovery, instead of + the one given on the command line. A legacy JSON file is converted + first. Without this option, 'nvme connect' reads no configuration + file. 'none' is the same as no option. --devid-file=:: Write the kernel-assigned device name (for example, nvme0) to diff --git a/Documentation/nvme-discover.txt b/Documentation/nvme-discover.txt index 06a7d437fd..7130db326e 100644 --- a/Documentation/nvme-discover.txt +++ b/Documentation/nvme-discover.txt @@ -27,10 +27,12 @@ DESCRIPTION Send one or more Get Log Page requests to a NVMe-over-Fabrics Discovery Controller. -If no parameters are given, then 'nvme discover' will attempt to -find a @SYSCONFDIR@/nvme/discovery.conf file to use to supply a list of -Discovery commands to run. If no @SYSCONFDIR@/nvme/discovery.conf file -exists, the command will quit with an error. +If no Discovery Controller is given, 'nvme discover' reads the +NVMe-oF configuration, @SYSCONFDIR@/nvme/nvme-fabrics.conf and its +drop-in files (see nvme-fabrics.conf(5)), and queries every Discovery +Controller in it. A missing configuration is the same as an empty one. +A legacy @SYSCONFDIR@/nvme/discovery.conf or config.json is converted +first; see nvme-config-convert(1). Otherwise, a specific Discovery Controller should be specified using the --transport, --traddr, and if necessary the --trsvcid flags. A Discovery @@ -77,11 +79,11 @@ OPTIONS -J :: --config=:: - Use the specified JSON configuration file instead of the - default @SYSCONFDIR@/nvme/config.json file or 'none' to not read in - an existing configuration file. The JSON configuration file - format is documented in - https://github.com/linux-nvme/nvme-cli/blob/master/libnvme/doc/config-schema.json + Use the specified INI configuration file instead of the default + @SYSCONFDIR@/nvme/nvme-fabrics.conf, or 'none' to not read a + configuration file. The file is read only when no Discovery + Controller is given on the command line. A legacy JSON file is + converted first. The format is described in nvme-fabrics.conf(5). -p:: --persistent[=]:: @@ -160,19 +162,18 @@ Port 4420 is used by default: # nvme discover --nbft-path=/sys/firmware/acpi/tables/NBFT1 ------------ + -* Issue a 'nvme discover' command using a @SYSCONFDIR@/nvme/discovery.conf file: +* Issue a 'nvme discover' command for every Discovery Controller in +@SYSCONFDIR@/nvme/nvme-fabrics.conf. Query the two ports of the +Discovery Controller, because some resources may be accessible on one +port only: + ----------- -# Machine default 'nvme discover' commands. Query the -# Discovery Controller's two ports (some resources may only -# be accessible on a single port). Note an official -# nqn (Host) name defined in the NVMe specification is being used -# in this example. --t rdma -a 192.168.69.33 -s 4420 -q nqn.2014-08.com.example:nvme:nvm-subsystem-sn-d78432 --t rdma -a 192.168.1.4 -s 4420 -q nqn.2014-08.com.example:nvme:nvm-subsystem-sn-d78432 - -At the prompt type "nvme discover". +# @SYSCONFDIR@/nvme/nvme-fabrics.conf +[Discovery Controller] +controller = transport=rdma;traddr=192.168.69.33;trsvcid=4420 +controller = transport=rdma;traddr=192.168.1.4;trsvcid=4420 +# nvme discover ------------ SEE ALSO