istio.io/istio@v0.0.0-20240520182934-d79c90f27776/tests/integration/security/policy_attachment_only/jwt_gateway_test.go (about)

     1  //go:build integ
     2  // +build integ
     3  
     4  // Copyright Istio Authors
     5  //
     6  // Licensed under the Apache License, Version 2.0 (the "License");
     7  // you may not use this file except in compliance with the License.
     8  // You may obtain a copy of the License at
     9  //
    10  //	http://www.apache.org/licenses/LICENSE-2.0
    11  //
    12  // Unless required by applicable law or agreed to in writing, software
    13  // distributed under the License is distributed on an "AS IS" BASIS,
    14  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    15  // See the License for the specific language governing permissions and
    16  // limitations under the License.
    17  package policyattachmentonly
    18  
    19  import (
    20  	"fmt"
    21  	"net/http"
    22  	"testing"
    23  
    24  	"istio.io/istio/pilot/pkg/features"
    25  	"istio.io/istio/pkg/config/protocol"
    26  	"istio.io/istio/pkg/http/headers"
    27  	"istio.io/istio/pkg/test"
    28  	"istio.io/istio/pkg/test/framework"
    29  	"istio.io/istio/pkg/test/framework/components/crd"
    30  	"istio.io/istio/pkg/test/framework/components/echo"
    31  	"istio.io/istio/pkg/test/framework/components/echo/check"
    32  	"istio.io/istio/pkg/test/framework/components/echo/config"
    33  	"istio.io/istio/pkg/test/framework/components/echo/config/param"
    34  	"istio.io/istio/pkg/test/framework/components/echo/echotest"
    35  	"istio.io/istio/pkg/test/framework/components/echo/match"
    36  	"istio.io/istio/pkg/test/framework/components/istio/ingress"
    37  	"istio.io/istio/pkg/test/framework/label"
    38  	"istio.io/istio/tests/common/jwt"
    39  )
    40  
    41  func TestGatewayAPIRequestAuthentication(t *testing.T) {
    42  	framework.NewTest(t).
    43  		Label(label.IPv4). // https://github.com/istio/istio/issues/35835
    44  		Run(func(t framework.TestContext) {
    45  			crd.DeployGatewayAPIOrSkip(t)
    46  			config.New(t).
    47  				Source(config.File("testdata/requestauthn/gateway-api.yaml.tmpl").WithParams(param.Params{
    48  					param.Namespace.String(): apps.Namespace,
    49  				})).
    50  				Source(config.File("testdata/requestauthn/gateway-jwt.yaml.tmpl").WithParams(param.Params{
    51  					param.Namespace.String(): apps.Namespace,
    52  					"Services":               apps.A.Append(apps.B).Services(),
    53  				})).
    54  				BuildAll(nil, apps.A.Append(apps.B).Services()).
    55  				Apply()
    56  
    57  			t.NewSubTest("gateway-authn-policy-attachment-only").Run(func(t framework.TestContext) {
    58  				test.SetForTest(t, &features.EnableSelectorBasedK8sGatewayPolicy, false)
    59  				cases := []struct {
    60  					name          string
    61  					customizeCall func(opts *echo.CallOptions, to echo.Target)
    62  				}{
    63  					{
    64  						name: "deny without token",
    65  						customizeCall: func(opts *echo.CallOptions, to echo.Target) {
    66  							opts.HTTP.Path = "/"
    67  							opts.HTTP.Headers = headers.New().
    68  								WithHost(fmt.Sprintf("example.%s.com", to.ServiceName())).
    69  								Build()
    70  							opts.Check = check.Status(http.StatusForbidden)
    71  						},
    72  					},
    73  					{
    74  						name: "allow with sub-1 token",
    75  						customizeCall: func(opts *echo.CallOptions, to echo.Target) {
    76  							opts.HTTP.Path = "/"
    77  							opts.HTTP.Headers = headers.New().
    78  								WithHost(fmt.Sprintf("example.%s.com", to.ServiceName())).
    79  								WithAuthz(jwt.TokenIssuer1).
    80  								Build()
    81  							opts.Check = check.OK()
    82  						},
    83  					},
    84  					{
    85  						name: "deny with sub-1 token due to ignored RequestAuthentication",
    86  						customizeCall: func(opts *echo.CallOptions, to echo.Target) {
    87  							opts.HTTP.Path = "/"
    88  							opts.HTTP.Headers = headers.New().
    89  								WithHost(fmt.Sprintf("example.%s.com", to.ServiceName())).
    90  								WithAuthz(jwt.TokenIssuer3).
    91  								Build()
    92  							opts.Check = check.Status(http.StatusUnauthorized)
    93  						},
    94  					},
    95  					{
    96  						name: "deny with sub-2 token",
    97  						customizeCall: func(opts *echo.CallOptions, to echo.Target) {
    98  							opts.HTTP.Path = "/"
    99  							opts.HTTP.Headers = headers.New().
   100  								WithHost(fmt.Sprintf("example.%s.com", to.ServiceName())).
   101  								WithAuthz(jwt.TokenIssuer2).
   102  								Build()
   103  							opts.Check = check.Status(http.StatusForbidden)
   104  						},
   105  					},
   106  					{
   107  						name: "deny with expired token",
   108  						customizeCall: func(opts *echo.CallOptions, to echo.Target) {
   109  							opts.HTTP.Path = "/"
   110  							opts.HTTP.Headers = headers.New().
   111  								WithHost(fmt.Sprintf("example.%s.com", to.ServiceName())).
   112  								WithAuthz(jwt.TokenExpired).
   113  								Build()
   114  							opts.Check = check.Status(http.StatusUnauthorized)
   115  						},
   116  					},
   117  					{
   118  						name: "allow with sub-1 token on any.com",
   119  						customizeCall: func(opts *echo.CallOptions, to echo.Target) {
   120  							opts.HTTP.Path = "/"
   121  							opts.HTTP.Headers = headers.New().
   122  								WithHost(fmt.Sprintf("any-request-principal-ok.%s.com", to.ServiceName())).
   123  								WithAuthz(jwt.TokenIssuer1).
   124  								Build()
   125  							opts.Check = check.OK()
   126  						},
   127  					},
   128  					{
   129  						name: "allow with sub-2 token on any.com",
   130  						customizeCall: func(opts *echo.CallOptions, to echo.Target) {
   131  							opts.HTTP.Path = "/"
   132  							opts.HTTP.Headers = headers.New().
   133  								WithHost(fmt.Sprintf("any-request-principal-ok.%s.com", to.ServiceName())).
   134  								WithAuthz(jwt.TokenIssuer2).
   135  								Build()
   136  							opts.Check = check.OK()
   137  						},
   138  					},
   139  					{
   140  						name: "deny without token on any.com",
   141  						customizeCall: func(opts *echo.CallOptions, to echo.Target) {
   142  							opts.HTTP.Path = "/"
   143  							opts.HTTP.Headers = headers.New().
   144  								WithHost(fmt.Sprintf("any-request-principal-ok.%s.com", to.ServiceName())).
   145  								Build()
   146  							opts.Check = check.Status(http.StatusForbidden)
   147  						},
   148  					},
   149  					{
   150  						name: "deny with token on other host",
   151  						customizeCall: func(opts *echo.CallOptions, to echo.Target) {
   152  							opts.HTTP.Path = "/"
   153  							opts.HTTP.Headers = headers.New().
   154  								WithHost(fmt.Sprintf("other-host.%s.com", to.ServiceName())).
   155  								WithAuthz(jwt.TokenIssuer1).
   156  								Build()
   157  							opts.Check = check.Status(http.StatusForbidden)
   158  						},
   159  					},
   160  					{
   161  						name: "allow healthz",
   162  						customizeCall: func(opts *echo.CallOptions, to echo.Target) {
   163  							opts.HTTP.Path = "/healthz"
   164  							opts.HTTP.Headers = headers.New().
   165  								WithHost(fmt.Sprintf("example.%s.com", to.ServiceName())).
   166  								Build()
   167  							opts.Check = check.OK()
   168  						},
   169  					},
   170  				}
   171  
   172  				newTrafficTest(t, apps.A.Append(apps.B)).
   173  					RunViaGatewayIngress("istio", func(t framework.TestContext, from ingress.Instance, to echo.Target) {
   174  						for _, c := range cases {
   175  							t.NewSubTest(c.name).Run(func(t framework.TestContext) {
   176  								opts := echo.CallOptions{
   177  									Port: echo.Port{
   178  										Protocol: protocol.HTTP,
   179  									},
   180  								}
   181  
   182  								c.customizeCall(&opts, to)
   183  
   184  								from.CallOrFail(t, opts)
   185  							})
   186  						}
   187  					})
   188  			})
   189  		})
   190  }
   191  
   192  func TestGatewayAPIAuthorizationPolicy(t *testing.T) {
   193  	framework.NewTest(t).
   194  		Label(label.IPv4). // https://github.com/istio/istio/issues/35835
   195  		Run(func(t framework.TestContext) {
   196  			crd.DeployGatewayAPIOrSkip(t)
   197  			config.New(t).
   198  				Source(config.File("testdata/authz/gateway-api.yaml.tmpl").WithParams(param.Params{
   199  					param.Namespace.String(): apps.Namespace,
   200  				})).
   201  				Source(config.File("testdata/authz/gateway-authz.yaml.tmpl").WithParams(param.Params{
   202  					param.Namespace.String(): apps.Namespace,
   203  					"Services":               apps.A.Append(apps.B).Services(),
   204  				})).
   205  				BuildAll(nil, apps.A.Append(apps.B).Services()).
   206  				Apply()
   207  
   208  			t.NewSubTest("gateway-authz-policy-attachment-only").Run(func(t framework.TestContext) {
   209  				test.SetForTest(t, &features.EnableSelectorBasedK8sGatewayPolicy, false)
   210  				cases := []struct {
   211  					name          string
   212  					customizeCall func(opts *echo.CallOptions, to echo.Target)
   213  				}{
   214  					{
   215  						name: "allow with sub-1 token",
   216  						customizeCall: func(opts *echo.CallOptions, to echo.Target) {
   217  							opts.HTTP.Path = "/allow"
   218  							opts.HTTP.Method = "GET"
   219  							opts.HTTP.Headers = headers.New().
   220  								WithHost(fmt.Sprintf("example.%s.com", to.ServiceName())).
   221  								WithAuthz(jwt.TokenIssuer1).
   222  								Build()
   223  							opts.Check = check.OK()
   224  						},
   225  					},
   226  					{
   227  						name: "deny without token",
   228  						customizeCall: func(opts *echo.CallOptions, to echo.Target) {
   229  							opts.HTTP.Path = "/allow"
   230  							opts.HTTP.Method = "GET"
   231  							opts.HTTP.Headers = headers.New().
   232  								WithHost(fmt.Sprintf("example.%s.com", to.ServiceName())).
   233  								Build()
   234  							opts.Check = check.Status(http.StatusForbidden)
   235  						},
   236  					},
   237  					{
   238  						name: "deny based on unacceptable HTTP method",
   239  						customizeCall: func(opts *echo.CallOptions, to echo.Target) {
   240  							opts.HTTP.Path = "/allow"
   241  							opts.HTTP.Method = "POST"
   242  							opts.HTTP.Headers = headers.New().
   243  								WithHost(fmt.Sprintf("example.%s.com", to.ServiceName())).
   244  								WithAuthz(jwt.TokenIssuer1).
   245  								Build()
   246  							opts.Check = check.Status(http.StatusNotFound)
   247  						},
   248  					},
   249  					{
   250  						name: "deny based on unacceptable HTTP path",
   251  						customizeCall: func(opts *echo.CallOptions, to echo.Target) {
   252  							opts.HTTP.Path = "/deny"
   253  							opts.HTTP.Method = "GET"
   254  							opts.HTTP.Headers = headers.New().
   255  								WithHost(fmt.Sprintf("example.%s.com", to.ServiceName())).
   256  								WithAuthz(jwt.TokenIssuer1).
   257  								Build()
   258  							opts.Check = check.Status(http.StatusNotFound)
   259  						},
   260  					},
   261  				}
   262  
   263  				newTrafficTest(t, apps.A.Append(apps.B)).
   264  					RunViaGatewayIngress("istio", func(t framework.TestContext, from ingress.Instance, to echo.Target) {
   265  						for _, c := range cases {
   266  							t.NewSubTest(c.name).Run(func(t framework.TestContext) {
   267  								opts := echo.CallOptions{
   268  									Port: echo.Port{
   269  										Protocol: protocol.HTTP,
   270  									},
   271  								}
   272  
   273  								c.customizeCall(&opts, to)
   274  
   275  								from.CallOrFail(t, opts)
   276  							})
   277  						}
   278  					})
   279  			})
   280  		})
   281  }
   282  
   283  func newTrafficTest(t framework.TestContext, echos ...echo.Instances) *echotest.T {
   284  	var all []echo.Instance
   285  	for _, e := range echos {
   286  		all = append(all, e...)
   287  	}
   288  
   289  	return echotest.New(t, all).
   290  		WithDefaultFilters(1, 1).
   291  		FromMatch(match.And(
   292  			match.NotNaked,
   293  			match.NotProxylessGRPC)).
   294  		ToMatch(match.And(
   295  			match.NotNaked,
   296  			match.NotProxylessGRPC)).
   297  		ConditionallyTo(echotest.NoSelfCalls)
   298  }