From c04458c7a14669a739a39ecc332bfa19ee310058 Mon Sep 17 00:00:00 2001 From: Tom Clegg Date: Fri, 12 Aug 2022 16:02:07 -0400 Subject: [PATCH] 17344: Ensure server-to-controller traffic stays local. Arvados-DCO-1.1-Signed-off-by: Tom Clegg --- lib/boot/supervisor.go | 1 + sdk/go/arvados/client.go | 34 +++++++++++++++++++++++++++ sdk/go/arvadosclient/arvadosclient.go | 18 ++++++++------ 3 files changed, 46 insertions(+), 7 deletions(-) diff --git a/lib/boot/supervisor.go b/lib/boot/supervisor.go index ddc17953d2..ebe1eb6f68 100644 --- a/lib/boot/supervisor.go +++ b/lib/boot/supervisor.go @@ -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. diff --git a/sdk/go/arvados/client.go b/sdk/go/arvados/client.go index 24d5ac3e33..cdc07bb0af 100644 --- a/sdk/go/arvados/client.go +++ b/sdk/go/arvados/client.go @@ -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, diff --git a/sdk/go/arvadosclient/arvadosclient.go b/sdk/go/arvadosclient/arvadosclient.go index 24070c5b06..2044df6337 100644 --- a/sdk/go/arvadosclient/arvadosclient.go +++ b/sdk/go/arvadosclient/arvadosclient.go @@ -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, -- 2.30.2