...

Source file src/github.com/linkerd/linkerd2/test/integration/deep/norelay/norelay_test.go

Documentation: github.com/linkerd/linkerd2/test/integration/deep/norelay

     1  package norelay
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"net"
     7  	"os"
     8  	"strings"
     9  	"testing"
    10  
    11  	"github.com/linkerd/linkerd2/testutil"
    12  )
    13  
    14  var TestHelper *testutil.TestHelper
    15  
    16  func TestMain(m *testing.M) {
    17  	TestHelper = testutil.NewTestHelper()
    18  	// Block test execution until control plane is running
    19  	TestHelper.WaitUntilDeployReady(testutil.LinkerdDeployReplicasEdge)
    20  	os.Exit(m.Run())
    21  }
    22  
    23  // TestNoRelay verifies that hitting the proxy's outbound port doesn't result
    24  // in an open relay, by trying to leverage the l5d-dst-override header in an
    25  // ingress proxy.
    26  func TestNoRelay(t *testing.T) {
    27  	ctx := context.Background()
    28  	deployments := getDeployments(t)
    29  	TestHelper.WithDataPlaneNamespace(ctx, "norelay-test", map[string]string{}, t, func(t *testing.T, ns string) {
    30  		for name, res := range deployments {
    31  			out, err := TestHelper.KubectlApply(res, ns)
    32  			if err != nil {
    33  				testutil.AnnotatedFatalf(t, "unexpected error",
    34  					"unexpected error with deployment %s: %v output:\n%s",
    35  					name, err, out,
    36  				)
    37  			}
    38  		}
    39  
    40  		for name := range deployments {
    41  			err := TestHelper.CheckPods(ctx, ns, name, 1)
    42  			if err != nil {
    43  				//nolint:errorlint
    44  				if rce, ok := err.(*testutil.RestartCountError); ok {
    45  					testutil.AnnotatedWarn(t, "CheckPods timed-out", rce)
    46  				} else {
    47  					testutil.AnnotatedError(t, "CheckPods timed-out", err)
    48  				}
    49  			}
    50  		}
    51  
    52  		relayIPPort := getPodIPPort(t, ns, "app=server-relay", 4140)
    53  		o, err := TestHelper.Kubectl(
    54  			"", "-n", ns, "exec", "deploy/client",
    55  			"--", "curl", "-f", "-H", "l5d-dst-override: server-hello."+ns+".svc.cluster.local:8080", "http://"+relayIPPort,
    56  		)
    57  		if err == nil || err.Error() != "exit status 22" {
    58  			testutil.AnnotatedFatalf(t, "no error or unexpected error returned",
    59  				"no error or unexpected error returned: %s\n%s", o, err)
    60  		}
    61  	})
    62  }
    63  
    64  // TestRelay validates the previous test by running the same scenario but
    65  // forcing an open relay by changing the value of
    66  // LINKERD2_PROXY_OUTBOUND_LISTEN_ADDRS from 127.0.0.1:4140,[::1]:4140 to
    67  // 0.0.0.0:4140, which is not possible without manually changing the injected
    68  // proxy yaml
    69  //
    70  // We don't care if this behavior breaks--it's not a supported configuration.
    71  // However, this test is oddly useful in finding bugs in ingress-mode proxy
    72  // configurations, so we keep it around. ¯\_(ツ)_/¯
    73  func TestRelay(t *testing.T) {
    74  	ctx := context.Background()
    75  	deployments := getDeployments(t)
    76  
    77  	// account for both when IPv6 is enabled or disabled
    78  	deployments["server-relay"] = strings.ReplaceAll(
    79  		deployments["server-relay"],
    80  		"127.0.0.1:4140,[::1]:4140",
    81  		"'[::]:4140'",
    82  	)
    83  	deployments["server-relay"] = strings.ReplaceAll(
    84  		deployments["server-relay"],
    85  		"127.0.0.1:4140",
    86  		"0.0.0.0:4140",
    87  	)
    88  
    89  	TestHelper.WithDataPlaneNamespace(ctx, "relay-test", map[string]string{}, t, func(t *testing.T, ns string) {
    90  		for name, res := range deployments {
    91  			out, err := TestHelper.KubectlApply(res, ns)
    92  			if err != nil {
    93  				testutil.AnnotatedFatalf(t, "unexpected error",
    94  					"unexpected error with deployment %s: %v output:\n%s",
    95  					name, err, out,
    96  				)
    97  			}
    98  		}
    99  		waitForPods(t, ctx, ns, deployments)
   100  		relayIPPort := getPodIPPort(t, ns, "app=server-relay", 4140)
   101  
   102  		// Send a request to the outbound proxy port with a header that should route internally.
   103  		o, err := TestHelper.Kubectl(
   104  			"", "-n", ns, "exec", "deploy/client",
   105  			"--", "curl", "-fsv", "-H", "l5d-dst-override: server-hello."+ns+".svc.cluster.local:8080", "http://"+relayIPPort,
   106  		)
   107  		if err != nil {
   108  			log, err := TestHelper.Kubectl(
   109  				"", "logs",
   110  				"-n", ns,
   111  				"-l", "app=server-relay",
   112  				"-c", "linkerd-proxy",
   113  				"--tail=1000",
   114  			)
   115  			if err != nil {
   116  				log = fmt.Sprintf("failed to retrieve server-relay logs: %s", err)
   117  			}
   118  			testutil.AnnotatedFatalf(t, "unexpected error returned",
   119  				"unexpected error returned: %s\n%s\n---\n%s", o, err, log)
   120  		}
   121  		if !strings.Contains(o, "HELLO-FROM-SERVER") {
   122  			testutil.AnnotatedFatalf(t, "unexpected response returned",
   123  				"unexpected response returned: %s", o)
   124  		}
   125  	})
   126  }
   127  
   128  func getDeployments(t *testing.T) map[string]string {
   129  	deploys := make(map[string]string)
   130  	var err error
   131  
   132  	// server-hello is injected normally
   133  	deploys["server-hello"], err = TestHelper.LinkerdRun("inject", "testdata/server-hello.yml")
   134  	if err != nil {
   135  		testutil.AnnotatedFatal(t, "unexpected error", err)
   136  	}
   137  
   138  	// server-relay is injected in ingress mode, manually
   139  	deploys["server-relay"], err = TestHelper.LinkerdRun(
   140  		"inject", "--manual", "--ingress",
   141  		"--proxy-log-level=linkerd=debug,info",
   142  		"testdata/server-relay.yml")
   143  	if err != nil {
   144  		testutil.AnnotatedFatal(t, "unexpected error", err)
   145  	}
   146  
   147  	// client is not injected
   148  	deploys["client"], err = testutil.ReadFile("testdata/client.yml")
   149  	if err != nil {
   150  		testutil.AnnotatedFatalf(t, "failed to read 'client.yml'",
   151  			"failed to read 'client.yml': %s", err)
   152  	}
   153  
   154  	return deploys
   155  }
   156  
   157  func getPodIPPort(t *testing.T, ns, selector string, port int) string {
   158  	t.Helper()
   159  	ip, err := TestHelper.Kubectl(
   160  		"", "-n", ns, "get", "po", "-l", selector,
   161  		"-o", "jsonpath='{range .items[*]}{@.status.podIP}{end}'",
   162  	)
   163  	if err != nil {
   164  		testutil.AnnotatedFatalf(t, "failed to retrieve pod IP",
   165  			"failed to retrieve pod IP: %s", err)
   166  	}
   167  	ip = strings.Trim(ip, "'")
   168  	parsedIP := net.ParseIP(ip)
   169  	if parsedIP == nil {
   170  		testutil.AnnotatedFatalf(t, "invalid pod IP",
   171  			"invalid pod IP: %s", err)
   172  	}
   173  	if parsedIP.To4() != nil {
   174  		return fmt.Sprintf("%s:%d", ip, port)
   175  	}
   176  	return fmt.Sprintf("[%s]:%d", ip, port)
   177  }
   178  
   179  func waitForPods(t *testing.T, ctx context.Context, ns string, deployments map[string]string) {
   180  	t.Helper()
   181  	for name := range deployments {
   182  		err := TestHelper.CheckPods(ctx, ns, name, 1)
   183  		if err != nil {
   184  			//nolint:errorlint
   185  			if rce, ok := err.(*testutil.RestartCountError); ok {
   186  				testutil.AnnotatedWarn(t, "CheckPods timed-out", rce)
   187  			} else {
   188  				testutil.AnnotatedError(t, "CheckPods timed-out", err)
   189  			}
   190  		}
   191  	}
   192  }
   193  

View as plain text