Add IP-to-country geolocation service

Introduce a geoloc package that stores CIDR-to-country mappings in
PostgreSQL using the native cidr type with a GiST index for fast
containment lookups. Data comes from the ipverse/country-ip-blocks
dataset added as a git submodule.

A standalone geoloc-import command reads the TXT files from disk
and bulk-loads them via COPY. probod wires the service and logs a
warning when the table is empty.

Signed-off-by: Émile Ré <emile@getprobo.com>
This commit is contained in:
Émile Ré
2026-05-06 13:05:28 +04:00
parent 0ea991b628
commit ad22fec81d
6 changed files with 332 additions and 0 deletions

View File

@@ -57,6 +57,7 @@ import (
"go.probo.inc/probo/pkg/evidencedescriber"
"go.probo.inc/probo/pkg/file"
"go.probo.inc/probo/pkg/filemanager"
"go.probo.inc/probo/pkg/geoloc"
"go.probo.inc/probo/pkg/html2pdf"
"go.probo.inc/probo/pkg/iam"
"go.probo.inc/probo/pkg/iam/oauth2server"
@@ -260,6 +261,24 @@ func (impl *Implm) Run(
return fmt.Errorf("cannot migrate database schema: %w", err)
}
geolocService := geoloc.NewService(pgClient)
err = pgClient.WithConn(
ctx,
func(ctx context.Context, conn pg.Querier) error {
populated, err := geolocService.IsPopulated(ctx, conn)
if err != nil {
return err
}
if !populated {
l.Warn("IP geolocation table is empty; run geoloc-import to populate it")
}
return nil
},
)
if err != nil {
l.ErrorCtx(ctx, "cannot check geoloc table", log.Error(err))
}
hp, err := passwdhash.NewProfile(pepper, uint32(impl.cfg.Auth.Password.Iterations))
if err != nil {
return fmt.Errorf("cannot create hashing profile: %w", err)