Merge: + dnsforward: add dnssec_enabled option

Close #66

* commit '79bfa6a72b6cda8c0f5211a3a6df0293c4b6c7d1':
  + client: handle DNSSEC setting
  + dnsforward: add dnssec_enabled option
This commit is contained in:
Simon Zolin 2020-03-18 18:21:06 +03:00
commit 72c20acb86
9 changed files with 104 additions and 6 deletions

View File

@ -888,6 +888,7 @@ Response:
"blocking_ipv4": "1.2.3.4", "blocking_ipv4": "1.2.3.4",
"blocking_ipv6": "1:2:3::4", "blocking_ipv6": "1:2:3::4",
"edns_cs_enabled": true | false, "edns_cs_enabled": true | false,
"dnssec_enabled": true | false
"disable_ipv6": true | false, "disable_ipv6": true | false,
} }
@ -905,6 +906,7 @@ Request:
"blocking_ipv4": "1.2.3.4", "blocking_ipv4": "1.2.3.4",
"blocking_ipv6": "1:2:3::4", "blocking_ipv6": "1:2:3::4",
"edns_cs_enabled": true | false, "edns_cs_enabled": true | false,
"dnssec_enabled": true | false
"disable_ipv6": true | false, "disable_ipv6": true | false,
} }
@ -1254,6 +1256,7 @@ Response:
} }
... ...
], ],
"answer_dnssec": true,
"client":"127.0.0.1", "client":"127.0.0.1",
"elapsedMs":"0.098403", "elapsedMs":"0.098403",
"filterId":1, "filterId":1,

View File

@ -479,5 +479,7 @@
"install_static_configure": "We have detected that a dynamic IP address is used — <0>{{ip}}</0>. Do you want to use it as your static address?", "install_static_configure": "We have detected that a dynamic IP address is used — <0>{{ip}}</0>. Do you want to use it as your static address?",
"confirm_static_ip": "AdGuard Home will configure {{ip}} to be your static IP address. Do you want to proceed?", "confirm_static_ip": "AdGuard Home will configure {{ip}} to be your static IP address. Do you want to proceed?",
"list_updated": "{{count}} list updated", "list_updated": "{{count}} list updated",
"list_updated_plural": "{{count}} lists updated" "list_updated_plural": "{{count}} lists updated",
} "dnssec_enable": "Enable DNSSEC",
"dnssec_enable_desc": "Set DNSSEC flag in the outcoming DNS queries and check the result (DNSSEC-enabled resolver is required)"
}

View File

@ -65,6 +65,18 @@ let Form = ({
/> />
</div> </div>
</div> </div>
<div className="col-12">
<div className="form__group form__group--settings">
<Field
name="dnssec_enabled"
type="checkbox"
component={renderSelectField}
placeholder={t('dnssec_enable')}
disabled={processing}
subtitle={t('dnssec_enable_desc')}
/>
</div>
</div>
<div className="col-12"> <div className="col-12">
<div className="form__group form__group--settings"> <div className="form__group form__group--settings">
<Field <Field

View File

@ -16,6 +16,7 @@ const Config = ({ t, dnsConfig, setDnsConfig }) => {
blocking_ipv4, blocking_ipv4,
blocking_ipv6, blocking_ipv6,
edns_cs_enabled, edns_cs_enabled,
dnssec_enabled,
disable_ipv6, disable_ipv6,
processingSetConfig, processingSetConfig,
} = dnsConfig; } = dnsConfig;
@ -35,6 +36,7 @@ const Config = ({ t, dnsConfig, setDnsConfig }) => {
blocking_ipv6, blocking_ipv6,
edns_cs_enabled, edns_cs_enabled,
disable_ipv6, disable_ipv6,
dnssec_enabled,
}} }}
onSubmit={handleFormSubmit} onSubmit={handleFormSubmit}
processing={processingSetConfig} processing={processingSetConfig}

View File

@ -45,6 +45,7 @@ const dnsConfig = handleActions(
blocking_ipv6: DEFAULT_BLOCKING_IPV6, blocking_ipv6: DEFAULT_BLOCKING_IPV6,
edns_cs_enabled: false, edns_cs_enabled: false,
disable_ipv6: false, disable_ipv6: false,
dnssec_enabled: false,
}, },
); );

