17344: arvados-server boot: set X-External-Client header.
authorTom Clegg <tom@curii.com>
Tue, 2 Aug 2022 14:12:51 +0000 (10:12 -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/nginx.go
sdk/python/tests/nginx.conf
sdk/python/tests/run_test_server.py

index b391c4dc8c93f61f50d221bb116cf71da3c10960..9f1091eac39177f15855de7b7e8ccf2b0ac443f3 100644 (file)
@@ -5,6 +5,7 @@
 package boot
 
 import (
+       "bytes"
        "context"
        "fmt"
        "io/ioutil"
@@ -17,6 +18,7 @@ import (
        "strings"
 
        "git.arvados.org/arvados.git/sdk/go/arvados"
+       "github.com/sirupsen/logrus"
 )
 
 // Run an Nginx process that proxies the supervisor's configured
@@ -46,6 +48,7 @@ func (runNginx) Run(ctx context.Context, fail func(error), super *Supervisor) er
        vars := map[string]string{
                "LISTENHOST":       extListenHost,
                "UPSTREAMHOST":     super.ListenHost,
+               "INTERNALSUBNETS":  internalSubnets(super.logger),
                "SSLCERT":          filepath.Join(super.tempdir, "server.crt"),
                "SSLKEY":           filepath.Join(super.tempdir, "server.key"),
                "ACCESSLOG":        filepath.Join(super.tempdir, "nginx_access.log"),
@@ -150,3 +153,27 @@ func (runNginx) Run(ctx context.Context, fail func(error), super *Supervisor) er
        }
        return waitForConnect(ctx, testurl.Host)
 }
+
+// Return 0 or more local subnets as "geo" fragments for Nginx config,
+// e.g., "1.2.3.0/24 0; 10.1.0.0/16 0;".
+func internalSubnets(logger logrus.FieldLogger) string {
+       iproutes, err := exec.Command("ip", "route").CombinedOutput()
+       if err != nil {
+               logger.Warnf("treating all clients as external because `ip route` failed: %s (%q)", err, iproutes)
+               return ""
+       }
+       subnets := ""
+       for _, line := range bytes.Split(iproutes, []byte("\n")) {
+               fields := strings.Fields(string(line))
+               if len(fields) > 2 && fields[1] == "dev" {
+                       // lan example:
+                       // 192.168.86.0/24 dev ens3 proto kernel scope link src 192.168.86.196
+                       // gcp example (private subnet):
+                       // 10.47.0.0/24 dev eth0 proto kernel scope link src 10.47.0.5
+                       // gcp example (no private subnet):
+                       // 10.128.0.1 dev ens4 scope link
+                       subnets += fields[0] + " 0; "
+               }
+       }
+       return subnets
+}
index 4ad3eda42008b4c7f5ddd200dca9ca14336a436a..a1a75bbcc216e0fb9546f713fd99c4aa2914b989 100644 (file)
@@ -15,6 +15,11 @@ http {
   fastcgi_temp_path "{{TMPDIR}}";
   uwsgi_temp_path "{{TMPDIR}}";
   scgi_temp_path "{{TMPDIR}}";
+  geo $external_client {
+    default 1;
+    127.0.0.0/8 0;
+    {{INTERNALSUBNETS}}
+  }
   upstream controller {
     server {{UPSTREAMHOST}}:{{CONTROLLERPORT}};
   }
@@ -26,7 +31,10 @@ http {
     client_max_body_size 0;
     location  / {
       proxy_pass http://controller;
+      proxy_set_header Upgrade $http_upgrade;
+      proxy_set_header Connection "upgrade";
       proxy_set_header Host $http_host;
+      proxy_set_header X-External-Client $external_client;
       proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
       proxy_set_header X-Forwarded-Proto https;
       proxy_redirect off;
index e32d385f73fc0849c30d4a730cb3b83c6a4425c6..7147c7aa8fdf826c6b469a0a0dc3b7631a69d133 100644 (file)
@@ -660,6 +660,7 @@ def run_nginx():
     nginxconf['ACCESSLOG'] = _logfilename('nginx_access')
     nginxconf['ERRORLOG'] = _logfilename('nginx_error')
     nginxconf['TMPDIR'] = TEST_TMPDIR + '/nginx'
+    nginxconf['INTERNALSUBNETS'] = '169.254.0.0/16 0;'
 
     conftemplatefile = os.path.join(MY_DIRNAME, 'nginx.conf')
     conffile = os.path.join(TEST_TMPDIR, 'nginx.conf')