Skip to content

Commit 51166dd

Browse files
committed
Resolve trusted proxy host names to all available A/AAAA records
Closes #42782 Signed-off-by: Alexander Schwartz <[email protected]>
1 parent 54ec3c7 commit 51166dd

File tree

2 files changed

+108
-43
lines changed

2 files changed

+108
-43
lines changed

extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/ForwardedProxyHandler.java

Lines changed: 78 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,28 @@
22

33
import static io.quarkus.vertx.http.runtime.TrustedProxyCheck.denyAll;
44

5+
import java.net.Inet4Address;
6+
import java.net.Inet6Address;
57
import java.net.InetAddress;
8+
import java.util.ArrayList;
9+
import java.util.Collection;
610
import java.util.Iterator;
11+
import java.util.List;
712
import java.util.Map;
813
import java.util.Objects;
14+
import java.util.Set;
915
import java.util.function.Supplier;
16+
import java.util.stream.Collectors;
1017

1118
import org.jboss.logging.Logger;
1219

1320
import io.smallrye.common.net.Inet;
14-
import io.vertx.core.AsyncResult;
21+
import io.vertx.core.Future;
1522
import io.vertx.core.Handler;
1623
import io.vertx.core.Vertx;
1724
import io.vertx.core.dns.DnsClient;
1825
import io.vertx.core.http.HttpServerRequest;
26+
import io.vertx.core.net.SocketAddress;
1927
import io.vertx.core.net.impl.SocketAddressImpl;
2028

