Skip to content

Support private/public IP in Realm response + ORM update + DB migration #1489

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: master
Choose a base branch
from

Conversation

Belnadifia
Copy link

  • Added two new columns to the realmlist table:
    • public_proxy_address
    • public_realm_address
  • Updated RealmModels.py to include the new columns.
  • Updated RealmManager.py:
    • The server now dynamically chooses to send either the private or public IP to clients based on their incoming IP range.
    • No more need for FORWARD_ADDRESS_OVERRIDE environment variable.
  • Included an SQL migration script:
    • ALTER TABLE statements to add the new columns with default values 0.0.0.0.
    • Optional initial copy of proxy_address and realm_address into their public counterparts.

✅ Tests performed:

  • Confirmed correct behavior with both LAN and WAN clients.
  • Realm list and world redirection tested successfully.
  • Running on a Kubernetes cluster using MetalLB as LoadBalancer provider.

Check the user IP to see if it's a private range IP (172.16.0.0/12 192.168.0.0/16 or 10.0.0.0/8) 
If it's a private one -> Send the realm.proxy_adresse, else send the realm.public_proxy_address

Force to listen on 0.0.0.0 (And not based on the IP provided in the DBB)
add public_realm_address and public_proxy_adress to realmlist table
Set 0.0.0.0 to default value for Public IPs in Realmlist table
@diff3
Copy link
Contributor

diff3 commented Apr 28, 2025

Does it also work when running Alpha-Core in Docker?

@Belnadifia
Copy link
Author

Belnadifia commented Apr 28, 2025

I was able to run it on my K8S, that use docker image.
Since the server is listening on 0.0.0.0, you just need to set the good IPs in the DB (reachable from Lan or Wan)

But I can give it a try on Docker Desktop

@Belnadifia
Copy link
Author

Tested on Docker Desktop (Windows + WSL2) :

Works fine when connecting from the same machine (Config.wtf set to 127.0.0.1).
For LAN/WAN access (via NAT), you need to set up port forwarding using netsh.
This is a known workaround due to Docker Desktop's network limitations on Windows with WSL2.

Example:
netsh interface portproxy add v4tov4 listenport=3724 listenaddress=0.0.0.0 connectport=3724 connectaddress=172.x.x.x

This lets external clients reach the server through your host IP.

sck.sendall(packet)

@staticmethod
def start_realm(running, realm_server_ready):
local_realm = REALMLIST[config.Server.Connection.Realm.local_realm_id]
with SocketBuilder.build_socket(local_realm.realm_address, local_realm.realm_port, timeout=2) as server_socket:
with SocketBuilder.build_socket('0.0.0.0', local_realm.realm_port, timeout=2) as server_socket:
Copy link
Member

Choose a reason for hiding this comment

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

Why bind to 0.0.0.0 here?

Copy link
Author

Choose a reason for hiding this comment

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

When setting up a server socket, you might think to bind directly to a specific interface or IP address. However, in most modern deployments—especially in Kubernetes or cloud environments—binding to 0.0.0.0 is the correct and robust choice.

Why not bind to a specific IP?
Pods in Kubernetes have their own internal IPs (e.g., 10.x.x.x), which are only reachable from inside the cluster.

Externally, your service is typically accessed through a Load Balancer or NodePort with a different IP (e.g., 172.16.x.x).

Your application (or database, etc.) configuration might advertise the external (load balancer) IP, but your pod/container does not have that IP as a local interface!

If you bind your server to this external IP, the server won’t actually receive any connections, because that IP does not exist inside the pod.

Example scenario
Suppose:

Your pod’s internal IP is 10.0.0.1 (only reachable from within the cluster).

Your service is exposed externally at 172.16.0.54 (load balancer or worker node).

In your database or config, you have to advertise 172.16.0.54 as the address clients should use.

If you bind your socket to 172.16.0.54, your server will not listen on any actual interface inside the pod—because that IP does not exist inside the pod.

The correct solution: Bind to 0.0.0.0
Binding to 0.0.0.0 means “listen on all available interfaces and all incoming destination IPs”.
This ensures:

Your server can accept connections, no matter which network interface or cluster routing path is used.

Clients (inside or outside the cluster) can reach your service as long as the port is exposed.

It works for both internal and external clients, across NAT, service mesh, and Kubernetes routing.

Copy link
Member

Choose a reason for hiding this comment

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

I understand that, but it shouldn't be hardcoded; listening on 0.0.0.0 might not be suitable for everyone, depending on their server setup. Also, since FORWARD_ADDRESS_OVERRIDE becomes obsolete with these changes, all references to it should be removed, including in the Docker setup script.

Copy link
Author

Choose a reason for hiding this comment

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

If think listening on 0.0.0.0 is suitable for almost 90% of users ^•^
Having multiple NIC is mostly rare for common user (well, if we put asside WLAN)
Using 0.0.0.0 will accept any connection, no matter the server setup.
I understand it's shouldn't be hardcoded for specific case (most of the time, we use 0.0.0.0) , but then you should create a new ENV Variable and prosion it from conf file with 0.0.0.0 as default value.

And for FORWARD_ADDRESS_OVERRIDE, been a certain so I haven't the code in mind at the moment.

@@ -90,7 +103,7 @@ def start_realm(running, realm_server_ready):
@staticmethod
def start_proxy(running, proxy_server_ready):
local_realm = REALMLIST[config.Server.Connection.Realm.local_realm_id]
with SocketBuilder.build_socket(local_realm.proxy_address, local_realm.proxy_port, timeout=2) as server_socket:
with SocketBuilder.build_socket('0.0.0.0', local_realm.proxy_port, timeout=2) as server_socket:
Copy link
Member

Choose a reason for hiding this comment

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

Same here.

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.

3 participants