checker: add DNSSEC test
This commit is contained in:
parent
5d0693180d
commit
eab1af36f4
1 changed files with 174 additions and 0 deletions
|
@ -19,6 +19,7 @@ import (
|
||||||
|
|
||||||
const (
|
const (
|
||||||
DEFAULT_RESOLVER = "2a01:e0a:2b:2250::1"
|
DEFAULT_RESOLVER = "2a01:e0a:2b:2250::1"
|
||||||
|
year68 = 1 << 31 // For RFC1982 (Serial Arithmetic) calculations in 32 bits. Taken from miekg/dns
|
||||||
)
|
)
|
||||||
|
|
||||||
var verbose = false
|
var verbose = false
|
||||||
|
@ -88,9 +89,11 @@ func check_dns(domain, ip string) (aaaa net.IP, err error) {
|
||||||
|
|
||||||
if r == nil {
|
if r == nil {
|
||||||
err = errors.New("response is nil")
|
err = errors.New("response is nil")
|
||||||
|
return
|
||||||
}
|
}
|
||||||
if r.Rcode != dns.RcodeSuccess {
|
if r.Rcode != dns.RcodeSuccess {
|
||||||
err = errors.New("failed to get a valid answer")
|
err = errors.New("failed to get a valid answer")
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, answer := range r.Answer {
|
for _, answer := range r.Answer {
|
||||||
|
@ -102,6 +105,160 @@ func check_dns(domain, ip string) (aaaa net.IP, err error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func check_dnssec(domain, ip string) (err error) {
|
||||||
|
client := dns.Client{Timeout: time.Second * 5}
|
||||||
|
|
||||||
|
// Get DNSKEY
|
||||||
|
m := new(dns.Msg)
|
||||||
|
m.SetEdns0(4096, true)
|
||||||
|
m.SetQuestion(domain, dns.TypeDNSKEY)
|
||||||
|
|
||||||
|
var r *dns.Msg
|
||||||
|
r, _, err = client.Exchange(m, fmt.Sprintf("[%s]:53", ip))
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if r == nil {
|
||||||
|
return errors.New("response is nil")
|
||||||
|
}
|
||||||
|
if r.Rcode != dns.RcodeSuccess {
|
||||||
|
return errors.New("failed to get a valid answer when getting DNSKEY")
|
||||||
|
}
|
||||||
|
|
||||||
|
var rrs []dns.RR
|
||||||
|
var dnskeys []*dns.DNSKEY
|
||||||
|
var dnskeysig *dns.RRSIG
|
||||||
|
for _, answer := range r.Answer {
|
||||||
|
if t, ok := answer.(*dns.DNSKEY); ok {
|
||||||
|
dnskeys = append(dnskeys, t)
|
||||||
|
rrs = append(rrs, dns.RR(t))
|
||||||
|
} else if t, ok := answer.(*dns.RRSIG); ok {
|
||||||
|
dnskeysig = t
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
found := false
|
||||||
|
for _, dnskey := range dnskeys {
|
||||||
|
if err = dnskeysig.Verify(dnskey, rrs); err == nil {
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !found {
|
||||||
|
return fmt.Errorf("Unable to verify DNSKEY record signature: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check AAAA validity
|
||||||
|
m = new(dns.Msg)
|
||||||
|
m.SetEdns0(4096, true)
|
||||||
|
m.SetQuestion(domain, dns.TypeAAAA)
|
||||||
|
|
||||||
|
r, _, err = client.Exchange(m, fmt.Sprintf("[%s]:53", ip))
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if r == nil {
|
||||||
|
return errors.New("response is nil")
|
||||||
|
}
|
||||||
|
if r.Rcode != dns.RcodeSuccess {
|
||||||
|
return errors.New("failed to get a valid answer when getting AAAA records")
|
||||||
|
}
|
||||||
|
|
||||||
|
rrs = []dns.RR{}
|
||||||
|
var aaaas []*dns.AAAA
|
||||||
|
var aaaasig *dns.RRSIG
|
||||||
|
for _, answer := range r.Answer {
|
||||||
|
if t, ok := answer.(*dns.AAAA); ok {
|
||||||
|
aaaas = append(aaaas, t)
|
||||||
|
rrs = append(rrs, t)
|
||||||
|
} else if t, ok := answer.(*dns.RRSIG); ok {
|
||||||
|
aaaasig = t
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(aaaas) == 0 {
|
||||||
|
return errors.New("")
|
||||||
|
}
|
||||||
|
|
||||||
|
found = false
|
||||||
|
for _, dnskey := range dnskeys {
|
||||||
|
if err = aaaasig.Verify(dnskey, rrs); err == nil {
|
||||||
|
found = true
|
||||||
|
|
||||||
|
if !aaaasig.ValidityPeriod(time.Now()) {
|
||||||
|
utc := time.Now().UTC().Unix()
|
||||||
|
|
||||||
|
modi := (int64(aaaasig.Inception) - utc) / year68
|
||||||
|
ti := int64(aaaasig.Inception) + modi*year68
|
||||||
|
|
||||||
|
mode := (int64(aaaasig.Expiration) - utc) / year68
|
||||||
|
te := int64(aaaasig.Expiration) + mode*year68
|
||||||
|
|
||||||
|
if ti > utc {
|
||||||
|
return fmt.Errorf("Unable to verify AAAA record signature: signature not yet valid")
|
||||||
|
} else if utc > te {
|
||||||
|
return fmt.Errorf("Unable to verify AAAA record signature: signature expired")
|
||||||
|
} else {
|
||||||
|
return fmt.Errorf("Unable to verify AAAA record signature: signature expired or not yet valid")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !found {
|
||||||
|
return fmt.Errorf("Unable to verify AAAA record signature: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check DS
|
||||||
|
m = new(dns.Msg)
|
||||||
|
m.SetQuestion(domain, dns.TypeDS)
|
||||||
|
m.RecursionDesired = false
|
||||||
|
m.SetEdns0(4096, true)
|
||||||
|
|
||||||
|
r, _, err = client.Exchange(m, "[2a01:e0a:2b:2250::b]:53")
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if r == nil {
|
||||||
|
return errors.New("response is nil")
|
||||||
|
}
|
||||||
|
if r.Rcode != dns.RcodeSuccess {
|
||||||
|
return errors.New("failed to get a valid answer when getting DS records in parent server")
|
||||||
|
}
|
||||||
|
|
||||||
|
found = false
|
||||||
|
for _, answer := range r.Answer {
|
||||||
|
if t, ok := answer.(*dns.DS); ok {
|
||||||
|
for _, dnskey := range dnskeys {
|
||||||
|
expectedDS := dnskey.ToDS(dns.SHA256)
|
||||||
|
if expectedDS.KeyTag == t.KeyTag && expectedDS.Algorithm == t.Algorithm && expectedDS.DigestType == t.DigestType && expectedDS.Digest == t.Digest {
|
||||||
|
found = true
|
||||||
|
err = nil
|
||||||
|
break
|
||||||
|
} else {
|
||||||
|
err = fmt.Errorf("DS record found in parent zone differs from DNSKEY %v vs. %v.", expectedDS, t)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !found {
|
||||||
|
if err == nil {
|
||||||
|
return fmt.Errorf("Unable to find a valid DS record in parent zone.")
|
||||||
|
} else {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// PORT 80
|
// PORT 80
|
||||||
|
|
||||||
func check_http(ip, dn string) (err error) {
|
func check_http(ip, dn string) (err error) {
|
||||||
|
@ -367,6 +524,23 @@ func studentsChecker() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check DNSSEC (only if GLUE Ok)
|
||||||
|
if glueErr == nil {
|
||||||
|
if err := check_dnssec(std.MyDelegatedDomain(), dnsIP); err == nil {
|
||||||
|
if verbose {
|
||||||
|
log.Printf("%s just unlocked DNSSEC challenge\n", std.Login)
|
||||||
|
}
|
||||||
|
if _, err := std.UnlockChallenge(100*(tunnel_version-1)+7, ""); err != nil {
|
||||||
|
log.Printf("Unable to register challenge for %s: %s\n", std.Login, err.Error())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
std.RegisterChallengeError(100*(tunnel_version-1)+7, err)
|
||||||
|
if verbose {
|
||||||
|
log.Printf("%s and DNSSEC: %s\n", std.Login, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if errreg := std.RegisterChallengeError(100*(tunnel_version-1)+3, err); errreg != nil {
|
if errreg := std.RegisterChallengeError(100*(tunnel_version-1)+3, err); errreg != nil {
|
||||||
|
|
Reference in a new issue