diff --git a/doc/configuration.txt b/doc/configuration.txt index bc4c622da..6af5e4e97 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 9e908a616..30209df90 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 99b6acc74..f10146e49 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 171982c0b..352953fb7 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 8d6f58766..ae2f5ebf3 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(); }