- Updated Dockerfile to create and set ownership for the /var/lib/telemt-gateway directory, ensuring the gateway user has the necessary permissions. - Added documentation in GEOIP.md to clarify directory permissions required for GeoIP data downloads, including guidance on volume mounting and user ID consistency.
114 lines
3.0 KiB
Go
114 lines
3.0 KiB
Go
package geoip
|
|
|
|
import (
|
|
"compress/gzip"
|
|
"context"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
)
|
|
|
|
// DownloadMMDB downloads an MMDB URL to destPath. If the URL ends with `.gz`, the body is gunzipped.
|
|
// Otherwise the response body is written as-is (raw `.mmdb`, e.g. GitHub raw).
|
|
func DownloadMMDB(ctx context.Context, client *http.Client, url, destPath string) error {
|
|
if strings.HasSuffix(strings.ToLower(strings.TrimSpace(url)), ".gz") {
|
|
return downloadGzippedMMDB(ctx, client, url, destPath)
|
|
}
|
|
return downloadRawMMDB(ctx, client, url, destPath)
|
|
}
|
|
|
|
func downloadRawMMDB(ctx context.Context, client *http.Client, url, destPath string) error {
|
|
if client == nil {
|
|
client = http.DefaultClient
|
|
}
|
|
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
resp, err := client.Do(req)
|
|
if err != nil {
|
|
return fmt.Errorf("geoip download: %w", err)
|
|
}
|
|
defer resp.Body.Close()
|
|
if resp.StatusCode != http.StatusOK {
|
|
return fmt.Errorf("geoip download: http %s", resp.Status)
|
|
}
|
|
dir := filepath.Dir(destPath)
|
|
if err := os.MkdirAll(dir, 0o755); err != nil {
|
|
return fmt.Errorf("geoip mkdir %q: %w (directory must exist or be creatable by the process)", dir, err)
|
|
}
|
|
tmp, err := os.CreateTemp(dir, "geolite-*.mmdb.part")
|
|
if err != nil {
|
|
return fmt.Errorf("geoip temp file in %q: %w", dir, err)
|
|
}
|
|
tmpPath := tmp.Name()
|
|
_, copyErr := io.Copy(tmp, resp.Body)
|
|
closeErr := tmp.Close()
|
|
if copyErr != nil {
|
|
_ = os.Remove(tmpPath)
|
|
return copyErr
|
|
}
|
|
if closeErr != nil {
|
|
_ = os.Remove(tmpPath)
|
|
return closeErr
|
|
}
|
|
_ = os.Remove(destPath)
|
|
if err := os.Rename(tmpPath, destPath); err != nil {
|
|
_ = os.Remove(tmpPath)
|
|
return fmt.Errorf("geoip rename: %w", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func downloadGzippedMMDB(ctx context.Context, client *http.Client, url, destPath string) error {
|
|
if client == nil {
|
|
client = http.DefaultClient
|
|
}
|
|
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
resp, err := client.Do(req)
|
|
if err != nil {
|
|
return fmt.Errorf("geoip download: %w", err)
|
|
}
|
|
defer resp.Body.Close()
|
|
if resp.StatusCode != http.StatusOK {
|
|
return fmt.Errorf("geoip download: http %s", resp.Status)
|
|
}
|
|
gzr, err := gzip.NewReader(resp.Body)
|
|
if err != nil {
|
|
return fmt.Errorf("geoip gzip: %w", err)
|
|
}
|
|
defer gzr.Close()
|
|
|
|
dir := filepath.Dir(destPath)
|
|
if err := os.MkdirAll(dir, 0o755); err != nil {
|
|
return fmt.Errorf("geoip mkdir %q: %w (directory must exist or be creatable by the process)", dir, err)
|
|
}
|
|
tmp, err := os.CreateTemp(dir, "geolite-*.mmdb.part")
|
|
if err != nil {
|
|
return fmt.Errorf("geoip temp file in %q: %w", dir, err)
|
|
}
|
|
tmpPath := tmp.Name()
|
|
_, copyErr := io.Copy(tmp, gzr)
|
|
closeErr := tmp.Close()
|
|
if copyErr != nil {
|
|
_ = os.Remove(tmpPath)
|
|
return copyErr
|
|
}
|
|
if closeErr != nil {
|
|
_ = os.Remove(tmpPath)
|
|
return closeErr
|
|
}
|
|
_ = os.Remove(destPath)
|
|
if err := os.Rename(tmpPath, destPath); err != nil {
|
|
_ = os.Remove(tmpPath)
|
|
return fmt.Errorf("geoip rename: %w", err)
|
|
}
|
|
return nil
|
|
}
|