From c1739856cec0f108312b3cd2c7286aa98df3d7fb Mon Sep 17 00:00:00 2001 From: nhyatt Date: Mon, 15 Jan 2024 15:02:37 -0600 Subject: [PATCH] minor tweaks for multi-line nsupdate --- assets/html/index.tplt | 2 +- internal/config/struct-config.go | 10 +++--- internal/nsupdate/nsupdate.go | 52 +++++++++++++++++++++++----- internal/nsupdate/struct-nsupdate.go | 5 +-- main.go | 44 +++++++++++++++++++++++ 5 files changed, 97 insertions(+), 16 deletions(-) diff --git a/assets/html/index.tplt b/assets/html/index.tplt index b65dbd7..014a290 100644 --- a/assets/html/index.tplt +++ b/assets/html/index.tplt @@ -19,7 +19,7 @@ - [txtdig +short txt istheinternetonfire.app [json] + [txtdig +short txt istheinternetonfire.app | sed 's/" "//g' |sed 's/%n/\n/g' [json]
{{- if gt (len .CVEs) 0 }} diff --git a/internal/config/struct-config.go b/internal/config/struct-config.go index da7977d..864f08c 100644 --- a/internal/config/struct-config.go +++ b/internal/config/struct-config.go @@ -32,10 +32,12 @@ type Config struct { RefreshSeconds int `default:"14400" env:"refresh_seconds"` // nsupdate - NSUKey string `env:"nsupdate-key" secret:"true"` - NSUPort int `default:"53" env:"nsupdate-port"` - NSUServer string `default:"ns1.example.com" env:"nsupdate-server"` - NSUZone string `default:"example.com" env:"nsupdate-zone"` + NSUKeyLabel string `env:"nsupdate-key-label"` + NSUKey string `env:"nsupdate-key" secret:"true"` + NSUKeyAlgorithm string `default:"hmac-sha512" env:"nsupdate-key-algorithm"` + NSUPort int `default:"53" env:"nsupdate-port"` + NSUServer string `default:"ns1.example.com" env:"nsupdate-server"` + NSUZone string `default:"example.com" env:"nsupdate-zone"` } // New initializes the config variable for use with a prepared set of defaults. diff --git a/internal/nsupdate/nsupdate.go b/internal/nsupdate/nsupdate.go index b0606b3..9afc69f 100644 --- a/internal/nsupdate/nsupdate.go +++ b/internal/nsupdate/nsupdate.go @@ -13,15 +13,49 @@ import ( "istheinternetonfire.app/internal/config" ) -func New() NsUpdateStruct { - return NsUpdateStruct{} +func New(keyLabel, keyAlgorithm, keySecret, server string, port int, zone string) NsUpdateStruct { + return NsUpdateStruct{ + Key: KeyStruct{ + Label: keyLabel, + Algorithm: keyAlgorithm, + Secret: keySecret, + }, + Server: server, + Port: port, + Zone: zone, + } } +var ( + // escape characters + specialChars = []string{ + `"`, + } +) + func (c NsUpdateStruct) Update(record, recordType, value string) error { + if strings.ToUpper(recordType) == "TXT" { + // sanitize TXT value + for _, v := range specialChars { + value = strings.ReplaceAll(value, v, fmt.Sprintf("\\%s", v)) + } + // convert to rune + a := []rune(value) + // split into 255 character blocks + value = "" + for i, r := range a { + value += string(r) + if i > 0 && (i+1)%254 == 0 { + value += string(`" "`) + } + } + // convert new lines into safe string + value = strings.ReplaceAll(value, "\n", "%n") + } + r, err := getRecord(c.Server, c.Port, recordType, record) if err != nil { - config.Cfg.Log.Debug("creating record", "record", record) - return c.Create(record, recordType, value) + config.Cfg.Log.Info("unable to get existing record", "record", record, "error", err) } if r != value { @@ -48,7 +82,7 @@ func (c NsUpdateStruct) Delete(record, recordType string) error { command := fmt.Sprintf(`#/bin/env/sh read -r -d '' DDNS_KEY <<- EOF -key "update" { +key "%s" { algorithm "%s"; secret "%s"; }; @@ -60,7 +94,7 @@ send EOF nsupdate -v -k <(printf '%%s' "${DDNS_KEY}") <(printf '%%s\n\n' "${COMMAND}") -`, c.Key.Algorithm, c.Key.Secret, c.Server, record, strings.ToUpper(recordType)) +`, c.Key.Label, c.Key.Algorithm, c.Key.Secret, c.Server, record, strings.ToUpper(recordType)) cmd := exec.Command("/usr/bin/sh", "-c", command) cmd.Stdout = &stdout cmd.Stderr = &stderr @@ -80,19 +114,19 @@ func (c NsUpdateStruct) Create(record, recordType, value string) error { command := fmt.Sprintf(`#/bin/env/sh read -r -d '' DDNS_KEY <<- EOF -key "update" { +key "%s" { algorithm "%s"; secret "%s"; }; EOF read -r -d '' COMMAND <<- EOF server %s -update add %s. 30 IN %s %s +update add %s. 30 IN %s "%s" send EOF nsupdate -v -k <(printf '%%s' "${DDNS_KEY}") <(printf '%%s\n\n' "${COMMAND}") -`, c.Key.Algorithm, c.Key.Secret, c.Server, record, strings.ToUpper(recordType), value) +`, c.Key.Label, c.Key.Algorithm, c.Key.Secret, c.Server, record, strings.ToUpper(recordType), value) cmd := exec.Command("/usr/bin/sh", "-c", command) cmd.Stdout = &stdout cmd.Stderr = &stderr diff --git a/internal/nsupdate/struct-nsupdate.go b/internal/nsupdate/struct-nsupdate.go index fb128fe..b723c28 100644 --- a/internal/nsupdate/struct-nsupdate.go +++ b/internal/nsupdate/struct-nsupdate.go @@ -1,13 +1,14 @@ package nsupdate type NsUpdateStruct struct { - Key KeyStruct `json:"key" yaml:"key"` + Key KeyStruct `json:"key" yaml:"key"` Server string `json:"server" yaml:"server"` Port int `json:"port" yaml:"port"` Zone string `json:"zone" yaml:"zone"` } type KeyStruct struct { + Label string `json:"key-label" yaml:"key-label"` Algorithm string `json:"algorithm" yaml:"algorithm"` - Secret string `json:"Secret" yaml:"secret"` + Secret string `json:"secret" yaml:"secret"` } diff --git a/main.go b/main.go index 0174dcb..5a91131 100644 --- a/main.go +++ b/main.go @@ -1,13 +1,16 @@ package main import ( + "fmt" "log" "os" "os/signal" "syscall" + "time" "istheinternetonfire.app/internal/cisa" "istheinternetonfire.app/internal/config" + "istheinternetonfire.app/internal/nsupdate" "istheinternetonfire.app/internal/webserver" ) @@ -34,5 +37,46 @@ func main() { // get remote data go cisa.Start() + // update DNS records + go func() { + dns := nsupdate.New(config.Cfg.NSUKeyLabel, config.Cfg.NSUKeyAlgorithm, config.Cfg.NSUKey, config.Cfg.NSUServer, config.Cfg.NSUPort, config.Cfg.NSUZone) + + time.Sleep(time.Second * time.Duration(15)) + for { + var ( + cves []cisa.VulStruct + num int = 3 + ) + + c := cisa.Read() + for _, i := range c.Vulnerabilities { + t, _ := time.Parse("2006-01-02", i.DateAdded) + if t.After(time.Now().Add(-time.Hour * 720)) { + cves = append(cves, i) + } + } + if len(cves) == 0 { + if err := dns.Delete("istheinternetonfire.app", "TXT"); err != nil { + config.Cfg.Log.Error("unable to delete dns record", "error", err) + time.Sleep(time.Second * time.Duration(config.Cfg.RefreshSeconds)) + continue + } + } else if len(cves) < 3 { + num = len(cves) + } + + var txtData string + for _, v := range cves[len(cves)-num:] { + txtData += fmt.Sprintf("%s - %s - %s\n", v.CveID, v.Product, v.ShortDescription) + } + if err := dns.Update("istheinternetonfire.app", "TXT", txtData); err != nil { + config.Cfg.Log.Error("unable to add dns record", "error", err) + time.Sleep(time.Second * time.Duration(config.Cfg.RefreshSeconds)) + continue + } + time.Sleep(time.Second * time.Duration(config.Cfg.RefreshSeconds)) + } + }() + forever() }