Skip to content

Commit 3521e8e

Browse files
committed
Pull request 2382: AGDNS-2714-tls-config
Merge in DNS/adguard-home from AGDNS-2714-tls-config to master Squashed commit of the following: commit 073e5ec Merge: 18f38c9 4d25897 Author: Stanislav Chzhen <s.chzhen@adguard.com> Date: Wed Apr 16 18:25:23 2025 +0300 Merge branch 'master' into AGDNS-2714-tls-config commit 18f38c9 Author: Stanislav Chzhen <s.chzhen@adguard.com> Date: Fri Apr 11 15:02:00 2025 +0300 dnsforward: imp docs commit ed56d3c Merge: 3ef281e 1cc6c00 Author: Stanislav Chzhen <s.chzhen@adguard.com> Date: Thu Apr 10 17:25:08 2025 +0300 Merge branch 'master' into AGDNS-2714-tls-config commit 3ef281e Author: Stanislav Chzhen <s.chzhen@adguard.com> Date: Thu Apr 10 17:24:29 2025 +0300 all: imp docs commit b75f287 Author: Stanislav Chzhen <s.chzhen@adguard.com> Date: Mon Apr 7 17:16:59 2025 +0300 dnsforward: imp code commit 8ab17b9 Author: Stanislav Chzhen <s.chzhen@adguard.com> Date: Fri Apr 4 21:26:37 2025 +0300 all: imp code commit 1abce97 Author: Stanislav Chzhen <s.chzhen@adguard.com> Date: Wed Apr 2 18:22:15 2025 +0300 home: imp code commit debf710 Author: Stanislav Chzhen <s.chzhen@adguard.com> Date: Tue Apr 1 14:52:21 2025 +0300 home: imp code commit 4aa26f1 Author: Stanislav Chzhen <s.chzhen@adguard.com> Date: Tue Apr 1 14:16:16 2025 +0300 all: imp code commit 1a3e72f Author: Stanislav Chzhen <s.chzhen@adguard.com> Date: Mon Mar 31 21:22:40 2025 +0300 all: imp code commit 776ab82 Author: Stanislav Chzhen <s.chzhen@adguard.com> Date: Thu Mar 27 14:00:33 2025 +0300 home: tls config mu commit 9ebf912 Author: Stanislav Chzhen <s.chzhen@adguard.com> Date: Wed Mar 26 18:58:47 2025 +0300 all: tls config
1 parent 4d25897 commit 3521e8e

21 files changed

+408
-289
lines changed

CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,8 @@ See also the [v0.107.59 GitHub milestone][ms-v0.107.59].
6464

6565
### Fixed
6666

