Skip to content

Commit

Permalink
* Refactored DNS resolver (#14)
Browse files Browse the repository at this point in the history
* Now returning default certificate if hostname can't be matched w/
  certificate from keystore to avoid breaking changes
  • Loading branch information
cmkrafft authored Feb 29, 2024
1 parent e310619 commit 1d31bf1
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 34 deletions.
9 changes: 5 additions & 4 deletions src/main/java/com/hivemq/security/ssl/DnsResolver.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
package com.hivemq.security.ssl;

import java.util.Map;
import java.util.Optional;

public class DnsResolver {

Expand All @@ -25,23 +26,23 @@ public class DnsResolver {
this.dnsMap = dnsMap;
}

String resolve(final String domain) {
Optional<String> resolve(final String domain) {
String alias = dnsMap.get(domain);
if (alias != null) {
return alias;
return Optional.of(alias);
}

int index = domain.indexOf('.');
while (index >= 0) {
final String wildcardDomain = "*" + domain.substring(index);
alias = dnsMap.get(wildcardDomain);
if (alias != null) {
return alias;
return Optional.of(alias);
}
index = domain.indexOf('.', index + 1);
}

return null;
return Optional.empty();
}

}
20 changes: 15 additions & 5 deletions src/main/java/com/hivemq/security/ssl/SslContextFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

Expand Down Expand Up @@ -115,15 +116,24 @@ public String chooseEngineServerAlias(
final String keyType, final Principal[] issuers, final SSLEngine engine) {
final String hostname = engine.getPeerHost();
final String certificateAlias;

if (hostname == null) {
// Without SNI activated the hostname is null, so we use the default alias
certificateAlias = tls.getDefaultKeystoreAlias();
log.debug("No SNI hostname given, using default alias: {}", certificateAlias);
} else {
certificateAlias = dnsResolver.resolve(hostname);
final Optional<String> optionalHostname = dnsResolver.resolve(hostname);

if (optionalHostname.isEmpty()) {
certificateAlias = tls.getDefaultKeystoreAlias();
log.debug("No certificate found for hostname: {}, defaulting to alias: {}", hostname, certificateAlias);
} else {
certificateAlias = optionalHostname.get();
}
}
log.trace("Choose engine server alias for host: {} found alias: {}", hostname,
certificateAlias);

log.trace("Choose engine server alias for host: {} found alias: {}", hostname, certificateAlias);

return certificateAlias;
}
};
Expand Down Expand Up @@ -206,8 +216,8 @@ private static Map<String, String> createDnsHostnameMap(
}
}

log.info("Parsed hostNames: {}",
dnsHostnameMap.isEmpty() ? "no hostnames available" : String.join(", ", dnsHostnameMap.values()));
log.info("Parsed hostnames: {}",
dnsHostnameMap.isEmpty() ? "no hostnames available" : String.join(", ", dnsHostnameMap.keySet()));

return dnsHostnameMap;
}
Expand Down
63 changes: 38 additions & 25 deletions src/test/java/com/hivemq/security/ssl/DnsResolverTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import org.junit.Test;

import java.util.Map;
import java.util.Optional;

import static org.junit.Assert.*;

Expand All @@ -35,72 +36,84 @@ public void test_resolve_simple_dns_name() {
final DnsResolver dnsResolver =
new DnsResolver(Map.of(TEST_EXAMPLE_COM, ALIAS_1, TEST_OTHER_COM, ALIAS_2, TEST_FOO_BAR, ALIAS_3));

final String resolve = dnsResolver.resolve(TEST_EXAMPLE_COM);
final Optional<String> resolvedHostname = dnsResolver.resolve(TEST_EXAMPLE_COM);

assertNotNull(resolve);
assertEquals(ALIAS_1, resolve);
assertTrue(resolvedHostname.isPresent());
assertEquals(ALIAS_1, resolvedHostname.get());
}

