github.com/projectcontour/contour@v1.28.2/site/content/posts/2019-10-23-httpproxy-in-action.md (about) 1 --- 2 title: HTTPProxy in Action 3 image: /img/posts/proxyinaction.png 4 excerpt: This blog post covers a practical demonstration of how HTTPProxy works. 5 author_name: Steve Sloka 6 author_avatar: /img/contributors/steve-sloka.png 7 categories: [kubernetes] 8 # Tag should match author to drive author pages 9 tags: ['Contour Team', 'Steve Sloka', 'tutorial'] 10 date: 2019-10-23 11 slug: httpproxy-in-action 12 --- 13 14 In our previous [blog post][1], Dave Cheney walked through Contour’s evolution from `IngressRoute` to `HTTPProxy` and explained how & why the move happened. 15 16 Now with `HTTPProxy`, Contour allows for additional routing configuration outside of just supporting a `path prefix`. 17 18 This post demonstrates a practical implementation of `HTTPProxy` and reviews some examples that explain how you can use it in your cluster today. 19 20 [![img][2]][3] 21 *Here's a quick video demonstration walking through the rest of the blog post.* 22 23 ## Prerequisites 24 If you’d like to follow along in your own cluster, you’ll need a working Kubernetes cluster as well as Contour deployed. There are a number of ways to get these up and working. A simple way to test this locally is to use Kubernetes in Docker (Kind); you can check out our previous blog post on how to get this up and running on your local machine: [https://projectcontour.io/kindly-running-contour/][4] 25 26 ## Demo Time 27 Let’s walk through a simple scenario to quickly demonstrate how these new features work with `HTTPProxy`. This demo will progress through various features of `HTTPProxy` by starting off with a set of prerequisite services and deployments. Then it will move to implement `conditions` on routes to further specify request route matching. Finally, we’ll introduce `includes`, which will allow us to delegate path and header conditions to other `HTTPProxy` resources in different namespaces. 28 29 ## Setup and Prerequisites 30 We’ll start by creating a sample set of applications and services, which we will use to set up some routing with `HTTPProxy`: 31 32 ```bash 33 $ kubectl apply -f https://projectcontour.io/examples/proxydemo/01-prereq.yaml 34 ``` 35 36 ## Basic HTTPProxy 37 Next, we’ll apply our root HTTPProxy. This proxy is unique since it defines the `fqdn` for the requests. In our example, the FQDN is `local.projectcontour.io`. It configures two routes, one for `/` and another for `/secure`. 38 39 _Note: If you’re running this in your own cluster, update the `fqdn` value in the spec.virtualhost section of the HTTPProxy before applying each HTTPProxy update. `local.projectcontour.io` points to 127.0.0.1 and works well if you’re running a `kind` setup._ 40 41 ```bash 42 $ kubectl apply -f https://projectcontour.io/examples/proxydemo/02-proxy-basic.yaml 43 44 apiVersion: projectcontour.io/v1 45 kind: HTTPProxy 46 metadata: 47 name: root 48 namespace: projectcontour-roots 49 spec: 50 virtualhost: 51 fqdn: local.projectcontour.io 52 routes: 53 - services: 54 - name: rootapp 55 port: 80 56 conditions: 57 - prefix: / 58 - services: 59 - name: secureapp-default 60 port: 80 61 conditions: 62 - prefix: /secure 63 ``` 64 65 This proxy configures any request to `projectcontour.io/` to be handled by the service `rootapp`. Requests to `/secure` will be handled by the service `secureapp-default`. 66 67 At this point, the following requests map like this: 68 69 - GET projectcontour.io/ → `rootapp:80` service 70 - GET projectcontour.io/secure → `secureapp-default:80` service 71 72 ### Sample Requests 73 ```bash 74 $ curl http://local.projectcontour.io/ 75 76 ECHO Request Server: 77 -------------------- 78 App: 79 This is the default app site! 80 Request: 81 http://local.projectcontour.io/ 82 83 $ curl http://local.projectcontour.io/secure 84 85 ECHO Request Server: 86 -------------------- 87 App: 88 This is the secure app site! 89 Request: 90 http://local.projectcontour.io/secure 91 ``` 92 ## Conditions 93 New in `HTTPProxy` is a concept called `conditions`, which allows you to define a set of request parameters that need to match for a route to receive requests. Contour allows for a `prefix` condition as well as a set of `header` conditions to be defined on requests. 94 95 Let’s reconfigure the `root` proxy to handle requests with HTTP headers. We will target any request to `local.projectcontour.io/secure` that matches the header `User-Agent` containing the value of `Chrome` to route to the `secureapp` backend. Any other requests to `local.projectcontour.io/secure` that do not match the header we defined should route to `secureapp-default`. 96 97 In the previous example, we added `prefix` conditions to the route. In this example, we add a new type called `header`, which allows us to define a header key and value that must match the request. Header conditions can use `exact` to match the value exactly, or they can use `contains` to match a specified value that exists somewhere in the header value. (We can also specify `notexact` and `notcontains`, which inverse the match.) 98 99 ```bash 100 $ kubectl apply -f https://projectcontour.io/examples/proxydemo/03-proxy-conditions.yaml 101 102 apiVersion: projectcontour.io/v1 103 kind: HTTPProxy 104 metadata: 105 name: root 106 namespace: projectcontour-roots 107 spec: 108 virtualhost: 109 fqdn: local.projectcontour.io 110 routes: 111 - services: 112 - name: rootapp 113 port: 80 114 conditions: 115 - prefix: / 116 - services: 117 - name: secureapp-default 118 port: 80 119 conditions: 120 - prefix: /secure 121 - services: 122 - name: secureapp 123 port: 80 124 conditions: 125 - prefix: /secure 126 - header: 127 name: User-Agent 128 contains: Chrome 129 ``` 130 131 It’s important to call out that for a request to match a route, all `conditions` defined must match. In the example we just applied, for the request to route to the `secreapp` service, it must have a path prefix of `/secure` as well as have a header `User-Agent` containing the value `Chrome`. Adding additional header conditions would extend the match requirements for that route. 132 133 At this point the following requests map as follows: 134 135 - GET local.projectcontour.io/ → `rootapp:80` service 136 - GET local.projectcontour.io/secure → `secureapp-default:80` service 137 - GET local.projectcontour.io/secure + Header[User-Agent: Chrome] → `secureapp:80` service 138 139 ### Sample Requests 140 ``` 141 $ curl http://local.projectcontour.io/secure 142 143 ECHO Request Server: 144 -------------------- 145 App: 146 This is the DEFAULT secure app site! 147 Request: 148 http://local.projectcontour.io/secure 149 150 $ curl -H "User-Agent: Chrome" http://local.projectcontour.io/secure 151 152 ECHO Request Server: 153 -------------------- 154 App: 155 This is the secure app site! 156 Request: 157 http://local.projectcontour.io/secure 158 ``` 159 160 ## Includes across Namespaces 161 Also new in `HTTPProxy` is the concept of `includes` for a resource. Defining an `include` on an `HTTPProxy` causes Contour to prepend any `conditions` defined in the include to the conditions of the child proxy referenced. This can be used to delegate path prefixes to teams in different namespaces or require specific headers to be present on requests. There isn’t a limit to the number of times a proxy can reference an `include`. 162 163 First, let’s set up a new marketing team namespace allowing them to self-manage its `HTTPProxy` resources as well as deploy a sample app/service. The marketing team will be in charge of managing the `/blog` path from the `local.projectcontour.io` domain. 164 165 ```bash 166 $ kubectl apply -f https://projectcontour.io/examples/proxydemo/04-marketing-prereq.yaml 167 ``` 168 169 Next, let’s update our `root` proxy to pass off a path to the marketing team working in the `projectcontour-marketing` namespace. We’ll also create the marketing team’s `HTTPProxy`, which is referenced from the `root` HTTPProxy and will include the path condition of `/blog` to the marketing team’s proxy named `blogsite`. 170 171 ```bash 172 $ kubectl apply -f https://projectcontour.io/examples/proxydemo/04-proxy-include-basic.yaml 173 174 apiVersion: projectcontour.io/v1 175 kind: HTTPProxy 176 metadata: 177 name: root 178 namespace: projectcontour-roots 179 spec: 180 virtualhost: 181 fqdn: local.projectcontour.io 182 includes: 183 - name: blogsite 184 namespace: projectcontour-marketing 185 conditions: 186 - prefix: /blog 187 routes: 188 - services: 189 - name: rootapp 190 port: 80 191 conditions: 192 - prefix: / 193 - services: 194 - name: secureapp-default 195 port: 80 196 conditions: 197 - prefix: /secure 198 - services: 199 - name: secureapp 200 port: 80 201 conditions: 202 - prefix: /secure 203 - header: 204 name: User-Agent 205 contains: Chrome 206 --- 207 apiVersion: projectcontour.io/v1 208 kind: HTTPProxy 209 metadata: 210 name: blogsite 211 namespace: projectcontour-marketing 212 spec: 213 routes: 214 - services: 215 - name: wwwblog 216 port: 80 217 ``` 218 219 Since the `root` proxy included the path of `/blog`, requests that match `local.projectcontour.io/blog` will route to the marketing team’s service named `wwwblog` in the `projectcontour-marketing` namespace. You will notice we didn’t define any `conditions` on the marketing teams proxy. We don’t need to define them because the root proxy passed the path prefix condition to this HTTPProxy through the include. 220 221 It’s important to note that no one else in the cluster can now utilize this path prefix. If another team would create an HTTPProxy referencing the same path, Contour would reject it because it is not part of a delegation chain. 222 223 ### Sample Requests 224 ``` 225 $ curl http://local.projectcontour.io/blog 226 227 ECHO Request Server: 228 -------------------- 229 App: 230 This is the blog site! 231 Request: 232 http://local.projectcontour.io/blog 233 ``` 234 235 ## Includes to the same Namespace 236 Just as we learned in the first example of how conditions are applied to routes, the same logic is applied to conditions on an include. Currently, the marketing team is responsible for the path `/blog`, but let’s add a few more requirements. 237 238 The marketing team wants to create an `information` site, which will be served by the path `/blog/info`. We will create another HTTPProxy to define how the information application should be accessed. This new `HTTPProxy` will be included in the path `/info` from the `blogsite` `HTTPProxy` in the `projectcontour-marketing` namespace. 239 240 Since `Conditions` are appended to the `HTTPProxy` when Contour processes them, the result for the information site will have the path `/blog/info`. 241 242 ```bash 243 $ kubectl apply -f https://projectcontour.io/examples/proxydemo/05-proxy-include-info.yaml 244 245 apiVersion: projectcontour.io/v1 246 kind: HTTPProxy 247 metadata: 248 name: blogsite 249 namespace: projectcontour-marketing 250 spec: 251 includes: 252 - name: infosite 253 conditions: 254 - prefix: /info 255 routes: 256 - services: 257 - name: wwwblog 258 port: 80 259 --- 260 apiVersion: projectcontour.io/v1 261 kind: HTTPProxy 262 metadata: 263 name: infosite 264 namespace: projectcontour-marketing 265 spec: 266 routes: 267 - services: 268 - name: info 269 port: 80 270 ``` 271 272 ### Sample Requests 273 ``` 274 $ curl http://local.projectcontour.io/blog/info 275 276 ECHO Request Server: 277 -------------------- 278 App: 279 This is the blog site! 280 Request: 281 http://local.projectcontour.io/blog/info 282 ``` 283 284 ## Includes with Headers 285 Finally to complete our example, the administrative team now doesn’t want the marketing site to be served by Chrome or Firefox browsers. This rule needs to apply to all applications in the `projectcontour-marketing` namespace. 286 287 We can easily implement this requirement by just adding another set of conditions to the `root` HTTPProxy. Once we add those, they will take effect across all the children of the root HTTPProxy defined in the `projectcontour-marketing` namespace. 288 289 ```bash 290 $ kubectl apply -f https://projectcontour.io/examples/proxydemo/06-proxy-include-headers.yaml 291 292 apiVersion: projectcontour.io/v1 293 kind: HTTPProxy 294 metadata: 295 name: root 296 namespace: projectcontour-roots 297 spec: 298 virtualhost: 299 fqdn: local.projectcontour.io 300 includes: 301 - name: blogsite 302 namespace: projectcontour-marketing 303 conditions: 304 - prefix: /blog 305 - header: 306 name: User-Agent 307 notcontains: Chrome 308 - header: 309 name: User-Agent 310 notcontains: Firefox 311 routes: 312 - services: 313 - name: rootapp 314 port: 80 315 conditions: 316 - prefix: / 317 - services: 318 - name: secureapp-default 319 port: 80 320 conditions: 321 - prefix: /secure 322 - services: 323 - name: secureapp 324 port: 80 325 conditions: 326 - prefix: /secure 327 - header: 328 name: User-Agent 329 contains: Chrome 330 ``` 331 332 Requests to `local.projectcontour.io/blog/*` that do not match the `User-Agent` header of `Chrome` or `Firefox` will now route to the appropriate services in the marketing team’s namespace. Any other requests will be handled by the `rootapp` service because it matches the other requests. 333 334 ### Sample Requests 335 ```bash 336 $ curl -H "User-Agent: Safari" http://local.projectcontour.io/blog/info 337 338 ECHO Request Server: 339 -------------------- 340 App: 341 This is the INFO site! 342 Request: 343 http://local.projectcontour.io/blog/info 344 345 $ curl -H "User-Agent: Firefox" http://local.projectcontour.io/blog/info 346 347 ECHO Request Server: 348 -------------------- 349 App: 350 This is the default app site! 351 Request: 352 http://local.projectcontour.io/blog/info 353 ``` 354 355 [1]: {% post_url 2019-09-27-from-ingressroute-to-httpproxy %} 356 [2]: {% link img/posts/kind-contour-video.png %} 357 [3]: https://youtu.be/YA82A4Rcs_A 358 [4]: {% post_url 2019-07-11-kindly-running-contour %}