checker: add DNSSEC test
This commit is contained in:
parent
5d0693180d
commit
eab1af36f4
|
@ -19,6 +19,7 @@ import (
|
|||
|
||||
const (
|
||||
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
|
||||
|
@ -88,9 +89,11 @@ func check_dns(domain, ip string) (aaaa net.IP, err error) {
|
|||
|
||||
if r == nil {
|
||||
err = errors.New("response is nil")
|
||||
return
|
||||
}
|
||||
if r.Rcode != dns.RcodeSuccess {
|
||||
err = errors.New("failed to get a valid answer")
|
||||
return
|
||||
}
|
||||
|
||||
for _, answer := range r.Answer {
|
||||
|
@ -102,6 +105,160 @@ func check_dns(domain, ip string) (aaaa net.IP, err error) {
|
|||
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
|
||||
|
||||
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 {
|
||||
if errreg := std.RegisterChallengeError(100*(tunnel_version-1)+3, err); errreg != nil {
|
||||
|
|
Reference in New Issue