This repository has been archived on 2024-03-03. You can view files and clone it, but cannot push or open issues or pull requests.
adlin/token-validator/domain.go
2020-02-21 01:40:15 +01:00

495 lines
15 KiB
Go

package main
import (
"crypto/sha256"
"encoding/base64"
"encoding/binary"
"encoding/hex"
"encoding/json"
"errors"
"fmt"
"log"
"net"
"os"
"os/exec"
"strconv"
"strings"
"time"
"github.com/julienschmidt/httprouter"
"github.com/miekg/dns"
)
const (
AssociatedDomainSuffix = "adlin2020.p0m.fr."
DelegatedDomainSuffix = "srs.p0m.fr."
)
var tsigSecret = map[string]string{"ddns.": ""}
func init() {
router.GET("/api/adomains/", apiAuthHandler(func (student Student, ps httprouter.Params, body []byte) (interface{}, error) {
return student.GetAssociatedDomains(), nil
}))
router.POST("/api/adomains/", apiAuthHandler(func (student Student, ps httprouter.Params, body []byte) (interface{}, error) {
return true, student.AddAssociatedDomains()
}))
router.GET("/api/adomains/:dn", apiAuthHandler(func (student Student, ps httprouter.Params, body []byte) (interface{}, error) {
return student.GetAssociatedDomain(ps.ByName("dn"))
}))
router.GET("/api/ddomains/", apiAuthHandler(func (student Student, ps httprouter.Params, body []byte) (interface{}, error) {
return []string{student.MyDelegatedDomain()}, nil
}))
router.GET("/api/ddomains/:dn/", apiAuthHandler(func (student Student, ps httprouter.Params, body []byte) (interface{}, error) {
return student.getRRDelegatedDomain(ps.ByName("dn"), "")
}))
router.GET("/api/ddomains/:dn/NS", apiAuthHandler(func (student Student, ps httprouter.Params, body []byte) (interface{}, error) {
return student.getRRDelegatedDomain(ps.ByName("dn"), "NS")
}))
router.POST("/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.AddNSDelegatedDomain(ps.ByName("dn"), ue.TTL, 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)
}))
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)
}))
router.GET("/api/ddomains/:dn/GLUE", apiAuthHandler(func (student Student, ps httprouter.Params, body []byte) (interface{}, error) {
return student.getRRDelegatedDomain(ps.ByName("dn"), "AAAA")
}))
router.POST("/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.AddGLUEDelegatedDomain(ps.ByName("dn"), ue.TTL, 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)
}))
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)
}))
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)
}))
router.GET("/api/ddomains/:dn/DS", apiAuthHandler(func (student Student, ps httprouter.Params, body []byte) (interface{}, error) {
return student.getRRDelegatedDomain(ps.ByName("dn"), "DS")
}))
router.POST("/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.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)
}))
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)
}))
}
type Entry struct {
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) {
t := new(dns.Transfer)
m := new(dns.Msg)
t.TsigSecret = tsigSecret
m.SetAxfr(globalDomain)
m.SetTsig("ddns.", dns.HmacSHA256, 300, time.Now().Unix())
c, err := t.In(m, "127.0.0.1:53")
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)
if strings.HasSuffix(r.Header().Name, domain) {
rr = append(rr, Entry{r.Header().Name, r.Header().Ttl, dns.TypeToString[r.Header().Rrtype], "", z.Rdata})
}
}
}
return
}
func (student Student) myAssociatedDomain() (string) {
return fmt.Sprintf("%s.%s", strings.Trim(strings.Replace(student.Login, "_", "-", -1), "-_"), AssociatedDomainSuffix)
}
func (student Student) GetAssociatedDomains() (ds []string) {
studentDomain := student.myAssociatedDomain()
ds = append(ds, studentDomain)
return
}
func (student Student) GetAssociatedDomain(dn string) (rrs []Entry, err error) {
domains := student.GetAssociatedDomains()
found := false
for _, d := range domains {
if d == dn {
found = true
}
}
if !found {
err = errors.New(fmt.Sprintf("Unable to find domain %q.", dn))
}
if entries, errr := parseKnotZoneRead(AssociatedDomainSuffix, dn); err != nil {
return nil, errr
} else {
for _, e := range entries {
if e.RR != "RRSIG" && e.RR != "NSEC" && e.RR != "NSEC3" {
rrs = append(rrs, e)
}
}
}
return
}
func (student Student) AddAssociatedDomains() (err error) {
m1 := new(dns.Msg)
m1.Id = dns.Id()
m1.Opcode = dns.OpcodeUpdate
m1.Question = make([]dns.Question, 1)
m1.Question[0] = dns.Question{AssociatedDomainSuffix, dns.TypeSOA, dns.ClassINET}
rrAd := new(dns.A)
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})
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})
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")
return
}
func (student Student) MyDelegatedDomain() (string) {
return fmt.Sprintf("%s.%s", strings.Trim(strings.Replace(student.Login, "_", "-", -1), "-_"), DelegatedDomainSuffix)
}
func (student Student) getRRDelegatedDomain(dn string, rr string) (rrs []Entry, err error) {
domains := []string{student.MyDelegatedDomain()}
found := false
for _, d := range domains {
if d == dn {
found = true
}
}
if !found {
err = errors.New(fmt.Sprintf("Unable to find domain %q.", dn))
}
if entries, errr := parseKnotZoneRead(DelegatedDomainSuffix, dn); err != nil {
return nil, errr
} else {
for _, e := range entries {
if e.RR == rr && strings.HasSuffix(strings.TrimSuffix(e.Domain, "."), strings.TrimSuffix(dn, ".")) {
rrs = append(rrs, e)
}
}
}
return
}
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
}
}
}
return
}
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
}
}
}
return
}
func (student Student) AddGLUEDelegatedDomain(dn string, ttl uint32, aaaa string) (err error) {
domains := []string{student.MyDelegatedDomain()}
found := false
for _, d := range domains {
if strings.HasSuffix(dn, d) {
found = true
}
}
if !found {
err = errors.New(fmt.Sprintf("Unable to find domain %q in your whitelist.", dn))
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
}
}
return
}
func (student Student) UpdateGLUEDelegatedDomain(dn string, ttl uint32, oldaaaa string, aaaa string) (err error) {
domains := []string{student.MyDelegatedDomain()}
found := false
for _, d := range domains {
if strings.HasSuffix(dn, d) {
found = true
}
}
if !found {
err = errors.New(fmt.Sprintf("Unable to find domain %q in your whitelist.", dn))
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
}
}
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
}
}
}
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
}
}
}
return
}
func (student Student) DeleteRRDelegatedDomain(dn string, rr string, values ...string) (err error) {
domains := []string{student.MyDelegatedDomain()}
found := false
for _, d := range domains {
if strings.HasSuffix(dn, d) {
found = true
}
}
if !found {
err = errors.New(fmt.Sprintf("Unable to find domain %q in your whitelist.", dn))
return
}
zu := []string{"zone-unset", DelegatedDomainSuffix, dn, rr}
zu = append(zu, values...)
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
}
}
return
}