github.com/projectcontour/contour@v1.28.2/site/content/docs/1.24/guides/external-authorization.md (about) 1 --- 2 title: External Authorization Support 3 --- 4 5 Starting in version 1.9, Contour supports routing client requests to an 6 external authorization server. This feature can be used to centralize 7 client authorization so that applications don't have to implement their 8 own authorization mechanisms. 9 10 ## Authorization Architecture 11 12 An external authorization server is a server that implements the Envoy 13 external authorization [GRPC protocol][3]. Contour supports any server 14 that implements this protocol. 15 16 You can bind an authorization server to Contour by creating a 17 [`ExtensionService`][4] resource. 18 This resource tells Contour the service exists, and that it should 19 program Envoy with an upstream cluster directing traffic to it. 20 Note that the `ExtensionService` resource just binds the server; at this 21 point Contour doesn't assume that the server is an authorization server. 22 23 Once you have created `ExtensionService` resource, you can bind it to a 24 particular application by referencing it in a [`HTTPProxy`][5] resource. 25 In the `virtualhost` field, a new `authorization` field specifies the name 26 of an `ExtensionService` to bind for the virtual host. 27 When you specify a resource name here, Contour will program Envoy to 28 send authorization checks to the extension service cluster before routing 29 the request to the upstream application. 30 31 ## Authorization Request Flow 32 33 It is helpful to have a mental model of how requests flow through the various 34 servers involved in authorizing HTTP requests. 35 The flow diagram below shows the actors that participate in the successful 36 authorization of an HTTP request. 37 Note that in some cases, these actors can be combined into a single 38 application server. 39 For example, there is no requirement for the external authorization server to 40 be a separate application from the authorization provider. 41 42 43 <p align="center"> 44 <img src="/img/uml/client-auth-sequence-ext.png" alt="client authorization sequence diagram"/> 45 </p> 46 47 A HTTP Client generates an HTTP request and sends it to 48 an Envoy instance that Contour has programmed with an external 49 authorization configuration. 50 Envoy holds the HTTP request and sends an authorization check request 51 to the Authorization server that Contour has bound to the virtual host. 52 The Authorization server may be able to verify the request locally, but in 53 many cases it will need to make additional requests to an Authorization 54 Provider server to verify or obtain an authorization token. 55 56 In this flow, the ExtAuth server is able to authorize the request, and sends an 57 authorization response back to the Proxy. 58 The response includes the authorization status, and a set of HTTP headers 59 modifications to make to the HTTP request. 60 Since this authorization was successful, the Proxy modifies the request and 61 forwards it to the application. 62 If the authorization was not successful, the Proxy would have immediately 63 responded to the client with an HTTP error. 64 65 ## Using the Contour Authorization Server 66 67 The Contour project has built a simple authorization server named 68 [`contour-authserver`][1]. `contour-authserver` supports an authorization 69 testing server, and an HTTP basic authorization server that accesses 70 credentials stored in [htpasswd][2] format. 71 72 To get started, ensure that Contour is deployed and that you have 73 [cert-manager][6] installed in your cluster so that you can easily issue 74 self-signed TLS certificates. 75 76 At this point, we should also create a cluster-wide self-signed certificate 77 issuer, just to make it easier to provision TLS certificates later: 78 79 ```bash 80 $ kubectl apply -f - <<EOF 81 apiVersion: cert-manager.io/v1 82 kind: ClusterIssuer 83 metadata: 84 name: selfsigned 85 spec: 86 selfSigned: {} 87 EOF 88 clusterissuer.cert-manager.io/selfsigned created 89 ``` 90 91 ### Deploying the Authorization Server 92 93 The first step is to deploy `contour-authserver` to the `projectcontour-auth` 94 namespace. 95 To do this, we will use [`kustomize`][8] to build a set of YAML object that we can 96 deploy using kubectl. 97 In a new directory, create the following `kustomization.yaml` file: 98 99 ```yaml 100 apiVersion: kustomize.config.k8s.io/v1beta1 101 kind: Kustomization 102 103 namespace: projectcontour-auth 104 105 resources: 106 - github.com/projectcontour/contour-authserver/config/htpasswd 107 108 patchesJson6902: 109 - target: 110 group: cert-manager.io 111 version: v1 112 kind: Certificate 113 name: htpasswd 114 namespace: projectcontour-auth 115 patch: |- 116 - op: add 117 path: /spec/issuerRef/kind 118 value: ClusterIssuer 119 120 images: 121 - name: contour-authserver:latest 122 newName: docker.io/projectcontour/contour-authserver 123 newTag: v2 124 ``` 125 126 Note that the kustomization patches the `Certificate` resource to use the 127 "selfsigned" `ClusterIssuer` that we created earlier. 128 This is required because the `contour-authserver` deployment includes a 129 request for a self-signed TLS server certificate. 130 In a real deployment, this certificate should be requested from a real trusted 131 certificate issuer. 132 133 Now create the `projectcontour-auth` namespace, build the deployment YAML, 134 and apply to your cluster: 135 136 ```bash 137 $ kubectl create namespace projectcontour-auth 138 namespace/projectcontour-auth created 139 $ kubectl apply -f <(kustomize build .) 140 serviceaccount/htpasswd created 141 clusterrole.rbac.authorization.k8s.io/contour:authserver:htpasswd created 142 clusterrolebinding.rbac.authorization.k8s.io/contour:authserver:htpasswd created 143 service/htpasswd created 144 deployment.apps/htpasswd created 145 certificate.cert-manager.io/htpasswd created 146 ``` 147 148 At this point, `contour-authserver` is deployed and is exposed to 149 the cluster as the Service `projectcontour-auth/htpasswd`. 150 It has a self-signed TLS certificate and is accepting secure connections 151 on port 9443. 152 153 In the default configuration, `contour-authserver` will accept htpasswd data 154 from secrets with the `projectcontour.io/auth-type: basic` annotation. 155 Most systems install the Apache [`htpasswd`][7] tool, which we can use 156 to generate the password file: 157 158 ```bash 159 $ touch auth 160 $ htpasswd -b auth user1 password1 161 Adding password for user user1 162 $ htpasswd -b auth user2 password2 163 Adding password for user user2 164 $ htpasswd -b auth user3 password3 165 Adding password for user user3 166 ``` 167 168 Once we have some password data, we can populate a Kubernetes secret with it. 169 Note that the password data must be in the `auth` key in the secret, and that 170 the secret must be annotated with the `projectcontour.io/auth-type` key. 171 172 ```bash 173 $ kubectl create secret generic -n projectcontour-auth passwords --from-file=auth 174 secret/passwords created 175 $ kubectl annotate secret -n projectcontour-auth passwords projectcontour.io/auth-type=basic 176 secret/passwords annotated 177 ``` 178 179 ### Creating an Extension Service 180 181 Now that `contour-authserver` is deployed, the next step is to create a 182 `ExtensionService` resource. 183 184 ```yaml 185 apiVersion: projectcontour.io/v1alpha1 186 kind: ExtensionService 187 metadata: 188 name: htpasswd 189 namespace: projectcontour-auth 190 spec: 191 protocol: h2 192 services: 193 - name: htpasswd 194 port: 9443 195 ``` 196 197 The `ExtensionService` resource must be created in the same namespace 198 as the services that it binds. 199 This policy ensures that the creator of the `ExtensionService` also has 200 the authority over those services. 201 202 ```bash 203 $ kubectl apply -f htpasswd.yaml 204 extensionservice.projectcontour.io/htpasswd created 205 ``` 206 207 ### Deploying a Sample Application 208 209 To demonstrate how to use the authorization server in a `HTTPProxy` resource, 210 we first need to deploy a simple echo application. 211 212 ```yaml 213 apiVersion: apps/v1 214 kind: Deployment 215 metadata: 216 name: ingress-conformance-echo 217 spec: 218 replicas: 1 219 selector: 220 matchLabels: 221 app.kubernetes.io/name: ingress-conformance-echo 222 template: 223 metadata: 224 labels: 225 app.kubernetes.io/name: ingress-conformance-echo 226 spec: 227 containers: 228 - name: conformance-echo 229 image: agervais/ingress-conformance-echo:latest 230 ports: 231 - name: http-api 232 containerPort: 3000 233 readinessProbe: 234 httpGet: 235 path: /health 236 port: 3000 237 --- 238 apiVersion: v1 239 kind: Service 240 metadata: 241 name: ingress-conformance-echo 242 spec: 243 ports: 244 - name: http 245 port: 80 246 targetPort: http-api 247 selector: 248 app.kubernetes.io/name: ingress-conformance-echo 249 ``` 250 251 This echo server will respond with a JSON object that reports information about 252 the HTTP request it received, including the request headers. 253 254 ```bash 255 $ kubectl apply -f echo.yaml 256 deployment.apps/ingress-conformance-echo created 257 service/ingress-conformance-echo created 258 ``` 259 260 Once the application is running, we can expose it to Contour with a `HTTPProxy` 261 resource. 262 263 ```yaml 264 apiVersion: cert-manager.io/v1 265 kind: Certificate 266 metadata: 267 name: ingress-conformance-echo 268 spec: 269 dnsNames: 270 - local.projectcontour.io 271 secretName: ingress-conformance-echo 272 issuerRef: 273 name: selfsigned 274 kind: ClusterIssuer 275 --- 276 apiVersion: projectcontour.io/v1 277 kind: HTTPProxy 278 metadata: 279 name: echo 280 spec: 281 virtualhost: 282 fqdn: local.projectcontour.io 283 tls: 284 secretName: ingress-conformance-echo 285 routes: 286 - services: 287 - name: ingress-conformance-echo 288 port: 80 289 ``` 290 291 _Note that we created a TLS secret and exposed the application over HTTPS._ 292 293 ```bash 294 $ kubectl apply -f echo-proxy.yaml 295 certificate.cert-manager.io/ingress-conformance-echo created 296 httpproxy.projectcontour.io/echo created 297 $ kubectl get proxies echo 298 NAME FQDN TLS SECRET STATUS STATUS DESCRIPTION 299 echo local.projectcontour.io ingress-conformance-echo valid valid HTTPProxy 300 ``` 301 302 We can verify that the application is working by requesting any path: 303 304 ```bash 305 $ curl -k https://local.projectcontour.io/test/$((RANDOM)) 306 {"TestId":"","Path":"/test/12707","Host":"local.projectcontour.io","Method":"GET","Proto":"HTTP/1.1","Headers":{"Accept":["*/*"],"Content-Length":["0"],"User-Agent":["curl/7.64.1"],"X-Envoy-Expected-Rq-Timeout-Ms":["15000"],"X-Envoy-Internal":["true"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Proto":["https"],"X-Request-Id":["7b87d5d1-8ee8-40e3-81ac-7d74dfd4d50b"],"X-Request-Start":["t=1601596511.489"]}} 307 ``` 308 309 ### Using the Authorization Server 310 311 Now that we have a working application exposed by a `HTTPProxy` resource, we 312 can add HTTP basic authorization by binding to the `ExtensionService` that we 313 created earlier. 314 The simplest configuration is to add an `authorization` field that names the 315 authorization server `ExtensionService` resource that we created earlier. 316 317 ```yaml 318 apiVersion: projectcontour.io/v1 319 kind: HTTPProxy 320 metadata: 321 name: echo 322 spec: 323 virtualhost: 324 fqdn: local.projectcontour.io 325 tls: 326 secretName: ingress-conformance-echo 327 authorization: 328 extensionRef: 329 name: htpasswd 330 namespace: projectcontour-auth 331 routes: 332 - services: 333 - name: ingress-conformance-echo 334 port: 80 335 ``` 336 337 ```bash 338 $ kubectl apply -f echo-auth.yaml 339 httpproxy.projectcontour.io/echo configured 340 ``` 341 342 Now, when we make the same HTTP request, we find the response requests 343 authorization: 344 345 ```bash 346 $ curl -k -I https://local.projectcontour.io/test/$((RANDOM)) 347 HTTP/2 401 348 www-authenticate: Basic realm="default", charset="UTF-8" 349 date: Fri, 02 Oct 2020 00:27:49 GMT 350 server: envoy 351 ``` 352 353 Providing a user credential from the password file that we created 354 earlier allows the request to succeed. Note that `contour-authserver` 355 has injected a number of headers (prefixed with `Auth-`) to let the 356 application know how the request has been authorized. 357 358 ```bash 359 $ curl -k --user user1:password1 https://local.projectcontour.io/test/$((RANDOM)) 360 {"TestId":"","Path":"/test/27132","Host":"local.projectcontour.io","Method":"GET","Proto":"HTTP/1.1","Headers":{"Accept":["*/*"],"Auth-Handler":["htpasswd"],"Auth-Realm":["default"],"Auth-Username":["user1"],"Authorization":["Basic dXNlcjE6cGFzc3dvcmQx"],"Content-Length":["0"],"User-Agent":["curl/7.64.1"],"X-Envoy-Expected-Rq-Timeout-Ms":["15000"],"X-Envoy-Internal":["true"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Proto":["https"],"X-Request-Id":["2c0ae102-4cf6-400e-a38f-5f0b844364cc"],"X-Request-Start":["t=1601601826.102"]}} 361 ``` 362 363 ## Caveats 364 365 There are a few caveats to consider when deploying external 366 authorization: 367 368 1. Only one external authorization server can be configured on a virtual host 369 1. Only HTTPS virtual hosts are supported 370 1. External authorization cannot be used with the TLS fallback certificate (i.e. client SNI support is required) 371 372 [1]: https://github.com/projectcontour/contour-authserver 373 [2]: https://httpd.apache.org/docs/current/misc/password_encryptions.html 374 [3]: https://www.envoyproxy.io/docs/envoy/latest/api-v3/service/auth/v3/external_auth.proto 375 [4]: ../config/api/#projectcontour.io/v1alpha1.ExtensionService 376 [5]: ../config/api/#projectcontour.io/v1.HTTPProxy 377 [6]: https://cert-manager.io/ 378 [7]: https://httpd.apache.org/docs/current/programs/htpasswd.html 379 [8]: https://kubernetes-sigs.github.io/kustomize/