2129
/**
@@ -75,30 +83,28 @@ private void lookupHostNamesAndHandleRequest(HttpServerRequest event,
7583
// we do not cache result as IP address may change, and we advise users to use IP or CIDR
7684
final var entry = iterator.next();
7785
final String hostName = entry.getKey();
78-
dnsClient.lookup(hostName,
79-
new Handler<AsyncResult<String>>() {
80-
@Override
81-
public void handle(AsyncResult<String> stringAsyncResult) {
82-
if (stringAsyncResult.succeeded() && stringAsyncResult.result() != null) {
83-
var trustedIP = Inet.parseInetAddress(stringAsyncResult.result());
84-
if (trustedIP != null) {
85-
// create proxy check for resolved IP and proceed with the lookup
86-
lookupHostNamesAndHandleRequest(event, iterator,
87-
builder.withTrustedIP(trustedIP, entry.getValue()), dnsClient);
88-
} else {
89-
logInvalidIpAddress(hostName);
90-
// ignore this hostname proxy check and proceed with the lookup
91-
lookupHostNamesAndHandleRequest(event, iterator, builder, dnsClient);
92-
}
93-
} else {
94-
// inform we can't cope without IP
95-
logDnsLookupFailure(hostName);
96-
// ignore this hostname proxy check and proceed with the lookup
97-
lookupHostNamesAndHandleRequest(event, iterator, builder, dnsClient);
98-
}
99-
}
10086

101-
});
87+
resolveHostNameToAllIpAddresses(dnsClient, hostName, event.remoteAddress(), results -> {
88+
if (!results.isEmpty()) {
89+
Set<InetAddress> trustedIPs = results.stream().map(Inet::parseInetAddress).filter(Objects::nonNull)
90+
.collect(Collectors.toSet());
91+
if (!trustedIPs.isEmpty()) {
92+
// create proxy check for resolved IP and proceed with the lookup
93+
lookupHostNamesAndHandleRequest(event, iterator,
94+
builder.withTrustedIP(trustedIPs, entry.getValue()), dnsClient);
95+
} else {
96+
logInvalidIpAddress(hostName);
97+
// ignore this hostname proxy check and proceed with the lookup
98+
lookupHostNamesAndHandleRequest(event, iterator, builder, dnsClient);
99+
}
100+
} else {
101+
// inform we can't cope without IP
102+
logDnsLookupFailure(hostName);
103+
// ignore this hostname proxy check and proceed with the lookup
104+
lookupHostNamesAndHandleRequest(event, iterator, builder, dnsClient);
105+
}
106+
});
107+
102108
} else {
103109
// DNS lookup is done
104110
if (builder.hasProxyChecks()) {
@@ -110,6 +116,38 @@ public void handle(AsyncResult<String> stringAsyncResult) {
110116
}
111117
}
112118

119+
private void resolveHostNameToAllIpAddresses(DnsClient dnsClient, String hostName, SocketAddress callersSocketAddress,
120+
Handler<Collection<String>> handler) {
121+
ArrayList<Future<List<String>>> results = new ArrayList<>();
122+
InetAddress proxyIP = null;
123+
if (callersSocketAddress != null) {
124+
proxyIP = ((SocketAddressImpl) callersSocketAddress).ipAddress();
125+
}
126+
// Match the lookup with the address type of the caller
127+
if (proxyIP == null || proxyIP instanceof Inet4Address) {
128+
results.add(dnsClient.resolveA(hostName));
129+
}
130+
if (proxyIP == null || proxyIP instanceof Inet6Address) {
131+
results.add(dnsClient.resolveAAAA(hostName));
132+
}
133+
processFutures(results, new ArrayList<>(), handler);
134+
}
135+
136+
private void processFutures(ArrayList<Future<List<String>>> future, Collection<String> results,
137+
Handler<Collection<String>> handler) {
138+
if (!future.isEmpty()) {
139+
Future<List<String>> poll = future.remove(0);
140+
poll.onComplete(result -> {
141+
if (result.succeeded() && result.result() != null) {
142+
results.addAll(result.result());
143+
}
144+
processFutures(future, results, handler);
145+
});
146+
} else {
147+
handler.handle(results);
148+
}
149+
}
150+
113151
private void resolveProxyIpAndHandleRequest(HttpServerRequest event,
114152
TrustedProxyCheck.TrustedProxyCheckBuilder builder) {
115153
InetAddress proxyIP = ((SocketAddressImpl) event.remoteAddress()).ipAddress();
@@ -121,28 +159,26 @@ private void resolveProxyIpAndHandleRequest(HttpServerRequest event,
121159
if (proxyIP == null) {
122160
// perform DNS lookup, then create proxy check and handle request
123161
final String hostName = Objects.requireNonNull(event.remoteAddress().hostName());
124-
vertx.get().createDnsClient().lookup(hostName,
125-
new Handler<AsyncResult<String>>() {
126-
@Override
127-
public void handle(AsyncResult<String> stringAsyncResult) {
128-
TrustedProxyCheck proxyCheck;
129-
if (stringAsyncResult.succeeded()) {
130-
// use resolved IP to build proxy check
131-
final var proxyIP = Inet.parseInetAddress(stringAsyncResult.result());
132-
if (proxyIP != null) {
133-
proxyCheck = builder.build(proxyIP, event.remoteAddress().port());
134-
} else {
135-
logInvalidIpAddress(hostName);
136-
proxyCheck = denyAll();
137-
}
162+
resolveHostNameToAllIpAddresses(vertx.get().createDnsClient(), hostName, null,
163+
results -> {
164+
TrustedProxyCheck proxyCheck;
165+
if (!results.isEmpty()) {
166+
// use resolved IP to build proxy check
167+
Set<InetAddress> proxyIPs = results.stream().map(Inet::parseInetAddress).filter(Objects::nonNull)
168+
.collect(Collectors.toSet());
169+
if (!proxyIPs.isEmpty()) {
170+
proxyCheck = builder.build(proxyIPs, event.remoteAddress().port());
138171
} else {
139-
// we can't cope without IP => ignore headers
140-
logDnsLookupFailure(hostName);
172+
logInvalidIpAddress(hostName);
141173
proxyCheck = denyAll();
142174
}
143-
144-
handleForwardedServerRequest(event, proxyCheck);
175+
} else {
176+
// we can't cope without IP => ignore headers
177+
logDnsLookupFailure(hostName);
178+
proxyCheck = denyAll();
145179
}
180+
181+
handleForwardedServerRequest(event, proxyCheck);
146182
});
147183
} else {
148184
// we have proxy IP => create proxy check and handle request

extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/TrustedProxyCheck.java

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import java.net.InetAddress;
44
import java.util.ArrayList;
5+
import java.util.Collection;
56
import java.util.HashMap;
67
import java.util.List;
78
import java.util.Map;
@@ -62,7 +63,7 @@ static TrustedProxyCheckBuilder builder(List<TrustedProxyCheckPart> parts) {
6263
return new TrustedProxyCheckBuilder(hostNameToPort, proxyChecks);
6364
}
6465

65-
TrustedProxyCheckBuilder withTrustedIP(InetAddress trustedIP, int trustedPort) {
66+
TrustedProxyCheckBuilder withTrustedIP(Collection<InetAddress> trustedIP, int trustedPort) {
6667
final List<BiPredicate<InetAddress, Integer>> proxyChecks = new ArrayList<>(this.proxyChecks);
6768
proxyChecks.add(createNewIpCheck(trustedIP, trustedPort));
6869
return new TrustedProxyCheckBuilder(null, proxyChecks);
@@ -87,6 +88,20 @@ public boolean isProxyAllowed() {
8788
};
8889
}
8990

91+
TrustedProxyCheck build(Collection<InetAddress> proxyIPs, int proxyPort) {
92+
Objects.requireNonNull(proxyIPs);
93+
return () -> {
94+
for (BiPredicate<InetAddress, Integer> proxyCheck : proxyChecks) {
95+
for (InetAddress proxyIP : proxyIPs) {
96+
if (proxyCheck.test(proxyIP, proxyPort)) {
97+
return true;
98+
}
99+
}
100+
}
101+
return false;
102+
};
103+
}
104+
90105
boolean hasHostNames() {
91106
return hasHostNames(this.hostNameToPort);
92107
}
@@ -115,6 +130,20 @@ private boolean isPortOk(int port) {
115130
};
116131
}
117132

133+
static BiPredicate<InetAddress, Integer> createNewIpCheck(Collection<InetAddress> trustedIP, int trustedPort) {
134+
final boolean doNotCheckPort = trustedPort == 0;
135+
return new BiPredicate<>() {
136+
@Override
137+
public boolean test(InetAddress proxyIP, Integer proxyPort) {
138+
return isPortOk(proxyPort) && trustedIP.contains(proxyIP);
139+
}
140+
141+
private boolean isPortOk(int port) {
142+
return doNotCheckPort || port == trustedPort;
143+
}
144+
};
145+
}
146+
118147
final class TrustedProxyCheckPart {
119148

120149
final BiPredicate<InetAddress, Integer> proxyCheck;

0 commit comments

Comments
 (0)