View File

@ -136,6 +136,8 @@ type FilteringConfig struct {
EnableEDNSClientSubnet bool `yaml:"edns_client_subnet"` // Enable EDNS Client Subnet option EnableEDNSClientSubnet bool `yaml:"edns_client_subnet"` // Enable EDNS Client Subnet option
EnableDNSSEC bool `yaml:"enable_dnssec"` // Set DNSSEC flag in outcoming DNS request
// Respond with an empty answer to all AAAA requests // Respond with an empty answer to all AAAA requests
AAAADisabled bool `yaml:"aaaa_disabled"` AAAADisabled bool `yaml:"aaaa_disabled"`
@ -512,6 +514,7 @@ type dnsContext struct {
err error // error returned from the module err error // error returned from the module
protectionEnabled bool // filtering is enabled, dnsfilter object is ready protectionEnabled bool // filtering is enabled, dnsfilter object is ready
responseFromUpstream bool // response is received from upstream servers responseFromUpstream bool // response is received from upstream servers
origReqDNSSEC bool // DNSSEC flag in the original request from user
} }
const ( const (
@ -588,6 +591,18 @@ func processUpstream(ctx *dnsContext) int {
} }
} }
if s.conf.EnableDNSSEC {
opt := d.Req.IsEdns0()
if opt == nil {
log.Debug("DNS: Adding OPT record with DNSSEC flag")
d.Req.SetEdns0(4096, true)
} else if !opt.Do() {
opt.SetDo(true)
} else {
ctx.origReqDNSSEC = true
}
}
// request was not filtered so let it be processed further // request was not filtered so let it be processed further
err := s.dnsProxy.Resolve(d) err := s.dnsProxy.Resolve(d)
if err != nil { if err != nil {
@ -599,6 +614,50 @@ func processUpstream(ctx *dnsContext) int {
return resultDone return resultDone
} }
// Process DNSSEC after response from upstream server
func processDNSSECAfterResponse(ctx *dnsContext) int {
d := ctx.proxyCtx
if !ctx.responseFromUpstream || // don't process response if it's not from upstream servers
!ctx.srv.conf.EnableDNSSEC {
return resultDone
}
optResp := d.Res.IsEdns0()
if !ctx.origReqDNSSEC && optResp != nil && optResp.Do() {
return resultDone
}
// Remove RRSIG records from response
// because there is no DO flag in the original request from client,
// but we have EnableDNSSEC set, so we have set DO flag ourselves,
// and now we have to clean up the DNS records our client didn't ask for.
answers := []dns.RR{}
for _, a := range d.Res.Answer {
switch a.(type) {
case *dns.RRSIG:
log.Debug("Removing RRSIG record from response: %v", a)
default:
answers = append(answers, a)
}
}
d.Res.Answer = answers
answers = []dns.RR{}
for _, a := range d.Res.Ns {
switch a.(type) {
case *dns.RRSIG:
log.Debug("Removing RRSIG record from response: %v", a)
default:
answers = append(answers, a)
}
}
d.Res.Ns = answers
return resultDone
}
// Apply filtering logic after we have received response from upstream servers // Apply filtering logic after we have received response from upstream servers
func processFilteringAfterResponse(ctx *dnsContext) int { func processFilteringAfterResponse(ctx *dnsContext) int {
s := ctx.srv s := ctx.srv
@ -606,10 +665,6 @@ func processFilteringAfterResponse(ctx *dnsContext) int {
res := ctx.result res := ctx.result
var err error var err error
if !ctx.responseFromUpstream {
return resultDone // don't process response if it's not from upstream servers
}
if res.Reason == dnsfilter.ReasonRewrite && len(res.CanonName) != 0 { if res.Reason == dnsfilter.ReasonRewrite && len(res.CanonName) != 0 {
d.Req.Question[0] = ctx.origQuestion d.Req.Question[0] = ctx.origQuestion
d.Res.Question[0] = ctx.origQuestion d.Res.Question[0] = ctx.origQuestion
@ -688,14 +743,19 @@ func (s *Server) handleDNSRequest(p *proxy.Proxy, d *proxy.DNSContext) error {
processInitial, processInitial,
processFilteringBeforeRequest, processFilteringBeforeRequest,
processUpstream, processUpstream,
processDNSSECAfterResponse,
processFilteringAfterResponse, processFilteringAfterResponse,
processQueryLogsAndStats, processQueryLogsAndStats,
} }
for _, process := range mods { for _, process := range mods {
r := process(ctx) r := process(ctx)
switch r { switch r {
case resultDone:
// continue: call the next filter
case resultFinish: case resultFinish:
return nil return nil
case resultError: case resultError:
return ctx.err return ctx.err
} }

View File

@ -28,6 +28,7 @@ type dnsConfigJSON struct {
BlockingIPv4 string `json:"blocking_ipv4"` BlockingIPv4 string `json:"blocking_ipv4"`
BlockingIPv6 string `json:"blocking_ipv6"` BlockingIPv6 string `json:"blocking_ipv6"`
EDNSCSEnabled bool `json:"edns_cs_enabled"` EDNSCSEnabled bool `json:"edns_cs_enabled"`
DNSSECEnabled bool `json:"dnssec_enabled"`
DisableIPv6 bool `json:"disable_ipv6"` DisableIPv6 bool `json:"disable_ipv6"`
} }
@ -40,6 +41,7 @@ func (s *Server) handleGetConfig(w http.ResponseWriter, r *http.Request) {
resp.BlockingIPv6 = s.conf.BlockingIPv6 resp.BlockingIPv6 = s.conf.BlockingIPv6
resp.RateLimit = s.conf.Ratelimit resp.RateLimit = s.conf.Ratelimit
resp.EDNSCSEnabled = s.conf.EnableEDNSClientSubnet resp.EDNSCSEnabled = s.conf.EnableEDNSClientSubnet
resp.DNSSECEnabled = s.conf.EnableDNSSEC
resp.DisableIPv6 = s.conf.AAAADisabled resp.DisableIPv6 = s.conf.AAAADisabled
s.RUnlock() s.RUnlock()
@ -119,6 +121,10 @@ func (s *Server) handleSetConfig(w http.ResponseWriter, r *http.Request) {
restart = true restart = true
} }
if js.Exists("dnssec_enabled") {
s.conf.EnableDNSSEC = req.DNSSECEnabled
}
if js.Exists("disable_ipv6") { if js.Exists("disable_ipv6") {
s.conf.AAAADisabled = req.DisableIPv6 s.conf.AAAADisabled = req.DisableIPv6
} }

View File

@ -1110,6 +1110,8 @@ definitions:
type: "string" type: "string"
edns_cs_enabled: edns_cs_enabled:
type: "boolean" type: "boolean"
dnssec_enabled:
type: "boolean"
UpstreamsConfig: UpstreamsConfig:
type: "object" type: "object"
@ -1517,6 +1519,8 @@ definitions:
description: "Answer from upstream server (optional)" description: "Answer from upstream server (optional)"
items: items:
$ref: "#/definitions/DnsAnswer" $ref: "#/definitions/DnsAnswer"
answer_dnssec:
type: "boolean"
client: client:
type: "string" type: "string"
example: "192.168.0.1" example: "192.168.0.1"

View File

@ -286,7 +286,15 @@ func logEntryToJSONEntry(entry *logEntry) map[string]interface{} {
if msg != nil { if msg != nil {
jsonEntry["status"] = dns.RcodeToString[msg.Rcode] jsonEntry["status"] = dns.RcodeToString[msg.Rcode]
opt := msg.IsEdns0()
dnssecOk := false
if opt != nil {
dnssecOk = opt.Do()
}
jsonEntry["answer_dnssec"] = dnssecOk
} }
if len(entry.Result.Rule) > 0 { if len(entry.Result.Rule) > 0 {
jsonEntry["rule"] = entry.Result.Rule jsonEntry["rule"] = entry.Result.Rule
jsonEntry["filterId"] = entry.Result.FilterID jsonEntry["filterId"] = entry.Result.FilterID