This commit is contained in:
2024-01-14 18:40:47 -06:00
parent 12167e8d5a
commit a4430227e3
17 changed files with 13370 additions and 48 deletions

View File

@@ -2,12 +2,10 @@ package cisa
import (
"encoding/json"
"net/http"
"sync"
"time"
"istheinternetonfire.app/internal/config"
"istheinternetonfire.app/internal/httpclient"
)
var (
@@ -24,12 +22,16 @@ func Read() CisaJSON {
func Start() {
for {
c := httpclient.NewClient(http.DefaultClient)
d, err := c.Get(config.Cfg.RemoteURL)
if err != nil {
time.Sleep(time.Second * 120)
continue
}
/*
c := httpclient.NewClient(http.DefaultClient)
d, err := c.Get(config.Cfg.RemoteURL)
if err != nil {
time.Sleep(time.Second * 120)
continue
}
*/
d := GetExampleData()
mu.Lock()
if err := json.Unmarshal(d, &Cisa); err != nil {

12780
internal/cisa/example.go Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -19,4 +19,4 @@ type VulStruct struct {
ShortDescription string `json:"shortDescription"`
VendorProject string `json:"vendorProject"`
VulnerabilityName string `json:"vulnerabilityName"`
}
}

View File

@@ -18,7 +18,7 @@ type structInfo struct {
Tags reflect.StructTag
Type reflect.Type
DefaultValue interface{}
Secret interface{}
Secret bool
}
func getEnv[t string | bool | int | int64 | float64](env string, def t) (t, error) {
@@ -97,7 +97,7 @@ func getStructInfo(spec interface{}) ([]structInfo, error) {
f = f.Elem()
}
secret, err := typeConversion(ftype.Type.String(), ftype.Tag.Get("secret"))
secret, err := strconv.ParseBool((ftype.Tag.Get("secret")))
if err != nil {
secret = false
}

View File

@@ -3,6 +3,7 @@ package config
import (
"log"
"os"
"os/exec"
"time"
)
@@ -32,6 +33,12 @@ func Init() {
os.Exit(1)
}
// check to see if nsupdate is installed
cmd := "command -v nsupdate"
if err := exec.Command("/usr/bin/sh", "-c", cmd).Run(); err != nil {
panic("nsupdate is not installed")
}
// print running config
printRunningConfig(&Cfg, cfgInfo)
}

View File

@@ -30,6 +30,12 @@ type Config struct {
// cisa
RemoteURL string `default:"https://www.cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json?sort_by=field_date_added" env:"remote_url"`
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"`
}
// New initializes the config variable for use with a prepared set of defaults.
@@ -70,16 +76,20 @@ func setLogLevel(cfg *Config) {
func printRunningConfig(cfg *Config, cfgInfo []structInfo) {
for _, info := range cfgInfo {
switch info.Type.String() {
case "string":
p := reflect.ValueOf(cfg).Elem().FieldByName(info.Name).Addr().Interface().(*string)
cfg.Log.Debug("Running Configuration", info.Alt, *p)
case "bool":
p := reflect.ValueOf(cfg).Elem().FieldByName(info.Name).Addr().Interface().(*bool)
cfg.Log.Debug("Running Configuration", info.Alt, strconv.FormatBool(*p))
case "int":
p := reflect.ValueOf(cfg).Elem().FieldByName(info.Name).Addr().Interface().(*int)
cfg.Log.Debug("Running Configuration", info.Alt, strconv.FormatInt(int64(*p), 10))
if info.Secret {
cfg.Log.Debug("Running Configuration", info.Alt, "REDACTED")
} else {
switch info.Type.String() {
case "string":
p := reflect.ValueOf(cfg).Elem().FieldByName(info.Name).Addr().Interface().(*string)
cfg.Log.Debug("Running Configuration", info.Alt, *p)
case "bool":
p := reflect.ValueOf(cfg).Elem().FieldByName(info.Name).Addr().Interface().(*bool)
cfg.Log.Debug("Running Configuration", info.Alt, strconv.FormatBool(*p))
case "int":
p := reflect.ValueOf(cfg).Elem().FieldByName(info.Name).Addr().Interface().(*int)
cfg.Log.Debug("Running Configuration", info.Alt, strconv.FormatInt(int64(*p), 10))
}
}
}
}

View File

@@ -0,0 +1,431 @@
package nsupdate
import (
"bytes"
"fmt"
"net"
"strconv"
"strings"
"os/exec"
"github.com/miekg/dns"
"istheinternetonfire.app/internal/config"
)
func New() NsUpdateStruct {
return NsUpdateStruct{}
}
func (c NsUpdateStruct) Update(record, recordType, value string) error {
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)
}
if r != value {
config.Cfg.Log.Debug("deleting record", "record", record)
if err := c.Delete(record, recordType); err != nil {
return err
}
config.Cfg.Log.Debug("creating record", "record", record)
if err := c.Create(record, recordType, value); err != nil {
return err
}
return nil
}
config.Cfg.Log.Debug("no update necessary")
return nil
}
func (c NsUpdateStruct) Delete(record, recordType string) error {
var (
stdout bytes.Buffer
stderr bytes.Buffer
)
command := fmt.Sprintf(`#/bin/env/sh
read -r -d '' DDNS_KEY <<- EOF
key "update" {
algorithm "%s";
secret "%s";
};
EOF
read -r -d '' COMMAND <<- EOF
server %s
update delete %s. 30 IN %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))
cmd := exec.Command("/usr/bin/sh", "-c", command)
cmd.Stdout = &stdout
cmd.Stderr = &stderr
if err := cmd.Run(); err != nil {
config.Cfg.Log.Error("error deleting record", "error", err, "stderr", stderr.String(), "stdout", stdout.String())
return err
}
return nil
}
func (c NsUpdateStruct) Create(record, recordType, value string) error {
var (
stdout bytes.Buffer
stderr bytes.Buffer
)
command := fmt.Sprintf(`#/bin/env/sh
read -r -d '' DDNS_KEY <<- EOF
key "update" {
algorithm "%s";
secret "%s";
};
EOF
read -r -d '' COMMAND <<- EOF
server %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)
cmd := exec.Command("/usr/bin/sh", "-c", command)
cmd.Stdout = &stdout
cmd.Stderr = &stderr
if err := cmd.Run(); err != nil {
config.Cfg.Log.Error("error adding record", "error", err, "stderr", stderr.String(), "stdout", stdout.String())
return err
}
return nil
}
func getRecord(server string, port int, queryType, query string) (string, error) {
config.Cfg.Log.Debug("looking up dns record", "query", query, "type", queryType)
recordType, err := getRecordType(queryType)
if err != nil {
return "", err
}
c := new(dns.Client)
m := new(dns.Msg)
m.SetQuestion(fmt.Sprintf("%s.", query), recordType)
m.RecursionDesired = true
r, _, err := c.Exchange(m, net.JoinHostPort(server, strconv.Itoa(port)))
if err != nil {
return "", err
}
if r.Rcode != dns.RcodeSuccess {
return "", err
}
for _, a := range r.Answer {
return getRecordFromResult(a, queryType)
}
return "", fmt.Errorf("no result")
}
func getRecordFromResult(a dns.RR, queryType string) (string, error) {
switch strings.ToLower(queryType) {
case "a":
if res, ok := a.(*dns.A); ok {
return res.String(), nil
}
case "aaaa":
if res, ok := a.(*dns.AAAA); ok {
return res.String(), nil
}
case "afsdb":
if res, ok := a.(*dns.AFSDB); ok {
return res.String(), nil
}
case "apl":
if res, ok := a.(*dns.APL); ok {
return res.String(), nil
}
case "caa":
if res, ok := a.(*dns.CAA); ok {
return res.String(), nil
}
case "cdnskey":
if res, ok := a.(*dns.CDNSKEY); ok {
return res.String(), nil
}
case "cds":
if res, ok := a.(*dns.CDS); ok {
return res.String(), nil
}
case "cert":
if res, ok := a.(*dns.CERT); ok {
return res.String(), nil
}
case "cname":
if res, ok := a.(*dns.CNAME); ok {
return res.String(), nil
}
case "csync":
if res, ok := a.(*dns.CSYNC); ok {
return res.String(), nil
}
case "dhcid":
if res, ok := a.(*dns.DHCID); ok {
return res.String(), nil
}
case "dlv":
if res, ok := a.(*dns.DLV); ok {
return res.String(), nil
}
case "dname":
if res, ok := a.(*dns.DNAME); ok {
return res.String(), nil
}
case "dnskey":
if res, ok := a.(*dns.DNSKEY); ok {
return res.String(), nil
}
case "ds":
if res, ok := a.(*dns.DS); ok {
return res.String(), nil
}
case "eui48":
if res, ok := a.(*dns.EUI48); ok {
return res.String(), nil
}
case "eui64":
if res, ok := a.(*dns.EUI64); ok {
return res.String(), nil
}
case "hinfo":
if res, ok := a.(*dns.HINFO); ok {
return res.String(), nil
}
case "hip":
if res, ok := a.(*dns.HIP); ok {
return res.String(), nil
}
case "https":
if res, ok := a.(*dns.HTTPS); ok {
return res.String(), nil
}
case "ipseckey":
if res, ok := a.(*dns.IPSECKEY); ok {
return res.String(), nil
}
case "key":
if res, ok := a.(*dns.KEY); ok {
return res.String(), nil
}
case "kx":
if res, ok := a.(*dns.KX); ok {
return res.String(), nil
}
case "loc":
if res, ok := a.(*dns.LOC); ok {
return res.String(), nil
}
case "mx":
if res, ok := a.(*dns.MX); ok {
return res.String(), nil
}
case "naptr":
if res, ok := a.(*dns.NAPTR); ok {
return res.String(), nil
}
case "ns":
if res, ok := a.(*dns.NS); ok {
return res.String(), nil
}
case "nsec":
if res, ok := a.(*dns.NSEC); ok {
return res.String(), nil
}
case "nsec3":
if res, ok := a.(*dns.NSEC3); ok {
return res.String(), nil
}
case "nsec3param":
if res, ok := a.(*dns.NSEC3PARAM); ok {
return res.String(), nil
}
case "openpgpkey":
if res, ok := a.(*dns.OPENPGPKEY); ok {
return res.String(), nil
}
case "ptr":
if res, ok := a.(*dns.PTR); ok {
return res.String(), nil
}
case "rrsig":
if res, ok := a.(*dns.RRSIG); ok {
return res.String(), nil
}
case "rp":
if res, ok := a.(*dns.RP); ok {
return res.String(), nil
}
case "sig":
if res, ok := a.(*dns.SIG); ok {
return res.String(), nil
}
case "smimea":
if res, ok := a.(*dns.SMIMEA); ok {
return res.String(), nil
}
case "soa":
if res, ok := a.(*dns.SOA); ok {
return res.String(), nil
}
case "srv":
if res, ok := a.(*dns.SRV); ok {
return res.String(), nil
}
case "sshfp":
if res, ok := a.(*dns.SSHFP); ok {
return res.String(), nil
}
case "svcb":
if res, ok := a.(*dns.SVCB); ok {
return res.String(), nil
}
case "ta":
if res, ok := a.(*dns.TA); ok {
return res.String(), nil
}
case "tkey":
if res, ok := a.(*dns.TKEY); ok {
return res.String(), nil
}
case "tlsa":
if res, ok := a.(*dns.TLSA); ok {
return res.String(), nil
}
case "tsig":
if res, ok := a.(*dns.TSIG); ok {
return res.String(), nil
}
case "txt":
if res, ok := a.(*dns.TXT); ok {
return res.String(), nil
}
case "uri":
if res, ok := a.(*dns.URI); ok {
return res.String(), nil
}
case "zonemd":
if res, ok := a.(*dns.ZONEMD); ok {
return res.String(), nil
}
}
return "", fmt.Errorf("invalid record type")
}
func getRecordType(queryType string) (uint16, error) {
var recordType uint16
switch strings.ToLower(queryType) {
case "a":
recordType = dns.TypeA
case "aaaa":
recordType = dns.TypeAAAA
case "afsdb":
recordType = dns.TypeAFSDB
case "apl":
recordType = dns.TypeAPL
case "caa":
recordType = dns.TypeCAA
case "cdnskey":
recordType = dns.TypeCDNSKEY
case "cds":
recordType = dns.TypeCDS
case "cert":
recordType = dns.TypeCERT
case "cname":
recordType = dns.TypeCNAME
case "csync":
recordType = dns.TypeCSYNC
case "dhcid":
recordType = dns.TypeDHCID
case "dlv":
recordType = dns.TypeDLV
case "dname":
recordType = dns.TypeDNAME
case "dnskey":
recordType = dns.TypeDNSKEY
case "ds":
recordType = dns.TypeDS
case "eui48":
recordType = dns.TypeEUI48
case "eui64":
recordType = dns.TypeEUI64
case "hinfo":
recordType = dns.TypeHINFO
case "hip":
recordType = dns.TypeHIP
case "https":
recordType = dns.TypeHTTPS
case "ipseckey":
recordType = dns.TypeIPSECKEY
case "key":
recordType = dns.TypeKEY
case "kx":
recordType = dns.TypeKX
case "loc":
recordType = dns.TypeLOC
case "mx":
recordType = dns.TypeMX
case "naptr":
recordType = dns.TypeNAPTR
case "ns":
recordType = dns.TypeNS
case "nsec":
recordType = dns.TypeNSEC
case "nsec3":
recordType = dns.TypeNSEC3
case "nsec3param":
recordType = dns.TypeNSEC3PARAM
case "openpgpkey":
recordType = dns.TypeOPENPGPKEY
case "ptr":
recordType = dns.TypePTR
case "rrsig":
recordType = dns.TypeRRSIG
case "rp":
recordType = dns.TypeRP
case "sig":
recordType = dns.TypeSIG
case "smimea":
recordType = dns.TypeSMIMEA
case "soa":
recordType = dns.TypeSOA
case "srv":
recordType = dns.TypeSRV
case "sshfp":
recordType = dns.TypeSSHFP
case "svcb":
recordType = dns.TypeSVCB
case "ta":
recordType = dns.TypeTA
case "tkey":
recordType = dns.TypeTKEY
case "tlsa":
recordType = dns.TypeTLSA
case "tsig":
recordType = dns.TypeTSIG
case "txt":
recordType = dns.TypeTXT
case "uri":
recordType = dns.TypeURI
case "zonemd":
recordType = dns.TypeZONEMD
default:
return uint16(0), fmt.Errorf("invalid record type")
}
return recordType, nil
}

View File

@@ -0,0 +1,13 @@
package nsupdate
type NsUpdateStruct struct {
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 {
Algorithm string `json:"algorithm" yaml:"algorithm"`
Secret string `json:"Secret" yaml:"secret"`
}

View File

@@ -16,29 +16,31 @@ import (
)
const (
TYPE_APPLICATION_PEM string = "application/x-pem-file"
TYPE_APPLICATION_JSON string = "application/json"
TYPE_AUDIO_MPEG string = "audio/mpeg"
TYPE_FONT_WOFF string = "font/woff"
TYPE_FONT_WOFF2 string = "font/woff2"
TYPE_IMAGE_JPG string = "image/jpg"
TYPE_IMAGE_PNG string = "image/png"
TYPE_TEXT_CSS string = "text/css"
TYPE_TEXT_HTML string = "text/html"
TYPE_TEXT_JS string = "text/javascript"
TYPE_TEXT_PLAIN string = "text/plain"
TYPE_TEXT_RAW string = "text/raw"
TYPE_APPLICATION_JSON string = "application/json"
TYPE_APPLICATION_MANIFEST_JSON string = "application/manifest+json"
TYPE_APPLICATION_PEM string = "application/x-pem-file"
TYPE_AUDIO_MPEG string = "audio/mpeg"
TYPE_FONT_WOFF string = "font/woff"
TYPE_FONT_WOFF2 string = "font/woff2"
TYPE_IMAGE_JPG string = "image/jpg"
TYPE_IMAGE_PNG string = "image/png"
TYPE_TEXT_CSS string = "text/css"
TYPE_TEXT_HTML string = "text/html"
TYPE_TEXT_JS string = "text/javascript"
TYPE_TEXT_PLAIN string = "text/plain"
TYPE_TEXT_RAW string = "text/raw"
)
var validFiles map[string]string = map[string]string{
"/robots.txt": TYPE_TEXT_PLAIN,
"/apple-touch-icon.png": TYPE_IMAGE_PNG,
"/favicon.ico": TYPE_IMAGE_PNG,
"/favicon-16x16.png": TYPE_IMAGE_PNG,
"/favicon-32x32.png": TYPE_IMAGE_PNG,
"/js/bootstrap.bundle.min.js": TYPE_TEXT_JS,
"/favicon.ico": TYPE_IMAGE_PNG,
"/js/bootstrap.bundle.min.js.map": TYPE_APPLICATION_JSON,
"/js/bootstrap.bundle.min.js": TYPE_TEXT_JS,
"/js/jquery.min.js": TYPE_TEXT_JS,
"/robots.txt": TYPE_TEXT_PLAIN,
"/site.webmanifest": TYPE_APPLICATION_MANIFEST_JSON, // https://developer.mozilla.org/en-US/docs/Web/Manifest
}
func isValidReq(file string) (string, error) {
@@ -110,9 +112,9 @@ func webRoot(w http.ResponseWriter, r *http.Request) {
w.Header().Add("Content-Encoding", "gzip")
gw := gzip.NewWriter(w)
defer gw.Close()
gw.Write(o)
gw.Write(o) //nolint:errcheck
} else {
w.Write(o)
w.Write(o) //nolint:errcheck
}
}
}

View File

@@ -75,7 +75,7 @@ func tmpltWebRoot(w http.ResponseWriter, r *http.Request) {
return
}
w.Write(msgBuffer.Bytes())
w.Write(msgBuffer.Bytes()) //nolint:errcheck
}
func tmpltStatusNotFound(w http.ResponseWriter, path string) {
@@ -98,5 +98,5 @@ func tmpltStatusNotFound(w http.ResponseWriter, path string) {
tmpltError(w, http.StatusInternalServerError, "Template Parse Error.")
return
}
w.Write(msgBuffer.Bytes())
w.Write(msgBuffer.Bytes()) //nolint:errcheck
}