From 701fd10c1cc519d73e88c88b9be5dd9ad398276c Mon Sep 17 00:00:00 2001 From: Eugene Bujak Date: Tue, 27 Nov 2018 21:25:03 +0300 Subject: [PATCH] Protect against users deleting the filter ID's in the config file. Incidentally, it also simplifies upgrade schema from 0 to 1. --- app.go | 39 +++++++++++++++++++++++++++++---------- config.go | 26 ++++++++++++++++++-------- control.go | 11 +++++++++-- upgrade.go | 25 +------------------------ 4 files changed, 57 insertions(+), 44 deletions(-) diff --git a/app.go b/app.go index 61989816..ca5c2487 100644 --- a/app.go +++ b/app.go @@ -9,6 +9,7 @@ import ( "os" "path/filepath" "strconv" + "time" "github.com/gobuffalo/packr" "golang.org/x/crypto/ssh/terminal" @@ -135,6 +136,34 @@ func main() { } } + // Load filters from the disk + // And if any filter has zero ID, assign a new one + for i := range config.Filters { + filter := &config.Filters[i] // otherwise we're operating on a copy + if filter.ID == 0 { + filter.ID = assignUniqueFilterID() + } + err := filter.load() + if err != nil { + // This is okay for the first start, the filter will be loaded later + log.Printf("Couldn't load filter %d contents due to %s", filter.ID, err) + // clear LastUpdated so it gets fetched right away + } + if len(filter.Contents) == 0 { + filter.LastUpdated = time.Time{} + } + } + + // Update filters we've just loaded right away, don't wait for periodic update timer + go func() { + checkFiltersUpdates(false) + // Save the updated config + err := writeConfig() + if err != nil { + log.Fatal(err) + } + }() + // Eat all args so that coredns can start happily if len(os.Args) > 1 { os.Args = os.Args[:1] @@ -146,16 +175,6 @@ func main() { log.Fatal(err) } - // Load filters from the disk - for i := range config.Filters { - filter := &config.Filters[i] - err = filter.load() - if err != nil { - // This is okay for the first start, the filter will be loaded later - log.Printf("Couldn't load filter %d contents due to %s", filter.ID, err) - } - } - address := net.JoinHostPort(config.BindHost, strconv.Itoa(config.BindPort)) runFiltersUpdatesTimer() diff --git a/config.go b/config.go index 3f52cf95..120d4940 100644 --- a/config.go +++ b/config.go @@ -22,7 +22,7 @@ const ( ) // Just a counter that we use for incrementing the filter ID -var NextFilterId = time.Now().Unix() +var nextFilterID int64 = time.Now().Unix() // configuration is loaded from YAML // field ordering is important -- yaml fields will mirror ordering from here @@ -74,7 +74,7 @@ type filter struct { Name string `json:"name" yaml:"name"` RulesCount int `json:"rulesCount" yaml:"-"` LastUpdated time.Time `json:"lastUpdated,omitempty" yaml:"last_updated,omitempty"` - ID int64 // auto-assigned when filter is added (see NextFilterId) + ID int64 // auto-assigned when filter is added (see nextFilterID) Contents []byte `json:"-" yaml:"-"` // not in yaml or json } @@ -165,12 +165,7 @@ func parseConfig() error { config.Filters = config.Filters[:i] } - // Set the next filter ID to max(filter.ID) + 1 - for i := range config.Filters { - if NextFilterId < config.Filters[i].ID { - NextFilterId = config.Filters[i].ID + 1 - } - } + updateUniqueFilterID(config.Filters) return nil } @@ -293,3 +288,18 @@ func generateCoreDNSConfigText() (string, error) { configText = removeEmptyLines.ReplaceAllString(configText, "\n") return configText, nil } + +// Set the next filter ID to max(filter.ID) + 1 +func updateUniqueFilterID(filters []filter) { + for _, filter := range filters { + if nextFilterID < filter.ID { + nextFilterID = filter.ID + 1 + } + } +} + +func assignUniqueFilterID() int64 { + value := nextFilterID + nextFilterID += 1 + return value +} diff --git a/control.go b/control.go index e0584070..5c17ee0e 100644 --- a/control.go +++ b/control.go @@ -343,9 +343,8 @@ func handleFilteringAddURL(w http.ResponseWriter, r *http.Request) { } // Set necessary properties - filter.ID = NextFilterId + filter.ID = assignUniqueFilterID() filter.Enabled = true - NextFilterId++ // Download the filter contents ok, err := filter.update(true) @@ -550,6 +549,11 @@ func checkFiltersUpdates(force bool) int { updateCount := 0 for i := range config.Filters { filter := &config.Filters[i] // otherwise we will be operating on a copy + + if filter.ID == 0 { // protect against users modifying the yaml and removing the ID + filter.ID = assignUniqueFilterID() + } + updated, err := filter.update(force) if err != nil { log.Printf("Failed to update filter %s: %s\n", filter.URL, err) @@ -601,6 +605,9 @@ func parseFilterContents(contents []byte) (int, string) { // If "force" is true -- does not check the filter's LastUpdated field // Call "save" to persist the filter contents func (filter *filter) update(force bool) (bool, error) { + if filter.ID == 0 { // protect against users deleting the ID + filter.ID = assignUniqueFilterID() + } if !filter.Enabled { return false, nil } diff --git a/upgrade.go b/upgrade.go index 19912d92..a9e26934 100644 --- a/upgrade.go +++ b/upgrade.go @@ -87,30 +87,7 @@ func upgradeSchema0to1(diskConfig *map[string]interface{}) error { trace("Called") // The first schema upgrade: - // Added "ID" field to "filter" -- we need to populate this field now - // Added "config.ourDataDir" -- where we will now store filters contents - for i := range config.Filters { - filter := &config.Filters[i] // otherwise we will be operating on a copy - - // Set the filter ID - log.Printf("Seting ID=%d for filter %s", NextFilterId, filter.URL) - filter.ID = NextFilterId - NextFilterId++ - - // Forcibly update the filter - _, err := filter.update(true) - if err != nil { - log.Fatal(err) - } - - // Saving it to the filters dir now - err = filter.save() - if err != nil { - log.Fatal(err) - } - } - - // No more "dnsfilter.txt", filters are now loaded from config.ourDataDir/filters/ + // No more "dnsfilter.txt", filters are now kept in data/filters/ dnsFilterPath := filepath.Join(config.ourBinaryDir, "dnsfilter.txt") _, err := os.Stat(dnsFilterPath) if !os.IsNotExist(err) {