Skip to content

Commit 0af5881

Browse files
authored
Merge pull request #43188 from ahus1/is-42782-fix-dns-lookup-trusted-proxies
Resolve trusted proxy host names to all available A/AAAA records
2 parents 052129a + 36af0c1 commit 0af5881

File tree

8 files changed

+918
-44
lines changed

8 files changed

+918
-44
lines changed

Diff for: extensions/vertx-http/deployment/pom.xml

+10
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@
1212
<artifactId>quarkus-vertx-http-deployment</artifactId>
1313
<name>Quarkus - Vert.x - HTTP - Deployment</name>
1414

15+
<properties>
16+
<apacheds-protocol-dns.version>2.0.0-M23</apacheds-protocol-dns.version>
17+
</properties>
18+
1519
<dependencies>
1620
<dependency>
1721
<groupId>io.quarkus</groupId>
@@ -139,6 +143,12 @@
139143
<artifactId>smallrye-certificate-generator-junit5</artifactId>
140144
<scope>test</scope>
141145
</dependency>
146+
<dependency>
147+
<groupId>org.apache.directory.server</groupId>
148+
<artifactId>apacheds-protocol-dns</artifactId>
149+
<version>${apacheds-protocol-dns.version}</version>
150+
<scope>test</scope>
151+
</dependency>
142152
</dependencies>
143153