67+
- Validation process for the DNS-over-TLS, DNS-over-QUIC, and HTTPS ports on the *Encryption Settings* page.
68+
6769
- Rules with the `client` modifier not working ([#7708]).
6870

6971
- The search form not working in the query log ([#7704]).

internal/dnsforward/beforerequest.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ func (s *Server) clientIDFromDNSContext(pctx *proxy.DNSContext) (clientID string
7474
return "", nil
7575
}
7676

77-
hostSrvName := s.conf.ServerName
77+
hostSrvName := s.conf.TLSConf.ServerName
7878
if hostSrvName == "" {
7979
return "", nil
8080
}
@@ -87,7 +87,7 @@ func (s *Server) clientIDFromDNSContext(pctx *proxy.DNSContext) (clientID string
8787
clientID, err = clientIDFromClientServerName(
8888
hostSrvName,
8989
cliSrvName,
90-
s.conf.StrictSNICheck,
90+
s.conf.TLSConf.StrictSNICheck,
9191
)
9292
if err != nil {
9393
return "", fmt.Errorf("clientid check: %w", err)

internal/dnsforward/beforerequest_internal_test.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ func TestServer_HandleBefore_tls(t *testing.T) {
121121
t.Run(tc.name, func(t *testing.T) {
122122
t.Parallel()
123123

124-
s, _ := createTestTLS(t, TLSConfig{
124+
s, _ := createTestTLS(t, &TLSConfig{
125125
TLSListenAddrs: []*net.TCPAddr{{}},
126126
ServerName: tlsServerName,
127127
})
@@ -259,6 +259,7 @@ func TestServer_HandleBefore_udp(t *testing.T) {
259259
}, ServerConfig{
260260
UDPListenAddrs: []*net.UDPAddr{{}},
261261
TCPListenAddrs: []*net.TCPAddr{{}},
262+
TLSConf: &TLSConfig{},
262263
Config: Config{
263264
AllowedClients: tc.allowedClients,
264265
DisallowedClients: tc.disallowedClients,

internal/dnsforward/clientid_internal_test.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -212,13 +212,13 @@ func TestServer_clientIDFromDNSContext(t *testing.T) {
212212

213213
for _, tc := range testCases {
214214
t.Run(tc.name, func(t *testing.T) {
215-
tlsConf := TLSConfig{
215+
tlsConf := &TLSConfig{
216216
ServerName: tc.confSrvName,
217217
StrictSNICheck: tc.strictSNI,
218218
}
219219

220220
srv := &Server{
221-
conf: ServerConfig{TLSConfig: tlsConf},
221+
conf: ServerConfig{TLSConf: tlsConf},
222222
baseLogger: slogutil.NewDiscardLogger(),
223223
}
224224

internal/dnsforward/config.go

+36-55
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import (
1111
"strings"
1212
"time"
1313

14-
"github.com/AdguardTeam/AdGuardHome/internal/aghalg"
1514
"github.com/AdguardTeam/AdGuardHome/internal/aghhttp"
1615
"github.com/AdguardTeam/AdGuardHome/internal/aghnet"
1716
"github.com/AdguardTeam/AdGuardHome/internal/aghtls"
@@ -168,43 +167,34 @@ type EDNSClientSubnet struct {
168167
UseCustom bool `yaml:"use_custom"`
169168
}
170169

171-
// TLSConfig is the TLS configuration for HTTPS, DNS-over-HTTPS, and DNS-over-TLS
170+
// TLSConfig contains the TLS configuration settings for DNS-over-HTTPS (DoH),
171+
// DNS-over-TLS (DoT), DNS-over-QUIC (DoQ), and Discovery of Designated
172+
// Resolvers (DDR).
172173
type TLSConfig struct {
173-
cert tls.Certificate
174+
// Cert is the TLS certificate used for TLS connections. It is nil if
175+
// encryption is disabled.
176+
Cert *tls.Certificate
174177

175-
TLSListenAddrs []*net.TCPAddr `yaml:"-" json:"-"`
176-
QUICListenAddrs []*net.UDPAddr `yaml:"-" json:"-"`
177-
HTTPSListenAddrs []*net.TCPAddr `yaml:"-" json:"-"`
178+
// TLSListenAddrs are the addresses to listen on for DoT connections. Each
179+
// item in the list must be non-nil if Cert is not nil.
180+
TLSListenAddrs []*net.TCPAddr
178181

179-
// PEM-encoded certificates chain
180-
CertificateChain string `yaml:"certificate_chain" json:"certificate_chain"`
181-
// PEM-encoded private key
182-
PrivateKey string `yaml:"private_key" json:"private_key"`
182+
// QUICListenAddrs are the addresses to listen on for DoQ connections. Each
183+
// item in the list must be non-nil if Cert is not nil.
184+
QUICListenAddrs []*net.UDPAddr
183185

184-
CertificatePath string `yaml:"certificate_path" json:"certificate_path"`
185-
PrivateKeyPath string `yaml:"private_key_path" json:"private_key_path"`
186-
187-
CertificateChainData []byte `yaml:"-" json:"-"`
188-
PrivateKeyData []byte `yaml:"-" json:"-"`
186+
// HTTPSListenAddrs should be the addresses AdGuard Home is listening on for
187+
// DoH connections. These addresses are announced with DDR. Each item in
188+
// the list must be non-nil.
189+
HTTPSListenAddrs []*net.TCPAddr
189190

190191
// ServerName is the hostname of the server. Currently, it is only being
191192
// used for ClientID checking and Discovery of Designated Resolvers (DDR).
192-
ServerName string `yaml:"-" json:"-"`
193-
194-
// DNS names from certificate (SAN) or CN value from Subject
195-
dnsNames []string
196-
197-
// OverrideTLSCiphers, when set, contains the names of the cipher suites to
198-
// use. If the slice is empty, the default safe suites are used.
199-
OverrideTLSCiphers []string `yaml:"override_tls_ciphers,omitempty" json:"-"`
193+
ServerName string
200194

201195
// StrictSNICheck controls if the connections with SNI mismatching the
202196
// certificate's ones should be rejected.
203-
StrictSNICheck bool `yaml:"strict_sni_check" json:"-"`
204-
205-
// hasIPAddrs is set during the certificate parsing and is true if the
206-
// configured certificate contains at least a single IP address.
207-
hasIPAddrs bool
197+
StrictSNICheck bool
208198
}
209199

210200
// DNSCryptConfig is the DNSCrypt server configuration struct.
@@ -239,8 +229,11 @@ type ServerConfig struct {
239229
// Remove that.
240230
AddrProcConf *client.DefaultAddrProcConfig
241231

232+
// TLSConf is the TLS configuration for DNS-over-TLS, DNS-over-QUIC, and
233+
// HTTPS. It must not be nil.
234+
TLSConf *TLSConfig
235+
242236
Config
243-
TLSConfig
244237
DNSCryptConfig
245238
TLSAllowUnencryptedDoH bool
246239

@@ -608,45 +601,33 @@ func (conf *ServerConfig) ourAddrsSet() (m addrPortSet, err error) {
608601
}
609602
}
610603

611-
// prepareTLS - prepares TLS configuration for the DNS proxy
604+
// prepareTLS sets up the TLS configuration for the DNS proxy.
612605
func (s *Server) prepareTLS(proxyConfig *proxy.Config) (err error) {
613-
if len(s.conf.CertificateChainData) == 0 || len(s.conf.PrivateKeyData) == 0 {
614-
return nil
606+
if s.conf.TLSConf.Cert == nil {
607+
return
615608
}
616609

617-
if s.conf.TLSListenAddrs == nil && s.conf.QUICListenAddrs == nil {
610+
if s.conf.TLSConf.TLSListenAddrs == nil && s.conf.TLSConf.QUICListenAddrs == nil {
618611
return nil
619612
}
620613

621-
proxyConfig.TLSListenAddr = aghalg.CoalesceSlice(
622-
s.conf.TLSListenAddrs,
623-
proxyConfig.TLSListenAddr,
624-
)
625-
626-
proxyConfig.QUICListenAddr = aghalg.CoalesceSlice(
627-
s.conf.QUICListenAddrs,
628-
proxyConfig.QUICListenAddr,
629-
)
630-
631-
s.conf.cert, err = tls.X509KeyPair(s.conf.CertificateChainData, s.conf.PrivateKeyData)
632-
if err != nil {
633-
return fmt.Errorf("failed to parse TLS keypair: %w", err)
634-
}
614+
proxyConfig.TLSListenAddr = s.conf.TLSConf.TLSListenAddrs
615+
proxyConfig.QUICListenAddr = s.conf.TLSConf.QUICListenAddrs
635616

636-
cert, err := x509.ParseCertificate(s.conf.cert.Certificate[0])
617+
cert, err := x509.ParseCertificate(s.conf.TLSConf.Cert.Certificate[0])
637618
if err != nil {
638619
return fmt.Errorf("x509.ParseCertificate(): %w", err)
639620
}
640621

641-
s.conf.hasIPAddrs = aghtls.CertificateHasIP(cert)
622+
s.hasIPAddrs = aghtls.CertificateHasIP(cert)
642623

643-
if s.conf.StrictSNICheck {
624+
if s.conf.TLSConf.StrictSNICheck {
644625
if len(cert.DNSNames) != 0 {
645-
s.conf.dnsNames = cert.DNSNames
626+
s.dnsNames = cert.DNSNames
646627
log.Debug("dns: using certificate's SAN as DNS names: %v", cert.DNSNames)
647-
slices.Sort(s.conf.dnsNames)
628+
slices.Sort(s.dnsNames)
648629
} else {
649-
s.conf.dnsNames = append(s.conf.dnsNames, cert.Subject.CommonName)
630+
s.dnsNames = []string{cert.Subject.CommonName}
650631
log.Debug("dns: using certificate's CN as DNS name: %s", cert.Subject.CommonName)
651632
}
652633
}
@@ -695,11 +676,11 @@ func anyNameMatches(dnsNames []string, sni string) (ok bool) {
695676
// Called by 'tls' package when Client Hello is received
696677
// If the server name (from SNI) supplied by client is incorrect - we terminate the ongoing TLS handshake.
697678
func (s *Server) onGetCertificate(ch *tls.ClientHelloInfo) (*tls.Certificate, error) {
698-
if s.conf.StrictSNICheck && !anyNameMatches(s.conf.dnsNames, ch.ServerName) {
679+
if s.conf.TLSConf.StrictSNICheck && !anyNameMatches(s.dnsNames, ch.ServerName) {
699680
log.Info("dns: tls: unknown SNI in Client Hello: %s", ch.ServerName)
700681
return nil, fmt.Errorf("invalid SNI")
701682
}
702-
return &s.conf.cert, nil
683+
return s.conf.TLSConf.Cert, nil
703684
}
704685

705686
// preparePlain prepares the plain-DNS configuration for the DNS proxy.

internal/dnsforward/dns64_internal_test.go

+2
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,7 @@ func TestServer_HandleDNSRequest_dns64(t *testing.T) {
296296
UDPListenAddrs: []*net.UDPAddr{{}},
297297
TCPListenAddrs: []*net.TCPAddr{{}},
298298
UseDNS64: true,
299+
TLSConf: &TLSConfig{},
299300
Config: Config{
300301
UpstreamMode: UpstreamModeLoadBalance,
301302
EDNSClientSubnet: &EDNSClientSubnet{Enabled: false},
@@ -335,6 +336,7 @@ func TestServer_dns64WithDisabledRDNS(t *testing.T) {
335336
UDPListenAddrs: []*net.UDPAddr{{}},
336337
TCPListenAddrs: []*net.TCPAddr{{}},
337338
UseDNS64: true,
339+
TLSConf: &TLSConfig{},
338340
Config: Config{
339341
UpstreamMode: UpstreamModeLoadBalance,
340342
EDNSClientSubnet: &EDNSClientSubnet{Enabled: false},

internal/dnsforward/dnsforward.go

+51-43
Original file line numberDiff line numberDiff line change
@@ -103,54 +103,70 @@ type SystemResolvers interface {
103103
//
104104
// The zero Server is empty and ready for use.
105105
type Server struct {
106-
// dnsProxy is the DNS proxy for forwarding client's DNS requests.
107-
dnsProxy *proxy.Proxy
106+
// addrProc, if not nil, is used to process clients' IP addresses with rDNS,
107+
// WHOIS, etc.
108+
addrProc client.AddressProcessor
108109

109-
// dnsFilter is the DNS filter for filtering client's DNS requests and
110-
// responses.
111-
dnsFilter *filtering.DNSFilter
110+
// bootstrap is the resolver for upstreams' hostnames.
111+
bootstrap upstream.Resolver
112+
113+
// clientIDCache is a temporary storage for ClientIDs that were extracted
114+
// during the BeforeRequestHandler stage.
115+
clientIDCache cache.Cache
112116

113117
// dhcpServer is the DHCP server for accessing lease data.
114118
dhcpServer DHCP
115119

120+
// etcHosts contains the current data from the system's hosts files.
121+
etcHosts upstream.Resolver
122+
123+
// privateNets is the configured set of IP networks considered private.
124+
privateNets netutil.SubnetSet
125+
116126
// queryLog is the query log for client's DNS requests, responses and
117127
// filtering results.
118128
queryLog querylog.QueryLog
119129

120130
// stats is the statistics collector for client's DNS usage data.
121131
stats stats.Interface
122132

133+
// sysResolvers used to fetch system resolvers to use by default for private
134+
// PTR resolving.
135+
sysResolvers SystemResolvers
136+
123137
// access drops disallowed clients.
124138
access *accessManager
125139

140+
// anonymizer masks the client's IP addresses if needed.
141+
anonymizer *aghnet.IPMut
142+
126143
// baseLogger is used to create loggers for other entities. It should not
127144
// have a prefix and must not be nil.
128145
baseLogger *slog.Logger
129146

130-
// localDomainSuffix is the suffix used to detect internal hosts. It
131-
// must be a valid domain name plus dots on each side.
132-
localDomainSuffix string
147+
// dnsFilter is the DNS filter for filtering client's DNS requests and
148+
// responses.
149+
dnsFilter *filtering.DNSFilter
150+
151+
// dnsProxy is the DNS proxy for forwarding client's DNS requests.
152+
dnsProxy *proxy.Proxy
153+
154+
// internalProxy resolves internal requests from the application itself. It
155+
// isn't started and so no listen ports are required.
156+
internalProxy *proxy.Proxy
133157

134158
// ipset processes DNS requests using ipset data. It must not be nil after
135159
// initialization. See [newIpsetHandler].
136160
ipset *ipsetHandler
137161

138-
// privateNets is the configured set of IP networks considered private.
139-
privateNets netutil.SubnetSet
140-
141-
// addrProc, if not nil, is used to process clients' IP addresses with rDNS,
142-
// WHOIS, etc.
143-
addrProc client.AddressProcessor
144-
145-
// sysResolvers used to fetch system resolvers to use by default for private
146-
// PTR resolving.
147-
sysResolvers SystemResolvers
148-
149-
// etcHosts contains the current data from the system's hosts files.
150-
etcHosts upstream.Resolver
162+
// dns64Pref is the NAT64 prefix used for DNS64 response mapping. The major
163+
// part of DNS64 happens inside the [proxy] package, but there still are
164+
// some places where response mapping is needed (e.g. DHCP).
165+
dns64Pref netip.Prefix
151166

152-
// bootstrap is the resolver for upstreams' hostnames.
153-
bootstrap upstream.Resolver
167+
// localDomainSuffix is the suffix used to detect internal hosts. It
168+
// must be a valid domain name plus dots on each side.
169+
localDomainSuffix string
154170

155171
// bootResolvers are the resolvers that should be used for
156172
// bootstrapping along with [etcHosts].
@@ -159,34 +175,26 @@ type Server struct {
159175
// [upstream.Resolver] interface.
160176
bootResolvers []*upstream.UpstreamResolver
161177

162-
// dns64Pref is the NAT64 prefix used for DNS64 response mapping. The major
163-
// part of DNS64 happens inside the [proxy] package, but there still are
164-
// some places where response mapping is needed (e.g. DHCP).
165-
dns64Pref netip.Prefix
166-
167-
// anonymizer masks the client's IP addresses if needed.
168-
anonymizer *aghnet.IPMut
178+
// dnsNames are the DNS names from certificate (SAN) or CN value from
179+
// Subject.
180+
dnsNames []string
169181

170-
// clientIDCache is a temporary storage for ClientIDs that were extracted
171-
// during the BeforeRequestHandler stage.
172-
clientIDCache cache.Cache
173-
174-
// internalProxy resolves internal requests from the application itself. It
175-
// isn't started and so no listen ports are required.
176-
internalProxy *proxy.Proxy
182+
// conf is the current configuration of the server.
183+
conf ServerConfig
177184

178-
// isRunning is true if the DNS server is running.
179-
isRunning bool
185+
// serverLock protects Server.
186+
serverLock sync.RWMutex
180187

181188
// protectionUpdateInProgress is used to make sure that only one goroutine
182189
// updating the protection configuration after a pause is running at a time.
183190
protectionUpdateInProgress atomic.Bool
184191

185-
// conf is the current configuration of the server.
186-
conf ServerConfig
192+
// isRunning is true if the DNS server is running.
193+
isRunning bool
187194

188-
// serverLock protects Server.
189-
serverLock sync.RWMutex
195+
// hasIPAddrs is set during the certificate parsing and is true if the
196+
// configured certificate contains at least a single IP address.
197+
hasIPAddrs bool
190198
}
191199

192200
// defaultLocalDomainSuffix is the default suffix used to detect internal hosts

0 commit comments

Comments
 (0)