github.com/dbernstein1/tyk@v2.9.0-beta9-dl-apic+incompatible/gateway/grpc_test.go (about)

     1  package gateway
     2  
     3  import (
     4  	"context"
     5  	"crypto/tls"
     6  	"crypto/x509"
     7  	"encoding/base64"
     8  	"encoding/json"
     9  	"fmt"
    10  	"io/ioutil"
    11  	"net"
    12  	"net/http"
    13  	"net/http/httptest"
    14  	"strings"
    15  	"testing"
    16  	"time"
    17  
    18  	"github.com/TykTechnologies/tyk/config"
    19  	"github.com/TykTechnologies/tyk/test"
    20  	"github.com/TykTechnologies/tyk/user"
    21  	"golang.org/x/net/http2"
    22  	"google.golang.org/grpc"
    23  	"google.golang.org/grpc/credentials"
    24  	pb "google.golang.org/grpc/examples/helloworld/helloworld"
    25  )
    26  
    27  // For gRPC, we should be sure that HTTP/2 works with Tyk.
    28  func TestHTTP2_TLS(t *testing.T) {
    29  	defer ResetTestConfig()
    30  
    31  	expected := "HTTP/2.0"
    32  
    33  	// Certificates
    34  	_, _, _, clientCert := genCertificate(&x509.Certificate{})
    35  	serverCertPem, _, combinedPEM, _ := genServerCertificate()
    36  	certID, _ := CertificateManager.Add(combinedPEM, "")
    37  	defer CertificateManager.Delete(certID)
    38  
    39  	// Upstream server supporting HTTP/2
    40  	upstream := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    41  		actual := r.Proto
    42  		if expected != actual {
    43  			t.Fatalf("Tyk-Upstream connection protocol is expected %s, actual %s", expected, actual)
    44  		}
    45  
    46  		fmt.Fprintln(w, "Hello, I am an HTTP/2 Server")
    47  
    48  	}))
    49  	upstream.TLS = new(tls.Config)
    50  	upstream.TLS.NextProtos = []string{"h2"}
    51  	upstream.StartTLS()
    52  	defer upstream.Close()
    53  
    54  	// Tyk
    55  	globalConf := config.Global()
    56  	globalConf.ProxySSLInsecureSkipVerify = true
    57  	globalConf.ProxyEnableHttp2 = true
    58  	globalConf.HttpServerOptions.EnableHttp2 = true
    59  	globalConf.HttpServerOptions.SSLCertificates = []string{certID}
    60  	globalConf.HttpServerOptions.UseSSL = true
    61  	config.SetGlobal(globalConf)
    62  	defer ResetTestConfig()
    63  
    64  	ts := StartTest()
    65  	defer ts.Close()
    66  
    67  	BuildAndLoadAPI(func(spec *APISpec) {
    68  		spec.Proxy.ListenPath = "/"
    69  		spec.UseKeylessAccess = true
    70  		spec.Proxy.TargetURL = upstream.URL
    71  	})
    72  
    73  	// HTTP/2 client
    74  	http2Client := getTLSClient(&clientCert, serverCertPem)
    75  	http2.ConfigureTransport(http2Client.Transport.(*http.Transport))
    76  
    77  	ts.Run(t, test.TestCase{Client: http2Client, Path: "", Code: 200, Proto: "HTTP/2.0", BodyMatch: "Hello, I am an HTTP/2 Server"})
    78  }
    79  
    80  func TestGRPC_TLS(t *testing.T) {
    81  	defer ResetTestConfig()
    82  
    83  	_, _, combinedPEM, _ := genServerCertificate()
    84  	certID, _ := CertificateManager.Add(combinedPEM, "")
    85  	defer CertificateManager.Delete(certID)
    86  
    87  	// gRPC server
    88  	s := startGRPCServer(t, nil)
    89  	defer s.GracefulStop()
    90  
    91  	// Tyk
    92  	globalConf := config.Global()
    93  	globalConf.ProxySSLInsecureSkipVerify = true
    94  	globalConf.ProxyEnableHttp2 = true
    95  	globalConf.HttpServerOptions.EnableHttp2 = true
    96  	globalConf.HttpServerOptions.SSLCertificates = []string{certID}
    97  	globalConf.HttpServerOptions.UseSSL = true
    98  	config.SetGlobal(globalConf)
    99  	defer ResetTestConfig()
   100  
   101  	ts := StartTest()
   102  	defer ts.Close()
   103  
   104  	BuildAndLoadAPI(func(spec *APISpec) {
   105  		spec.Proxy.ListenPath = "/"
   106  		spec.UseKeylessAccess = true
   107  		spec.Proxy.TargetURL = "https://localhost:50051"
   108  	})
   109  
   110  	address := strings.TrimPrefix(ts.URL, "https://")
   111  	name := "Furkan"
   112  
   113  	// gRPC client
   114  	r := sayHelloWithGRPCClient(t, nil, nil, false, "", address, name)
   115  
   116  	// Test result
   117  	expected := "Hello " + name
   118  	actual := r.Message
   119  
   120  	if expected != actual {
   121  		t.Fatalf("Expected %s, actual %s", expected, actual)
   122  	}
   123  }
   124  
   125  func TestGRPC_MutualTLS(t *testing.T) {
   126  	// Mutual Authentication for both downstream-tyk and tyk-upstream
   127  	defer ResetTestConfig()
   128  
   129  	_, _, combinedClientPEM, clientCert := genCertificate(&x509.Certificate{})
   130  	clientCert.Leaf, _ = x509.ParseCertificate(clientCert.Certificate[0])
   131  	serverCertPem, _, combinedPEM, _ := genServerCertificate()
   132  
   133  	certID, _ := CertificateManager.Add(combinedPEM, "") // For tyk to know downstream
   134  	defer CertificateManager.Delete(certID)
   135  
   136  	clientCertID, _ := CertificateManager.Add(combinedClientPEM, "") // For upstream to know tyk
   137  	defer CertificateManager.Delete(clientCertID)
   138  
   139  	// Protected gRPC server
   140  	s := startGRPCServer(t, clientCert.Leaf)
   141  	defer s.GracefulStop()
   142  
   143  	// Tyk
   144  	globalConf := config.Global()
   145  	globalConf.ProxySSLInsecureSkipVerify = true
   146  	globalConf.ProxyEnableHttp2 = true
   147  	globalConf.HttpServerOptions.EnableHttp2 = true
   148  	globalConf.HttpServerOptions.SSLCertificates = []string{certID}
   149  	globalConf.HttpServerOptions.UseSSL = true
   150  	config.SetGlobal(globalConf)
   151  	defer ResetTestConfig()
   152  
   153  	ts := StartTest()
   154  	defer ts.Close()
   155  
   156  	BuildAndLoadAPI(func(spec *APISpec) {
   157  		spec.Proxy.ListenPath = "/"
   158  		spec.UseKeylessAccess = true
   159  		spec.UpstreamCertificates = map[string]string{
   160  			"*": clientCertID,
   161  		}
   162  		spec.Proxy.TargetURL = "https://localhost:50051"
   163  	})
   164  
   165  	address := strings.TrimPrefix(ts.URL, "https://")
   166  	name := "Furkan"
   167  
   168  	// gRPC client
   169  	r := sayHelloWithGRPCClient(t, &clientCert, serverCertPem, false, "", address, name)
   170  
   171  	// Test result
   172  	expected := "Hello " + name
   173  	actual := r.Message
   174  
   175  	if expected != actual {
   176  		t.Fatalf("Expected %s, actual %s", expected, actual)
   177  	}
   178  }
   179  
   180  func TestGRPC_BasicAuthentication(t *testing.T) {
   181  	defer ResetTestConfig()
   182  	_, _, combinedPEM, _ := genServerCertificate()
   183  	certID, _ := CertificateManager.Add(combinedPEM, "")
   184  	defer CertificateManager.Delete(certID)
   185  
   186  	// gRPC server
   187  	s := startGRPCServer(t, nil)
   188  	defer s.GracefulStop()
   189  
   190  	// Tyk
   191  	globalConf := config.Global()
   192  	globalConf.ProxySSLInsecureSkipVerify = true
   193  	globalConf.ProxyEnableHttp2 = true
   194  	globalConf.HttpServerOptions.EnableHttp2 = true
   195  	globalConf.HttpServerOptions.SSLCertificates = []string{certID}
   196  	globalConf.HttpServerOptions.UseSSL = true
   197  	config.SetGlobal(globalConf)
   198  	defer ResetTestConfig()
   199  
   200  	ts := StartTest()
   201  	defer ts.Close()
   202  
   203  	session := CreateStandardSession()
   204  	session.BasicAuthData.Password = "password"
   205  	session.AccessRights = map[string]user.AccessDefinition{"test": {APIID: "test", Versions: []string{"v1"}}}
   206  	session.OrgID = "default"
   207  
   208  	BuildAndLoadAPI(func(spec *APISpec) {
   209  		spec.UseBasicAuth = true
   210  		spec.Proxy.ListenPath = "/"
   211  		spec.UseKeylessAccess = false
   212  		spec.Proxy.TargetURL = "https://localhost:50051"
   213  		spec.OrgID = "default"
   214  	})
   215  
   216  	address := strings.TrimPrefix(ts.URL, "https://")
   217  	name := "Furkan"
   218  	client := getTLSClient(nil, nil)
   219  
   220  	// To create key
   221  	ts.Run(t, []test.TestCase{
   222  		{Method: "POST", Path: "/tyk/keys/defaultuser", Data: session, AdminAuth: true, Code: 200, Client: client},
   223  	}...)
   224  
   225  	// gRPC client
   226  	r := sayHelloWithGRPCClient(t, nil, nil, true, "", address, name)
   227  
   228  	// Test result
   229  	expected := "Hello " + name
   230  	actual := r.Message
   231  
   232  	if expected != actual {
   233  		t.Fatalf("Expected %s, actual %s", expected, actual)
   234  	}
   235  }
   236  
   237  func TestGRPC_TokenBasedAuthentication(t *testing.T) {
   238  	defer ResetTestConfig()
   239  	_, _, combinedPEM, _ := genServerCertificate()
   240  	certID, _ := CertificateManager.Add(combinedPEM, "")
   241  	defer CertificateManager.Delete(certID)
   242  
   243  	// gRPC server
   244  	s := startGRPCServer(t, nil)
   245  	defer s.GracefulStop()
   246  
   247  	// Tyk
   248  	globalConf := config.Global()
   249  	globalConf.ProxySSLInsecureSkipVerify = true
   250  	globalConf.ProxyEnableHttp2 = true
   251  	globalConf.HttpServerOptions.EnableHttp2 = true
   252  	globalConf.HttpServerOptions.SSLCertificates = []string{certID}
   253  	globalConf.HttpServerOptions.UseSSL = true
   254  	config.SetGlobal(globalConf)
   255  	defer ResetTestConfig()
   256  
   257  	ts := StartTest()
   258  	defer ts.Close()
   259  
   260  	session := CreateStandardSession()
   261  	session.AccessRights = map[string]user.AccessDefinition{"test": {APIID: "test", Versions: []string{"v1"}}}
   262  	session.OrgID = "default"
   263  
   264  	BuildAndLoadAPI(func(spec *APISpec) {
   265  		spec.Proxy.ListenPath = "/"
   266  		spec.UseKeylessAccess = false
   267  		spec.Proxy.TargetURL = "https://localhost:50051"
   268  		spec.OrgID = "default"
   269  	})
   270  
   271  	address := strings.TrimPrefix(ts.URL, "https://")
   272  	name := "Furkan"
   273  	client := getTLSClient(nil, nil)
   274  
   275  	// To create key
   276  	resp, _ := ts.Run(t, []test.TestCase{
   277  		{Method: "POST", Path: "/tyk/keys/create", Data: session, AdminAuth: true, Code: 200, Client: client},
   278  	}...)
   279  
   280  	// Read key
   281  	body, _ := ioutil.ReadAll(resp.Body)
   282  	var resMap map[string]string
   283  	err := json.Unmarshal(body, &resMap)
   284  	if err != nil {
   285  		t.Fatal(err)
   286  	}
   287  
   288  	// gRPC client
   289  	r := sayHelloWithGRPCClient(t, nil, nil, false, resMap["key"], address, name)
   290  
   291  	// Test result
   292  	expected := "Hello " + name
   293  	actual := r.Message
   294  
   295  	if expected != actual {
   296  		t.Fatalf("Expected %s, actual %s", expected, actual)
   297  	}
   298  }
   299  
   300  // server is used to implement helloworld.GreeterServer.
   301  type server struct{}
   302  
   303  // SayHello implements helloworld.GreeterServer
   304  func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
   305  	log.Printf("Received: %v", in.Name)
   306  	return &pb.HelloReply{Message: "Hello " + in.Name}, nil
   307  }
   308  
   309  func startGRPCServer(t *testing.T, clientCert *x509.Certificate) *grpc.Server {
   310  	// Server
   311  	lis, err := net.Listen("tcp", ":50051")
   312  	if err != nil {
   313  		t.Fatalf("failed to listen: %v", err)
   314  	}
   315  
   316  	cert, key, _, _ := genCertificate(&x509.Certificate{})
   317  	certificate, _ := tls.X509KeyPair(cert, key)
   318  
   319  	pool := x509.NewCertPool()
   320  
   321  	tlsConfig := &tls.Config{}
   322  	if clientCert != nil {
   323  		tlsConfig = &tls.Config{
   324  			ClientAuth:         tls.RequireAndVerifyClientCert,
   325  			ClientCAs:          pool,
   326  			InsecureSkipVerify: true,
   327  			Certificates:       []tls.Certificate{certificate},
   328  		}
   329  		pool.AddCert(clientCert)
   330  	} else {
   331  		tlsConfig = &tls.Config{
   332  			InsecureSkipVerify: true,
   333  			Certificates:       []tls.Certificate{certificate},
   334  		}
   335  	}
   336  
   337  	creds := credentials.NewTLS(tlsConfig)
   338  
   339  	if err != nil {
   340  		t.Fatalf("failed to listen: %v", err)
   341  	}
   342  
   343  	s := grpc.NewServer(grpc.Creds(creds))
   344  
   345  	pb.RegisterGreeterServer(s, &server{})
   346  
   347  	go func() {
   348  		err := s.Serve(lis)
   349  		if err != nil {
   350  			t.Fatalf("failed to serve: %v", err)
   351  		}
   352  	}()
   353  
   354  	return s
   355  }
   356  
   357  func sayHelloWithGRPCClient(t *testing.T, cert *tls.Certificate, caCert []byte, basicAuth bool, token string, address string, name string) *pb.HelloReply {
   358  	// gRPC client
   359  	tlsConfig := &tls.Config{}
   360  
   361  	if cert != nil {
   362  		tlsConfig.Certificates = []tls.Certificate{*cert}
   363  	}
   364  
   365  	if len(caCert) > 0 {
   366  		caCertPool := x509.NewCertPool()
   367  		caCertPool.AppendCertsFromPEM(caCert)
   368  		tlsConfig.RootCAs = caCertPool
   369  		tlsConfig.BuildNameToCertificate()
   370  	} else {
   371  		tlsConfig.InsecureSkipVerify = true
   372  	}
   373  
   374  	creds := credentials.NewTLS(tlsConfig)
   375  
   376  	opts := []grpc.DialOption{grpc.WithTransportCredentials(creds)}
   377  
   378  	if basicAuth {
   379  		opts = append(opts, grpc.WithPerRPCCredentials(&loginCredsOrToken{
   380  			Username: "user",
   381  			Password: "password",
   382  		}))
   383  	} else if token != "" { // Token Based Authentication
   384  		opts = append(opts, grpc.WithPerRPCCredentials(&loginCredsOrToken{
   385  			TokenBasedAuth: true,
   386  			Token:          token,
   387  		}))
   388  	}
   389  
   390  	conn, err := grpc.Dial(address, opts...)
   391  	if err != nil {
   392  		t.Fatalf("did not connect: %v", err)
   393  	}
   394  	defer conn.Close()
   395  	c := pb.NewGreeterClient(conn)
   396  
   397  	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
   398  	defer cancel()
   399  	r, err := c.SayHello(ctx, &pb.HelloRequest{Name: name})
   400  	if err != nil {
   401  		t.Fatalf("could not greet: %v", err)
   402  	}
   403  
   404  	return r
   405  }
   406  
   407  type loginCredsOrToken struct {
   408  	Username       string
   409  	Password       string
   410  	TokenBasedAuth bool
   411  	Token          string
   412  }
   413  
   414  func (l *loginCredsOrToken) GetRequestMetadata(context.Context, ...string) (headers map[string]string, err error) {
   415  	auth := l.Username + ":" + l.Password
   416  	enc := base64.StdEncoding.EncodeToString([]byte(auth))
   417  
   418  	headers = make(map[string]string)
   419  
   420  	if l.TokenBasedAuth {
   421  		headers["Authorization"] = l.Token
   422  	} else {
   423  		headers["Authorization"] = "Basic " + enc
   424  	}
   425  
   426  	return
   427  }
   428  
   429  func (*loginCredsOrToken) RequireTransportSecurity() bool {
   430  	return true
   431  }