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.
This commit is contained in:
Erwan Le Goas 2022-09-14 17:51:55 +02:00 committed by Willy Tarreau
parent acfdf7600b
commit b0c0501516
5 changed files with 234 additions and 2 deletions

View File

@ -3226,7 +3226,8 @@ anonkey <key>
This sets the global anonymizing key to <key>, 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-

View File

@ -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.

View File

@ -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 */

View File

@ -1893,6 +1893,219 @@ int readcfgfile(const char *file)
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, &sections, 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;

View File

@ -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();
}