Bind to specific IP address for routines using dst-ports 5775 and 7777

Hello, is there a configuration option to bind to a specific local IP address for the routines that are sending data to ports 5775 and 7777?
It looks like with standard config these are using host IP address, which might be undesirable in some setups.

Well, did you try to specify desired_IP:port instead of 0:port or :port? As 127.0.0.1:port vs 0:port works as expected for debug port, I see no reason why it should be different for 7777

1 Like

All the addresses I have specified as IP:port, but I do not see a way to define it for these data reports going to some google service. Because of this these reports are taking a different route than the rest of the storagenode.
The source port also looks to be random, so the definition would have to be an IP address only I think and I do not see such option in the config.

# lsof -i | grep 192.168.1.130
storageno 188688         storj    7u  IPv4 3593951118      0t0  UDP 192.168.1.130:41927->120.40.184.35.bc.googleusercontent.com:5775 

# config.yaml
console.address: 192.168.1.131:14002
debug.addr: 192.168.1.131:9651
server.address: 192.168.1.131:28967
server.private-address: 192.168.1.131:7778

It looks like only the destination can be specified for these routines, such as:

# address for jaeger agent
# tracing.agent-addr: agent.tracing.datasci.storj.io:5775

Because they don’t listen on ports, they pass data, so it’s not the services that connect to your node, but your node that establishes a connection to the tracing service.
It should be configured on your device in the routing table, not in the node’s config.

I understand, I’m doing that on the router, but that is based on the source IP address.
It looks like with IP tables you can do source NAT per process owner and change source IP that way, or I guess I can mark with DSCP on host and match and route based on that on the router, but that would mean creating multiple users on the host, messing with IP tables, creating additional rules on the router, and I like things simple.
So I think a better approach would be to be able to specify which local IP address to use for this outbound service traffic in the storagenode config, so here I’m hinting at code changes and I’m not familiar with Go :wink:.

it’s hard to implement, it’s the OS level, not application level. Yes of course you may try to re-implement the routing table, but we do not have free engineering resources to partially re-implent the system network stack.

It must be done by OS, not the storage application, because it’s an outgoing traffic initiated by the application. This is an exact job for the routing table to route this request properly.

This is the same as asking the browsers developers to implement the routing table in the browser to send requests from different IP.

The IP address is already present on the system, so in a way it should be as easy as tell the socket to use this IP address, something similar to telling the storagenode to listen on this IP:port, as for example 192.168.1.100:14002. In such case the web interface is only available on this IP address, even if there are multiple configured on the system.
Gen AI came up with something like below.
It looks to be http specific, but maybe something similar exists even for raw TCP/UDP sockets and in such case it would be adding one more parameter to the socket and creating a config file entry for it.

package main

import (
	"context"
	"fmt"
	"io"
	"log"
	"net"
	"net/http"
	"time"
)

func main() {
	// Replace with your desired local IP address
	localIP := "192.168.1.100"
	// Replace with the target URL
	targetURL := "https://www.example.com"

	// Create a custom dialer
	dialer := &net.Dialer{
		Timeout:   30 * time.Second,
		KeepAlive: 30 * time.Second,
		LocalAddr: &net.TCPAddr{IP: net.ParseIP(localIP)},
	}

	// Create a custom transport with the dialer
	transport := &http.Transport{
		Proxy:                 http.ProxyFromEnvironment,
		DialContext:           dialer.DialContext,
		ForceAttemptHTTP2:     true,
		MaxIdleConns:          100,
		IdleConnTimeout:       90 * time.Second,
		TLSHandshakeTimeout:   10 * time.Second,
		ExpectContinueTimeout: 1 * time.Second,
	}

	// Create an HTTP client with the custom transport
	client := &http.Client{
		Timeout:   time.Second * 10,
		Transport: transport,
	}

	// Perform the request
	resp, err := client.Get(targetURL)
	if err != nil {
		log.Fatalf("Error fetching URL: %v", err)
	}
	defer resp.Body.Close()

	body, _ := io.ReadAll(resp.Body)
	fmt.Println(string(body))
	fmt.Println("Response from:", resp.Request.URL.String())
}

Then you can implement this hack for your own storagenode, if you believe that it’s simpler than properly configure the routing table.

2 Likes