How to Use IP Utilities for Network Address Processing in go-pantheon/fabrica-kit
The ip package in go-pantheon/fabrica-kit provides pure Go functions to detect internal IPv4 addresses, resolve service advertisement endpoints, extract client IPs from Kratos contexts, and validate private network ranges without external dependencies.
The go-pantheon/fabrica-kit repository offers a lightweight networking toolkit designed for microservices that need reliable address detection across containerized and bare-metal environments. This guide examines the IP utilities for network address processing implemented in ip/ip.go, demonstrating how to leverage InternalIP(), Extract(), and GetClientIP() for robust service discovery and client identification.
Core IP Utility Functions in fabrica-kit
The ip package exposes five primary functions that handle distinct network address processing scenarios. Each utility operates without network I/O beyond local interface enumeration, making them safe for high-frequency calls during service startup and request handling.
InternalIP: Detecting Private IPv4 Addresses
The InternalIP() function scans the host's network interfaces to return the first available private IPv4 address. It iterates through net.Interfaces(), skips downed or loopback devices, and extracts the IPv4 component from each address using ipNet.IP.To4().
This function is essential when services must auto-detect their LAN address for registration with service discovery systems or health check endpoints. If no suitable interface exists, it returns an empty string rather than an error, allowing callers to implement fallback logic.
Extract: Resolving Advertised Service Addresses
The Extract(hostPort string, lis net.Listener) function builds a resolvable host:port string suitable for service advertisement. It implements a four-step resolution strategy:
- Parse the input using
net.SplitHostPort, returningErrInvalidHostPorton malformed input. - Override Port with the listener's actual port if available via the
Port()helper. - Explicit Address preservation when the host is not a wildcard (
0.0.0.0,[::],::). - Private IP Fallback that enumerates interfaces and returns the first address satisfying
isPrivateIP, or returnsErrNoPrivateIPFoundif none exist.
This function solves the common microservice problem of binding to 0.0.0.0:0 (all interfaces, random port) while still advertising a concrete, reachable address to registries.
Port: Retrieving Dynamic Listener Ports
The Port(lis net.Listener) function extracts the concrete TCP port from a listener. It type-asserts lis.Addr() to *net.TCPAddr and returns the Port field.
This utility is critical when starting servers with net.Listen("tcp", ":0"), where the operating system assigns an available ephemeral port. The returned port enables dynamic service registration without pre-configuration.
isPrivateIP: Validating Private Address Ranges
The isPrivateIP(addr string) function validates whether an IP address falls within standard private ranges. It implements RFC1918 checks for IPv4:
10.0.0.0/8(10.x.x.x)172.16.0.0/12(172.16.x.x - 172.31.x.x)192.168.0.0/16(192.168.x.x)
For IPv6, it checks the unique local address prefix FC00::/7 by verifying (ip[0] & 0xfe) == 0xfc.
The function returns false for public IPs, loopback addresses, and malformed strings, making it suitable for security filtering and network segmentation logic.
GetClientIP: Extracting Client Addresses in Kratos
The GetClientIP(ctx context.Context) function extracts the originating client IP from a Kratos server context. It retrieves the transport layer via transport.FromServerContext(ctx) and inspects request headers in priority order:
- X-Forwarded-For: Returns the first comma-separated entry if present (handles proxy chains).
- X-Real-IP: Returns the value if
X-Forwarded-Foris absent.
This function is essential for microservices running behind reverse proxies or load balancers, where the transport connection's RemoteAddr reflects the proxy rather than the end user. It returns an empty string when the context lacks transport information or headers.
Working with Network Address Errors
The ip package defines exported sentinel errors that enable precise error handling without string parsing. These constants reside in ip/ip.go and cover the primary failure modes:
ErrInvalidHostPort: Returned whenExtractreceives a malformedhost:portstring that failsnet.SplitHostPort.ErrNoPrivateIPFound: Returned whenExtractcannot locate a suitable private IP address on the host after checking all network interfaces.ErrInterfaceLookup: Returned when the underlying call tonet.Interfaces()fails, preventing address enumeration.ErrInvalidIPFormat: Returned whenisPrivateIPreceives a string that cannot be parsed as an IP address.ErrIPComponentOutOfRange: Returned when parsing IP components reveals values outside valid byte ranges.
Using these errors allows applications to implement differentiated retry logic, fallback to configuration values, or fail fast with clear telemetry.
Practical Implementation Examples
The following examples demonstrate common patterns for using fabrica-kit's IP utilities in production Go applications.
Example 1: Obtaining the Host's Private IP
Use InternalIP() when your service needs to register its LAN address with a service discovery registry.
package main
import (
"fmt"
"github.com/go-pantheon/fabrica-kit/ip"
)
func main() {
ifaceIP := ip.InternalIP()
if ifaceIP == "" {
fmt.Println("No internal IP found")
return
}
fmt.Println("Detected internal IP:", ifaceIP)
}
Example 2: Resolving Service Advertisement Addresses
Use Extract() when binding to wildcard addresses but advertising a concrete private IP to clients.
package main
import (
"fmt"
"net"
"github.com/go-pantheon/fabrica-kit/ip"
)
func main() {
// Bind to all interfaces with OS-assigned port
ln, err := net.Listen("tcp", "0.0.0.0:0")
if err != nil {
panic(err)
}
defer ln.Close()
// Resolve to private IP for service registration
addr, err := ip.Extract("0.0.0.0:0", ln)
if err != nil {
// Handle ErrNoPrivateIPFound or ErrInvalidHostPort
panic(err)
}
fmt.Println("Service should be advertised as:", addr)
}
Example 3: Handling Dynamic Ports
Use Port() to retrieve the actual port when using :0 for dynamic allocation.
ln, _ := net.Listen("tcp", ":0")
if port, ok := ip.Port(ln); ok {
fmt.Printf("Listener is on port %d\n", port)
}
Example 4: Validating Client IPs in Kratos Handlers
Use GetClientIP() inside Kratos handlers to identify real client addresses behind proxies.
import (
"context"
"github.com/go-kratos/kratos/v2/transport"
"github.com/go-pantheon/fabrica-kit/ip"
pb "myapp/api"
)
func MyHandler(ctx context.Context, req *pb.MyRequest) (*pb.MyResponse, error) {
clientIP := ip.GetClientIP(ctx)
if clientIP != "" {
// Log or validate the originating IP
fmt.Printf("Request from %s\n", clientIP)
}
// ... business logic ...
return &pb.MyResponse{}, nil
}
Summary
- The go-pantheon/fabrica-kit
ippackage provides pure Go utilities for network address processing without external dependencies. InternalIP()auto-detects the host's first private IPv4 address for service registration.Extract()resolves wildcard bind addresses (0.0.0.0:0) into concrete private IPs suitable for service discovery.Port()retrieves runtime-assigned ports from listeners created with:0.isPrivateIP()validates RFC1918 IPv4 and unique local IPv6 addresses for security filtering.GetClientIP()extracts real client IPs from Kratos contexts, respectingX-Forwarded-ForandX-Real-IPheaders.- All functions return exported sentinel errors (
ErrNoPrivateIPFound,ErrInvalidHostPort, etc.) for robust error handling.
Frequently Asked Questions
How does fabrica-kit determine if an IP is private?
The isPrivateIP function in ip/ip.go implements RFC1918 checks for IPv4 addresses by verifying if the IP falls within the 10.0.0.0/8, 172.16.0.0/12, or 192.168.0.0/16 ranges using byte-wise comparison. For IPv6, it checks the unique local address prefix FC00::/7 by validating that the first byte matches (ip[0] & 0xfe) == 0xfc.
What error should I handle when no private IP is available?
When calling Extract() or InternalIP() on hosts without private network interfaces (such as public cloud instances with only public IPs), handle ErrNoPrivateIPFound. This sentinel error defined in ip/ip.go indicates that the function successfully scanned all network interfaces but failed to locate any address satisfying the private IP criteria, allowing your application to fall back to configuration values or public IP detection.
Can I use these utilities without the Kratos framework?
Yes, all IP utilities except GetClientIP function independently of the Kratos framework. InternalIP(), Extract(), Port(), and isPrivateIP() rely solely on Go's standard library (net package) and require no external dependencies. Only GetClientIP depends on Kratos's transport package to extract headers from the context, but the core network address processing capabilities remain framework-agnostic.
How do I handle dynamic port allocation with fabrica-kit?
When binding to port 0 to allow the OS to assign an available ephemeral port, use the Port() function to retrieve the actual assigned port number. First create the listener with net.Listen("tcp", ":0"), then pass that listener to ip.Port(lis) which type-asserts the address to *net.TCPAddr and returns the concrete port integer. Combine this with Extract() to build the complete host:port string for service registration.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →