Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
a7f03cf
Test that ACCOUNT_ID_FILE overrides an invalid ACCOUNT_ID
horgh Oct 16, 2026
5ea9930
Check the env file error before the found flag
horgh Oct 16, 2026
b23157e
Document that the _FILE variable takes precedence
horgh Oct 16, 2026
b16dcc6
Test the errors for missing _FILE paths
horgh Oct 16, 2026
0df07b6
Test the _FILE variables without their base variables
horgh Oct 16, 2026
952fca1
Name PROXY_USER_PASSWORD_FILE in the test names
horgh Oct 16, 2026
2d15b7c
Document GEOIPUPDATE_PROXY_USER_PASSWORD_FILE for Docker
horgh Oct 16, 2026
4304fd4
Document GEOIPUPDATE_PROXY_USER_PASSWORD_FILE in GeoIP.conf.md
horgh Oct 16, 2026
9d412c0
Add a CHANGELOG entry for GEOIPUPDATE_PROXY_USER_PASSWORD_FILE
horgh Oct 16, 2026
2da284c
Move getValueFromEnvFile below its caller
horgh Oct 16, 2026
a76f87b
Remove an unnecessary err declaration
horgh Oct 16, 2026
a1946d8
Simplify getValueFromEnvFile
horgh Oct 16, 2026
56cc185
Say "failed to read" for _FILE read errors
horgh Oct 16, 2026
cb7ba22
Rename getValueFromEnvFile to lookupEnvOrFile
horgh Oct 16, 2026
28698ef
Describe lookupEnvOrFile in its doc comment
horgh Oct 16, 2026
99ac625
Return an error when a _FILE points to an empty file
horgh Oct 16, 2026
e44e59c
Restore the environment in withEnvVars if the test fails
horgh Oct 16, 2026
ca6c272
Unset shell GEOIPUPDATE_* variables in withEnvVars
horgh Oct 19, 2026
403f561
Use t.Setenv in withEnvVars
horgh Oct 19, 2026
0306d30
Document that geoipupdate trims _FILE contents
horgh Oct 19, 2026
b06fe04
Make the blank _FILE error more exact
horgh Oct 19, 2026
1b71dd2
Make the _FILE precedence docs exact
horgh Oct 19, 2026
5cbe6ef
Name the variables that have a _FILE form
horgh Oct 19, 2026
d80e86d
Replace withEnvVars with setEnvVars
horgh Oct 19, 2026
7fd6194
Rename lookupEnvOrFile to lookupFileOrEnv
horgh Oct 19, 2026
0bcb6c4
Test the proxy user and password through NewConfig
horgh Oct 19, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@
- The Docker images now carry `org.opencontainers.image.*` labels.
- A prerelease no longer moves the `latest`, `v8` and `v8.1` Docker tags, or the
release marked "Latest" on GitHub.
- Added the `GEOIPUPDATE_PROXY_USER_PASSWORD_FILE` environment variable. It sets

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Compared with 8.0.0, this release also changes GEOIPUPDATE_ACCOUNT_ID_FILE and GEOIPUPDATE_LICENSE_KEY_FILE in 3 ways that users can see:

  1. An empty or white-space-only file gives a new error. In 8.0.0, it gave "invalid account ID format" or "the LicenseKey option is required".
  2. A read error says "failed to read". In 8.0.0, it said "failed to open".
  3. An invalid GEOIPUPDATE_ACCOUNT_ID no longer causes an error when GEOIPUPDATE_ACCOUNT_ID_FILE is set.

Should the changelog mention these? Users who match on the error text, or who relied on the old error, get no notice.

Claude Code (an AI agent) wrote this comment for @oschwald.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We decided not to add these. They are too minor for the changelog.

Claude Code (an AI agent) wrote this reply for @horgh.

the proxy user name and password from a file, in the same way as
`GEOIPUPDATE_ACCOUNT_ID_FILE` and `GEOIPUPDATE_LICENSE_KEY_FILE`. Pull request
by Tom Hubrecht. GitHub #570.

## 8.0.0 (2026-07-10)

