Skip to content

Commit 607af35

Browse files
authored
DNS over HTTPS (#670)
1 parent e521835 commit 607af35

File tree

30 files changed

+819
-46
lines changed

30 files changed

+819
-46
lines changed

RELEASE-NOTES.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
## 5.7.0 Alpha
2+
* DNS over HTTPS, see #138
3+
14
## 5.6.2
25
* Fixing Getting Non-authoritative answer when unexpected, see #608.
36
* Fixing Getting NXDOMAIN when not expected, see #668.

docs/content/3-configuration/_index.en.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,14 @@ Current Version: `3`. See [how to set the configurations][5].
2424

2525
---
2626

27+
### DoH Server
28+
29+
| Name | Description | Default Value |
30+
|-------------------|-------------------------------------------------|---------------|
31+
| `server.doh.port` | When set, will activate doh server on that port | `` |
32+
33+
---
34+
2735
### WEB Server
2836

2937
| Name | Description | Default Value |

doh/README.md

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
## Configure Doh on firefox
2+
3+
* https://gemini.google.com/app/28a3034438bb1b72
4+
* about:networking#dns
5+
* about:preferences#privacy -> dns over https -> Increased Protection = `https://localhost:8443/dns-query`
6+
7+
Restrictions
8+
9+
* Import ca.crt on firefox under `about:preferences#privacy` -> Certificates -> Authorities to make the DPS doh server
10+
be accepted.
11+
* Activate `network.trr.allow-rfc1918` on `about:config` to make it work with private ip addresses;
12+
* Some real domains like `.dev` won't work depending on the combination of private ip + default port (80, 443), so evict
13+
them, .com seems to work
14+
15+
## Generate Certificates for DOH Server
16+
17+
### 1 - Criar uma CA local (uma vez só)
18+
19+
```bash
20+
openssl genrsa -out ca.key 4096
21+
```
22+
23+
```bash
24+
openssl req -x509 -new -nodes \
25+
-key ca.key \
26+
-sha256 \
27+
-days 36500 \
28+
-out ca.crt \
29+
-subj "/C=BR/ST=SP/L=SaoPaulo/O=Local Dev CA/CN=Local Dev Root CA"
30+
```
31+
32+
✔ Esse **é o certificado que você importa no Firefox**
33+
✔ Ele já vem com `CA:TRUE`
34+
35+
### 2 - Criar chave do servidor DoH
36+
37+
```bash
38+
openssl genrsa -out doh.key 2048
39+
```
40+
41+
### 3 - Criar CSR do servidor DoH
42+
43+
```bash
44+
openssl req -new \
45+
-key doh.key \
46+
-out doh.csr \
47+
-subj "/C=BR/ST=SP/L=SaoPaulo/O=Local Dev/CN=localhost"
48+
```
49+
50+
---
51+
52+
## 4 - Criar arquivo de extensões
53+
54+
Crie `doh.ext`:
55+
56+
```ini
57+
authorityKeyIdentifier=keyid,issuer
58+
basicConstraints=CA:FALSE
59+
keyUsage = digitalSignature, keyEncipherment
60+
extendedKeyUsage = serverAuth
61+
62+
subjectAltName = @alt_names
63+
64+
[alt_names]
65+
DNS.1 = localhost
66+
IP.1 = 127.0.0.1
67+
```
68+
69+
## 5 - Assinar o certificado do servidor com a CA
70+
71+
```bash
72+
openssl x509 -req \
73+
-in doh.csr \
74+
-CA ca.crt \
75+
-CAkey ca.key \
76+
-CAcreateserial \
77+
-out doh.crt \
78+
-days 36500 \
79+
-sha256 \
80+
-extfile doh.ext
81+
```
82+
83+
Agora você tem:
84+
85+
* `ca.crt`**Autoridade**
86+
* `doh.crt`**Servidor**
87+
* `doh.key`**Chave do servidor**
88+
89+
---
90+
91+
### 6 - Importar a CA no Firefox
92+
93+
1. `about:preferences#privacy`
94+
2. **Certificados → Ver certificados**
95+
3. Aba **Autoridades**
96+
4. **Importar `ca.crt`**
97+
5. Marcar:
98+
99+
**Confiar nesta CA para identificar sites*
100+
101+
⚠️ **NÃO importe o `doh.crt` no Firefox**
102+
103+
### 7 - Gerar `server.p12` a partir de PEM (OpenSSL) para usar no DohServer no DPS
104+
105+
```bash
106+
# Opcional: cria fullchain (server + CA) (bom pra browsers)
107+
cat doh.crt ca.crt > fullchain.crt
108+
109+
# cria PKCS12 com private key + cert chain
110+
openssl pkcs12 -export \
111+
-inkey doh.key \
112+
-in doh.crt \
113+
-certfile ca.crt \
114+
-name local \
115+
-out server.p12 \
116+
-passout pass:changeit
117+
```
118+
119+
Isso cria um keystore `server.p12` contendo:
120+
121+
* **PrivateKey**: `doh.key`
122+
* **Cert**: `doh.crt`
123+
* **Chain**: `ca.crt`
124+
125+
Use o arquivo `server.p12` no server no DPS.
126+
127+
[[ref][1]]
128+
129+
[1]: https://chatgpt.com/c/694d8e4e-13c8-8325-bef7-25fb4240b6c9

doh/ca.crt

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
-----BEGIN CERTIFICATE-----
2+
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
32+
xQptjCQeTw==
33+
-----END CERTIFICATE-----

doh/ssl.zip

15.6 KB
Binary file not shown.

src/main/java/com/mageddo/dns/utils/Messages.java

Lines changed: 45 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
package com.mageddo.dns.utils;
22

3+
import java.io.IOException;
4+
import java.io.UncheckedIOException;
35
import java.time.Duration;
6+
import java.util.List;
47
import java.util.Optional;
58

69
import com.mageddo.commons.lang.Objects;
@@ -41,6 +44,11 @@ public static Message authoritative(Message m) {
4144
return m;
4245
}
4346

47+
private static Name findQuestionName(Message m) {
48+
return m.getQuestion()
49+
.getName();
50+
}
51+
4452
public static String simplePrint(Response res) {
4553
return simplePrint(res.getMessage());
4654
}
@@ -115,7 +123,7 @@ public static Message aAnswer(Message query, String ip) {
115123
}
116124

117125
public static Message aAnswer(Message query, String ip, long ttl) {
118-
final var res = withNoErrorResponse(query.clone());
126+
final var res = withNoErrorResponse(copy(query));
119127
if (StringUtils.isBlank(ip)) {
120128
return res;
121129
}
@@ -182,7 +190,7 @@ public static Entry.Type findQuestionType(Message msg) {
182190
* @return a clone with the combination.
183191
*/
184192
public static Message combine(Message source, Message target) {
185-
final var clone = clone(target);
193+
final var clone = copy(target);
186194
for (int i = 1; i < 4; i++) {
187195
final var section = source.getSection(i);
188196
for (final var record : section) {
@@ -298,7 +306,7 @@ public static Message withDefaultResponseHeaders(Message res) {
298306
return res;
299307
}
300308

301-
static Message clone(Message msg) {
309+
public static Message copy(Message msg) {
302310
if (msg == null) {
303311
return null;
304312
}
@@ -311,6 +319,12 @@ public static Message setFlag(Message m, int flag) {
311319
return m;
312320
}
313321

322+
public static Message unsetFlag(Message m, int flag) {
323+
m.getHeader()
324+
.unsetFlag(flag);
325+
return m;
326+
}
327+
314328
public static boolean hasFlag(Message msg, int flag) {
315329
return msg.getHeader()
316330
.getFlag(flag);
@@ -323,7 +337,6 @@ public static HostnameQuery toHostnameQuery(Message query) {
323337
return HostnameQuery.of(host, version);
324338
}
325339

326-
327340
public static boolean isSuccess(Message res) {
328341
return res.getRcode() == Rcode.NOERROR;
329342
}
@@ -340,11 +353,25 @@ public static Message noData(Message query) {
340353
return withResponseCode(query.clone(), Rcode.NOERROR);
341354
}
342355

356+
public static Message of(byte[] m) {
357+
try {
358+
return new Message(m);
359+
} catch (IOException e) {
360+
throw new UncheckedIOException(e);
361+
}
362+
}
363+
364+
public static Message unsetAuthoritative(Message m) {
365+
return unsetFlag(m, Flags.AA);
366+
}
367+
343368
public static Message authoritativeAnswer(Message query, String ip, IP.Version version) {
344369
return authoritative(answer(query, ip, version));
345370
}
346371

347-
public static Message authoritativeAnswer(Message query, String ip, IP.Version version, long ttl) {
372+
public static Message authoritativeAnswer(
373+
Message query, String ip, IP.Version version, long ttl
374+
) {
348375
return authoritative(answer(query, ip, version, ttl));
349376
}
350377

@@ -355,4 +382,17 @@ public static boolean isAuthoritative(Message m) {
355382
public static boolean isRecursionAvailable(Message m) {
356383
return hasFlag(m, Flags.RA);
357384
}
385+
386+
public static int getId(Message m) {
387+
return m.getHeader()
388+
.getID();
389+
}
390+
391+
public static Message notSupportedHttps(Message m) {
392+
return authoritative(withNoErrorResponse(copy(m)));
393+
}
394+
395+
public static List<Record> getAnswers(Message m) {
396+
return m.getSection(Section.ANSWER);
397+
}
358398
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package com.mageddo.dns.utils;
2+
3+
import org.xbill.DNS.DClass;
4+
import org.xbill.DNS.Name;
5+
import org.xbill.DNS.SOARecord;
6+
import org.xbill.DNS.TextParseException;
7+
8+
public class SoaRecordMapper {
9+
10+
public static final int SERIAL = 1;
11+
public static final int REFRESH = 3600;
12+
public static final int RETRY = 600;
13+
public static final int EXPIRE = 86400;
14+
public static final int MINIMUM = 60;
15+
16+
public static SOARecord of(Name zone) {
17+
try {
18+
final var mname = Name.fromString("ns." + zone);
19+
final var rname = Name.fromString("dps." + zone);
20+
return new SOARecord(
21+
zone,
22+
DClass.IN,
23+
256,
24+
mname,
25+
rname,
26+
SERIAL,
27+
REFRESH,
28+
RETRY,
29+
EXPIRE,
30+
MINIMUM
31+
);
32+
} catch (TextParseException e) {
33+
throw new IllegalArgumentException(e);
34+
}
35+
36+
}
37+
}

0 commit comments

Comments
 (0)