@Test
public void test_resolve_non_matching_dns_name() {
final DnsResolver dnsResolver =
new DnsResolver(Map.of(TEST_EXAMPLE_COM, ALIAS_1, TEST_OTHER_COM, ALIAS_2, TEST_FOO_BAR, ALIAS_3));

final String resolve = dnsResolver.resolve("other.example.com");
final Optional<String> resolvedHostname = dnsResolver.resolve("other.example.com");

assertNull(resolve);
assertTrue(resolvedHostname.isEmpty());
}

@Test
public void test_resolve_wildcard_dns_name() {
final DnsResolver dnsResolver =
new DnsResolver(Map.of(WILDCARD_EXAMPLE_COM, ALIAS_1, TEST_OTHER_COM, ALIAS_2, TEST_FOO_BAR, ALIAS_3));

final String resolve = dnsResolver.resolve(TEST_EXAMPLE_COM);
final Optional<String> resolvedHostname = dnsResolver.resolve(TEST_EXAMPLE_COM);

assertNotNull(resolve);
assertEquals(ALIAS_1, resolve);
assertTrue(resolvedHostname.isPresent());
assertEquals(ALIAS_1, resolvedHostname.get());
}

@Test
public void test_resolve_nested_wildcard_dns_name() {
final DnsResolver dnsResolver =
new DnsResolver(Map.of(WILDCARD_EXAMPLE_COM, ALIAS_1, "test.example.com", ALIAS_2, TEST_FOO_BAR, ALIAS_3));
final DnsResolver dnsResolver = new DnsResolver(Map.of(WILDCARD_EXAMPLE_COM,
ALIAS_1,
"test.example.com",
ALIAS_2,
TEST_FOO_BAR,
ALIAS_3));

final String resolve = dnsResolver.resolve("sub.test.example.com");
final Optional<String> resolvedHostname = dnsResolver.resolve("sub.test.example.com");

assertNotNull(resolve);
assertEquals(ALIAS_1, resolve);
assertTrue(resolvedHostname.isPresent());
assertEquals(ALIAS_1, resolvedHostname.get());
}

@Test
public void test_resolve_non_matching_wildcard_dns_name() {
final DnsResolver dnsResolver = new DnsResolver(Map.of("*.other.com", ALIAS_2, TEST_FOO_BAR, ALIAS_3));

final String resolve = dnsResolver.resolve("test.example.com");
final Optional<String> resolvedHostname = dnsResolver.resolve("test.example.com");

assertNull(resolve);
assertTrue(resolvedHostname.isEmpty());
}

@Test
public void test_prefer_full_cert_before_wildcard_cert() {
final DnsResolver dnsResolver =
new DnsResolver(Map.of("test.example.com", ALIAS_1, WILDCARD_EXAMPLE_COM, ALIAS_2, TEST_FOO_BAR, ALIAS_3));
final DnsResolver dnsResolver = new DnsResolver(Map.of("test.example.com",
ALIAS_1,
WILDCARD_EXAMPLE_COM,
ALIAS_2,
TEST_FOO_BAR,
ALIAS_3));

final String resolve = dnsResolver.resolve("test.example.com");
final Optional<String> resolvedHostname = dnsResolver.resolve("test.example.com");

assertNotNull(resolve);
assertEquals(ALIAS_1, resolve);
assertTrue(resolvedHostname.isPresent());
assertEquals(ALIAS_1, resolvedHostname.get());
}

@Test
public void test_prefer_best_matching_wildcard_cert() {
final DnsResolver dnsResolver =
new DnsResolver(Map.of(WILDCARD_EXAMPLE_COM, ALIAS_1, "*.test.example.com", ALIAS_2, TEST_FOO_BAR, ALIAS_3));
final DnsResolver dnsResolver = new DnsResolver(Map.of(WILDCARD_EXAMPLE_COM,
ALIAS_1,
"*.test.example.com",
ALIAS_2,
TEST_FOO_BAR,
ALIAS_3));

final String resolve = dnsResolver.resolve("sub.test.example.com");
final Optional<String> resolvedHostname = dnsResolver.resolve("sub.test.example.com");

assertNotNull(resolve);
assertEquals(ALIAS_2, resolve);
assertTrue(resolvedHostname.isPresent());
assertEquals(ALIAS_2, resolvedHostname.get());
}
}

0 comments on commit 1d31bf1

Please sign in to comment.