diff --git a/README.md b/README.md index 027e963..564b71d 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,26 @@ # mikrotik-geoipblock -Generate RouterOS Address Lists based on country code. \ No newline at end of file +Generate RouterOS Address Lists based on country code. + +```sh +# Example usage +./mikrotik-geoipblock myAddressList example.json +``` + +Createa a config file containing the address lists you want to block + +```json +{ + "countries": [ + { + "name": "CANADA", + "url": "https://raw.githubusercontent.com/herrbischoff/country-ip-blocks/refs/heads/master/ipv4/ca.cidr" + }, + { + "name": "AUSTRALIA", + "url": "https://raw.githubusercontent.com/herrbischoff/country-ip-blocks/refs/heads/master/ipv4/au.cidr" + } + ] +} +``` + diff --git a/example.json b/example.json new file mode 100644 index 0000000..4ef95c8 --- /dev/null +++ b/example.json @@ -0,0 +1,12 @@ +{ + "countries": [ + { + "name": "CANADA", + "url": "https://raw.githubusercontent.com/herrbischoff/country-ip-blocks/refs/heads/master/ipv4/ca.cidr" + }, + { + "name": "AUSTRALIA", + "url": "https://raw.githubusercontent.com/herrbischoff/country-ip-blocks/refs/heads/master/ipv4/au.cidr" + } + ] +} \ No newline at end of file diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..0f90c88 --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module code.stevenpolley.net/steven/mikrotik-geoipblock + +go 1.23.0 diff --git a/main.go b/main.go new file mode 100644 index 0000000..3672b9b --- /dev/null +++ b/main.go @@ -0,0 +1,107 @@ +package main + +import ( + "bufio" + "encoding/json" + "fmt" + "log" + "net" + "net/http" + "os" +) + +type Country struct { + Name string `json:"name"` + Url string `json:"url"` + v4Addresses []string +} + +type JsonListFile struct { + Countries []Country `json:"countries"` +} + +func main() { + + // Validate arguments + if len(os.Args) < 3 { + log.Fatalf("usage: %s ", os.Args[0]) + } + blockListName := os.Args[1] + jsonListFile := os.Args[2] + fmt.Printf("generating blocklist %s\n", blockListName) + + // Load blocklist config file + countries, err := readJsonListFile(jsonListFile) + if err != nil { + log.Fatalf("failed to read jsonlistfile '%s': %v", jsonListFile, err) + } + + // Download up to date geoip CIDR data + for i := range countries { + countries[i].v4Addresses, err = downloadAddressList(countries[i].Url) + if err != nil { + log.Fatalf("failed to download address list for county'%s': %v", countries[i].Name, err) + } + } + + // Generate mikrotik block list + err = generateOutput(countries, blockListName) + if err != nil { + log.Fatalf("failed to generate output file: %v", err) + } +} + +func generateOutput(countries []Country, blockListName string) error { + output := "/ip firewall address-list\n" + for _, country := range countries { + for _, v4Address := range country.v4Addresses { + output += fmt.Sprintf("add address=%s comment=\"%s\" list=%s\n", v4Address, country.Name, blockListName) + } + } + + err := os.WriteFile(fmt.Sprintf("%s.rsc", blockListName), []byte(output), os.ModePerm) + if err != nil { + return fmt.Errorf("failed to write file '%s.rsc': %v", blockListName, err) + } + return nil +} + +func downloadAddressList(url string) ([]string, error) { + resp, err := http.Get(url) + if err != nil { + return nil, fmt.Errorf("http get error on url '%s': %v", url, err) + } + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("unexpected http status - expected '%d' but got '%d'", http.StatusOK, resp.StatusCode) + } + scanner := bufio.NewScanner(resp.Body) + scanner.Split(bufio.ScanLines) + v4Addresses := make([]string, 0) + for scanner.Scan() { + line := scanner.Text() + _, ipnet, err := net.ParseCIDR(line) + if err != nil { + log.Printf("skippine line: failed to parse line '%s' to cidr: %v", line, err) + continue + } + v4Addresses = append(v4Addresses, ipnet.String()) + } + return v4Addresses, nil +} + +// reads the json config file containing the lists of countries you want to block +func readJsonListFile(filename string) ([]Country, error) { + b, err := os.ReadFile(filename) + if err != nil { + return nil, fmt.Errorf("failed to open file %s: %v", filename, err) + } + + jsonListFile := JsonListFile{} + + err = json.Unmarshal(b, &jsonListFile) + if err != nil { + return nil, fmt.Errorf("failed to unmarshal json file to jsonListFile struct: %v", err) + } + + return jsonListFile.Countries, nil +}