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 }