3

I'm trying to figure out how to create a simple API key protected proxy with Ambassador on k8s, yet can't seem to find any docs on this.

Specifically, I just want to set it up so it can take a request with API-KEY header, authenticate it, and if API-KEY is valid for some client, pass it onto my backend.

kozyr
  • 1,334
  • 1
  • 20
  • 30

2 Answers2

4

I suggest you do the following:

  1. Create an Authentication Application: for each protected endpoint, this app will be responsible for validating the Api Key.

  2. Configuring Ambassador to redirect requests to this service: you just need to annotate your authentication app service definition. Example:

    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: auth-app
      annotations:
        getambassador.io/config: |
          ---
          apiVersion: ambassador/v1
          kind:  AuthService
          name:  authentication
          auth_service: "auth-app:8080"
          allowed_request_headers:
          - "API-KEY"
    spec:
      type: ClusterIP
      selector:
        app: auth-app
      ports:
      - port: 8080
        name: auth-app
        targetPort: auth-app
  1. Configure an endpoint in auth-app corresponding to the endpoint of the app you want to authenticate. Suppose you have an app with a Mapping like this:
    apiVersion: ambassador/v1
    kind:  Mapping
    name:  myapp-mapping
    prefix: /myapp/
    service: myapp:8000

Then you need to have an endpoint "/myapp/" in auth-app. You will read your API-KEY header there. If the key is valid, return a HTTP 200 (OK). Ambassador will then send the original message to myapp. If auth-app returns any other thing besides a HTTP 200, Ambassador will return that response to the client.

  1. Bypass the authentication in needed apps. For example you might need a login app, responsible for providing an API Key to the clients. You can bypass authentication for these apps using bypass_auth: true in the mapping:
    apiVersion: ambassador/v1
    kind:  Mapping
    name:  login-mapping
    prefix: /login/
    service: login-app:8080
    bypass_auth: true

Check this if you want to know more about authentication in Ambassador

EDIT: According to this answer it is a good practice if you use as header Authorization: Bearer {base64-API-KEY}. In Ambassador the Authorization header is allowed by default, so you don't need to pass it in the allowed_request_headers field.

victortv
  • 7,874
  • 2
  • 23
  • 27
  • 1
    Does that mean that Ambassador doesn't do any API key checks on its own, and I'd always have to supply an extra service for this? – kozyr Apr 24 '19 at 23:48
  • In the case of the open source version then yes, the only authentication method is via an external service. My dev team was also disappointed with this, but after developing the extra service we enjoyed the flexibility to create our own rules and authentication mechanisms, which can be different for each endpoint. According to the paid version [Ambassador Pro](https://www.getambassador.io/pro) page that version has integration with OAuth, JWT validation and an API Keys feature coming soon. The Pro version is quite new, so I assume most users of Ambassador got by fine with external auth. – victortv Apr 25 '19 at 00:01
1

I settled on this quick and dirty solution after not finding a simple approach (that would not involve spinning up an external authentication service).

You can use Header-based Routing and only allow incoming requests with a matching header:value.

---
apiVersion: getambassador.io/v2
kind: Mapping
metadata:
  name: protected-mapping
  namespace: default
spec:
  prefix: /protected-path/
  rewrite: /
  headers:
    # Poorman's Bearer authentication
    # Ambassador will return a 404 error unless the Authorization header value is set as below on the incoming requests.
    Authorization: "Bearer <token>"
  service: app:80

Testing

# Not authenticated => 404
$ curl -sI -X GET https://ambassador/protected-path/
HTTP/1.1 404 Not Found
date: Thu, 11 Mar 2021 18:30:27 GMT
server: envoy
content-length: 0

# Authenticated => 200
$ curl -sI -X GET -H 'Authorization: Bearer eEVCV1JtUzBSVUFvQmw4eVRVM25BenJa' https://ambassador/protected-path/
HTTP/1.1 200 OK
content-type: application/json; charset=utf-8
vary: Origin
date: Thu, 11 Mar 2021 18:23:20 GMT
content-length: 15
x-envoy-upstream-service-time: 3
server: envoy

While you could technically use any header:value pair (e.g., x-my-auth-header: header-value) here, the Authorization: Bearer ... scheme seems to be the best option if you want to follow a standard.

Whether to base64-encode or not your token in this case is up to you.

Here's a lengthy explanation of how to read and understand the spec(s) in this regard: https://stackoverflow.com/a/56704746/4550880

It boils down to the following regex format for the token value:

[-a-zA-Z0-9._~+/]+=*
Leonid Makarov
  • 1,118
  • 8
  • 8