API key auth for Ambassador
I suggest you do the following:
Create an Authentication Application: for each protected endpoint, this app will be responsible for validating the Api Key.
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
- 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.
- 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.
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/v2kind: Mappingmetadata: name: protected-mapping namespace: defaultspec: 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 Founddate: Thu, 11 Mar 2021 18:30:27 GMTserver: envoycontent-length: 0# Authenticated => 200$ curl -sI -X GET -H 'Authorization: Bearer eEVCV1JtUzBSVUFvQmw4eVRVM25BenJa' https://ambassador/protected-path/HTTP/1.1 200 OKcontent-type: application/json; charset=utf-8vary: Origindate: Thu, 11 Mar 2021 18:23:20 GMTcontent-length: 15x-envoy-upstream-service-time: 3server: 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._~+/]+=*