Added multi server support
This commit is contained in:
@@ -74,6 +74,7 @@ public final class PluginRuntime {
|
||||
lines.add("Node: " + status.nodeName());
|
||||
lines.add("Agent: " + (status.running() ? "running" : "stopped"));
|
||||
lines.add("Tunnel: " + (status.tunnelConnected() ? "connected" : "not connected"));
|
||||
lines.add("Connected tunnels: " + status.connectedTunnels());
|
||||
lines.add("Active streams: " + status.activeStreams());
|
||||
lines.add("TLS: " + (status.tlsEnabled() ? "enabled" : "disabled"));
|
||||
lines.add("Last event: " + status.lastEvent());
|
||||
@@ -99,6 +100,10 @@ public final class PluginRuntime {
|
||||
lines.add("Config: valid");
|
||||
lines.add("Role: " + lower(loaded.role()));
|
||||
lines.add("TLS: " + (loaded.tls().enabled() ? "enabled" : "disabled"));
|
||||
lines.add("Routes: " + loaded.routes().size());
|
||||
if (loaded.role() == AgentConfig.Role.BACKEND) {
|
||||
lines.add("Frontend endpoints: " + loaded.frontends().size());
|
||||
}
|
||||
|
||||
if (!loaded.tls().enabled()) {
|
||||
lines.add("Security: TLS is disabled. This is okay for local testing, not ideal for paid production use.");
|
||||
@@ -136,14 +141,19 @@ public final class PluginRuntime {
|
||||
return new BackendAgent(config, this::saveLearnedTlsFingerprint);
|
||||
}
|
||||
|
||||
private void saveLearnedTlsFingerprint(String fingerprint) {
|
||||
private void saveLearnedTlsFingerprint(String endpointName, String fingerprint) {
|
||||
if (configPath == null || fingerprint == null || fingerprint.isBlank()) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
ConfigFileEditor.setProperty(configPath, "tunnel.tls.pinnedCertificateSha256", fingerprint);
|
||||
ConfigFileEditor.setProperty(configPath,
|
||||
"frontend." + endpointName + ".tls.pinnedCertificateSha256",
|
||||
fingerprint);
|
||||
if (config != null && config.frontends().size() == 1) {
|
||||
ConfigFileEditor.setProperty(configPath, "tunnel.tls.pinnedCertificateSha256", fingerprint);
|
||||
}
|
||||
config = AgentConfigIO.load(configPath);
|
||||
logger.info("Saved frontend TLS certificate pin to " + configPath);
|
||||
logger.info("Saved frontend TLS certificate pin for " + endpointName + " to " + configPath);
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.WARNING, "Unable to save frontend TLS certificate pin", e);
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ import java.util.concurrent.ThreadLocalRandom;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
@@ -34,21 +34,21 @@ public final class BackendAgent implements ManagedAgent {
|
||||
private static final Logger LOGGER = Logger.getLogger(BackendAgent.class.getName());
|
||||
|
||||
private final AgentConfig config;
|
||||
private final Consumer<String> learnedTlsFingerprintConsumer;
|
||||
private final BiConsumer<String, String> learnedTlsFingerprintConsumer;
|
||||
private final ExecutorService executor = Executors.newThreadPerTaskExecutor(
|
||||
new NamedThreadFactory("proxylink-backend", true)
|
||||
);
|
||||
private final AtomicBoolean running = new AtomicBoolean(false);
|
||||
private final AtomicReference<String> learnedTlsFingerprint = new AtomicReference<>("");
|
||||
private final Map<String, String> learnedTlsFingerprints = new ConcurrentHashMap<>();
|
||||
private final Map<String, BackendTunnel> activeTunnels = new ConcurrentHashMap<>();
|
||||
private final AtomicReference<String> lastEvent = new AtomicReference<>("not started");
|
||||
private volatile BackendTunnel activeTunnel;
|
||||
|
||||
public BackendAgent(AgentConfig config) {
|
||||
this(config, ignored -> {
|
||||
this(config, (ignoredEndpoint, ignoredFingerprint) -> {
|
||||
});
|
||||
}
|
||||
|
||||
public BackendAgent(AgentConfig config, Consumer<String> learnedTlsFingerprintConsumer) {
|
||||
public BackendAgent(AgentConfig config, BiConsumer<String, String> learnedTlsFingerprintConsumer) {
|
||||
this.config = config;
|
||||
this.learnedTlsFingerprintConsumer = learnedTlsFingerprintConsumer;
|
||||
}
|
||||
@@ -58,22 +58,30 @@ public final class BackendAgent implements ManagedAgent {
|
||||
if (!running.compareAndSet(false, true)) {
|
||||
return;
|
||||
}
|
||||
executor.execute(this::runReconnectLoop);
|
||||
lastEvent.set("connecting to frontend " + config.tunnel().connectHost() + ":" + config.tunnel().connectPort());
|
||||
LOGGER.info(() -> "Backend agent connecting to frontend "
|
||||
+ config.tunnel().connectHost() + ":" + config.tunnel().connectPort());
|
||||
for (AgentConfig.FrontendEndpointConfig frontend : config.frontends()) {
|
||||
executor.execute(() -> runReconnectLoop(frontend));
|
||||
}
|
||||
lastEvent.set("connecting to " + config.frontends().size() + " frontend endpoint(s)");
|
||||
LOGGER.info(() -> "Backend agent connecting to " + config.frontends().size() + " frontend endpoint(s)");
|
||||
}
|
||||
|
||||
@Override
|
||||
public TunnelStatus status() {
|
||||
BackendTunnel tunnel = activeTunnel;
|
||||
boolean connected = running.get() && tunnel != null && tunnel.isOpen();
|
||||
int activeStreams = connected ? tunnel.streamCount() : 0;
|
||||
int connectedTunnels = 0;
|
||||
int activeStreams = 0;
|
||||
for (BackendTunnel tunnel : activeTunnels.values()) {
|
||||
if (tunnel.isOpen()) {
|
||||
connectedTunnels++;
|
||||
activeStreams += tunnel.streamCount();
|
||||
}
|
||||
}
|
||||
boolean connected = running.get() && connectedTunnels > 0;
|
||||
return new TunnelStatus(
|
||||
config.role(),
|
||||
config.nodeName(),
|
||||
running.get(),
|
||||
connected,
|
||||
connectedTunnels,
|
||||
activeStreams,
|
||||
config.tls().enabled(),
|
||||
lastEvent.get()
|
||||
@@ -85,76 +93,94 @@ public final class BackendAgent implements ManagedAgent {
|
||||
if (!running.compareAndSet(true, false)) {
|
||||
return;
|
||||
}
|
||||
BackendTunnel tunnel = activeTunnel;
|
||||
if (tunnel != null) {
|
||||
for (BackendTunnel tunnel : activeTunnels.values()) {
|
||||
tunnel.close("backend shutting down");
|
||||
}
|
||||
activeTunnels.clear();
|
||||
executor.shutdownNow();
|
||||
lastEvent.set("backend stopped");
|
||||
LOGGER.info("Backend agent stopped");
|
||||
}
|
||||
|
||||
private void runReconnectLoop() {
|
||||
private void runReconnectLoop(AgentConfig.FrontendEndpointConfig frontend) {
|
||||
long delayMillis = config.tunnel().reconnectInitialMillis();
|
||||
while (running.get()) {
|
||||
try {
|
||||
runSingleConnection();
|
||||
runSingleConnection(frontend);
|
||||
delayMillis = config.tunnel().reconnectInitialMillis();
|
||||
} catch (Exception e) {
|
||||
if (running.get()) {
|
||||
lastEvent.set("connection failed: " + e.getMessage());
|
||||
LOGGER.log(Level.WARNING, "Backend tunnel connection failed", e);
|
||||
lastEvent.set("connection to " + frontend.name() + " failed: " + e.getMessage());
|
||||
LOGGER.log(Level.WARNING, "Backend tunnel connection to " + frontend.name() + " failed", e);
|
||||
}
|
||||
}
|
||||
|
||||
if (running.get()) {
|
||||
sleepWithBackoff(delayMillis);
|
||||
sleepWithBackoff(frontend, delayMillis);
|
||||
delayMillis = Math.min(delayMillis * 2, config.tunnel().reconnectMaxMillis());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void runSingleConnection() throws IOException, GeneralSecurityException {
|
||||
Socket socket = TlsSocketFactory.createClientSocket(connectionConfig(), this::learnTlsFingerprint);
|
||||
FrameCodec codec = new FrameCodec(socket.getInputStream(), socket.getOutputStream());
|
||||
authenticateToFrontend(codec);
|
||||
private void runSingleConnection(AgentConfig.FrontendEndpointConfig frontend)
|
||||
throws IOException, GeneralSecurityException {
|
||||
Socket socket = null;
|
||||
boolean handedToTunnel = false;
|
||||
try {
|
||||
AgentConfig.FrontendEndpointConfig pinnedFrontend = frontendWithLearnedPin(frontend);
|
||||
socket = TlsSocketFactory.createClientSocket(
|
||||
config,
|
||||
pinnedFrontend,
|
||||
fingerprint -> learnTlsFingerprint(frontend, fingerprint)
|
||||
);
|
||||
FrameCodec codec = new FrameCodec(socket.getInputStream(), socket.getOutputStream());
|
||||
authenticateToFrontend(codec);
|
||||
|
||||
BackendTunnel tunnel = new BackendTunnel(socket, codec);
|
||||
activeTunnel = tunnel;
|
||||
lastEvent.set("authenticated to frontend");
|
||||
LOGGER.info(() -> "Backend tunnel authenticated to "
|
||||
+ config.tunnel().connectHost() + ":" + config.tunnel().connectPort());
|
||||
tunnel.runReadLoop();
|
||||
BackendTunnel tunnel = new BackendTunnel(frontend, socket, codec);
|
||||
BackendTunnel previous = activeTunnels.put(frontend.name(), tunnel);
|
||||
if (previous != null) {
|
||||
previous.close("replaced by a new authenticated tunnel to " + frontend.name());
|
||||
}
|
||||
|
||||
handedToTunnel = true;
|
||||
lastEvent.set("authenticated to frontend " + frontend.name());
|
||||
LOGGER.info(() -> "Backend tunnel authenticated to " + frontend.name()
|
||||
+ " at " + frontend.connectHost() + ":" + frontend.connectPort());
|
||||
tunnel.runReadLoop();
|
||||
} finally {
|
||||
if (!handedToTunnel && socket != null) {
|
||||
try {
|
||||
socket.close();
|
||||
} catch (IOException ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private AgentConfig connectionConfig() {
|
||||
String pin = learnedTlsFingerprint.get();
|
||||
if (pin.isBlank()) {
|
||||
return config;
|
||||
private AgentConfig.FrontendEndpointConfig frontendWithLearnedPin(AgentConfig.FrontendEndpointConfig frontend) {
|
||||
if (!frontend.pinnedCertificateSha256().isBlank()) {
|
||||
return frontend;
|
||||
}
|
||||
AgentConfig.TlsConfig tls = config.tls();
|
||||
AgentConfig.TlsConfig pinnedTls = new AgentConfig.TlsConfig(
|
||||
tls.enabled(),
|
||||
tls.keyStorePath(),
|
||||
tls.keyStorePassword(),
|
||||
tls.trustStorePath(),
|
||||
tls.trustStorePassword(),
|
||||
tls.requireClientAuth(),
|
||||
tls.autoGenerate(),
|
||||
tls.trustOnFirstUse(),
|
||||
String pin = learnedTlsFingerprints.getOrDefault(frontend.name(), "");
|
||||
if (pin.isBlank()) {
|
||||
return frontend;
|
||||
}
|
||||
return new AgentConfig.FrontendEndpointConfig(
|
||||
frontend.name(),
|
||||
frontend.connectHost(),
|
||||
frontend.connectPort(),
|
||||
pin
|
||||
);
|
||||
return new AgentConfig(config.role(), config.nodeName(), config.tunnel(), pinnedTls, config.routes());
|
||||
}
|
||||
|
||||
private void learnTlsFingerprint(String fingerprint) {
|
||||
private void learnTlsFingerprint(AgentConfig.FrontendEndpointConfig frontend, String fingerprint) {
|
||||
if (fingerprint == null || fingerprint.isBlank()) {
|
||||
return;
|
||||
}
|
||||
if (learnedTlsFingerprint.compareAndSet("", fingerprint)) {
|
||||
learnedTlsFingerprintConsumer.accept(fingerprint);
|
||||
lastEvent.set("pinned frontend TLS certificate");
|
||||
LOGGER.info("Pinned frontend TLS certificate fingerprint: " + fingerprint);
|
||||
if (learnedTlsFingerprints.putIfAbsent(frontend.name(), fingerprint) == null) {
|
||||
learnedTlsFingerprintConsumer.accept(frontend.name(), fingerprint);
|
||||
lastEvent.set("pinned frontend TLS certificate for " + frontend.name());
|
||||
LOGGER.info("Pinned frontend TLS certificate fingerprint for " + frontend.name() + ": " + fingerprint);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -189,10 +215,10 @@ public final class BackendAgent implements ManagedAgent {
|
||||
}
|
||||
}
|
||||
|
||||
private void sleepWithBackoff(long delayMillis) {
|
||||
private void sleepWithBackoff(AgentConfig.FrontendEndpointConfig frontend, long delayMillis) {
|
||||
long jitter = ThreadLocalRandom.current().nextLong(0, Math.max(1, delayMillis / 4));
|
||||
long sleepMillis = delayMillis + jitter;
|
||||
LOGGER.info(() -> "Reconnecting backend tunnel in " + sleepMillis + "ms");
|
||||
LOGGER.info(() -> "Reconnecting backend tunnel to " + frontend.name() + " in " + sleepMillis + "ms");
|
||||
try {
|
||||
Thread.sleep(sleepMillis);
|
||||
} catch (InterruptedException e) {
|
||||
@@ -201,6 +227,7 @@ public final class BackendAgent implements ManagedAgent {
|
||||
}
|
||||
|
||||
private final class BackendTunnel {
|
||||
private final AgentConfig.FrontendEndpointConfig frontend;
|
||||
private final Socket socket;
|
||||
private final FrameCodec codec;
|
||||
private final AtomicBoolean open = new AtomicBoolean(true);
|
||||
@@ -210,7 +237,8 @@ public final class BackendAgent implements ManagedAgent {
|
||||
);
|
||||
private volatile long lastReadMillis = System.currentTimeMillis();
|
||||
|
||||
private BackendTunnel(Socket socket, FrameCodec codec) {
|
||||
private BackendTunnel(AgentConfig.FrontendEndpointConfig frontend, Socket socket, FrameCodec codec) {
|
||||
this.frontend = frontend;
|
||||
this.socket = socket;
|
||||
this.codec = codec;
|
||||
}
|
||||
@@ -236,13 +264,11 @@ public final class BackendAgent implements ManagedAgent {
|
||||
}
|
||||
} catch (Exception e) {
|
||||
if (open.get()) {
|
||||
LOGGER.log(Level.WARNING, "Backend tunnel read loop stopped", e);
|
||||
LOGGER.log(Level.WARNING, "Backend tunnel read loop stopped for frontend=" + frontend.name(), e);
|
||||
}
|
||||
} finally {
|
||||
close("frontend tunnel disconnected");
|
||||
if (activeTunnel == this) {
|
||||
activeTunnel = null;
|
||||
}
|
||||
activeTunnels.remove(frontend.name(), this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -266,8 +292,8 @@ public final class BackendAgent implements ManagedAgent {
|
||||
stream.closeSocket();
|
||||
}
|
||||
streams.clear();
|
||||
lastEvent.set("closed backend tunnel: " + reason);
|
||||
LOGGER.info(() -> "Closed backend tunnel reason=" + reason);
|
||||
lastEvent.set("closed backend tunnel to " + frontend.name() + ": " + reason);
|
||||
LOGGER.info(() -> "Closed backend tunnel to " + frontend.name() + " reason=" + reason);
|
||||
}
|
||||
|
||||
private void scheduleHeartbeat() {
|
||||
@@ -283,7 +309,7 @@ public final class BackendAgent implements ManagedAgent {
|
||||
return;
|
||||
}
|
||||
if (idleMillis > interval * 2) {
|
||||
lastEvent.set("heartbeat delayed " + idleMillis + "ms; tunnel still open");
|
||||
lastEvent.set("heartbeat delayed " + idleMillis + "ms for " + frontend.name() + "; tunnel still open");
|
||||
}
|
||||
try {
|
||||
codec.writeFrame(Frame.empty(FrameType.PING, 0));
|
||||
|
||||
@@ -40,7 +40,7 @@ public final class FrontendAgent implements ManagedAgent {
|
||||
new NamedThreadFactory("proxylink-frontend", true)
|
||||
);
|
||||
private final AtomicBoolean running = new AtomicBoolean(false);
|
||||
private final AtomicReference<FrontendTunnel> activeTunnel = new AtomicReference<>();
|
||||
private final Map<String, FrontendTunnel> activeTunnels = new ConcurrentHashMap<>();
|
||||
private final AtomicReference<String> lastEvent = new AtomicReference<>("not started");
|
||||
private final List<ServerSocket> routeServerSockets = new ArrayList<>();
|
||||
private volatile ServerSocket tunnelServerSocket;
|
||||
@@ -75,14 +75,21 @@ public final class FrontendAgent implements ManagedAgent {
|
||||
|
||||
@Override
|
||||
public TunnelStatus status() {
|
||||
FrontendTunnel tunnel = activeTunnel.get();
|
||||
boolean connected = running.get() && tunnel != null && tunnel.isOpen();
|
||||
int activeStreams = connected ? tunnel.streamCount() : 0;
|
||||
int connectedTunnels = 0;
|
||||
int activeStreams = 0;
|
||||
for (FrontendTunnel tunnel : activeTunnels.values()) {
|
||||
if (tunnel.isOpen()) {
|
||||
connectedTunnels++;
|
||||
activeStreams += tunnel.streamCount();
|
||||
}
|
||||
}
|
||||
boolean connected = running.get() && connectedTunnels > 0;
|
||||
return new TunnelStatus(
|
||||
config.role(),
|
||||
config.nodeName(),
|
||||
running.get(),
|
||||
connected,
|
||||
connectedTunnels,
|
||||
activeStreams,
|
||||
config.tls().enabled(),
|
||||
lastEvent.get()
|
||||
@@ -100,10 +107,10 @@ public final class FrontendAgent implements ManagedAgent {
|
||||
closeQuietly(serverSocket);
|
||||
}
|
||||
|
||||
FrontendTunnel tunnel = activeTunnel.getAndSet(null);
|
||||
if (tunnel != null) {
|
||||
for (FrontendTunnel tunnel : activeTunnels.values()) {
|
||||
tunnel.close("frontend shutting down");
|
||||
}
|
||||
activeTunnels.clear();
|
||||
|
||||
executor.shutdownNow();
|
||||
lastEvent.set("frontend stopped");
|
||||
@@ -130,7 +137,7 @@ public final class FrontendAgent implements ManagedAgent {
|
||||
FrameCodec codec = new FrameCodec(socket.getInputStream(), socket.getOutputStream());
|
||||
AuthPayloads.Hello hello = authenticateBackend(codec);
|
||||
FrontendTunnel tunnel = new FrontendTunnel(socket, codec, hello.nodeName());
|
||||
FrontendTunnel previous = activeTunnel.getAndSet(tunnel);
|
||||
FrontendTunnel previous = activeTunnels.put(hello.nodeName(), tunnel);
|
||||
if (previous != null) {
|
||||
previous.close("replaced by a new authenticated backend tunnel");
|
||||
}
|
||||
@@ -186,10 +193,10 @@ public final class FrontendAgent implements ManagedAgent {
|
||||
playerSocket.setTcpNoDelay(true);
|
||||
playerSocket.setKeepAlive(true);
|
||||
|
||||
FrontendTunnel tunnel = activeTunnel.get();
|
||||
FrontendTunnel tunnel = selectTunnelForRoute(route);
|
||||
if (tunnel == null || !tunnel.isOpen()) {
|
||||
LOGGER.warning(() -> "Rejecting player connection for route " + route.name()
|
||||
+ " because no backend tunnel is authenticated");
|
||||
+ " because no matching backend tunnel is authenticated");
|
||||
closeQuietly(playerSocket);
|
||||
continue;
|
||||
}
|
||||
@@ -203,8 +210,34 @@ public final class FrontendAgent implements ManagedAgent {
|
||||
}
|
||||
}
|
||||
|
||||
private FrontendTunnel selectTunnelForRoute(AgentConfig.RouteConfig route) {
|
||||
if (!route.backendNode().isBlank()) {
|
||||
return activeTunnels.get(route.backendNode());
|
||||
}
|
||||
|
||||
FrontendTunnel selected = null;
|
||||
int openTunnels = 0;
|
||||
for (FrontendTunnel tunnel : activeTunnels.values()) {
|
||||
if (!tunnel.isOpen()) {
|
||||
continue;
|
||||
}
|
||||
selected = tunnel;
|
||||
openTunnels++;
|
||||
}
|
||||
|
||||
if (openTunnels == 1) {
|
||||
return selected;
|
||||
}
|
||||
if (openTunnels > 1) {
|
||||
lastEvent.set("route " + route.name() + " is ambiguous; set route." + route.name() + ".backendNode");
|
||||
LOGGER.warning(() -> "Route " + route.name()
|
||||
+ " has no backendNode but multiple backend tunnels are connected");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void clearActiveTunnel(FrontendTunnel tunnel) {
|
||||
activeTunnel.compareAndSet(tunnel, null);
|
||||
activeTunnels.remove(tunnel.backendNodeName, tunnel);
|
||||
}
|
||||
|
||||
private static void closeQuietly(ServerSocket serverSocket) {
|
||||
|
||||
@@ -62,13 +62,27 @@ final class TlsSocketFactory {
|
||||
|
||||
static Socket createClientSocket(AgentConfig config, Consumer<String> learnedFingerprintConsumer)
|
||||
throws IOException, GeneralSecurityException {
|
||||
AgentConfig.FrontendEndpointConfig endpoint = new AgentConfig.FrontendEndpointConfig(
|
||||
"default",
|
||||
config.tunnel().connectHost(),
|
||||
config.tunnel().connectPort(),
|
||||
config.tls().pinnedCertificateSha256()
|
||||
);
|
||||
return createClientSocket(config, endpoint, learnedFingerprintConsumer);
|
||||
}
|
||||
|
||||
static Socket createClientSocket(
|
||||
AgentConfig config,
|
||||
AgentConfig.FrontendEndpointConfig endpoint,
|
||||
Consumer<String> learnedFingerprintConsumer
|
||||
) throws IOException, GeneralSecurityException {
|
||||
Socket socket;
|
||||
PinnedCertificateTrustManager pinningTrustManager = null;
|
||||
|
||||
if (!config.tls().enabled()) {
|
||||
socket = SocketFactory.getDefault().createSocket();
|
||||
} else {
|
||||
pinningTrustManager = pinningTrustManager(config);
|
||||
pinningTrustManager = pinningTrustManager(config, endpoint);
|
||||
SSLContext context = sslContext(
|
||||
config.tls().keyStorePath(),
|
||||
config.tls().keyStorePassword(),
|
||||
@@ -82,7 +96,7 @@ final class TlsSocketFactory {
|
||||
socket.setTcpNoDelay(true);
|
||||
socket.setKeepAlive(true);
|
||||
socket.connect(
|
||||
new InetSocketAddress(config.tunnel().connectHost(), config.tunnel().connectPort()),
|
||||
new InetSocketAddress(endpoint.connectHost(), endpoint.connectPort()),
|
||||
config.tunnel().connectTimeoutMillis()
|
||||
);
|
||||
|
||||
@@ -117,10 +131,16 @@ final class TlsSocketFactory {
|
||||
return context;
|
||||
}
|
||||
|
||||
private static PinnedCertificateTrustManager pinningTrustManager(AgentConfig config) {
|
||||
if (!config.tls().pinnedCertificateSha256().isBlank() || config.tls().trustOnFirstUse()) {
|
||||
private static PinnedCertificateTrustManager pinningTrustManager(
|
||||
AgentConfig config,
|
||||
AgentConfig.FrontendEndpointConfig endpoint
|
||||
) {
|
||||
String pinnedCertificate = endpoint.pinnedCertificateSha256().isBlank()
|
||||
? config.tls().pinnedCertificateSha256()
|
||||
: endpoint.pinnedCertificateSha256();
|
||||
if (!pinnedCertificate.isBlank() || config.tls().trustOnFirstUse()) {
|
||||
return new PinnedCertificateTrustManager(
|
||||
config.tls().pinnedCertificateSha256(),
|
||||
pinnedCertificate,
|
||||
config.tls().trustOnFirstUse()
|
||||
);
|
||||
}
|
||||
|
||||
@@ -7,11 +7,12 @@ public record TunnelStatus(
|
||||
String nodeName,
|
||||
boolean running,
|
||||
boolean tunnelConnected,
|
||||
int connectedTunnels,
|
||||
int activeStreams,
|
||||
boolean tlsEnabled,
|
||||
String lastEvent
|
||||
) {
|
||||
public static TunnelStatus stopped(AgentConfig.Role role, String nodeName, boolean tlsEnabled, String lastEvent) {
|
||||
return new TunnelStatus(role, nodeName, false, false, 0, tlsEnabled, lastEvent);
|
||||
return new TunnelStatus(role, nodeName, false, false, 0, 0, tlsEnabled, lastEvent);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
# DirtSimpleP2P Bungee/Waterfall frontend config.
|
||||
# Put this same jar in the Bungee plugins folder.
|
||||
# Bungee should point its backend server entry at route.minecraft.frontendBindHost:route.minecraft.frontendBindPort.
|
||||
# Add more routes when this proxy should reach multiple backend servers.
|
||||
|
||||
role=frontend
|
||||
node.name=bungee-frontend
|
||||
@@ -31,3 +32,13 @@ tunnel.tls.requireClientAuth=false
|
||||
routes=minecraft
|
||||
route.minecraft.frontendBindHost=127.0.0.1
|
||||
route.minecraft.frontendBindPort=25566
|
||||
route.minecraft.backendNode=paper-backend
|
||||
|
||||
# Multi-backend example:
|
||||
# routes=minecraft,lobby
|
||||
# route.minecraft.frontendBindHost=127.0.0.1
|
||||
# route.minecraft.frontendBindPort=25566
|
||||
# route.minecraft.backendNode=survival-backend
|
||||
# route.lobby.frontendBindHost=127.0.0.1
|
||||
# route.lobby.frontendBindPort=25567
|
||||
# route.lobby.backendNode=lobby-backend
|
||||
|
||||
@@ -5,8 +5,20 @@
|
||||
role=backend
|
||||
node.name=paper-backend
|
||||
|
||||
tunnel.connectHost=YOUR_BUNGEE_OR_VPS_IP
|
||||
tunnel.connectPort=24445
|
||||
frontends=proxy1
|
||||
frontend.proxy1.connectHost=YOUR_BUNGEE_OR_VPS_IP
|
||||
frontend.proxy1.connectPort=24445
|
||||
frontend.proxy1.tls.pinnedCertificateSha256=
|
||||
|
||||
# Multi-proxy example:
|
||||
# frontends=proxy1,proxy2
|
||||
# frontend.proxy1.connectHost=proxy1.example.com
|
||||
# frontend.proxy1.connectPort=24445
|
||||
# frontend.proxy1.tls.pinnedCertificateSha256=
|
||||
# frontend.proxy2.connectHost=proxy2.example.com
|
||||
# frontend.proxy2.connectPort=24445
|
||||
# frontend.proxy2.tls.pinnedCertificateSha256=
|
||||
|
||||
# Replaced automatically on first start. Paste the same value from the Bungee config here.
|
||||
tunnel.authToken=AUTO_GENERATED_ON_FIRST_START
|
||||
|
||||
@@ -24,7 +36,6 @@ tunnel.reconnectMaxMillis=10000
|
||||
tunnel.tls.enabled=false
|
||||
tunnel.tls.allowInsecure=true
|
||||
tunnel.tls.trustOnFirstUse=true
|
||||
tunnel.tls.pinnedCertificateSha256=
|
||||
|
||||
routes=minecraft
|
||||
route.minecraft.backendTargetHost=127.0.0.1
|
||||
|
||||
Binary file not shown.
@@ -1,6 +1,7 @@
|
||||
# DirtSimpleP2P Bungee/Waterfall frontend config.
|
||||
# Put this same jar in the Bungee plugins folder.
|
||||
# Bungee should point its backend server entry at route.minecraft.frontendBindHost:route.minecraft.frontendBindPort.
|
||||
# Add more routes when this proxy should reach multiple backend servers.
|
||||
|
||||
role=frontend
|
||||
node.name=bungee-frontend
|
||||
@@ -31,3 +32,13 @@ tunnel.tls.requireClientAuth=false
|
||||
routes=minecraft
|
||||
route.minecraft.frontendBindHost=127.0.0.1
|
||||
route.minecraft.frontendBindPort=25566
|
||||
route.minecraft.backendNode=paper-backend
|
||||
|
||||
# Multi-backend example:
|
||||
# routes=minecraft,lobby
|
||||
# route.minecraft.frontendBindHost=127.0.0.1
|
||||
# route.minecraft.frontendBindPort=25566
|
||||
# route.minecraft.backendNode=survival-backend
|
||||
# route.lobby.frontendBindHost=127.0.0.1
|
||||
# route.lobby.frontendBindPort=25567
|
||||
# route.lobby.backendNode=lobby-backend
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -5,8 +5,20 @@
|
||||
role=backend
|
||||
node.name=paper-backend
|
||||
|
||||
tunnel.connectHost=YOUR_BUNGEE_OR_VPS_IP
|
||||
tunnel.connectPort=24445
|
||||
frontends=proxy1
|
||||
frontend.proxy1.connectHost=YOUR_BUNGEE_OR_VPS_IP
|
||||
frontend.proxy1.connectPort=24445
|
||||
frontend.proxy1.tls.pinnedCertificateSha256=
|
||||
|
||||
# Multi-proxy example:
|
||||
# frontends=proxy1,proxy2
|
||||
# frontend.proxy1.connectHost=proxy1.example.com
|
||||
# frontend.proxy1.connectPort=24445
|
||||
# frontend.proxy1.tls.pinnedCertificateSha256=
|
||||
# frontend.proxy2.connectHost=proxy2.example.com
|
||||
# frontend.proxy2.connectPort=24445
|
||||
# frontend.proxy2.tls.pinnedCertificateSha256=
|
||||
|
||||
# Replaced automatically on first start. Paste the same value from the Bungee config here.
|
||||
tunnel.authToken=AUTO_GENERATED_ON_FIRST_START
|
||||
|
||||
@@ -24,7 +36,6 @@ tunnel.reconnectMaxMillis=10000
|
||||
tunnel.tls.enabled=false
|
||||
tunnel.tls.allowInsecure=true
|
||||
tunnel.tls.trustOnFirstUse=true
|
||||
tunnel.tls.pinnedCertificateSha256=
|
||||
|
||||
routes=minecraft
|
||||
route.minecraft.backendTargetHost=127.0.0.1
|
||||
|
||||
Binary file not shown.
Reference in New Issue
Block a user