From 57996a271cdeb276214282dadbabf09dfbd2cbc2 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Fri, 27 Mar 2020 13:06:23 +0100 Subject: [PATCH] maatma: update domain through dynamic-dns instead of knot interface --- token-validator/domain.go | 392 +++++++++++----------- token-validator/htdocs/views/domains.html | 1 - 2 files changed, 197 insertions(+), 196 deletions(-) diff --git a/token-validator/domain.go b/token-validator/domain.go index bd014bf..a6b0199 100644 --- a/token-validator/domain.go +++ b/token-validator/domain.go @@ -1,18 +1,11 @@ package main import ( - "crypto/sha256" - "encoding/base64" - "encoding/binary" - "encoding/hex" "encoding/json" "errors" "fmt" "log" "net" - "os" - "os/exec" - "strconv" "strings" "time" @@ -21,8 +14,9 @@ import ( ) const ( - AssociatedDomainSuffix = "adlin2020.p0m.fr." + AssociatedDomainSuffix = "adlin2021.p0m.fr." DelegatedDomainSuffix = "srs.p0m.fr." + ControlSocket = "[2a01:e0a:2b:2250::b]:53" ) var tsigSecret = map[string]string{"ddns.": "so6ZGir4GPAqINNh9U5c3A=="} @@ -52,21 +46,21 @@ func init() { if err := json.Unmarshal(body, &ue); err != nil { return nil, err } - return true, student.AddNSDelegatedDomain(ps.ByName("dn"), ue.TTL, ue.Values) + return true, student.AddNSDelegatedDomain(ps.ByName("dn"), ue.TTL, strings.Join(ue.Values, " ")) })) router.PATCH("/api/ddomains/:dn/NS", apiAuthHandler(func(student Student, ps httprouter.Params, body []byte) (interface{}, error) { var ue Entry if err := json.Unmarshal(body, &ue); err != nil { return nil, err } - return true, student.UpdateNSDelegatedDomain(ps.ByName("dn"), ue.TTL, ue.ValuesFrom, ue.Values) + return true, student.UpdateNSDelegatedDomain(ps.ByName("dn"), ue.TTL, ue.ValuesFrom, strings.Join(ue.Values, "")) })) router.DELETE("/api/ddomains/:dn/NS", apiAuthHandler(func(student Student, ps httprouter.Params, body []byte) (interface{}, error) { var ue Entry if err := json.Unmarshal(body, &ue); err != nil { return nil, err } - return true, student.DeleteRRDelegatedDomain(ps.ByName("dn"), "NS", ue.Values) + return true, student.DeleteRRDelegatedDomain(ps.ByName("dn"), "NS", strings.Join(ue.Values, " ")) })) router.GET("/api/ddomains/:dn/GLUE", apiAuthHandler(func(student Student, ps httprouter.Params, body []byte) (interface{}, error) { return student.getRRDelegatedDomain(ps.ByName("dn"), "AAAA") @@ -76,28 +70,28 @@ func init() { if err := json.Unmarshal(body, &ue); err != nil { return nil, err } - return true, student.AddGLUEDelegatedDomain(ps.ByName("dn"), ue.TTL, ue.Values) + return true, student.AddGLUEDelegatedDomain(ps.ByName("dn"), ue.TTL, strings.Join(ue.Values, " ")) })) router.PATCH("/api/ddomains/:dn/AAAA", apiAuthHandler(func(student Student, ps httprouter.Params, body []byte) (interface{}, error) { var ue Entry if err := json.Unmarshal(body, &ue); err != nil { return nil, err } - return true, student.UpdateGLUEDelegatedDomain(ps.ByName("dn"), ue.TTL, ue.ValuesFrom, ue.Values) + return true, student.UpdateGLUEDelegatedDomain(ps.ByName("dn"), ue.TTL, ue.ValuesFrom, strings.Join(ue.Values, " ")) })) router.POST("/api/ddomains/:dn/GLUE", apiAuthHandler(func(student Student, ps httprouter.Params, body []byte) (interface{}, error) { var ue Entry if err := json.Unmarshal(body, &ue); err != nil { return nil, err } - return true, student.UpdateGLUEDelegatedDomain(ps.ByName("dn"), ue.TTL, ue.ValuesFrom, ue.Values) + return true, student.UpdateGLUEDelegatedDomain(ps.ByName("dn"), ue.TTL, ue.ValuesFrom, strings.Join(ue.Values, " ")) })) router.DELETE("/api/ddomains/:dn/AAAA", apiAuthHandler(func(student Student, ps httprouter.Params, body []byte) (interface{}, error) { var ue Entry if err := json.Unmarshal(body, &ue); err != nil { return nil, err } - return true, student.DeleteRRDelegatedDomain(ps.ByName("dn"), "AAAA", ue.Values) + return true, student.DeleteRRDelegatedDomain(ps.ByName("dn"), "AAAA", strings.Join(ue.Values, " ")) })) router.GET("/api/ddomains/:dn/DS", apiAuthHandler(func(student Student, ps httprouter.Params, body []byte) (interface{}, error) { return student.getRRDelegatedDomain(ps.ByName("dn"), "DS") @@ -107,61 +101,58 @@ func init() { if err := json.Unmarshal(body, &ue); err != nil { return nil, err } - return true, student.AddDSDelegatedDomain(ps.ByName("dn"), ue.TTL, ue.Values) - })) - router.PATCH("/api/ddomains/:dn/DS", apiAuthHandler(func(student Student, ps httprouter.Params, body []byte) (interface{}, error) { - var ue Entry - if err := json.Unmarshal(body, &ue); err != nil { - return nil, err - } - return true, student.UpdateDSDelegatedDomain(ps.ByName("dn"), ue.TTL, ue.ValuesFrom, ue.Values) + return true, student.AddDSDelegatedDomain(ps.ByName("dn"), ue.TTL, strings.Join(ue.Values, " ")) })) router.DELETE("/api/ddomains/:dn/DS", apiAuthHandler(func(student Student, ps httprouter.Params, body []byte) (interface{}, error) { var ue Entry if err := json.Unmarshal(body, &ue); err != nil { return nil, err } - return true, student.DeleteRRDelegatedDomain(ps.ByName("dn"), "DS", ue.Values) + return true, student.DeleteRRDelegatedDomain(ps.ByName("dn"), "DS", strings.Join(ue.Values, " ")) })) } type Entry struct { - Domain string `json:"domain"` - TTL uint32 `json:"ttl"` - RR string `json:"rr"` - ValuesFrom string `json:"valuesfrom,omitempty"` - Values string `json:"values"` + Domain string `json:"domain"` + TTL uint32 `json:"ttl"` + RR string `json:"rr"` + ValuesFrom string `json:"valuesfrom,omitempty"` + Values []string `json:"values"` } -func runKnotc(args ...string) (out []byte, err error) { - cmd := exec.Command("knotc", args...) - cmd.Env = append(os.Environ(), - "LD_PRELOAD=/usr/lib/gcc/armv5tel-softfloat-linux-gnueabi/8.2.0/libgcc_s.so.1", - ) - - return cmd.CombinedOutput() -} - -func parseKnotZoneRead(globalDomain string, domain string) (rr []Entry, err error) { +func parseZoneRead(globalDomain string, domain string) (rr []Entry, err error) { t := new(dns.Transfer) m := new(dns.Msg) t.TsigSecret = tsigSecret m.SetAxfr(globalDomain) - m.SetTsig("ddns.", dns.HmacSHA256, 300, time.Now().Unix()) + m.SetTsig("rndc-key.", dns.HmacSHA256, 300, time.Now().Unix()) - c, err := t.In(m, "127.0.0.1:53") + c, err := t.In(m, ControlSocket) if err != nil { log.Println(globalDomain, err) return nil, err } - for l := range c { - for _, r := range l.RR { - var z dns.RFC3597 - z.ToRFC3597(r) + for { + response, ok := <-c + if !ok { + break + } + + for _, r := range response.RR { if strings.HasSuffix(r.Header().Name, domain) { - rr = append(rr, Entry{r.Header().Name, r.Header().Ttl, dns.TypeToString[r.Header().Rrtype], "", z.Rdata}) + var z string + if v, ok := r.(*dns.A); ok { + z = fmt.Sprintf("%s", v.A) + } else if v, ok := r.(*dns.AAAA); ok { + z = fmt.Sprintf("%s", v.AAAA) + } else if v, ok := r.(*dns.NS); ok { + z = fmt.Sprintf("%s", v.Ns) + } else if v, ok := r.(*dns.DS); ok { + z = fmt.Sprintf("%s", v.Digest) + } + rr = append(rr, Entry{r.Header().Name, r.Header().Ttl, dns.TypeToString[r.Header().Rrtype], "", []string{z}}) } } } @@ -194,7 +185,7 @@ func (student Student) GetAssociatedDomain(dn string) (rrs []Entry, err error) { err = errors.New(fmt.Sprintf("Unable to find domain %q.", dn)) } - if entries, errr := parseKnotZoneRead(AssociatedDomainSuffix, dn); err != nil { + if entries, errr := parseZoneRead(AssociatedDomainSuffix, dn); err != nil { return nil, errr } else { for _, e := range entries { @@ -218,27 +209,41 @@ func (student Student) AddAssociatedDomains() (err error) { rrAd.Hdr = dns.RR_Header{Name: student.myAssociatedDomain(), Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: 0} m1.Remove([]dns.RR{rrAd}) - rrA := new(dns.A) - rrA.Hdr = dns.RR_Header{Name: student.myAssociatedDomain(), Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: 3600} - rrA.A = net.IPv4(82, 64, 31, 248) - m1.Insert([]dns.RR{rrA}) - rrAAAAd := new(dns.AAAA) rrAAAAd.Hdr = dns.RR_Header{Name: student.myAssociatedDomain(), Rrtype: dns.TypeAAAA, Class: dns.ClassINET, Ttl: 0} m1.Remove([]dns.RR{rrAAAAd}) + c := new(dns.Client) + c.TsigSecret = tsigSecret + m1.SetTsig("rndc-key.", dns.HmacSHA256, 300, time.Now().Unix()) + + _, _, err = c.Exchange(m1, ControlSocket) + if err != nil { + return + } + + m2 := new(dns.Msg) + m2.Id = dns.Id() + m2.Opcode = dns.OpcodeUpdate + m2.Question = make([]dns.Question, 1) + m2.Question[0] = dns.Question{AssociatedDomainSuffix, dns.TypeSOA, dns.ClassINET} + + rrA := new(dns.A) + rrA.Hdr = dns.RR_Header{Name: student.myAssociatedDomain(), Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: 3600} + rrA.A = net.IPv4(82, 64, 31, 248) + m2.Insert([]dns.RR{rrA}) + rrAAAA := new(dns.AAAA) rrAAAA.Hdr = dns.RR_Header{Name: student.myAssociatedDomain(), Rrtype: dns.TypeAAAA, Class: dns.ClassINET, Ttl: 3600} rrAAAA.AAAA = studentIP(student.Id) rrAAAA.AAAA[15] = 1 - m1.Insert([]dns.RR{rrAAAA}) + m2.Insert([]dns.RR{rrAAAA}) - c := new(dns.Client) + c = new(dns.Client) c.TsigSecret = tsigSecret - m1.SetTsig("ddns.", dns.HmacSHA256, 300, time.Now().Unix()) - - _, _, err = c.Exchange(m1, "127.0.0.1:53") + m2.SetTsig("rndc-key.", dns.HmacSHA256, 300, time.Now().Unix()) + _, _, err = c.Exchange(m2, ControlSocket) return } @@ -259,7 +264,7 @@ func (student Student) getRRDelegatedDomain(dn string, rr string) (rrs []Entry, err = errors.New(fmt.Sprintf("Unable to find domain %q.", dn)) } - if entries, errr := parseKnotZoneRead(DelegatedDomainSuffix, dn); err != nil { + if entries, errr := parseZoneRead(DelegatedDomainSuffix, dn); err != nil { return nil, errr } else { for _, e := range entries { @@ -274,20 +279,22 @@ func (student Student) getRRDelegatedDomain(dn string, rr string) (rrs []Entry, func (student Student) AddNSDelegatedDomain(dn string, ttl uint32, ns string) (err error) { for _, d := range []string{student.MyDelegatedDomain()} { - for _, cmd := range [][]string{ - []string{"zone-begin", DelegatedDomainSuffix}, - []string{"zone-set", DelegatedDomainSuffix, d, fmt.Sprintf("%d", ttl), "NS", ns}, - []string{"zone-commit", DelegatedDomainSuffix}, - } { - var out []byte - out, err = runKnotc(cmd...) - if err != nil && cmd[0] != "zone-unset" { - log.Printf("An error occurs on command '%s': %s", strings.Join(cmd, " "), err.Error()) - err = errors.New(string(out)) - runKnotc("zone-abort", DelegatedDomainSuffix) - return - } - } + m1 := new(dns.Msg) + m1.Id = dns.Id() + m1.Opcode = dns.OpcodeUpdate + m1.Question = make([]dns.Question, 1) + m1.Question[0] = dns.Question{DelegatedDomainSuffix, dns.TypeSOA, dns.ClassINET} + + rrNS := new(dns.NS) + rrNS.Hdr = dns.RR_Header{Name: d, Rrtype: dns.TypeNS, Class: dns.ClassINET, Ttl: ttl} + rrNS.Ns = ns + m1.Insert([]dns.RR{rrNS}) + + c := new(dns.Client) + c.TsigSecret = tsigSecret + m1.SetTsig("rndc-key.", dns.HmacSHA256, 300, time.Now().Unix()) + + _, _, err = c.Exchange(m1, ControlSocket) } return @@ -295,21 +302,27 @@ func (student Student) AddNSDelegatedDomain(dn string, ttl uint32, ns string) (e func (student Student) UpdateNSDelegatedDomain(dn string, ttl uint32, oldns string, ns string) (err error) { for _, d := range []string{student.MyDelegatedDomain()} { - for _, cmd := range [][]string{ - []string{"zone-begin", DelegatedDomainSuffix}, - []string{"zone-unset", DelegatedDomainSuffix, d, "NS", oldns}, - []string{"zone-set", DelegatedDomainSuffix, d, fmt.Sprintf("%d", ttl), "NS", ns}, - []string{"zone-commit", DelegatedDomainSuffix}, - } { - var out []byte - out, err = runKnotc(cmd...) - if err != nil && cmd[0] != "zone-unset" { - log.Printf("An error occurs on command '%s': %s", strings.Join(cmd, " "), err.Error()) - err = errors.New(string(out)) - runKnotc("zone-abort", DelegatedDomainSuffix) - return - } - } + m1 := new(dns.Msg) + m1.Id = dns.Id() + m1.Opcode = dns.OpcodeUpdate + m1.Question = make([]dns.Question, 1) + m1.Question[0] = dns.Question{DelegatedDomainSuffix, dns.TypeSOA, dns.ClassINET} + + rrOldNS := new(dns.NS) + rrOldNS.Hdr = dns.RR_Header{Name: d, Rrtype: dns.TypeNS, Class: dns.ClassINET} + rrOldNS.Ns = oldns + m1.Remove([]dns.RR{rrOldNS}) + + rrNS := new(dns.NS) + rrNS.Hdr = dns.RR_Header{Name: d, Rrtype: dns.TypeNS, Class: dns.ClassINET, Ttl: ttl} + rrNS.Ns = ns + m1.Insert([]dns.RR{rrNS}) + + c := new(dns.Client) + c.TsigSecret = tsigSecret + m1.SetTsig("rndc-key.", dns.HmacSHA256, 300, time.Now().Unix()) + + _, _, err = c.Exchange(m1, ControlSocket) } return @@ -329,20 +342,24 @@ func (student Student) AddGLUEDelegatedDomain(dn string, ttl uint32, aaaa string return } - for _, cmd := range [][]string{ - []string{"zone-begin", DelegatedDomainSuffix}, - []string{"zone-set", DelegatedDomainSuffix, dn, fmt.Sprintf("%d", ttl), "AAAA", aaaa}, - []string{"zone-commit", DelegatedDomainSuffix}, - } { - var out []byte - out, err = runKnotc(cmd...) - if err != nil && cmd[0] != "zone-unset" { - log.Printf("An error occurs on command '%s': %s", strings.Join(cmd, " "), err.Error()) - err = errors.New(string(out)) - runKnotc("zone-abort", DelegatedDomainSuffix) - return - } + m1 := new(dns.Msg) + m1.Id = dns.Id() + m1.Opcode = dns.OpcodeUpdate + m1.Question = make([]dns.Question, 1) + m1.Question[0] = dns.Question{DelegatedDomainSuffix, dns.TypeSOA, dns.ClassINET} + + var rr dns.RR + rr, err = dns.NewRR(fmt.Sprintf("%s %d IN AAAA %s", dn, ttl, aaaa)) + if err != nil { + return } + m1.Insert([]dns.RR{rr}) + + c := new(dns.Client) + c.TsigSecret = tsigSecret + m1.SetTsig("rndc-key.", dns.HmacSHA256, 300, time.Now().Unix()) + + _, _, err = c.Exchange(m1, ControlSocket) return } @@ -361,96 +378,81 @@ func (student Student) UpdateGLUEDelegatedDomain(dn string, ttl uint32, oldaaaa return } - for _, cmd := range [][]string{ - []string{"zone-begin", DelegatedDomainSuffix}, - []string{"zone-unset", DelegatedDomainSuffix, student.MyDelegatedDomain(), "AAAA", oldaaaa}, - []string{"zone-unset", DelegatedDomainSuffix, dn, "AAAA", oldaaaa}, - []string{"zone-set", DelegatedDomainSuffix, dn, fmt.Sprintf("%d", ttl), "AAAA", aaaa}, - []string{"zone-commit", DelegatedDomainSuffix}, - } { - var out []byte - out, err = runKnotc(cmd...) - if err != nil && cmd[0] != "zone-unset" { - log.Printf("An error occurs on command '%s': %s", strings.Join(cmd, " "), err.Error()) - err = errors.New(string(out)) - runKnotc("zone-abort", DelegatedDomainSuffix) - return - } - } + m1 := new(dns.Msg) + m1.Id = dns.Id() + m1.Opcode = dns.OpcodeUpdate + m1.Question = make([]dns.Question, 1) + m1.Question[0] = dns.Question{DelegatedDomainSuffix, dns.TypeSOA, dns.ClassINET} + var rr dns.RR + + rr, err = dns.NewRR(fmt.Sprintf("%s IN AAAA %s", dn, oldaaaa)) + if err != nil { + return + } + m1.Remove([]dns.RR{rr}) + + rr, err = dns.NewRR(fmt.Sprintf("%s %d IN AAAA %s", dn, ttl, aaaa)) + if err != nil { + return + } + m1.Insert([]dns.RR{rr}) + + c := new(dns.Client) + c.TsigSecret = tsigSecret + m1.SetTsig("rndc-key.", dns.HmacSHA256, 300, time.Now().Unix()) + + _, _, err = c.Exchange(m1, ControlSocket) return } func (student Student) AddDSDelegatedDomain(dn string, ttl uint32, rdata string) (err error) { - dnskey := strings.Split(rdata, " ") - - if len(dnskey) != 4 { - return errors.New("Wrong number of value for this record") - } - - dshash := sha256.New() - dshash.Write([]byte("nemunai.re")) - - var flag uint64 - if flag, err = strconv.ParseUint(dnskey[1], 10, 16); err != nil { - return - } - binary.Write(dshash, binary.BigEndian, flag) - - var proto uint8 = 3 - dshash.Write([]byte{proto}) - - var alg uint64 - if alg, err = strconv.ParseUint(dnskey[2], 10, 8); err != nil { - return - } - dshash.Write([]byte{uint8(alg)}) - - var pubkey []byte - if pubkey, err = base64.StdEncoding.DecodeString(strings.Replace(dnskey[3], " ", "", -1)); err != nil { - return - } - dshash.Write(pubkey) - - for _, d := range []string{student.MyDelegatedDomain()} { - for _, cmd := range [][]string{ - []string{"zone-begin", DelegatedDomainSuffix}, - []string{"zone-set", DelegatedDomainSuffix, d, fmt.Sprintf("%d", ttl), "DS", dnskey[0], dnskey[2], hex.EncodeToString(dshash.Sum(nil))}, - []string{"zone-commit", DelegatedDomainSuffix}, - } { - var out []byte - out, err = runKnotc(cmd...) - if err != nil && cmd[0] != "zone-unset" { - log.Printf("An error occurs on command '%s': %s", strings.Join(cmd, " "), err.Error()) - err = errors.New(string(out)) - runKnotc("zone-abort", DelegatedDomainSuffix) - return - } + domains := []string{student.MyDelegatedDomain()} + found := false + for _, d := range domains { + if dn == d { + found = true } } - return -} - -func (student Student) UpdateDSDelegatedDomain(dn string, ttl uint32, oldds string, ds string) (err error) { - for _, d := range []string{student.MyDelegatedDomain()} { - for _, cmd := range [][]string{ - []string{"zone-begin", DelegatedDomainSuffix}, - []string{"zone-unset", DelegatedDomainSuffix, d, "DS", oldds}, - []string{"zone-set", DelegatedDomainSuffix, d, fmt.Sprintf("%d", ttl), "DS", ds}, - []string{"zone-commit", DelegatedDomainSuffix}, - } { - var out []byte - out, err = runKnotc(cmd...) - if err != nil && cmd[0] != "zone-unset" { - log.Printf("An error occurs on command '%s': %s", strings.Join(cmd, " "), err.Error()) - err = errors.New(string(out)) - runKnotc("zone-abort", DelegatedDomainSuffix) - return - } - } + if !found { + err = errors.New(fmt.Sprintf("Unable to find domain %q in your whitelist.", dn)) + return } + var rr dns.RR + rr, err = dns.NewRR(fmt.Sprintf("%s IN DNSKEY %s", dn, rdata)) + if err != nil { + return + } + + var dnskey dns.DNSKEY + if v, ok := rr.(*dns.DNSKEY); ok { + dnskey = *v + } else { + err = errors.New(fmt.Sprintf("This is not a valid DNSKEY record.")) + return + } + + m1 := new(dns.Msg) + m1.Id = dns.Id() + m1.Opcode = dns.OpcodeUpdate + m1.Question = make([]dns.Question, 1) + m1.Question[0] = dns.Question{DelegatedDomainSuffix, dns.TypeSOA, dns.ClassINET} + + var ds *dns.DS + ds = dnskey.ToDS(dns.SHA256) + if ds == nil { + err = errors.New(fmt.Sprintf("Unable to generate corresponding DS record, please check given data.")) + return + } + m1.Insert([]dns.RR{ds}) + + c := new(dns.Client) + c.TsigSecret = tsigSecret + m1.SetTsig("rndc-key.", dns.HmacSHA256, 300, time.Now().Unix()) + + _, _, err = c.Exchange(m1, ControlSocket) return } @@ -468,23 +470,23 @@ func (student Student) DeleteRRDelegatedDomain(dn string, rr string, values ...s return } - zu := []string{"zone-unset", DelegatedDomainSuffix, dn, rr} - zu = append(zu, values...) + m1 := new(dns.Msg) + m1.Id = dns.Id() + m1.Opcode = dns.OpcodeUpdate + m1.Question = make([]dns.Question, 1) + m1.Question[0] = dns.Question{DelegatedDomainSuffix, dns.TypeSOA, dns.ClassINET} - for _, cmd := range [][]string{ - []string{"zone-begin", DelegatedDomainSuffix}, - zu, - []string{"zone-commit", DelegatedDomainSuffix}, - } { - var out []byte - out, err = runKnotc(cmd...) - if err != nil { - log.Printf("An error occurs on command '%s': %s", strings.Join(cmd, " "), err.Error()) - err = errors.New(string(out)) - runKnotc("zone-abort", DelegatedDomainSuffix) - return - } + rrr, errr := dns.NewRR(fmt.Sprintf("%s %s %s", dn, rr, strings.Join(values, " "))) + if errr != nil { + return errr } + m1.Remove([]dns.RR{rrr}) + + c := new(dns.Client) + c.TsigSecret = tsigSecret + m1.SetTsig("rndc-key.", dns.HmacSHA256, 300, time.Now().Unix()) + + _, _, err = c.Exchange(m1, ControlSocket) return } diff --git a/token-validator/htdocs/views/domains.html b/token-validator/htdocs/views/domains.html index 3b08590..016ea11 100644 --- a/token-validator/htdocs/views/domains.html +++ b/token-validator/htdocs/views/domains.html @@ -155,7 +155,6 @@ {{ val }} -