Expand Down
21 changes: 17 additions & 4 deletions doc/GeoIP.conf.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,29 @@ The file consists of one setting per line. Lines starting with `#`
are comments and will not be processed. All setting keywords are case
sensitive.

The `AccountID`, `LicenseKey` and `ProxyUserPassword` settings can also be
overridden at run time by an environment variable that contains the path to a
file, for example `GEOIPUPDATE_LICENSE_KEY_FILE`. The file holds the value.
`geoipupdate` removes leading and trailing white space from the contents of
the file. If the file is empty or contains only white space, `geoipupdate`
returns an error.

## Required settings:

`AccountID`

: Your MaxMind account ID. This was formerly known as `UserId`. This can be
overridden at run time by either the `GEOIPUPDATE_ACCOUNT_ID` or the
`GEOIPUPDATE_ACCOUNT_ID_FILE` environment variables.
`GEOIPUPDATE_ACCOUNT_ID_FILE` environment variables. If
`GEOIPUPDATE_ACCOUNT_ID_FILE` is set and not empty, `geoipupdate` uses the
file and ignores `GEOIPUPDATE_ACCOUNT_ID`.

`LicenseKey`

: Your case-sensitive MaxMind license key. This can be overridden at run time
by either the `GEOIPUPDATE_LICENSE_KEY` or `GEOIPUPDATE_LICENSE_KEY_FILE`
environment variables.
environment variables. If `GEOIPUPDATE_LICENSE_KEY_FILE` is set and not
empty, `geoipupdate` uses the file and ignores `GEOIPUPDATE_LICENSE_KEY`.

`EditionIDs`

Expand Down Expand Up @@ -59,8 +69,11 @@ sensitive.
`ProxyUserPassword`

: The proxy user name and password, separated by a colon. For instance,
`username:password`. This can be overridden at run time by the
`GEOIPUPDATE_PROXY_USER_PASSWORD` environment variable.
`username:password`. This can be overridden at run time by either the
`GEOIPUPDATE_PROXY_USER_PASSWORD` or the
`GEOIPUPDATE_PROXY_USER_PASSWORD_FILE` environment variables. If
`GEOIPUPDATE_PROXY_USER_PASSWORD_FILE` is set and not empty, `geoipupdate`
uses the file and ignores `GEOIPUPDATE_PROXY_USER_PASSWORD`.

`PreserveFileTimes`

Expand Down
10 changes: 10 additions & 0 deletions doc/docker.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ The following environment variables are optional:
1080 will be used.
* `GEOIPUPDATE_PROXY_USER_PASSWORD` - The proxy user name and password,
separated by a colon. For instance, `username:password`.
* `GEOIPUPDATE_PROXY_USER_PASSWORD_FILE` - A file containing the proxy user
name and password, separated by a colon.
* `GEOIPUPDATE_PRESERVE_FILE_TIMES` - Whether to preserve modification times
of files downloaded from the server. This option is either `0` or `1`. The
default is `0`.
Expand All @@ -52,6 +54,14 @@ individually with the `-e` flag.

Environment variables override any options set in the config file.

`GEOIPUPDATE_ACCOUNT_ID`, `GEOIPUPDATE_LICENSE_KEY` and
`GEOIPUPDATE_PROXY_USER_PASSWORD` each have a `_FILE` form, for example
`GEOIPUPDATE_LICENSE_KEY_FILE`. If the `_FILE` form is set and not empty,
`geoipupdate` uses the value from the file and ignores the other variable.
`geoipupdate` removes leading and trailing white space from the contents of the
file. If the file is empty or contains only white space, `geoipupdate` returns
an error.

## Running

### docker run
Expand Down
88 changes: 45 additions & 43 deletions internal/geoipupdate/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -287,39 +287,14 @@ func setConfigFromFile(config *Config, path string) error {
return nil
}

// getValueFromEnvFile returns the value associated to an env variable or from a file containing it.
func getValueFromEnvFile(key string) (string, bool, error) {
var res string
var found bool

if value, ok := os.LookupEnv(key); ok {
res = value
found = true
}

if value := os.Getenv(key + "_FILE"); value != "" {
var err error

content, err := os.ReadFile(filepath.Clean(value))
if err != nil {
return "", true, fmt.Errorf("failed to open %s_FILE: %w", key, err)
}

found = true
res = strings.TrimSpace(string(content))
}

return res, found, nil
}

