From: Erwan Le Goas Date: Wed, 14 Sep 2022 15:51:55 +0000 (+0200) Subject: MINOR: config: add command-line -dC to dump the configuration file X-Git-Tag: v2.7-dev6~2 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=b0c05015168f482eb84b1db78fdd6f158e5383a2;p=thirdparty%2Fhaproxy.git MINOR: config: add command-line -dC to dump the configuration file This commit adds a new command line option -dC to dump the configuration file. An optional key may be appended to -dC in order to produce an anonymized dump using this key. The anonymizing process uses the same algorithm as the CLI so that the same key will produce the same hashes for the same identifiers. This way an admin may share an anonymized extract of a configuration to match against live dumps. Note that key 0 will not anonymize the output. However, in any case, the configuration is dumped after tokenizing, thus comments are lost. --- diff --git a/doc/configuration.txt b/doc/configuration.txt index bc4c622da4..6af5e4e97c 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -3226,7 +3226,8 @@ anonkey This sets the global anonymizing key to , which must be a 32-bit number between 0 and 4294967295. This is the key that will be used by default by CLI commands when anonymized mode is enabled. This key may also be set at runtime - from the CLI command "set global-key". + from the CLI command "set global-key". See also command line argument "-dC" + in the management manual. quiet Do not display any message during startup. It is equivalent to the command- diff --git a/doc/management.txt b/doc/management.txt index 9e908a6168..30209df906 100644 --- a/doc/management.txt +++ b/doc/management.txt @@ -204,6 +204,15 @@ list of options is : in foreground and to show incoming and outgoing events. It must never be used in an init script. + -dC[key] : dump the configuration file. It is performed after the lines are + tokenized, so comments are stripped and indenting is forced. If a non-zero + key is specified, lines are truncated before sensitive/confidential fields, + and identifiers and addresses are emitted hashed with this key using the + same algorithmm as the one used by the anonymized mode on the CLI. This + means that the output may safely be shared with a developer who needs it + to figure what's happening in a dump that was anonymized using the same + key. Please also see the CLI's "set anon" command. + -dD : enable diagnostic mode. This mode will output extra warnings about suspicious configuration statements. This will never prevent startup even in "zero-warning" mode nor change the exit status code. diff --git a/include/haproxy/global-t.h b/include/haproxy/global-t.h index 99b6acc749..f10146e490 100644 --- a/include/haproxy/global-t.h +++ b/include/haproxy/global-t.h @@ -42,6 +42,7 @@ #define MODE_STOPPING 0x1000 /* the process is in the deinit phase, the event loop is not running anymore. */ #define MODE_DUMP_LIBS 0x2000 /* dump loaded libraries at the end of init phase */ #define MODE_DUMP_KWD 0x4000 /* dump registered keywords (see kwd_dump for the list) */ +#define MODE_DUMP_CFG 0x8000 /* dump the configure file */ /* list of last checks to perform, depending on config options */ #define LSTCHK_CAP_BIND 0x00000001 /* check that we can bind to any port */ diff --git a/src/cfgparse.c b/src/cfgparse.c index 171982c0bb..352953fb76 100644 --- a/src/cfgparse.c +++ b/src/cfgparse.c @@ -1893,6 +1893,219 @@ next_line: break; } + /* dump cfg */ + if (global.mode & MODE_DUMP_CFG) { + if (args[0] != NULL) { + struct cfg_section *sect; + int is_sect = 0; + int i = 0; + uint32_t g_key = HA_ATOMIC_LOAD(&global.anon_key); + + qfprintf(stdout, "%d\t", linenum); + + /* if a word is in sections list, is_sect = 1 */ + list_for_each_entry(sect, §ions, list) { + if (strcmp(args[0], sect->section_name) == 0) { + is_sect = 1; + break; + } + } + + if (g_key == 0) { + /* no anonymizing needed, dump the config as-is (but without comments). + * Note: tabs were lost during tokenizing, so we reinsert for non-section + * keywords. + */ + if (!is_sect) + qfprintf(stdout, "\t"); + + for (i = 0; i < arg; i++) { + qfprintf(stdout, "%s ", args[i]); + } + qfprintf(stdout, "\n"); + continue; + } + + /* We're anonymizing */ + + if (is_sect) { + /* new sections are optionally followed by an identifier */ + if (arg >= 2) { + qfprintf(stdout, "%s %s\n", args[0], HA_ANON_ID(g_key, args[1])); + } + else { + qfprintf(stdout, "%s\n", args[0]); + } + continue; + } + + /* non-section keywords start indented */ + qfprintf(stdout, "\t"); + + /* some keywords deserve special treatment */ + if (!*args[0]) { + qfprintf(stdout, "\n"); + } + + else if (strcmp(args[0], "anonkey") == 0) { + qfprintf(stdout, "%s [...]\n", args[0]); + } + + else if (strcmp(args[0], "maxconn") == 0) { + qfprintf(stdout, "%s %s\n", args[0], args[1]); + } + + else if (strcmp(args[0], "stats") == 0 && + (strcmp(args[1], "timeout") == 0 || strcmp(args[1], "maxconn") == 0)) { + qfprintf(stdout, "%s %s %s\n", args[0], args[1], args[2]); + } + + else if (strcmp(args[0], "stats") == 0 && strcmp(args[1], "socket") == 0) { + qfprintf(stdout, "%s %s ", args[0], args[1]); + + if (arg > 1) { + qfprintf(stdout, "%s ", args[2]); + + if (arg > 2) { + qfprintf(stdout, "[...]\n"); + } + else { + qfprintf(stdout, "\n"); + } + } + else { + qfprintf(stdout, "\n"); + } + } + + else if (strcmp(args[0], "timeout") == 0) { + qfprintf(stdout, "%s %s %s\n", args[0], args[1], args[2]); + } + + else if (strcmp(args[0], "mode") == 0) { + qfprintf(stdout, "%s %s\n", args[0], args[1]); + } + + /* It concerns user in global secion and in userlist */ + else if (strcmp(args[0], "user") == 0) { + qfprintf(stdout, "%s %s ", args[0], HA_ANON_ID(g_key, args[1])); + + if (arg > 2) { + qfprintf(stdout, "[...]\n"); + } + else { + qfprintf(stdout, "\n"); + } + } + + else if (strcmp(args[0], "bind") == 0) { + qfprintf(stdout, "%s ", args[0]); + qfprintf(stdout, "%s ", hash_ipanon(g_key, args[1])); + if (arg > 2) { + qfprintf(stdout, "[...]\n"); + } + else { + qfprintf(stdout, "\n"); + } + } + + else if (strcmp(args[0], "server") == 0) { + qfprintf(stdout, "%s ", args[0]); + + if (strcmp(args[1], "localhost") == 0) { + qfprintf(stdout, "%s ", args[1]); + } + else { + qfprintf(stdout, "%s ", HA_ANON_ID(g_key, args[1])); + } + if (arg > 2) { + qfprintf(stdout, "%s ", hash_ipanon(g_key, args[2])); + } + if (arg > 3) { + qfprintf(stdout, "[...]\n"); + } + else { + qfprintf(stdout, "\n"); + } + } + + else if (strcmp(args[0], "redirect") == 0) { + qfprintf(stdout, "%s %s ", args[0], args[1]); + + if (strcmp(args[1], "prefix") == 0 || strcmp(args[1], "location") == 0) { + qfprintf(stdout, "%s ", HA_ANON_PATH(g_key, args[2])); + } + else { + qfprintf(stdout, "%s ", args[2]); + } + if (arg > 3) { + qfprintf(stdout, "[...]"); + } + qfprintf(stdout, "\n"); + } + + else if (strcmp(args[0], "acl") == 0) { + qfprintf(stdout, "%s %s %s ", args[0], HA_ANON_ID(g_key, args[1]), args[2]); + + if (arg > 3) { + qfprintf(stdout, "[...]"); + } + qfprintf(stdout, "\n"); + } + + else if (strcmp(args[0], "log") == 0) { + qfprintf(stdout, "log "); + + if (strcmp(args[1], "global") == 0) { + qfprintf(stdout, "%s ", args[1]); + } + else { + qfprintf(stdout, "%s ", hash_ipanon(g_key, args[1])); + } + if (arg > 2) { + qfprintf(stdout, "[...]"); + } + qfprintf(stdout, "\n"); + } + + else if (strcmp(args[0], "peer") == 0) { + qfprintf(stdout, "%s %s ", args[0], HA_ANON_ID(g_key, args[1])); + qfprintf(stdout, "%s ", hash_ipanon(g_key, args[2])); + + if (arg > 3) { + qfprintf(stdout, "[...]"); + } + qfprintf(stdout, "\n"); + } + + else if (strcmp(args[0], "use_backend") == 0) { + qfprintf(stdout, "%s %s ", args[0], HA_ANON_ID(g_key, args[1])); + + if (arg > 2) { + qfprintf(stdout, "[...]"); + } + qfprintf(stdout, "\n"); + } + + else if (strcmp(args[0], "default_backend") == 0) { + qfprintf(stdout, "%s %s\n", args[0], HA_ANON_ID(g_key, args[1])); + } + + else { + /* display up to 3 words and mask the rest which might be confidential */ + for (i = 0; i < MIN(arg, 3); i++) { + qfprintf(stdout, "%s ", args[i]); + } + if (arg > 3) { + qfprintf(stdout, "[...]"); + } + qfprintf(stdout, "\n"); + } + } + continue; + } + /* end of config dump */ + /* empty line */ if (!**args) continue; diff --git a/src/haproxy.c b/src/haproxy.c index 8d6f587665..ae2f5ebf30 100644 --- a/src/haproxy.c +++ b/src/haproxy.c @@ -590,6 +590,7 @@ static void usage(char *name) " -N sets the default, per-proxy maximum # of connections (%d)\n" " -L set local peer name (default to hostname)\n" " -p writes pids of all children to this file\n" + " -dC[key] display the configure file, if there is a key, the file will be anonymise\n" #if defined(USE_EPOLL) " -de disables epoll() usage even when available\n" #endif @@ -1633,6 +1634,10 @@ static void init_args(int argc, char **argv) global.ssl_server_verify = SSL_SERVER_VERIFY_NONE; else if (*flag == 'V') arg_mode |= MODE_VERBOSE; + else if (*flag == 'd' && flag[1] == 'C') { + arg_mode |= MODE_DUMP_CFG; + HA_ATOMIC_STORE(&global.anon_key, atoll(flag + 2)); + } else if (*flag == 'd' && flag[1] == 'b') arg_mode |= MODE_FOREGROUND; else if (*flag == 'd' && flag[1] == 'D') @@ -1904,7 +1909,7 @@ static void init(int argc, char **argv) global.mode |= (arg_mode & (MODE_DAEMON | MODE_MWORKER | MODE_FOREGROUND | MODE_VERBOSE | MODE_QUIET | MODE_CHECK | MODE_DEBUG | MODE_ZERO_WARNING - | MODE_DIAG | MODE_CHECK_CONDITION | MODE_DUMP_LIBS | MODE_DUMP_KWD)); + | MODE_DIAG | MODE_CHECK_CONDITION | MODE_DUMP_LIBS | MODE_DUMP_KWD | MODE_DUMP_CFG)); if (getenv("HAPROXY_MWORKER_WAIT_ONLY")) { unsetenv("HAPROXY_MWORKER_WAIT_ONLY"); @@ -2226,6 +2231,9 @@ static void init(int argc, char **argv) exit(2); } + if (global.mode & MODE_DUMP_CFG) + deinit_and_exit(0); + if (global.mode & MODE_DIAG) { cfg_run_diagnostics(); }