Added multi server support
This commit is contained in:
@@ -2,9 +2,11 @@ package me.proxylink.common;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public record AgentConfig(
|
||||
@@ -12,6 +14,7 @@ public record AgentConfig(
|
||||
String nodeName,
|
||||
TunnelConfig tunnel,
|
||||
TlsConfig tls,
|
||||
List<FrontendEndpointConfig> frontends,
|
||||
List<RouteConfig> routes
|
||||
) {
|
||||
private static final Pattern ROUTE_NAME = Pattern.compile("[A-Za-z0-9._-]+");
|
||||
@@ -21,6 +24,7 @@ public record AgentConfig(
|
||||
nodeName = requireNonBlank(nodeName, "nodeName");
|
||||
Objects.requireNonNull(tunnel, "tunnel");
|
||||
Objects.requireNonNull(tls, "tls");
|
||||
frontends = List.copyOf(Objects.requireNonNull(frontends, "frontends"));
|
||||
routes = List.copyOf(Objects.requireNonNull(routes, "routes"));
|
||||
}
|
||||
|
||||
@@ -51,10 +55,27 @@ public record AgentConfig(
|
||||
}
|
||||
|
||||
if (role == Role.BACKEND) {
|
||||
validateHostPort(errors, "tunnel.connect", tunnel.connectHost(), tunnel.connectPort());
|
||||
if (frontends.isEmpty()) {
|
||||
errors.add("At least one frontend endpoint is required for backend mode");
|
||||
}
|
||||
Set<String> frontendNames = new HashSet<>();
|
||||
for (FrontendEndpointConfig frontend : frontends) {
|
||||
if (!ROUTE_NAME.matcher(frontend.name()).matches()) {
|
||||
errors.add("Frontend endpoint name contains invalid characters: " + frontend.name());
|
||||
}
|
||||
if (!frontendNames.add(frontend.name())) {
|
||||
errors.add("Duplicate frontend endpoint name: " + frontend.name());
|
||||
}
|
||||
validateHostPort(errors, "frontend." + frontend.name() + ".connect", frontend.connectHost(), frontend.connectPort());
|
||||
}
|
||||
if (tls.enabled()) {
|
||||
if (tls.trustStorePath() == null && tls.pinnedCertificateSha256().isBlank() && !tls.trustOnFirstUse()) {
|
||||
errors.add("TLS is enabled on backend, so set tunnel.tls.pinnedCertificateSha256, set tunnel.tls.trustOnFirstUse=true, or configure a truststore");
|
||||
if (tls.trustStorePath() == null && !tls.trustOnFirstUse()) {
|
||||
for (FrontendEndpointConfig frontend : frontends) {
|
||||
if (frontend.pinnedCertificateSha256().isBlank() && tls.pinnedCertificateSha256().isBlank()) {
|
||||
errors.add("TLS is enabled on backend, so set frontend." + frontend.name()
|
||||
+ ".tls.pinnedCertificateSha256, set tunnel.tls.trustOnFirstUse=true, or configure a truststore");
|
||||
}
|
||||
}
|
||||
}
|
||||
if (tls.trustStorePath() != null) {
|
||||
requireSecret(errors, "tunnel.tls.trustStorePassword", tls.trustStorePassword());
|
||||
@@ -85,12 +106,24 @@ public record AgentConfig(
|
||||
errors.add("tunnel.reconnectMaxMillis must be greater than or equal to tunnel.reconnectInitialMillis");
|
||||
}
|
||||
|
||||
Set<String> routeNames = new HashSet<>();
|
||||
Set<String> frontendBinds = new HashSet<>();
|
||||
for (RouteConfig route : routes) {
|
||||
if (!ROUTE_NAME.matcher(route.name()).matches()) {
|
||||
errors.add("Route name contains invalid characters: " + route.name());
|
||||
}
|
||||
if (!routeNames.add(route.name())) {
|
||||
errors.add("Duplicate route name: " + route.name());
|
||||
}
|
||||
if (role == Role.FRONTEND) {
|
||||
validateHostPort(errors, "route." + route.name() + ".frontendBind", route.frontendBindHost(), route.frontendBindPort());
|
||||
String bindKey = route.frontendBindHost() + ":" + route.frontendBindPort();
|
||||
if (!frontendBinds.add(bindKey)) {
|
||||
errors.add("Duplicate frontend bind address: " + bindKey);
|
||||
}
|
||||
if (!route.backendNode().isBlank() && !ROUTE_NAME.matcher(route.backendNode()).matches()) {
|
||||
errors.add("Route " + route.name() + " has an invalid backendNode: " + route.backendNode());
|
||||
}
|
||||
}
|
||||
if (role == Role.BACKEND) {
|
||||
validateHostPort(errors, "route." + route.name() + ".backendTarget", route.backendTargetHost(), route.backendTargetPort());
|
||||
@@ -192,15 +225,30 @@ public record AgentConfig(
|
||||
}
|
||||
}
|
||||
|
||||
public record FrontendEndpointConfig(
|
||||
String name,
|
||||
String connectHost,
|
||||
int connectPort,
|
||||
String pinnedCertificateSha256
|
||||
) {
|
||||
public FrontendEndpointConfig {
|
||||
name = requireNonBlank(name, "name");
|
||||
connectHost = requireNonBlank(connectHost, "connectHost");
|
||||
pinnedCertificateSha256 = pinnedCertificateSha256 == null ? "" : pinnedCertificateSha256.trim();
|
||||
}
|
||||
}
|
||||
|
||||
public record RouteConfig(
|
||||
String name,
|
||||
String frontendBindHost,
|
||||
int frontendBindPort,
|
||||
String backendNode,
|
||||
String backendTargetHost,
|
||||
int backendTargetPort
|
||||
) {
|
||||
public RouteConfig {
|
||||
name = requireNonBlank(name, "name");
|
||||
backendNode = backendNode == null ? "" : backendNode.trim();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,7 +56,43 @@ public final class AgentConfigIO {
|
||||
value(properties, "tunnel.tls.pinnedCertificateSha256", "")
|
||||
);
|
||||
|
||||
return new AgentConfig(role, nodeName, tunnel, tls, routes(properties));
|
||||
return new AgentConfig(role, nodeName, tunnel, tls, frontends(properties, tunnel, tls), routes(properties));
|
||||
}
|
||||
|
||||
private static List<AgentConfig.FrontendEndpointConfig> frontends(
|
||||
Properties properties,
|
||||
AgentConfig.TunnelConfig tunnel,
|
||||
AgentConfig.TlsConfig tls
|
||||
) {
|
||||
String frontendsValue = value(properties, "frontends", "");
|
||||
List<AgentConfig.FrontendEndpointConfig> frontends = new ArrayList<>();
|
||||
|
||||
if (frontendsValue.isBlank()) {
|
||||
if (!tunnel.connectHost().isBlank() && tunnel.connectPort() > 0) {
|
||||
frontends.add(new AgentConfig.FrontendEndpointConfig(
|
||||
"default",
|
||||
tunnel.connectHost(),
|
||||
tunnel.connectPort(),
|
||||
tls.pinnedCertificateSha256()
|
||||
));
|
||||
}
|
||||
return frontends;
|
||||
}
|
||||
|
||||
for (String rawName : frontendsValue.split(",")) {
|
||||
String name = rawName.trim();
|
||||
if (name.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
String prefix = "frontend." + name + ".";
|
||||
frontends.add(new AgentConfig.FrontendEndpointConfig(
|
||||
name,
|
||||
required(properties, prefix + "connectHost"),
|
||||
intValue(properties, prefix + "connectPort", -1),
|
||||
value(properties, prefix + "tls.pinnedCertificateSha256", "")
|
||||
));
|
||||
}
|
||||
return frontends;
|
||||
}
|
||||
|
||||
private static List<AgentConfig.RouteConfig> routes(Properties properties) {
|
||||
@@ -72,6 +108,7 @@ public final class AgentConfigIO {
|
||||
name,
|
||||
value(properties, prefix + "frontendBindHost", "127.0.0.1"),
|
||||
intValue(properties, prefix + "frontendBindPort", -1),
|
||||
value(properties, prefix + "backendNode", ""),
|
||||
value(properties, prefix + "backendTargetHost", "127.0.0.1"),
|
||||
intValue(properties, prefix + "backendTargetPort", -1)
|
||||
));
|
||||
|
||||
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.
+1
@@ -3,6 +3,7 @@ me/proxylink/common/AgentConfig.class
|
||||
me/proxylink/common/AgentConfigIO.class
|
||||
me/proxylink/common/AuthPayloads$Response.class
|
||||
me/proxylink/common/FrameType.class
|
||||
me/proxylink/common/AgentConfig$FrontendEndpointConfig.class
|
||||
me/proxylink/common/AuthPayloads$Challenge.class
|
||||
me/proxylink/common/ProtocolConstants.class
|
||||
me/proxylink/common/FrameCodec.class
|
||||
|
||||
Binary file not shown.
Reference in New Issue
Block a user