Skip to content

feat: add /server-ip endpoint to return server's local IP address#226

Open
paimus wants to merge 1 commit intomccutchen:mainfrom
paimus:main
Open

feat: add /server-ip endpoint to return server's local IP address#226
paimus wants to merge 1 commit intomccutchen:mainfrom
paimus:main

Conversation

@paimus
Copy link

@paimus paimus commented Nov 18, 2025

This change introduces a new /server-ip endpoint that returns the IP address of the server (or Pod in Kubernetes) handling the request, as opposed to the existing /ip endpoint which returns the client's IP address.

Changes:

  • Add getServerIP() helper in helpers.go to detect server's primary IP
    • Prefers non-loopback IPv4 addresses
    • Falls back to IPv6 if no IPv4 found
    • Uses UDP dial as last resort for IP detection
  • Add ServerIP handler in handlers.go
  • Register /server-ip route in httpbin.go
  • Add serverIPResponse struct in responses.go

This is useful for debugging and service discovery in containerized environments where the server's IP may need to be identified.

This change introduces a new /server-ip endpoint that returns the IP address
of the server (or Pod in Kubernetes) handling the request, as opposed to the
existing /ip endpoint which returns the client's IP address.

Changes:
- Add getServerIP() helper in helpers.go to detect server's primary IP
  - Prefers non-loopback IPv4 addresses
  - Falls back to IPv6 if no IPv4 found
  - Uses UDP dial as last resort for IP detection
- Add ServerIP handler in handlers.go
- Register /server-ip route in httpbin.go
- Add serverIPResponse struct in responses.go

This is useful for debugging and service discovery in containerized
environments where the server's IP may need to be identified.
@mccutchen
Copy link
Owner

I appreciate the contribution, but as noted in #113 (comment) I am reluctant to expose this potentially sensitive info about internal networks by default.

I'd consider a change that only exposed this endpoint if it was explicitly enabled, along the lines of the USE_REAL_HOSTNAME option added in #81.

That being said, I'm not sure about the implementation here. Why is IPv4 preferred over IPv6? Also I don't think making a throwaway UDP connection (why to port 80? does the port just not matter here?) is a reasonable thing to do.

@derekmpage
Copy link
Contributor

derekmpage commented Jan 7, 2026

Also I don't think making a throwaway UDP connection (why to port 80? does the port just not matter here?) is a reasonable thing to do.

I was curious about this so I did some research.

First off UDP is a connectionless protocol.

net.Dial("udp", "8.8.8.8:80") -> This asks the kernel, “If I wanted to send a UDP packet to 8.8.8.8, which IP/interface would you use?”

Then you use localAddr := conn.LocalAddr().(*net.UDPAddr) to receive the local IP of the interface used.
So no packets are actually sent unless you "Write" to the socket.

Since we are not planning on writing any data, the port does not mater but it's required.

For the "prefering" of ipv4

I would may consider just making a json payload that shows everything

{
  "primary_address": {
    "10.0.5.25"
  },
  "ip_addresses": {
    "ipv4": [
      "192.168.1.101",
      "10.0.5.25",
      "172.16.254.1"
    ],
    "ipv6": [
      "2001:0db8:85a3::8a2e:0370:7334",
      "fe80::1ff:fe23:4567:890a"
    ]
  }
}

Whatever you consider the primary address to be?

@mccutchen
Copy link
Owner

First off UDP is a connectionless protocol.

net.Dial("udp", "8.8.8.8:80") -> This asks the kernel, “If I wanted to send a UDP packet to 8.8.8.8, which IP/interface would you use?”

Then you use localAddr := conn.LocalAddr().(*net.UDPAddr) to receive the local IP of the interface used. So no packets are actually sent unless you "Write" to the socket.

Ah, thank you for the explanation, TIL!

I would may consider just making a json payload that shows everything

I think if we add this functionality I like this direction: return all the IP addresses we know about. Is there a reasonable heuristic we can use to choose a "primary" address that would make sense to most clients of this hypothetical endpoint? If not, I'd be inclined to just return them all and let the client choose one.

@aarnaud
Copy link
Contributor

aarnaud commented Jan 27, 2026

Hi @mccutchen

I can explain the primary_address it's base on src ip used to for the default route.

> ip route get 8.8.8.8
8.8.8.8 via 192.168.1.1 dev ens3 src 192.168.1.123 uid 0

In this example, the primary_address will be 192.168.1.123

It's the local IP address that the kernel selects as the source address when routing traffic
through the default gateway (typically for destinations without a more specific local route).

@aarnaud
Copy link
Contributor

aarnaud commented Jan 27, 2026

Hi @paimus I think to option can leak some sensitive information, shouldn't enable by default, should have a args to enable it

@mccutchen mccutchen changed the title Add /server-ip endpoint to return server's local IP address feat: add /server-ip endpoint to return server's local IP address Jan 30, 2026
@mccutchen
Copy link
Owner

Ah, thanks for that explanation @aarnaud, I think it makes some sense. This is a useful learning exercise for me, if nothing else.

Copy link
Owner

@mccutchen mccutchen left a comment

Choose a reason for hiding this comment

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

Okay, to reset here: I'd be willing to include this new endpoint with a few adjustments to the implementation:

  • Make it opt-in, along the lines of the USE_REAL_HOSTNAME option added in #81.
  • Make it return a more thorough response like the one suggested by @derekmpage above (lightly adapted here):
    {
      "primary_address": {
        "10.0.5.25"
      },
      "addresses": {
        "ipv4": [
          "192.168.1.101",
          "10.0.5.25",
          "172.16.254.1"
        ],
        "ipv6": [
          "2001:0db8:85a3::8a2e:0370:7334",
          "fe80::1ff:fe23:4567:890a"
        ]
      }
    }
  • where the primary address is chosen as described by @aarnaud above: "It's the local IP address that the kernel selects as the source address when routing traffic through the default gateway"

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants