lib/geoip, cmd/relaypoolsrv, cmd/ursrv: Automatically manage GeoIP updates (#9342)

This adds a small package `geoip` which knows how to download and manage
the Maxmind GeoLite2 database we use. This removes the need for various
scripts to download and manage the geoip database, something that today
happens on Docker startup for the relay pool server and using various
hand written hacks for the usage reporting server.

The database is downloaded when needed and then refreshed on a
best-effort basis weekly.
This commit is contained in:
Jakob Borg
2024-05-18 20:31:49 +03:00
committed by GitHub
parent 57d399317e
commit ba6ac2f604
8 changed files with 212 additions and 62 deletions
+22 -21
View File
@@ -8,6 +8,7 @@ package serve
import (
"bytes"
"context"
"database/sql"
"embed"
"encoding/json"
@@ -17,6 +18,7 @@ import (
"log"
"net"
"net/http"
"os"
"regexp"
"sort"
"strconv"
@@ -26,20 +28,21 @@ import (
"unicode"
_ "github.com/lib/pq" // PostgreSQL driver
"github.com/oschwald/geoip2-golang"
"github.com/prometheus/client_golang/prometheus/promhttp"
"golang.org/x/text/cases"
"golang.org/x/text/language"
"github.com/syncthing/syncthing/lib/geoip"
"github.com/syncthing/syncthing/lib/upgrade"
"github.com/syncthing/syncthing/lib/ur/contract"
)
type CLI struct {
Debug bool `env:"UR_DEBUG"`
DBConn string `env:"UR_DB_URL" default:"postgres://user:password@localhost/ur?sslmode=disable"`
Listen string `env:"UR_LISTEN" default:"0.0.0.0:8080"`
GeoIPPath string `env:"UR_GEOIP" default:"GeoLite2-City.mmdb"`
Debug bool `env:"UR_DEBUG"`
DBConn string `env:"UR_DB_URL" default:"postgres://user:password@localhost/ur?sslmode=disable"`
Listen string `env:"UR_LISTEN" default:"0.0.0.0:8080"`
GeoIPLicenseKey string `env:"UR_GEOIP_LICENSE_KEY"`
GeoIPAccountID int `env:"UR_GEOIP_ACCOUNT_ID"`
}
//go:embed static
@@ -189,10 +192,16 @@ func (cli *CLI) Run() error {
log.Fatalln("listen:", err)
}
geoip, err := geoip.NewGeoLite2CityProvider(context.Background(), cli.GeoIPAccountID, cli.GeoIPLicenseKey, os.TempDir())
if err != nil {
log.Fatalln("geoip:", err)
}
go geoip.Serve(context.TODO())
srv := &server{
db: db,
debug: cli.Debug,
geoIPPath: cli.GeoIPPath,
db: db,
debug: cli.Debug,
geoip: geoip,
}
http.HandleFunc("/", srv.rootHandler)
http.HandleFunc("/newdata", srv.newDataHandler)
@@ -213,9 +222,9 @@ func (cli *CLI) Run() error {
}
type server struct {
debug bool
db *sql.DB
geoIPPath string
debug bool
db *sql.DB
geoip *geoip.Provider
cacheMut sync.Mutex
cachedIndex []byte
@@ -238,7 +247,7 @@ func (s *server) cacheRefresher() {
}
func (s *server) refreshCacheLocked() error {
rep := getReport(s.db, s.geoIPPath)
rep := getReport(s.db, s.geoip)
buf := new(bytes.Buffer)
err := tpl.Execute(buf, rep)
if err != nil {
@@ -492,15 +501,7 @@ type weightedLocation struct {
Weight int `json:"weight"`
}
func getReport(db *sql.DB, geoIPPath string) map[string]interface{} {
geoip, err := geoip2.Open(geoIPPath)
if err != nil {
log.Println("opening geoip db", err)
geoip = nil
} else {
defer geoip.Close()
}
func getReport(db *sql.DB, geoip *geoip.Provider) map[string]interface{} {
nodes := 0
countriesTotal := 0
var versions []string