commit 2110963f2d8bb953903c0d66972e203b006a4387
parent e6f102bf5b8767b9f99921dd92be928d7023b762
Author: Christian Grothoff <christian@grothoff.org>
Date: Sun, 11 Oct 2026 13:54:42 +0200
make auditor-httpd ACCESS_TOKEN use strict, not a mere option
Diffstat:
15 files changed, 184 insertions(+), 28 deletions(-)
diff --git a/debian/etc-taler-auditor/taler-auditor/conf.d/auditor-system.conf b/debian/etc-taler-auditor/taler-auditor/conf.d/auditor-system.conf
@@ -11,3 +11,9 @@ DATABASE = postgres
SERVE = unix
UNIXPATH_MODE = 666
+
+# Access token for the /monitoring/ API, readable only by the
+# taler-auditor-httpd user. Inlined after the options above, so
+# that they are set before other users see the section marked
+# as inaccessible.
+@inline-secret@ auditor ../secrets/auditor-access-token.secret.conf
diff --git a/debian/etc-taler-auditor/taler-auditor/secrets/auditor-access-token.secret.conf b/debian/etc-taler-auditor/taler-auditor/secrets/auditor-access-token.secret.conf
@@ -0,0 +1,13 @@
+# Access token for the /monitoring/ API of taler-auditor-httpd.
+#
+# Clients (such as the auditor SPA) must send it as
+# "Authorization: Bearer secret-token:...". It may be stored
+# with or without the "secret-token:" prefix.
+#
+# The package generates a random token here on installation if
+# no token is configured yet. To rotate it, replace the value
+# and restart taler-auditor-httpd.
+
+[auditor]
+
+# ACCESS_TOKEN = CHANGE-ME
diff --git a/debian/taler-auditor.postinst b/debian/taler-auditor.postinst
@@ -2,6 +2,8 @@
set -e
+TOKEN_CONF="/etc/taler-auditor/secrets/auditor-access-token.secret.conf"
+
case "${1}" in
configure)
if ! getent group taler-auditor-httpd >/dev/null;
@@ -34,6 +36,29 @@ configure)
640 \
/etc/taler-auditor/secrets/exchange-accountcredentials-1.secret.conf
fi
+ if ! dpkg-statoverride --list ${TOKEN_CONF} >/dev/null 2>&1;
+ then
+ dpkg-statoverride --add \
+ --update \
+ taler-auditor-httpd taler-auditor-httpd \
+ 640 \
+ ${TOKEN_CONF}
+ fi
+
+ # taler-auditor-httpd refuses to start without an access token, so
+ # generate one on installation. Only done while the shipped
+ # placeholder is still in place and no token is configured anywhere
+ # else, so an existing token is never overwritten. The file is
+ # already 0640 (above) when the token is written.
+ if [ -f ${TOKEN_CONF} ] \
+ && grep -q '^# ACCESS_TOKEN = CHANGE-ME$' ${TOKEN_CONF} \
+ && [ -z "$(taler-auditor-config -c /etc/taler-auditor/taler-auditor.conf -s auditor -o ACCESS_TOKEN 2>/dev/null || true)" ];
+ then
+ TOKEN="secret-token:$(head -c 32 /dev/urandom | od -An -tx1 | tr -d ' \n')"
+ sed -i "s|^# ACCESS_TOKEN = CHANGE-ME$|ACCESS_TOKEN = ${TOKEN}|" ${TOKEN_CONF}
+ TOKEN=""
+ echo "taler-auditor: generated a random ACCESS_TOKEN in ${TOKEN_CONF}." >&2
+ fi
;;
diff --git a/debian/taler-auditor.postrm b/debian/taler-auditor.postrm
@@ -8,6 +8,8 @@ purge)
/etc/taler-auditor/secrets/auditor-db.secret.conf || true
dpkg-statoverride --remove \
/etc/taler-auditor/secrets/exchange-accountcredentials-1.secret.conf || true
+ dpkg-statoverride --remove \
+ /etc/taler-auditor/secrets/auditor-access-token.secret.conf || true
deluser --system \
--quiet \
taler-auditor-httpd || true
diff --git a/src/auditor/auditor.conf b/src/auditor/auditor.conf
@@ -19,6 +19,16 @@ AUDITOR_PRIV_FILE = ${TALER_DATA_HOME}offline-keys/auditor.priv
# a misbehaving exchange)?
BASE_URL = http://localhost:8083/
+# Access token that clients (such as the auditor SPA) must send as
+# "Authorization: Bearer secret-token:TOKEN" to use the /monitoring/
+# API. It may be given with or without the RFC 8959 "secret-token:"
+# prefix. The TALER_AUDITOR_ACCESS_TOKEN environment variable, if set,
+# overrides this option. taler-auditor-httpd refuses to start without
+# a token unless it is given -n (--no-authentication). Keep the token
+# out of world-readable files, for example by putting it into a file
+# loaded with @inline-secret@.
+# ACCESS_TOKEN = secret-token:CHANGE-ME
+
# Network configuration for the normal API/service HTTP server
# serve via tcp socket (on PORT)
diff --git a/src/auditor/batch.sh b/src/auditor/batch.sh
@@ -144,7 +144,7 @@ taler-exchange-secmod-cs -c $CONF 2> taler-exchange-secmod-cs.log &
taler-exchange-httpd -c $CONF 2> taler-exchange-httpd.log &
taler-merchant-httpd -c $CONF -L INFO 2> taler-merchant-httpd.log &
taler-exchange-wirewatch -c $CONF 2> taler-exchange-wirewatch.log &
-taler-auditor-httpd -L INFO -c $CONF 2> taler-auditor-httpd.log &
+taler-auditor-httpd -n -L INFO -c $CONF 2> taler-auditor-httpd.log &
# Wait for all bank to be available (usually the slowest)
for n in `seq 1 50`
diff --git a/src/auditor/taler-auditor-httpd.c b/src/auditor/taler-auditor-httpd.c
@@ -1411,6 +1411,68 @@ auditor_serve_process_config (void)
/**
+ * Load the access token clients must present to use the endpoints that
+ * require authentication, and derive #TAH_auth from it. The
+ * TALER_AUDITOR_ACCESS_TOKEN environment variable takes precedence over
+ * the ACCESS_TOKEN option in the [auditor] section. Clients send the
+ * token with the RFC 8959 "secret-token:" prefix; it MUST be stored with
+ * it.
+ *
+ * @return #GNUNET_OK on success, #GNUNET_SYSERR if no usable token is
+ * configured
+ */
+static enum GNUNET_GenericReturnValue
+load_access_token (void)
+{
+ const char *tok;
+ char *cfg_tok = NULL;
+
+ tok = getenv ("TALER_AUDITOR_ACCESS_TOKEN");
+ if ( (NULL == tok) &&
+ (GNUNET_OK ==
+ GNUNET_CONFIGURATION_get_value_string (cfg,
+ "auditor",
+ "ACCESS_TOKEN",
+ &cfg_tok)) )
+ tok = cfg_tok;
+ if (NULL == tok)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "No access token configured: set ACCESS_TOKEN in section [auditor] (or the TALER_AUDITOR_ACCESS_TOKEN environment variable), or pass -n to run without authentication\n");
+ return GNUNET_SYSERR;
+ }
+ if (0 != strncasecmp (tok,
+ RFC_8959_PREFIX,
+ strlen (RFC_8959_PREFIX)))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "The configured access token lacks the `%s' prefix\n",
+ RFC_8959_PREFIX);
+ GNUNET_free (cfg_tok);
+ return GNUNET_SYSERR;
+ }
+ tok += strlen (RFC_8959_PREFIX);
+ if ('\0' == *tok)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "The configured access token is empty\n");
+ GNUNET_free (cfg_tok);
+ return GNUNET_SYSERR;
+ }
+ GNUNET_assert (GNUNET_YES ==
+ GNUNET_CRYPTO_hkdf_gnunet (
+ &TAH_auth,
+ sizeof (TAH_auth),
+ KDF_SALT,
+ strlen (KDF_SALT),
+ tok,
+ strlen (tok)));
+ GNUNET_free (cfg_tok);
+ return GNUNET_OK;
+}
+
+
+/**
* Function run on shutdown.
*
* @param cls NULL
@@ -1503,35 +1565,22 @@ run (void *cls,
(void) cls;
(void) args;
(void) cfgfile;
- if (0 == disable_auth)
- {
- const char *tok;
-
- tok = getenv ("TALER_AUDITOR_ACCESS_TOKEN");
- if (NULL == tok)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "TALER_AUDITOR_ACCESS_TOKEN environment variable not set. Disabling authentication\n");
- disable_auth = 1;
- }
- else
- {
- GNUNET_assert (GNUNET_YES ==
- GNUNET_CRYPTO_hkdf_gnunet (
- &TAH_auth,
- sizeof (TAH_auth),
- KDF_SALT,
- strlen (KDF_SALT),
- tok,
- strlen (tok)));
- }
- }
-
go = TALER_MHD_GO_NONE;
if (auditor_connection_close)
go |= TALER_MHD_GO_FORCE_CONNECTION_CLOSE;
TALER_MHD_setup (go);
cfg = config;
+ if ( (0 == disable_auth) &&
+ (GNUNET_OK != load_access_token ()) )
+ {
+ /* Failing open here would publish the entire audit findings
+ database, including the DELETE and PATCH handlers, to anyone
+ able to reach the port. -n disables authentication on
+ purpose. */
+ global_ret = EXIT_NOTCONFIGURED;
+ GNUNET_SCHEDULER_shutdown ();
+ return;
+ }
GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
NULL);
diff --git a/src/auditor/test-auditor.sh b/src/auditor/test-auditor.sh
@@ -45,6 +45,8 @@ ALL_TESTS=$(seq 0 31)
TESTS=${1:-$ALL_TESTS}
export TALER_AUDITOR_TOKEN="secret-token:D4CST1Z6AHN3RT03M0T9NSTF2QGHTB5ZD2D3RYZB4HAWG8SX0JEFWBXCKXZHMB7Y3Z7KVFW0B3XPXD5BHCFP8EB0R6CNH2KAWDWVET0"
+# Make taler-auditor-httpd require the token the requests below send.
+export TALER_AUDITOR_ACCESS_TOKEN="${TALER_AUDITOR_TOKEN}"
export TALER_AUDITOR_SALT="64S36D1N6RVKGC9J6CT3ADHQ70RK4CSM6MV3EE1H68SK8D9P6WW32CHK6GTKCDSR64S36D1N6RVKGC9J6CT3ADHQ70RK4CSM6MV3EE0"
# Global variable to run the auditor processes under valgrind
diff --git a/src/auditor/test-kyc.sh b/src/auditor/test-kyc.sh
@@ -64,6 +64,8 @@ ALL_TESTS=$(seq 0 16)
TESTS=${1:-$ALL_TESTS}
export TALER_AUDITOR_TOKEN="secret-token:D4CST1Z6AHN3RT03M0T9NSTF2QGHTB5ZD2D3RYZB4HAWG8SX0JEFWBXCKXZHMB7Y3Z7KVFW0B3XPXD5BHCFP8EB0R6CNH2KAWDWVET0"
+# Make taler-auditor-httpd require the token the requests below send.
+export TALER_AUDITOR_ACCESS_TOKEN="${TALER_AUDITOR_TOKEN}"
export TALER_AUDITOR_SALT="64S36D1N6RVKGC9J6CT3ADHQ70RK4CSM6MV3EE1H68SK8D9P6WW32CHK6GTKCDSR64S36D1N6RVKGC9J6CT3ADHQ70RK4CSM6MV3EE0"
# Global variable to run the auditor processes under valgrind
diff --git a/src/auditor/test-revocation.sh b/src/auditor/test-revocation.sh
@@ -42,6 +42,8 @@ ALL_TESTS=$(seq 0 4)
TESTS=${1:-$ALL_TESTS}
export TALER_AUDITOR_TOKEN="secret-token:D4CST1Z6AHN3RT03M0T9NSTF2QGHTB5ZD2D3RYZB4HAWG8SX0JEFWBXCKXZHMB7Y3Z7KVFW0B3XPXD5BHCFP8EB0R6CNH2KAWDWVET0"
+# Make taler-auditor-httpd require the token the requests below send.
+export TALER_AUDITOR_ACCESS_TOKEN="${TALER_AUDITOR_TOKEN}"
export TALER_AUDITOR_SALT="64S36D1N6RVKGC9J6CT3ADHQ70RK4CSM6MV3EE1H68SK8D9P6WW32CHK6GTKCDSR64S36D1N6RVKGC9J6CT3ADHQ70RK4CSM6MV3EE0"
# Global variable to run the auditor processes under valgrind
diff --git a/src/testing/taler-unified-setup.sh b/src/testing/taler-unified-setup.sh
@@ -813,7 +813,16 @@ then
echo "Launching auditor using $CONF" > taler-auditor-httpd.log >&2
echo "Launching auditor using $AUDITOR_PUB from $AUDITOR_PRIV_FILE" \
>> taler-auditor-httpd.log
+ # taler-auditor-httpd refuses to start without an access token
+ # unless given -n: require authentication iff a token is configured.
+ AUDITOR_AUTH="-n"
+ if [ -n "${TALER_AUDITOR_ACCESS_TOKEN:-}" ] \
+ || [ -n "$(taler-auditor-config -c "$CONF" -s AUDITOR -o ACCESS_TOKEN 2> /dev/null || true)" ]
+ then
+ AUDITOR_AUTH=""
+ fi
$USE_VALGRIND taler-auditor-httpd \
+ $AUDITOR_AUTH \
-L "$LOGLEVEL" \
-c "$CONF" 2>> taler-auditor-httpd.log &
echo " DONE" >&2
diff --git a/src/testing/test_auditor_api-cs.conf b/src/testing/test_auditor_api-cs.conf
@@ -16,3 +16,8 @@ ENABLE_DEBIT = NO
[exchange-account-3]
ENABLE_CREDIT = NO
ENABLE_DEBIT = NO
+
+# Make taler-auditor-httpd require authentication, so that the
+# /monitoring/ requests of the auditor commands exercise it.
+[auditor]
+ACCESS_TOKEN = secret-token:test-auditor-api
diff --git a/src/testing/test_auditor_api-rsa.conf b/src/testing/test_auditor_api-rsa.conf
@@ -16,3 +16,8 @@ ENABLE_DEBIT = NO
[exchange-account-3]
ENABLE_CREDIT = NO
ENABLE_DEBIT = NO
+
+# Make taler-auditor-httpd require authentication, so that the
+# /monitoring/ requests of the auditor commands exercise it.
+[auditor]
+ACCESS_TOKEN = secret-token:test-auditor-api
diff --git a/src/testing/test_auditor_api_version.c b/src/testing/test_auditor_api_version.c
@@ -190,6 +190,7 @@ main (int argc,
"taler-auditor-httpd",
"taler-auditor-httpd",
"-c", CONFIG_FILE,
+ "-n",
"-L", "INFO",
NULL))
{
diff --git a/src/testing/testing_api_cmd_auditor_exec_auditor.c b/src/testing/testing_api_cmd_auditor_exec_auditor.c
@@ -393,15 +393,38 @@ auditor_run (void *cls,
TALER_TESTING_interpreter_fail (is);
return;
}
- GNUNET_CONFIGURATION_destroy (cfg);
{
const char *tok = getenv ("TALER_AUDITOR_ACCESS_TOKEN");
-
- /* taler-auditor-httpd requires the token iff it is set */
+ char *cfg_tok = NULL;
+
+ /* Find the token the way taler-auditor-httpd does: the environment
+ variable overrides the configuration, and the token may be stored
+ with or without the RFC 8959 prefix. If there is none, the httpd
+ must have been started with -n. */
+ if ( (NULL == tok) &&
+ (GNUNET_OK ==
+ GNUNET_CONFIGURATION_get_value_string (cfg,
+ "auditor",
+ "ACCESS_TOKEN",
+ &cfg_tok)) )
+ tok = cfg_tok;
if (NULL != tok)
{
char *hdr;
+ if (0 != strncasecmp (tok,
+ "secret-token:",
+ strlen ("secret-token:")))
+ {
+ GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
+ "auditor",
+ "ACCESS_TOKEN",
+ "lacks RFC 8959 prefix");
+ GNUNET_CONFIGURATION_destroy (cfg);
+ TALER_TESTING_interpreter_fail (is);
+ return;
+ }
+ tok += strlen ("secret-token:");
GNUNET_asprintf (&hdr,
"%s: Bearer secret-token:%s",
MHD_HTTP_HEADER_AUTHORIZATION,
@@ -410,7 +433,9 @@ auditor_run (void *cls,
hdr);
GNUNET_free (hdr);
}
+ GNUNET_free (cfg_tok);
}
+ GNUNET_CONFIGURATION_destroy (cfg);
run_next_helper (as);
}