Skip to content

Commit

Permalink
Feature/tls ports (#591)
Browse files Browse the repository at this point in the history
  • Loading branch information
lukefallows authored Jan 3, 2024
1 parent 0dfdab8 commit a83d746
Show file tree
Hide file tree
Showing 27 changed files with 691 additions and 23 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ public final class TlsConditionConfig extends ConditionConfig
{
public final String authority;
public final String alpn;
public final int[] ports;

public static TlsConditionConfigBuilder<TlsConditionConfig> builder()
{
Expand All @@ -37,9 +38,11 @@ public static <T> TlsConditionConfigBuilder<T> builder(

TlsConditionConfig(
String authority,
String alpn)
String alpn,
int[] ports)
{
this.authority = authority;
this.alpn = alpn;
this.ports = ports;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ public final class TlsConditionConfigBuilder<T> extends ConfigBuilder<T, TlsCond

private String authority;
private String alpn;
private int[] ports;

TlsConditionConfigBuilder(
Function<ConditionConfig, T> mapper)
Expand Down Expand Up @@ -54,9 +55,16 @@ public TlsConditionConfigBuilder<T> alpn(
return this;
}

public TlsConditionConfigBuilder<T> ports(
int[] ports)
{
this.ports = ports;
return this;
}

@Override
public T build()
{
return mapper.apply(new TlsConditionConfig(authority, alpn));
return mapper.apply(new TlsConditionConfig(authority, alpn, ports));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
import io.aklivity.zilla.runtime.binding.tls.internal.TlsConfiguration;
import io.aklivity.zilla.runtime.binding.tls.internal.identity.TlsClientX509ExtendedKeyManager;
import io.aklivity.zilla.runtime.binding.tls.internal.types.Array32FW;
import io.aklivity.zilla.runtime.binding.tls.internal.types.ProxyAddressFW;
import io.aklivity.zilla.runtime.binding.tls.internal.types.ProxyInfoFW;
import io.aklivity.zilla.runtime.binding.tls.internal.types.stream.ProxyBeginExFW;
import io.aklivity.zilla.runtime.engine.config.BindingConfig;
Expand Down Expand Up @@ -145,20 +146,62 @@ public TlsRouteConfig resolve(
{
Array32FW<ProxyInfoFW> infos = beginEx != null ? beginEx.infos() : null;
ProxyInfoFW authorityInfo = infos != null ? infos.matchFirst(a -> a.kind() == AUTHORITY) : null;
ProxyInfoFW alpnInfo = infos != null ? infos.matchFirst(a -> a.kind() == ALPN) : null;
String authority = authorityInfo != null ? authorityInfo.authority().asString() : null;

ProxyInfoFW alpnInfo = infos != null ? infos.matchFirst(a -> a.kind() == ALPN) : null;
String alpn = alpnInfo != null ? alpnInfo.alpn().asString() : null;

return resolve(authorization, authority, alpn);
int port = resolveDestinationPort(beginEx);

return resolve(authorization, authority, alpn, port);
}

public TlsRouteConfig resolvePortOnly(
long authorization,
int port)
{
return routes.stream()
.filter(r -> r.authorized(authorization) && r.matchesPortOnly(port))
.findFirst()
.orElse(null);
}

public static int resolveDestinationPort(
ProxyBeginExFW beginEx)
{
int port = 0;

if (beginEx != null)
{
ProxyAddressFW address = beginEx.address();

switch (address.kind())
{
case INET:
port = address.inet().destinationPort();
break;
case INET4:
port = address.inet4().destinationPort();
break;
case INET6:
port = address.inet6().destinationPort();
break;
default:
break;
}
}

return port;
}

public TlsRouteConfig resolve(
long authorization,
String hostname,
String alpn)
String alpn,
int port)
{
return routes.stream()
.filter(r -> r.authorized(authorization) && r.matches(hostname, alpn))
.filter(r -> r.authorized(authorization) && r.matches(hostname, alpn, port))
.findFirst()
.orElse(null);
}
Expand Down Expand Up @@ -237,7 +280,8 @@ public SSLEngine newClientEngine(
}

public SSLEngine newServerEngine(
long authorization)
long authorization,
int port)
{
SSLEngine engine = null;

Expand All @@ -261,7 +305,7 @@ public SSLEngine newServerEngine(
break;
}

engine.setHandshakeApplicationProtocolSelector((ngin, alpns) -> selectAlpn(ngin, alpns, authorization));
engine.setHandshakeApplicationProtocolSelector((ngin, alpns) -> selectAlpn(ngin, alpns, authorization, port));
}

return engine;
Expand All @@ -270,7 +314,8 @@ public SSLEngine newServerEngine(
private String selectAlpn(
SSLEngine engine,
List<String> protocols,
long authorization)
long authorization,
int port)
{
List<SNIServerName> serverNames = null;

Expand Down Expand Up @@ -319,7 +364,7 @@ private String selectAlpn(
}

if (route.authorized(authorization) &&
route.matches(authority, protocol))
route.matches(authority, protocol, port))
{
selected = protocol;
break;
Expand All @@ -341,7 +386,7 @@ private String selectAlpn(
}

if (route.authorized(authorization) &&
route.matches(null, protocol))
route.matches(null, protocol, port))
{
selected = protocol;
break;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,21 @@
*/
package io.aklivity.zilla.runtime.binding.tls.internal.config;

import java.util.stream.IntStream;

import jakarta.json.Json;
import jakarta.json.JsonArray;
import jakarta.json.JsonArrayBuilder;
import jakarta.json.JsonNumber;
import jakarta.json.JsonObject;
import jakarta.json.JsonObjectBuilder;
import jakarta.json.JsonString;
import jakarta.json.JsonValue;
import jakarta.json.bind.adapter.JsonbAdapter;

import org.agrona.collections.IntHashSet;
import org.agrona.collections.MutableInteger;

import io.aklivity.zilla.runtime.binding.tls.config.TlsConditionConfig;
import io.aklivity.zilla.runtime.binding.tls.config.TlsConditionConfigBuilder;
import io.aklivity.zilla.runtime.binding.tls.internal.TlsBinding;
Expand All @@ -30,6 +40,7 @@ public final class TlsConditionConfigAdapter implements ConditionConfigAdapterSp
{
private static final String AUTHORITY_NAME = "authority";
private static final String ALPN_NAME = "alpn";
private static final String PORT_NAME = "port";

@Override
public String type()
Expand All @@ -55,6 +66,24 @@ public JsonObject adaptToJson(
object.add(ALPN_NAME, tlsCondition.alpn);
}

if (tlsCondition.ports != null)
{
if (tlsCondition.ports.length == 1)
{
object.add(PORT_NAME, tlsCondition.ports[0]);
}
else
{
JsonArrayBuilder ports = Json.createArrayBuilder();
for (int port : tlsCondition.ports)
{
ports.add(port);
}

object.add(PORT_NAME, ports);
}
}

return object.build();
}

Expand All @@ -74,6 +103,61 @@ public ConditionConfig adaptFromJson(
tlsCondition.alpn(object.getString(ALPN_NAME));
}

if (object.containsKey(PORT_NAME))
{
JsonValue portsValue = object.get(PORT_NAME);

IntHashSet portsSet = new IntHashSet();
switch (portsValue.getValueType())
{
case ARRAY:
JsonArray portsArray = portsValue.asJsonArray();
portsArray.forEach(value -> adaptPortsValueFromJson(value, portsSet));
break;
default:
adaptPortsValueFromJson(portsValue, portsSet);
break;
}

int[] ports = new int[portsSet.size()];
MutableInteger index = new MutableInteger();
portsSet.forEach(i -> ports[index.value++] = i);

tlsCondition.ports(ports);
}

return tlsCondition.build();
}

private static void adaptPortsValueFromJson(
JsonValue value,
IntHashSet ports)
{
switch (value.getValueType())
{
case STRING:
{
String port = ((JsonString) value).getString();
int dashAt = port.indexOf('-');
if (dashAt != -1)
{
int portRangeLow = Integer.parseInt(port.substring(0, dashAt));
int portRangeHigh = Integer.parseInt(port.substring(dashAt + 1));
IntStream.range(portRangeLow, portRangeHigh + 1).forEach(ports::add);
}
else
{
ports.add(Integer.parseInt(port));
}
break;
}
case NUMBER:
default:
{
int port = ((JsonNumber) value).intValue();
ports.add(port);
break;
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,29 +15,42 @@
*/
package io.aklivity.zilla.runtime.binding.tls.internal.config;

import java.util.Arrays;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.agrona.collections.IntHashSet;

import io.aklivity.zilla.runtime.binding.tls.config.TlsConditionConfig;

public final class TlsConditionMatcher
{
public final Matcher authorityMatch;
public final Matcher alpnMatch;
public final IntHashSet ports;

public TlsConditionMatcher(
TlsConditionConfig condition)
{
this.authorityMatch = condition.authority != null ? asMatcher(condition.authority) : null;
this.alpnMatch = condition.alpn != null ? asMatcher(condition.alpn) : null;
this.ports = condition.ports != null ? asIntHashSet(condition.ports) : null;
}

public boolean matches(
String authority,
String alpn)
String alpn,
int port)
{
return matchesAuthority(authority) &&
matchesAlpn(alpn);
matchesAlpn(alpn) &&
matchesPort(port);
}

public boolean matchesPortOnly(
int port)
{
return matchesPort(port);
}

private boolean matchesAuthority(
Expand All @@ -52,9 +65,23 @@ private boolean matchesAlpn(
return alpnMatch == null || alpn != null && alpnMatch.reset(alpn).matches();
}

private boolean matchesPort(
int port)
{
return ports == null || ports.contains(port);
}

private static Matcher asMatcher(
String wildcard)
{
return Pattern.compile(wildcard.replace(".", "\\.").replace("*", ".*")).matcher("");
}

private static IntHashSet asIntHashSet(
int[] ports)
{
IntHashSet set = new IntHashSet(ports.length);
Arrays.stream(ports).forEach(set::add);
return set;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,15 @@ boolean authorized(

boolean matches(
String hostname,
String alpn)
String alpn,
int port)
{
return when.isEmpty() || when.stream().anyMatch(m -> m.matches(hostname, alpn));
return when.isEmpty() || when.stream().anyMatch(m -> m.matches(hostname, alpn, port));
}

boolean matchesPortOnly(
int port)
{
return when.isEmpty() || when.stream().anyMatch(m -> m.matchesPortOnly(port));
}
}
Loading

0 comments on commit a83d746

Please sign in to comment.