17344: Ensure server-to-controller traffic stays local.
authorTom Clegg <tom@curii.com>
Fri, 12 Aug 2022 20:02:07 +0000 (16:02 -0400)
committerTom Clegg <tom@curii.com>
Thu, 25 Aug 2022 14:59:05 +0000 (10:59 -0400)
Arvados-DCO-1.1-Signed-off-by: Tom Clegg <tom@curii.com>

lib/boot/supervisor.go
sdk/go/arvados/client.go
sdk/go/arvadosclient/arvadosclient.go

index ddc17953d2363d020d6aa37332c97c36c5b48646..ebe1eb6f68434bae2cf9167d8ee20f0ca548533d 100644 (file)
@@ -308,6 +308,7 @@ func (super *Supervisor) runCluster() error {
        if super.ClusterType != "production" {
                super.prependEnv("PATH", super.tempdir+"/bin:")
        }
+       super.setEnv("ARVADOS_SERVER_ADDRESS", super.ListenHost)
 
        // Now that we have the config, replace the bootstrap logger
        // with a new one according to the logging config.
index 24d5ac3e335c824f5ea4a444c6066ce37f3cc86f..cdc07bb0afd2c80b09985ad28f18c6c0fa1abcde 100644 (file)
@@ -15,6 +15,7 @@ import (
        "io/fs"
        "io/ioutil"
        "log"
+       "net"
        "net/http"
        "net/url"
        "os"
@@ -94,7 +95,40 @@ func NewClientFromConfig(cluster *Cluster) (*Client, error) {
        if ctrlURL.Host == "" {
                return nil, fmt.Errorf("no host in config Services.Controller.ExternalURL: %v", ctrlURL)
        }
+       var hc *http.Client
+       if srvaddr := os.Getenv("ARVADOS_SERVER_ADDRESS"); srvaddr != "" {
+               // When this client is used to make a request to
+               // https://{ctrlhost}:port/ (any port), it dials the
+               // indicated port on ARVADOS_SERVER_ADDRESS instead.
+               //
+               // This is invoked by arvados-server boot to ensure
+               // that server->server traffic (e.g.,
+               // keepproxy->controller) only hits local interfaces,
+               // even if the Controller.ExternalURL host is a load
+               // balancer / gateway and not a local interface
+               // address (e.g., when running on a cloud VM).
+               //
+               // This avoids unnecessary delay/cost of routing
+               // external traffic, and also allows controller to
+               // recognize other services as internal clients based
+               // on the connection source address.
+               divertedHost := (*url.URL)(&cluster.Services.Controller.ExternalURL).Hostname()
+               var dialer net.Dialer
+               hc = &http.Client{
+                       Transport: &http.Transport{
+                               TLSClientConfig: &tls.Config{InsecureSkipVerify: cluster.TLS.Insecure},
+                               DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
+                                       host, port, err := net.SplitHostPort(addr)
+                                       if err == nil && network == "tcp" && host == divertedHost {
+                                               addr = net.JoinHostPort(srvaddr, port)
+                                       }
+                                       return dialer.DialContext(ctx, network, addr)
+                               },
+                       },
+               }
+       }
        return &Client{
+               Client:   hc,
                Scheme:   ctrlURL.Scheme,
                APIHost:  ctrlURL.Host,
                Insecure: cluster.TLS.Insecure,
index 24070c5b0658d61f53104d9351352f8654611c27..2044df633783894a37b426a0963a33ca93ec5852 100644 (file)
@@ -166,16 +166,20 @@ func MakeTLSConfig(insecure bool) *tls.Config {
 // fields from configuration files but still need to use the
 // arvadosclient.ArvadosClient package.
 func New(c *arvados.Client) (*ArvadosClient, error) {
-       ac := &ArvadosClient{
-               Scheme:      "https",
-               ApiServer:   c.APIHost,
-               ApiToken:    c.AuthToken,
-               ApiInsecure: c.Insecure,
-               Client: &http.Client{
+       hc := c.Client
+       if hc == nil {
+               hc = &http.Client{
                        Timeout: 5 * time.Minute,
                        Transport: &http.Transport{
                                TLSClientConfig: MakeTLSConfig(c.Insecure)},
-               },
+               }
+       }
+       ac := &ArvadosClient{
+               Scheme:            "https",
+               ApiServer:         c.APIHost,
+               ApiToken:          c.AuthToken,
+               ApiInsecure:       c.Insecure,
+               Client:            hc,
                External:          false,
                Retries:           2,
                KeepServiceURIs:   c.KeepServiceURIs,