From 75158e9b030f18c7c14eafffe23d8ee2fbdb95b2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Markus=20M=C3=A4kel=C3=A4?= <markus.makela@mariadb.com>
Date: Thu, 8 Sep 2022 09:56:05 +0300
Subject: [PATCH] MXS-4277: Make "iss" claim configurable

This allows the issuer of the tokens to be identified if a hostname is
placed in the right field.
---
 .../Getting-Started/Configuration-Guide.md          | 13 +++++++++++++
 include/maxscale/config.hh                          |  2 ++
 server/core/admin.cc                                |  7 ++++---
 server/core/config.cc                               |  7 +++++++
 server/core/http_sql.cc                             | 10 +++++-----
 5 files changed, 31 insertions(+), 8 deletions(-)

diff --git a/Documentation/Getting-Started/Configuration-Guide.md b/Documentation/Getting-Started/Configuration-Guide.md
index f0d4813db8..04446a635a 100644
--- a/Documentation/Getting-Started/Configuration-Guide.md
+++ b/Documentation/Getting-Started/Configuration-Guide.md
@@ -150,6 +150,7 @@ runtime and can only be defined in a configuration file:
 * `admin_ssl_version`
 * `admin_jwt_algorithm`
 * `admin_jwt_key`
+* `admin_jwt_issuer`
 * `auto_tune`
 * `cachedir`
 * `connector_plugindir`
@@ -1469,6 +1470,18 @@ tokens will be signed by the latest key version that is available on startup:
 rotating the encryption key in the key management system will not cause the JWTs
 to be signed with newer versions of the key.
 
+### `admin_jwt_issuer`
+
+- **Type**: string
+- **Mandatory**: No
+- **Dynamic**: No
+- **Default**: `maxscale`
+
+The issuer (`"iss"`) claim of all JWTs generated by MaxScale. This can be set
+to a custom value to uniquely identify which MaxScale issued a JWT. This is
+especially useful for cases where the MaxScale GUI is used from behind
+a reverse proxy.
+
 ### `config_sync_cluster`
 
 - **Type**: monitor
diff --git a/include/maxscale/config.hh b/include/maxscale/config.hh
index 3141ab77fb..7126541c8e 100644
--- a/include/maxscale/config.hh
+++ b/include/maxscale/config.hh
@@ -291,6 +291,7 @@ public:
     mxb::ssl_version::Version admin_ssl_version;    /**< Admin allowed SSL versions */
     mxs::JwtAlgo              admin_jwt_algorithm;  /**< JWT signature key */
     std::string               admin_jwt_key;        /**< Key used with symmetric JWT algorithms */
+    std::string               admin_jwt_issuer;     /**< JWT issuer claim */
 
     std::string  local_address;                 /**< Local address to use when connecting */
     bool         load_persisted_configs;        /**< Load persisted configuration files on startup */
@@ -397,6 +398,7 @@ private:
     static config::ParamDeprecated<config::ParamAlias>  s_admin_ssl_ca_cert;// -> s_admin_ca
     static config::ParamEnum<mxs::JwtAlgo>              s_admin_jwt_algorithm;
     static config::ParamString                          s_admin_jwt_key;
+    static config::ParamString                          s_admin_jwt_issuer;
     static config::ParamString                          s_local_address;
     static config::ParamBool                            s_load_persisted_configs;
     static config::ParamBool                            s_persist_runtime_changes;
diff --git a/server/core/admin.cc b/server/core/admin.cc
index 3bfc993b15..a2e420abcc 100644
--- a/server/core/admin.cc
+++ b/server/core/admin.cc
@@ -74,7 +74,6 @@ const char* gui_not_secure_page =
 </html>
 )EOF";
 
-const std::string TOKEN_ISSUER = "maxscale";
 const std::string TOKEN_BODY = "token_body";
 const std::string TOKEN_SIG = "token_sig";
 
@@ -905,7 +904,8 @@ HttpResponse Client::generate_token(const HttpRequest& request)
         }
     }
 