144154
<build>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
package io.quarkus.vertx.http.proxy;
2+
3+
import org.hamcrest.Matchers;
4+
import org.jboss.shrinkwrap.api.asset.StringAsset;
5+
import org.junit.jupiter.api.AfterEach;
6+
import org.junit.jupiter.api.BeforeEach;
7+
import org.junit.jupiter.api.Test;
8+
import org.junit.jupiter.api.extension.RegisterExtension;
9+
10+
import io.quarkus.test.QuarkusUnitTest;
11+
import io.quarkus.vertx.http.ForwardedHandlerInitializer;
12+
import io.quarkus.vertx.http.proxy.fakedns.DnameRecordEncoder;
13+
import io.quarkus.vertx.http.proxy.fakedns.DnsMessageEncoder;
14+
import io.quarkus.vertx.http.proxy.fakedns.FakeDNSServer;
15+
import io.restassured.RestAssured;
16+
17+
public class TrustedForwarderDnsResolveTest {
18+
19+
private FakeDNSServer dnsServer;
20+
21+
@RegisterExtension
22+
static final QuarkusUnitTest config = new QuarkusUnitTest()
23+
.withApplicationRoot((jar) -> jar
24+
.addClasses(ForwardedHandlerInitializer.class, DnameRecordEncoder.class, DnsMessageEncoder.class,
25+
FakeDNSServer.class)
26+
.addAsResource(new StringAsset("quarkus.http.proxy.proxy-address-forwarding=true\n" +
27+
"quarkus.http.proxy.allow-forwarded=true\n" +
28+
"quarkus.http.proxy.enable-forwarded-host=true\n" +
29+
"quarkus.http.proxy.enable-forwarded-prefix=true\n" +
30+
"quarkus.vertx.resolver.servers=127.0.0.1:53530\n" +
31+
"quarkus.http.proxy.trusted-proxies=trusted.example.com"),
32+
"application.properties"));
33+
34+
@BeforeEach
35+
public void setUp() throws Exception {
36+
dnsServer = new FakeDNSServer();
37+
dnsServer.start();
38+
}
39+
40+
@AfterEach
41+
public void tearDown() {
42+
dnsServer.stop();
43+
}
44+
45+
@Test
46+
public void testTrustedProxyResolved() {
47+
dnsServer.addRecordsToStore("trusted.example.com", "127.0.0.3", "127.0.0.2", "127.0.0.1");
48+
RestAssured.given()
49+
.header("Forwarded", "proto=http;for=backend2:5555;host=somehost2")
50+
.get("/path")
51+
.then()
52+
.body(Matchers.equalTo("http|somehost2|backend2:5555|/path|http://somehost2/path"));
53+
}
54+
55+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package io.quarkus.vertx.http.proxy.fakedns;
2+
3+
import org.apache.directory.server.dns.io.encoder.ResourceRecordEncoder;
4+
import org.apache.directory.server.dns.messages.ResourceRecord;
5+
import org.apache.directory.server.dns.store.DnsAttribute;
6+
import org.apache.mina.core.buffer.IoBuffer;
7+
8+
public class DnameRecordEncoder extends ResourceRecordEncoder {
9+
10+
protected void putResourceRecordData(IoBuffer byteBuffer, ResourceRecord record) {
11+
String domainName = record.get(DnsAttribute.DOMAIN_NAME);
12+
putDomainName(byteBuffer, domainName);
13+
}
14+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
package io.quarkus.vertx.http.proxy.fakedns;
2+
3+
import java.io.IOException;
4+
import java.util.Collections;
5+
import java.util.HashMap;
6+
import java.util.Iterator;
7+
import java.util.List;
8+
import java.util.Map;
9+
10+
/*
11+
* Licensed to the Apache Software Foundation (ASF) under one
12+
* or more contributor license agreements. See the NOTICE file
13+
* distributed with this work for additional information
14+
* regarding copyright ownership. The ASF licenses this file
15+
* to you under the Apache License, Version 2.0 (the
16+
* "License"); you may not use this file except in compliance
17+
* with the License. You may obtain a copy of the License at
18+
*
19+
* http://www.apache.org/licenses/LICENSE-2.0
20+
*
21+
* Unless required by applicable law or agreed to in writing,
22+
* software distributed under the License is distributed on an
23+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
24+
* KIND, either express or implied. See the License for the
25+
* specific language governing permissions and limitations
26+
* under the License.
27+
*
28+
*/
29+
import org.apache.directory.server.dns.io.encoder.*;
30+
import org.apache.directory.server.dns.messages.DnsMessage;
31+
import org.apache.directory.server.dns.messages.MessageType;
32+
import org.apache.directory.server.dns.messages.OpCode;
33+
import org.apache.directory.server.dns.messages.QuestionRecord;
34+
import org.apache.directory.server.dns.messages.RecordType;
35+
import org.apache.directory.server.dns.messages.ResourceRecord;
36+
import org.apache.directory.server.dns.messages.ResponseCode;
37+
import org.apache.directory.server.i18n.I18n;
38+
import org.apache.mina.core.buffer.IoBuffer;
39+
import org.slf4j.Logger;
40+
import org.slf4j.LoggerFactory;
41+
42+
/**
43+
* An encoder for DNS messages. The primary usage of the DnsMessageEncoder is
44+
* to call the <code>encode(ByteBuffer, DnsMessage)</code> method which will
45+
* write the message to the outgoing ByteBuffer according to the DnsMessage
46+
* encoding in RFC-1035.
47+
*
48+
* @author <a href="mailto:[email protected]">Apache Directory Project</a>
49+
* @version $Rev$, $Date$
50+
*/
51+
public class DnsMessageEncoder {
52+
/** the log for this class */
53+
private static final Logger log = LoggerFactory.getLogger(DnsMessageEncoder.class);
54+
55+
/**
56+
* A Hashed Adapter mapping record types to their encoders.
57+
*/
58+
private static final Map<RecordType, RecordEncoder> DEFAULT_ENCODERS;
59+
60+
static {
61+
Map<RecordType, RecordEncoder> map = new HashMap<RecordType, RecordEncoder>();
62+
63+
map.put(RecordType.SOA, new StartOfAuthorityRecordEncoder());
64+
map.put(RecordType.A, new AddressRecordEncoder());
65+
map.put(RecordType.NS, new NameServerRecordEncoder());
66+
map.put(RecordType.CNAME, new CanonicalNameRecordEncoder());
67+
map.put(RecordType.PTR, new PointerRecordEncoder());
68+
map.put(RecordType.MX, new MailExchangeRecordEncoder());
69+
map.put(RecordType.SRV, new ServerSelectionRecordEncoder());
70+
map.put(RecordType.TXT, new TextRecordEncoder());
71+
map.put(RecordType.DNAME, new DnameRecordEncoder());
72+
73+
DEFAULT_ENCODERS = Collections.unmodifiableMap(map);
74+
}
75+
76+
/**
77+
* Encodes the {@link DnsMessage} into the {@link IoBuffer}.
78+
*
79+
* @param byteBuffer
80+
* @param message
81+
*/
82+
public void encode(IoBuffer byteBuffer, DnsMessage message) {
83+
byteBuffer.putShort((short) message.getTransactionId());
84+
85+
byte header = (byte) 0x00;
86+
header |= encodeMessageType(message.getMessageType());
87+
header |= encodeOpCode(message.getOpCode());
88+
header |= encodeAuthoritativeAnswer(message.isAuthoritativeAnswer());
89+
header |= encodeTruncated(message.isTruncated());
90+
header |= encodeRecursionDesired(message.isRecursionDesired());
91+
byteBuffer.put(header);
92+
93+
header = (byte) 0x00;
94+
header |= encodeRecursionAvailable(message.isRecursionAvailable());
95+
header |= encodeResponseCode(message.getResponseCode());
96+
byteBuffer.put(header);
97+
98+
byteBuffer
99+
.putShort((short) (message.getQuestionRecords() != null ? message.getQuestionRecords().size() : 0));
100+
byteBuffer.putShort((short) (message.getAnswerRecords() != null ? message.getAnswerRecords().size() : 0));
101+
byteBuffer.putShort((short) (message.getAuthorityRecords() != null ? message.getAuthorityRecords().size()
102+
: 0));
103+
byteBuffer.putShort((short) (message.getAdditionalRecords() != null ? message.getAdditionalRecords().size()
104+
: 0));
105+
106+
putQuestionRecords(byteBuffer, message.getQuestionRecords());
107+
putResourceRecords(byteBuffer, message.getAnswerRecords());
108+
putResourceRecords(byteBuffer, message.getAuthorityRecords());
109+
putResourceRecords(byteBuffer, message.getAdditionalRecords());
110+
}
111+
112+
private void putQuestionRecords(IoBuffer byteBuffer, List<QuestionRecord> questions) {
113+
if (questions == null) {
114+
return;
115+
}
116+
117+
QuestionRecordEncoder encoder = new QuestionRecordEncoder();
118+
119+
Iterator<QuestionRecord> it = questions.iterator();
120+
121+
while (it.hasNext()) {
122+
QuestionRecord question = it.next();
123+
encoder.put(byteBuffer, question);
124+
}
125+
}
126+
127+
private void putResourceRecords(IoBuffer byteBuffer, List<ResourceRecord> records) {
128+
if (records == null) {
129+
return;
130+
}
131+
132+
Iterator<ResourceRecord> it = records.iterator();
133+
134+
while (it.hasNext()) {
135+
ResourceRecord record = it.next();
136+
137+
try {
138+
put(byteBuffer, record);
139+
} catch (IOException ioe) {
140+
log.error(ioe.getLocalizedMessage(), ioe);
141+
}
142+
}
143+
}
144+
145+
private void put(IoBuffer byteBuffer, ResourceRecord record) throws IOException {
146+
RecordType type = record.getRecordType();
147+
148+
RecordEncoder encoder = DEFAULT_ENCODERS.get(type);
149+
150+
if (encoder == null) {
151+
throw new IOException(I18n.err(I18n.ERR_597, type));
152+
}
153+
154+
encoder.put(byteBuffer, record);
155+
}
156+
157+
private byte encodeMessageType(MessageType messageType) {
158+
byte oneBit = (byte) (messageType.convert() & 0x01);
159+
return (byte) (oneBit << 7);
160+
}
161+
162+
private byte encodeOpCode(OpCode opCode) {
163+
byte fourBits = (byte) (opCode.convert() & 0x0F);
164+
return (byte) (fourBits << 3);
165+
}
166+
167+
private byte encodeAuthoritativeAnswer(boolean authoritative) {
168+
if (authoritative) {
169+
return (byte) ((byte) 0x01 << 2);
170+
}
171+
return (byte) 0;
172+
}
173+
174+
private byte encodeTruncated(boolean truncated) {
175+
if (truncated) {
176+
return (byte) ((byte) 0x01 << 1);
177+
}
178+
return 0;
179+
}
180+
181+
private byte encodeRecursionDesired(boolean recursionDesired) {
182+
if (recursionDesired) {
183+
return (byte) 0x01;
184+
}
185+
return 0;
186+
}
187+
188+
private byte encodeRecursionAvailable(boolean recursionAvailable) {
189+
if (recursionAvailable) {
190+
return (byte) ((byte) 0x01 << 7);
191+
}
192+
return 0;
193+
}
194+
195+
private byte encodeResponseCode(ResponseCode responseCode) {
196+
return (byte) (responseCode.convert() & 0x0F);
197+
}
198+
}

0 commit comments

Comments
 (0)