Upload folder via GUI - src
This commit is contained in:
509
src/main/java/net/viper/backendguard/BackendJoinGuardPlugin.java
Normal file
509
src/main/java/net/viper/backendguard/BackendJoinGuardPlugin.java
Normal file
@@ -0,0 +1,509 @@
|
|||||||
|
package net.viper.backendguard;
|
||||||
|
|
||||||
|
import org.bukkit.ChatColor;
|
||||||
|
import org.bukkit.command.Command;
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
import org.bukkit.configuration.file.FileConfiguration;
|
||||||
|
import org.bukkit.event.EventHandler;
|
||||||
|
import org.bukkit.event.EventPriority;
|
||||||
|
import org.bukkit.event.Listener;
|
||||||
|
import org.bukkit.event.player.AsyncPlayerPreLoginEvent;
|
||||||
|
import org.bukkit.plugin.java.JavaPlugin;
|
||||||
|
import org.bukkit.scheduler.BukkitTask;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.net.HttpURLConnection;
|
||||||
|
import java.net.InetAddress;
|
||||||
|
import java.net.UnknownHostException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
public class BackendJoinGuardPlugin extends JavaPlugin implements Listener {
|
||||||
|
|
||||||
|
private boolean enforcementEnabled;
|
||||||
|
private boolean logBlockedAttempts;
|
||||||
|
private String kickMessage;
|
||||||
|
private final Set<String> allowedExactIps = new HashSet<String>();
|
||||||
|
private final List<SubnetRule> allowedCidrs = new ArrayList<SubnetRule>();
|
||||||
|
private boolean statusApiSyncEnabled;
|
||||||
|
private String statusApiBaseUrl;
|
||||||
|
private String statusApiEndpointPath;
|
||||||
|
private String statusApiApiKey;
|
||||||
|
private int statusApiIntervalSeconds;
|
||||||
|
private boolean statusApiLogSyncErrors;
|
||||||
|
private BukkitTask syncTask;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onEnable() {
|
||||||
|
saveDefaultConfig();
|
||||||
|
reloadGuardConfig();
|
||||||
|
getServer().getPluginManager().registerEvents(this, this);
|
||||||
|
getLogger().info("BackendJoinGuard aktiviert. Erlaubte Proxy-IPs=" + allowedExactIps.size() + ", CIDRs=" + allowedCidrs.size() + ", StatusAPI-Sync=" + statusApiSyncEnabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDisable() {
|
||||||
|
cancelSyncTask();
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler(priority = EventPriority.HIGHEST)
|
||||||
|
public void onAsyncPlayerPreLogin(AsyncPlayerPreLoginEvent event) {
|
||||||
|
if (!enforcementEnabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
InetAddress address = event.getAddress();
|
||||||
|
if (address == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isAllowed(address)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
event.disallow(AsyncPlayerPreLoginEvent.Result.KICK_OTHER, colorize(kickMessage));
|
||||||
|
if (logBlockedAttempts) {
|
||||||
|
getLogger().warning("Direktjoin blockiert: player=" + event.getName() + ", ip=" + address.getHostAddress());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
|
||||||
|
if (!"backendguard".equalsIgnoreCase(command.getName())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!sender.hasPermission("backendguard.admin")) {
|
||||||
|
sender.sendMessage(colorize("&cDafuer hast du keine Rechte."));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (args.length == 1 && "reload".equalsIgnoreCase(args[0])) {
|
||||||
|
reloadConfig();
|
||||||
|
reloadGuardConfig();
|
||||||
|
if (statusApiSyncEnabled) {
|
||||||
|
syncFromStatusApi(true);
|
||||||
|
sender.sendMessage(colorize("&aBackendJoinGuard neu geladen. StatusAPI-Sync aktiv."));
|
||||||
|
} else {
|
||||||
|
sender.sendMessage(colorize("&aBackendJoinGuard neu geladen. Standalone-Modus aktiv."));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
sender.sendMessage(colorize("&e/backendguard reload"));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void reloadGuardConfig() {
|
||||||
|
FileConfiguration config = getConfig();
|
||||||
|
enforcementEnabled = config.getBoolean("enforcement-enabled", true);
|
||||||
|
logBlockedAttempts = config.getBoolean("log-blocked-attempts", true);
|
||||||
|
kickMessage = config.getString("kick-message", "&cBitte verbinde dich nur ueber den Proxy.");
|
||||||
|
|
||||||
|
allowedExactIps.clear();
|
||||||
|
allowedCidrs.clear();
|
||||||
|
|
||||||
|
for (String entry : config.getStringList("allowed-proxy-ips")) {
|
||||||
|
String normalized = normalizeIp(entry);
|
||||||
|
if (normalized != null) {
|
||||||
|
allowedExactIps.add(normalized);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (String entry : config.getStringList("allowed-proxy-cidrs")) {
|
||||||
|
SubnetRule rule = parseSubnet(entry);
|
||||||
|
if (rule != null) {
|
||||||
|
allowedCidrs.add(rule);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
statusApiSyncEnabled = config.getBoolean("statusapi-sync.enabled", false);
|
||||||
|
statusApiBaseUrl = config.getString("statusapi-sync.base-url", "http://127.0.0.1:9191");
|
||||||
|
statusApiEndpointPath = config.getString("statusapi-sync.endpoint-path", "/network/backendguard/config");
|
||||||
|
statusApiApiKey = config.getString("statusapi-sync.api-key", "");
|
||||||
|
statusApiIntervalSeconds = Math.max(10, config.getInt("statusapi-sync.interval-seconds", 60));
|
||||||
|
statusApiLogSyncErrors = config.getBoolean("statusapi-sync.log-sync-errors", true);
|
||||||
|
|
||||||
|
cancelSyncTask();
|
||||||
|
if (statusApiSyncEnabled) {
|
||||||
|
// Erst lokale Werte laden, dann zentral ueberschreiben falls Sync klappt.
|
||||||
|
syncFromStatusApi(false);
|
||||||
|
syncTask = getServer().getScheduler().runTaskTimerAsynchronously(
|
||||||
|
this,
|
||||||
|
() -> syncFromStatusApi(false),
|
||||||
|
statusApiIntervalSeconds * 20L,
|
||||||
|
statusApiIntervalSeconds * 20L
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void cancelSyncTask() {
|
||||||
|
if (syncTask != null) {
|
||||||
|
syncTask.cancel();
|
||||||
|
syncTask = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void syncFromStatusApi(boolean manualTrigger) {
|
||||||
|
if (!statusApiSyncEnabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
HttpURLConnection conn = null;
|
||||||
|
try {
|
||||||
|
URL url = new URL(buildSyncUrl());
|
||||||
|
conn = (HttpURLConnection) url.openConnection();
|
||||||
|
conn.setRequestMethod("GET");
|
||||||
|
conn.setConnectTimeout(4000);
|
||||||
|
conn.setReadTimeout(6000);
|
||||||
|
conn.setRequestProperty("Accept", "application/json");
|
||||||
|
if (statusApiApiKey != null && !statusApiApiKey.trim().isEmpty()) {
|
||||||
|
conn.setRequestProperty("x-api-key", statusApiApiKey.trim());
|
||||||
|
}
|
||||||
|
|
||||||
|
int code = conn.getResponseCode();
|
||||||
|
if (code < 200 || code >= 300) {
|
||||||
|
if (statusApiLogSyncErrors || manualTrigger) {
|
||||||
|
getLogger().warning("StatusAPI-Sync fehlgeschlagen (HTTP " + code + "). Nutze lokale Guard-Werte.");
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
try (BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8))) {
|
||||||
|
String line;
|
||||||
|
while ((line = br.readLine()) != null) {
|
||||||
|
sb.append(line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String json = sb.toString();
|
||||||
|
Boolean remoteEnforcement = extractJsonBoolean(json, "enforcement_enabled");
|
||||||
|
Boolean remoteLogBlocked = extractJsonBoolean(json, "log_blocked_attempts");
|
||||||
|
String remoteKickMessage = extractJsonString(json, "kick_message");
|
||||||
|
List<String> remoteIps = extractJsonStringArray(json, "allowed_proxy_ips");
|
||||||
|
List<String> remoteCidrs = extractJsonStringArray(json, "allowed_proxy_cidrs");
|
||||||
|
|
||||||
|
if (remoteEnforcement != null) {
|
||||||
|
enforcementEnabled = remoteEnforcement;
|
||||||
|
}
|
||||||
|
if (remoteLogBlocked != null) {
|
||||||
|
logBlockedAttempts = remoteLogBlocked;
|
||||||
|
}
|
||||||
|
if (remoteKickMessage != null && !remoteKickMessage.trim().isEmpty()) {
|
||||||
|
kickMessage = remoteKickMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (remoteIps != null) {
|
||||||
|
allowedExactIps.clear();
|
||||||
|
for (String ip : remoteIps) {
|
||||||
|
String normalized = normalizeIp(ip);
|
||||||
|
if (normalized != null) {
|
||||||
|
allowedExactIps.add(normalized);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (remoteCidrs != null) {
|
||||||
|
allowedCidrs.clear();
|
||||||
|
for (String cidr : remoteCidrs) {
|
||||||
|
SubnetRule rule = parseSubnet(cidr);
|
||||||
|
if (rule != null) {
|
||||||
|
allowedCidrs.add(rule);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (manualTrigger) {
|
||||||
|
getLogger().info("StatusAPI-Sync erfolgreich. Erlaubte Proxy-IPs=" + allowedExactIps.size() + ", CIDRs=" + allowedCidrs.size());
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
if (statusApiLogSyncErrors || manualTrigger) {
|
||||||
|
getLogger().warning("StatusAPI-Sync Fehler: " + e.getMessage() + ". Nutze lokale Guard-Werte.");
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
if (conn != null) {
|
||||||
|
conn.disconnect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String buildSyncUrl() {
|
||||||
|
String base = statusApiBaseUrl == null ? "" : statusApiBaseUrl.trim();
|
||||||
|
String path = statusApiEndpointPath == null ? "" : statusApiEndpointPath.trim();
|
||||||
|
|
||||||
|
if (base.endsWith("/") && path.startsWith("/")) {
|
||||||
|
return base.substring(0, base.length() - 1) + path;
|
||||||
|
}
|
||||||
|
if (!base.endsWith("/") && !path.startsWith("/")) {
|
||||||
|
return base + "/" + path;
|
||||||
|
}
|
||||||
|
return base + path;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Boolean extractJsonBoolean(String json, String key) {
|
||||||
|
String token = extractJsonToken(json, key);
|
||||||
|
if (token == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if ("true".equalsIgnoreCase(token)) {
|
||||||
|
return Boolean.TRUE;
|
||||||
|
}
|
||||||
|
if ("false".equalsIgnoreCase(token)) {
|
||||||
|
return Boolean.FALSE;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String extractJsonString(String json, String key) {
|
||||||
|
if (json == null || key == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
String search = "\"" + key + "\"";
|
||||||
|
int idx = json.indexOf(search);
|
||||||
|
if (idx < 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
int colon = json.indexOf(':', idx + search.length());
|
||||||
|
if (colon < 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
int i = colon + 1;
|
||||||
|
while (i < json.length() && Character.isWhitespace(json.charAt(i))) {
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
if (i >= json.length() || json.charAt(i) != '"') {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
i++;
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
boolean escape = false;
|
||||||
|
while (i < json.length()) {
|
||||||
|
char ch = json.charAt(i++);
|
||||||
|
if (escape) {
|
||||||
|
if (ch == 'n') {
|
||||||
|
sb.append('\n');
|
||||||
|
} else if (ch == 'r') {
|
||||||
|
sb.append('\r');
|
||||||
|
} else {
|
||||||
|
sb.append(ch);
|
||||||
|
}
|
||||||
|
escape = false;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (ch == '\\') {
|
||||||
|
escape = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (ch == '"') {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
sb.append(ch);
|
||||||
|
}
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private String extractJsonToken(String json, String key) {
|
||||||
|
if (json == null || key == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
String search = "\"" + key + "\"";
|
||||||
|
int idx = json.indexOf(search);
|
||||||
|
if (idx < 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
int colon = json.indexOf(':', idx + search.length());
|
||||||
|
if (colon < 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
int i = colon + 1;
|
||||||
|
while (i < json.length() && Character.isWhitespace(json.charAt(i))) {
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
if (i >= json.length()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
while (i < json.length()) {
|
||||||
|
char ch = json.charAt(i);
|
||||||
|
if (ch == ',' || ch == '}' || ch == ']' || Character.isWhitespace(ch)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
sb.append(ch);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
return sb.toString().trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<String> extractJsonStringArray(String json, String key) {
|
||||||
|
if (json == null || key == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
String search = "\"" + key + "\"";
|
||||||
|
int idx = json.indexOf(search);
|
||||||
|
if (idx < 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
int colon = json.indexOf(':', idx + search.length());
|
||||||
|
if (colon < 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
int start = json.indexOf('[', colon + 1);
|
||||||
|
if (start < 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
int depth = 0;
|
||||||
|
int end = -1;
|
||||||
|
for (int i = start; i < json.length(); i++) {
|
||||||
|
char ch = json.charAt(i);
|
||||||
|
if (ch == '[') {
|
||||||
|
depth++;
|
||||||
|
} else if (ch == ']') {
|
||||||
|
depth--;
|
||||||
|
if (depth == 0) {
|
||||||
|
end = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (end < 0 || end <= start) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
String raw = json.substring(start + 1, end);
|
||||||
|
List<String> out = new ArrayList<String>();
|
||||||
|
boolean inString = false;
|
||||||
|
boolean escape = false;
|
||||||
|
StringBuilder current = new StringBuilder();
|
||||||
|
|
||||||
|
for (int i = 0; i < raw.length(); i++) {
|
||||||
|
char ch = raw.charAt(i);
|
||||||
|
if (!inString) {
|
||||||
|
if (ch == '"') {
|
||||||
|
inString = true;
|
||||||
|
current.setLength(0);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (escape) {
|
||||||
|
if (ch == 'n') {
|
||||||
|
current.append('\n');
|
||||||
|
} else if (ch == 'r') {
|
||||||
|
current.append('\r');
|
||||||
|
} else {
|
||||||
|
current.append(ch);
|
||||||
|
}
|
||||||
|
escape = false;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ch == '\\') {
|
||||||
|
escape = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ch == '"') {
|
||||||
|
inString = false;
|
||||||
|
out.add(current.toString());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
current.append(ch);
|
||||||
|
}
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isAllowed(InetAddress address) {
|
||||||
|
String normalized = normalizeIp(address.getHostAddress());
|
||||||
|
if (normalized != null && allowedExactIps.contains(normalized)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (SubnetRule rule : allowedCidrs) {
|
||||||
|
if (rule.matches(address)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String normalizeIp(String raw) {
|
||||||
|
if (raw == null || raw.trim().isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return InetAddress.getByName(raw.trim()).getHostAddress().toLowerCase(Locale.ROOT);
|
||||||
|
} catch (UnknownHostException e) {
|
||||||
|
getLogger().warning("Ungueltige IP in Config ignoriert: " + raw);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private SubnetRule parseSubnet(String raw) {
|
||||||
|
if (raw == null || raw.trim().isEmpty() || !raw.contains("/")) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
String[] parts = raw.trim().split("/", 2);
|
||||||
|
try {
|
||||||
|
InetAddress network = InetAddress.getByName(parts[0]);
|
||||||
|
int prefix = Integer.parseInt(parts[1]);
|
||||||
|
return new SubnetRule(network, prefix);
|
||||||
|
} catch (Exception e) {
|
||||||
|
getLogger().warning("Ungueltige CIDR in Config ignoriert: " + raw);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String colorize(String text) {
|
||||||
|
return ChatColor.translateAlternateColorCodes('&', text == null ? "" : text);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class SubnetRule {
|
||||||
|
private final byte[] networkBytes;
|
||||||
|
private final int prefixLength;
|
||||||
|
|
||||||
|
private SubnetRule(InetAddress network, int prefixLength) {
|
||||||
|
this.networkBytes = network.getAddress();
|
||||||
|
this.prefixLength = prefixLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean matches(InetAddress address) {
|
||||||
|
byte[] candidate = address.getAddress();
|
||||||
|
if (candidate.length != networkBytes.length) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int fullBytes = prefixLength / 8;
|
||||||
|
int remainderBits = prefixLength % 8;
|
||||||
|
|
||||||
|
for (int i = 0; i < fullBytes; i++) {
|
||||||
|
if (candidate[i] != networkBytes[i]) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (remainderBits == 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int mask = (-1) << (8 - remainderBits);
|
||||||
|
return (candidate[fullBytes] & mask) == (networkBytes[fullBytes] & mask);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
29
src/main/resources/config.yml
Normal file
29
src/main/resources/config.yml
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
# BackendJoinGuard
|
||||||
|
# Dieses Plugin kommt auf JEDEN Unterserver (Paper/Spigot), nicht auf den Proxy.
|
||||||
|
# Nur Verbindungen von diesen Proxy-IPs oder Netzbereichen duerfen joinen.
|
||||||
|
|
||||||
|
enforcement-enabled: true
|
||||||
|
log-blocked-attempts: true
|
||||||
|
kick-message: "&cBitte verbinde dich nur ueber den Proxy-Server. Direkte Unterserver-Joins sind blockiert."
|
||||||
|
|
||||||
|
# Exakte Proxy-IPs
|
||||||
|
allowed-proxy-ips:
|
||||||
|
- "127.0.0.1"
|
||||||
|
- "::1"
|
||||||
|
|
||||||
|
# Optional ganze Netze im CIDR-Format
|
||||||
|
allowed-proxy-cidrs: []
|
||||||
|
# Beispiel:
|
||||||
|
# allowed-proxy-cidrs:
|
||||||
|
# - "10.0.0.0/24"
|
||||||
|
# - "192.168.178.10/32"
|
||||||
|
|
||||||
|
# Optional: zentrale Steuerung ueber StatusAPI
|
||||||
|
# Standard bleibt Standalone (enabled=false), damit BackendJoinGuard auch ohne StatusAPI nutzbar ist.
|
||||||
|
statusapi-sync:
|
||||||
|
enabled: true
|
||||||
|
base-url: "http://127.0.0.1:9191"
|
||||||
|
endpoint-path: "/network/backendguard/config"
|
||||||
|
api-key: "bgSync_7Rk9pQ2nLm5xV8cH4tW1yZ6"
|
||||||
|
interval-seconds: 60
|
||||||
|
log-sync-errors: true
|
||||||
14
src/main/resources/plugin.yml
Normal file
14
src/main/resources/plugin.yml
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
name: BackendJoinGuard
|
||||||
|
main: net.viper.backendguard.BackendJoinGuardPlugin
|
||||||
|
version: 1.0.0
|
||||||
|
author: M_Viper
|
||||||
|
description: Blockiert direkte Joins auf Backendserver und erlaubt nur Proxy-IPs.
|
||||||
|
api-version: '1.20'
|
||||||
|
commands:
|
||||||
|
backendguard:
|
||||||
|
description: BackendJoinGuard neu laden
|
||||||
|
usage: /backendguard reload
|
||||||
|
permissions:
|
||||||
|
backendguard.admin:
|
||||||
|
description: Darf BackendJoinGuard verwalten
|
||||||
|
default: op
|
||||||
Reference in New Issue
Block a user