-    auto token = mxs::jwt::create(TOKEN_ISSUER, m_user, token_age);
+    const auto& cnf = mxs::Config::get();
+    auto token = mxs::jwt::create(cnf.admin_jwt_issuer, m_user, token_age);
 
     if (request.get_option("persist") == "yes")
     {
@@ -928,7 +928,8 @@ bool Client::auth_with_token(const std::string& token)
 {
     bool rval = false;
 
-    std::tie(rval, m_user) = mxs::jwt::get_subject(TOKEN_ISSUER, token);
+    const auto& cnf = mxs::Config::get();
+    std::tie(rval, m_user) = mxs::jwt::get_subject(cnf.admin_jwt_issuer, token);
 
     return rval;
 }
diff --git a/server/core/config.cc b/server/core/config.cc
index b47fa8104a..884d1a40a6 100644
--- a/server/core/config.cc
+++ b/server/core/config.cc
@@ -618,6 +618,12 @@ config::ParamString Config::s_admin_jwt_key(
     "generate a random key that is used to sign the JWT.",
     "");
 
+config::ParamString Config::s_admin_jwt_issuer(
+    &Config::s_specification,
+    "admin_jwt_issuer",
+    "The issuer claim for all JWTs generated by MaxScale.",
+    "maxscale");
+
 config::ParamString Config::s_local_address(
     &Config::s_specification,
     CN_LOCAL_ADDRESS,
@@ -879,6 +885,7 @@ Config::Config(int argc, char** argv)
     add_native(&Config::admin_ssl_version, &s_admin_ssl_version);
     add_native(&Config::admin_jwt_algorithm, &s_admin_jwt_algorithm);
     add_native(&Config::admin_jwt_key, &s_admin_jwt_key);
+    add_native(&Config::admin_jwt_issuer, &s_admin_jwt_issuer);
     add_native(&Config::local_address, &s_local_address);
     add_native(&Config::load_persisted_configs, &s_load_persisted_configs);
     add_native(&Config::persist_runtime_changes, &s_persist_runtime_changes);
diff --git a/server/core/http_sql.cc b/server/core/http_sql.cc
index 11c0a23fca..fb31f9f088 100644
--- a/server/core/http_sql.cc
+++ b/server/core/http_sql.cc
@@ -36,8 +36,6 @@ namespace
 const std::string CONN_ID_BODY = "conn_id_body_";
 const std::string CONN_ID_SIG = "conn_id_sig_";
 
-const std::string TOKEN_ISSUER = "mxs-query";
-
 const std::string COLLECTION_NAME = "sql";
 
 HttpResponse create_error(const std::string& err, int errcode = MHD_HTTP_BAD_REQUEST)
@@ -71,14 +69,15 @@ std::pair<std::string, std::string> get_connection_id(const HttpRequest& request
     std::string err;
     std::string token = request.get_option("token");
     std::string cookie = get_conn_id_cookie(request, requested_id);
+    const auto& cnf = mxs::Config::get();
 
     if (!token.empty())
     {
-        std::tie(ok, aud) = mxs::jwt::get_subject(TOKEN_ISSUER, token);
+        std::tie(ok, aud) = mxs::jwt::get_subject(cnf.admin_jwt_issuer, token);
     }
     else if (!cookie.empty())
     {
-        std::tie(ok, aud) = mxs::jwt::get_subject(TOKEN_ISSUER, cookie);
+        std::tie(ok, aud) = mxs::jwt::get_subject(cnf.admin_jwt_issuer, cookie);
     }
     else if (!requested_id.empty())
     {
@@ -330,7 +329,8 @@ json_t* all_connections_to_json(const std::string& host, const std::vector<std::
 HttpResponse create_connect_response(const std::string& host, const std::string& id, bool persist, int age)
 {
     int max_age = age > 0 ? age : 28800;
-    auto token = mxs::jwt::create(TOKEN_ISSUER, id, max_age);
+    const auto& cnf = mxs::Config::get();
+    auto token = mxs::jwt::create(cnf.admin_jwt_issuer, id, max_age);
 
     json_t* data = one_connection_to_json(host, id);
     HttpResponse response(MHD_HTTP_CREATED, data);
-- 
2.37.3