// setConfigFromEnv sets Config fields based on environment variables.
func setConfigFromEnv(config *Config) error {
if value, found, err := getValueFromEnvFile("GEOIPUPDATE_ACCOUNT_ID"); found {
if err != nil {
return err
}

config.AccountID, err = strconv.Atoi(value)
accountID, found, err := lookupFileOrEnv("GEOIPUPDATE_ACCOUNT_ID")
if err != nil {
return err
}
if found {
config.AccountID, err = strconv.Atoi(accountID)
if err != nil {
return errors.New("invalid account ID format")
}
Expand All @@ -344,12 +319,12 @@ func setConfigFromEnv(config *Config) error {
config.URL = u.String()
}

if value, found, err := getValueFromEnvFile("GEOIPUPDATE_LICENSE_KEY"); found {
if err != nil {
return err
}

config.LicenseKey = value
licenseKey, found, err := lookupFileOrEnv("GEOIPUPDATE_LICENSE_KEY")
if err != nil {
return err
}
if found {
config.LicenseKey = licenseKey
}

if value, ok := os.LookupEnv("GEOIPUPDATE_LOCK_FILE"); ok {
Expand Down Expand Up @@ -378,12 +353,12 @@ func setConfigFromEnv(config *Config) error {
config.proxyURL = value
}

if value, found, err := getValueFromEnvFile("GEOIPUPDATE_PROXY_USER_PASSWORD"); found {
if err != nil {
return err
}

config.proxyUserInfo = value
proxyUserInfo, found, err := lookupFileOrEnv("GEOIPUPDATE_PROXY_USER_PASSWORD")
if err != nil {
return err
}
if found {
config.proxyUserInfo = proxyUserInfo
}

if value, ok := os.LookupEnv("GEOIPUPDATE_RETRY_FOR"); ok {
Expand All @@ -404,6 +379,33 @@ func setConfigFromEnv(config *Config) error {
return nil
}

// lookupFileOrEnv returns the value of the environment variable named by key
// and whether it is set, as os.LookupEnv does. If the variable named by
// key+"_FILE" is not empty, lookupFileOrEnv instead reads the file at that
// path. It then returns the file contents, with leading and trailing white
// space removed, and true. If nothing remains, it returns an error. The file
// takes precedence over the variable named by key.
func lookupFileOrEnv(key string) (string, bool, error) {
if path := os.Getenv(key + "_FILE"); path != "" {
content, err := os.ReadFile(filepath.Clean(path))
if err != nil {
return "", false, fmt.Errorf("failed to read %s_FILE: %w", key, err)
}
value := strings.TrimSpace(string(content))

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

strings.TrimSpace also removes leading and trailing white space from the proxy user name and password. GEOIPUPDATE_PROXY_USER_PASSWORD keeps it. For example, a file that contains user:pass (the password ends in a space) gives the password pass. The same value in the variable gives pass .

The docs do not say that geoipupdate trims the file contents. Possible fixes:

  1. Say in doc/GeoIP.conf.md and doc/docker.md that geoipupdate removes leading and trailing white space from the file.
  2. Remove only the trailing newline for this key.

Claude Code (an AI agent) wrote this comment for @oschwald.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Documented in 0306d30 (option 1). doc/GeoIP.conf.md and doc/docker.md now say that geoipupdate removes leading and trailing white space from the file. The code did not change, so all 3 _FILE variables behave the same.

Claude Code (an AI agent) wrote this reply for @horgh.

if value == "" {
return "", false, fmt.Errorf(
"%s_FILE is set to %q, but that file is empty or contains only white space",
key,
path,
)
}
return value, true, nil
}

value, found := os.LookupEnv(key)
return value, found, nil
}

// setConfigFromFlags sets Config fields based on option flags.
func setConfigFromFlags(config *Config, flagOptions ...Option) error {
for _, option := range flagOptions {
Expand Down
Loading
Loading