Skip to content

fix(plugin): allow overriding server IP for NAT/private setups - #25

Open
Rushaway wants to merge 2 commits into
mainfrom
fix/issue-1555
Open

fix(plugin): allow overriding server IP for NAT/private setups#25
Rushaway wants to merge 2 commits into
mainfrom
fix/issue-1555

Conversation

@Rushaway

@Rushaway Rushaway commented Sep 3, 2026

Copy link
Copy Markdown
Member

Problem

Ports upstream issue sbpp#1555.

A gameserver running behind NAT (for example inside a container) has its
hostip cvar set to a private LAN address (e.g. 10.46.144.181). The
plugin uses that auto-detected value in every SQL statement that looks up
or inserts the server:

(SELECT sid FROM sb_servers WHERE ip = '10.46.144.181' AND port = '11127' LIMIT 0,1)

That never matches the public IP registered in the web panel, so bans and
comms are never associated with the server and stay stuck in the local
sourcebans-queue.sq3. The issue reporter tried a ServerIP key that did
not actually exist.

Fix

Add an optional "ServerIP" key to the Config section of
sourcebans.cfg. When non-empty it overrides the auto-detected IP used
for all server lookups/INSERTs in sbpp_main.sp and sbpp_comms.sp.
Empty by default, so existing auto-detect behaviour is unchanged.

The override is applied both when the config key is parsed and right
after the hostip is formatted in InsertServerInfo() / ServerInfo(),
so it holds regardless of load ordering.

No local SourcePawn compiler available; relying on plugin-build CI.

🤖 Generated with Claude Code

Rushaway and others added 2 commits September 3, 2026 10:55
Servers running behind NAT (e.g. in a container) auto-detect the private
LAN "hostip", which never matches the public IP registered in the web
panel, so bans/comms are never associated with the correct server.

Add an optional "ServerIP" key to the Config section of sourcebans.cfg.
When set, it overrides the auto-detected IP used in all SQL server
lookups and INSERTs in sbpp_main and sbpp_comms. Empty by default,
preserving existing auto-detect behaviour.

Refs: sbpp#1555

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Harden the "ServerIP" override added for NAT/containerized gameservers:

- Validate the configured value as a dotted-quad IPv4 and TrimString() it.
  ServerIp is interpolated into SQL unescaped, so a malformed/whitespace
  value would silently never match (or break) the sb_servers lookup.
  Invalid values are logged and fall back to the auto-detected hostip.
- Reset ServerIpOverride at the start of every ReadConfig() so removing or
  emptying the key on a reload no longer keeps a stale override, and
  re-apply it over the auto-detected IP after each parse
  (UpdateServerIp() in sbpp_main, ServerInfo() in sbpp_comms) instead of
  writing ServerIp from inside the SMC key handler.
- sbpp_main: parse the config before the first InsertServerInfo() when the
  database callback wins the race against OnMapStart, otherwise the
  AutoAddServer SELECT/INSERT could still run with the private hostip
  (and the default DatabasePrefix).
- Guard hostip/hostport cvar lookups against null.
- Document the IPv4 requirement, the invalid-value fallback, and that the
  port still comes from the hostport cvar (NAT port remaps are not covered).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@Rushaway

Rushaway commented Sep 3, 2026

Copy link
Copy Markdown
Member Author

Review: approve after fixes (pushed as review fixes)

The approach is right: sb_bans.sid / sb_comms lookups all funnel through the single ServerIp global, so overriding it in one place covers every SELECT sid FROM %s_servers WHERE ip = ... subquery (sbpp_main lines ~1123/1285/1494/1585/1721/2578, the AutoAddServer SELECT/INSERT, and the adminIp written for console/web-issued actions). I checked every ServerIp use in both plugins — no path still uses the raw hostip after the change. ServerPort is untouched and empty value ⇒ byte-identical previous behaviour.

Five things I fixed on top:

  1. No validation / no trimming. ServerIp is interpolated into SQL unescaped. "ServerIP" "1.2.3.4 " (trailing space) or any typo would silently never match sb_servers — i.e. exactly the symptom this PR is fixing, just with a different cause, and a quote in the value would break/inject into the query. Now the value is TrimString()d and validated as a dotted-quad IPv4; an invalid value is LogErrord and falls back to the auto-detected IP.
  2. Stale override on reload. ReadConfig() runs again on every map start (ResetSettings()) and on sb_reload. Since the key handler only ever wrote ServerIpOverride, removing or emptying the key left the old override in effect until the next map... forever, actually. ServerIpOverride is now cleared at the top of ReadConfig() and re-applied on top of the freshly detected hostip after each parse.
  3. Override applied from inside the SMC key handler. Replaced with an explicit UpdateServerIp() (sbpp_main) / existing ServerInfo() (sbpp_comms) call after the parse. Same effect for both orderings, but it is now a single place that owns ServerIp, and it also picks up a hostip that was still 0 at OnPluginStart (sbpp_comms called ServerInfo() there).
  4. Load-order race in sbpp_main. ReadConfig() is only reached via OnMapStartResetSettings(), while GotDatabase()InsertServerInfo() is an async DB callback. If the DB callback wins, AutoAddServer would run its SELECT/INSERT with the private hostip (and the default DatabasePrefix), inserting a bogus sb_servers row that nothing can retract. GotDatabase() now calls ResetSettings() first if the config hasn't been parsed yet.
  5. Null-guarded the hostip/hostport cvar handles, since UpdateServerIp()/ServerInfo() are now reachable outside the DB path.

Caveats worth knowing (documented in sourcebans.cfg, no code change):

  • Port is still taken from the hostport cvar. If your NAT remaps the port as well as the address, the panel entry must use the server's internal port, or the lookup still misses. A companion "ServerPort" key would be the follow-up if anyone hits that.
  • In sbpp_comms the override only affects the recorded adminIp. That plugin resolves sid from the ServerID config value, not from an IP lookup, so the NAT symptom never applied there — the key is there for parity/consistency.
  • IPv4 only; the [24] buffer and the validator both assume dotted quad, which matches the sb_servers.ip column usage.

CI: Plugin build green, no new compiler warnings.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant