From 8a7446fb40e8177c8493c43c6a340d6d55ddd590 Mon Sep 17 00:00:00 2001 From: CalDescent Date: Sun, 19 Sep 2021 09:34:48 +0100 Subject: [PATCH] Added "apiKeyDisabled" setting to bypass API key / loopback checking for those who need it. This should only be used if all of the following conditions are true: a) Your node is private and not shared with others b) Port 12391 (API port) isn't forwarded c) You have granted access to specific IP addresses using the "apiWhitelist" setting The node will warn on startup if this setting is used without a sensible access control whitelist. --- src/main/java/org/qortal/api/ApiService.java | 26 +++++++++++++++++++ src/main/java/org/qortal/api/Security.java | 5 ++++ .../java/org/qortal/settings/Settings.java | 7 +++++ 3 files changed, 38 insertions(+) diff --git a/src/main/java/org/qortal/api/ApiService.java b/src/main/java/org/qortal/api/ApiService.java index 5baf2c5d..cafba4ae 100644 --- a/src/main/java/org/qortal/api/ApiService.java +++ b/src/main/java/org/qortal/api/ApiService.java @@ -14,6 +14,8 @@ import java.security.SecureRandom; import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.SSLContext; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.eclipse.jetty.http.HttpVersion; import org.eclipse.jetty.rewrite.handler.RedirectPatternRule; import org.eclipse.jetty.rewrite.handler.RewriteHandler; @@ -50,6 +52,8 @@ import org.qortal.settings.Settings; public class ApiService { + private static final Logger LOGGER = LogManager.getLogger(ApiService.class); + private static ApiService instance; private final ResourceConfig config; @@ -203,6 +207,9 @@ public class ApiService { context.addServlet(TradeBotWebSocket.class, "/websockets/crosschain/tradebot"); context.addServlet(PresenceWebSocket.class, "/websockets/presence"); + // Warn about API security if needed + this.checkApiSecurity(); + // Start server this.server.start(); } catch (Exception e) { @@ -222,4 +229,23 @@ public class ApiService { this.server = null; } + private void checkApiSecurity() { + // Warn about API security if needed + boolean allConnectionsAllowed = false; + if (Settings.getInstance().isApiKeyDisabled()) { + for (String pattern : Settings.getInstance().getApiWhitelist()) { + if (pattern.startsWith("0.0.0.0/") || pattern.startsWith("::/") || pattern.endsWith("/0")) { + allConnectionsAllowed = true; + } + } + + if (allConnectionsAllowed) { + LOGGER.warn("Warning: API key validation is currently disabled, and the API whitelist " + + "is allowing all connections. This can be a security risk."); + LOGGER.warn("To fix, set the apiKeyDisabled setting to false, or allow only specific local " + + "IP addresses using the apiWhitelist setting."); + } + } + } + } diff --git a/src/main/java/org/qortal/api/Security.java b/src/main/java/org/qortal/api/Security.java index 448f951a..4e25b03b 100644 --- a/src/main/java/org/qortal/api/Security.java +++ b/src/main/java/org/qortal/api/Security.java @@ -12,6 +12,11 @@ public abstract class Security { public static final String API_KEY_HEADER = "X-API-KEY"; public static void checkApiCallAllowed(HttpServletRequest request) { + // If API key checking has been disabled, we will allow the request in all cases + boolean isApiKeyDisabled = Settings.getInstance().isApiKeyDisabled(); + if (isApiKeyDisabled) + return; + String expectedApiKey = Settings.getInstance().getApiKey(); String passedApiKey = request.getHeader(API_KEY_HEADER); diff --git a/src/main/java/org/qortal/settings/Settings.java b/src/main/java/org/qortal/settings/Settings.java index b8884c6c..6543c09b 100644 --- a/src/main/java/org/qortal/settings/Settings.java +++ b/src/main/java/org/qortal/settings/Settings.java @@ -68,6 +68,9 @@ public class Settings { }; private Boolean apiRestricted; private String apiKey = null; + /** Whether to disable API key or loopback address checking + * IMPORTANT: do not disable for shared nodes or low-security local networks */ + private boolean apiKeyDisabled = false; private boolean apiLoggingEnabled = false; private boolean apiDocumentationEnabled = false; // Both of these need to be set for API to use SSL @@ -356,6 +359,10 @@ public class Settings { return this.apiKey; } + public boolean isApiKeyDisabled() { + return this.apiKeyDisabled; + } + public boolean isApiLoggingEnabled() { return this.apiLoggingEnabled; }