API Keys
Sometimes when you need to protect a service, the set of users that will need to access it is known in advance and does not change frequently. For example, these users might be other services or specific persons or teams in your organization.
You might also want to retain direct control over how credentials are generated and when they expire. If one of these conditions applies to your use case, you should consider securing your service using API keys. API keys are secure, long-lived UUIDs that clients must provide when sending requests to a service that is protected using this method.
It is important to note that your services are only as secure as your API keys; securing API keys and proper API key rotation is up to the user, thus the security of the routes is up to the user.
To secure your services using API keys, you first need to provide Gloo Edge with your secret API keys in the form of Secrets
. After your API key secrets are in place, you can configure authentication on your Virtual Services by referencing the secrets in one of two ways:
- You can specify a label selector that matches one or more labelled API key secrets (this is the preferred option), or
- You can explicitly reference a set of secrets by their identifier (namespace and name).
When Gloo Edge matches a request to a route secured with API keys, it looks for a valid API key in the request headers. The name of the header that is expected to contain the API key is configurable. If the header is not present, or if the API key it contains does not match one of the API keys in the secrets referenced on the Virtual Service, Gloo Edge will deny the request and return a 401 response to the downstream client.
Internally, Gloo Edge will generate a mapping of API keys to user identities for all API keys present in the system. The user identity for a given API key is the name of the Secret
which contains the API key. The user identity will be added to the request as a header, x-user-id
by default, which can be utilized in subsequent filters. In this specific case, the extauth plugin (which handles the api key flow) is part of the AuthNStage
stage, so filters after this stage will have access to the user identity
header. For example, this functionality is used in Gloo Edge’s rate limiting API to provide different rate limits for anonymous vs. authorized users. For security reasons, this header will be sanitized from the response before it leaves the proxy.
Be sure to check the external auth configuration overview for detailed information about how authentication is configured on Virtual Services.
Setup
This guide assumes that you have deployed Gloo to the gloo-system
namespace and that the glooctl
command line utility
is installed on your machine. glooctl
provides several convenient functions to view, manipulate, and debug Gloo resources;
in particular, it is worth mentioning the following command, which we will use each time we need to retrieve the URL of
the Gloo Gateway that is running inside your cluster:
glooctl proxy url
Let’s create a Static Upstream named json-upstream
that routes to a static website; we will send requests to it during this tutorial.
apiVersion: gloo.solo.io/v1
kind: Upstream
metadata:
name: json-upstream
namespace: gloo-system
spec:
static:
hosts:
- addr: jsonplaceholder.typicode.com
port: 80
glooctl create upstream static --static-hosts jsonplaceholder.typicode.com:80 --name json-upstream
Creating a Virtual Service
Now let’s configure Gloo Edge to route requests to the upstream we just created. To do that, we define a simple Virtual Service to match all requests that:
- Contain a
Host
header with valuefoo
and - Have a path that starts with
/
(this will match all requests).
apiVersion: gateway.solo.io/v1
kind: VirtualService
metadata:
name: auth-tutorial
namespace: gloo-system
spec:
virtualHost:
domains:
- 'foo'
routes:
- matchers:
- prefix: /
routeAction:
single:
upstream:
name: json-upstream
namespace: gloo-system
options:
autoHostRewrite: true
Let’s send a request that matches the above route to the gateway proxy and make sure it works:
curl -H "Host: foo" $(glooctl proxy url)/posts/1
The above command should return:
{
"userId": 1,
"id": 1,
"title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
"body": "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto"
}
Securing the Virtual Service
The auth configuration format shown on this page was introduced with Gloo Enterprise, release 0.20.1. If you are using an earlier version, please refer to this page to see which configuration formats are supported by each version.
As we just saw, we were able to reach the upstream without having to provide any credentials. This is because by default Gloo Edge allows any request on routes that do not specify authentication configuration. Let’s change this behavior. We will update the Virtual Service so that only requests containing a valid API key are allowed.
We start by creating an API key secret using glooctl
:
glooctl create secret apikey infra-apikey \
--apikey N2YwMDIxZTEtNGUzNS1jNzgzLTRkYjAtYjE2YzRkZGVmNjcy \
--apikey-labels team=infrastructure
The above command creates a secret that:
- is named
infra-apikey
, - is placed in the
gloo-system
namespace (this is the default if no namespace is provided via the--namespace
flag), - is of kind
apikey
(this is just a Kubernetes secret which contains the API key in thedata
entry with the key expected by Gloo Edge), - is marked with a label named
team
with valueinfrastructure
, and - contains an API key with value
N2YwMDIxZTEtNGUzNS1jNzgzLTRkYjAtYjE2YzRkZGVmNjcy
.
Instead of providing a value for the API key we could also have asked glooctl
to generate one for us using the
--apikey-generate
flag.
Let’s verify that we have indeed created a valid secret.
kubectl get secret infra-apikey -n gloo-system -oyaml
You secret should look similar to this:
apiVersion: v1
kind: Secret
type: extauth.solo.io/apikey
metadata:
labels:
team: infrastructure
name: infra-apikey
namespace: gloo-system
data:
api-key: TjJZd01ESXhaVEV0TkdVek5TMWpOemd6TFRSa1lqQXRZakUyWXpSa1pHVm1OamN5
The value of data.api-key
is base64-encoded. Let’s decode it to verify that it is indeed our API key:
echo TjJZd01ESXhaVEV0TkdVek5TMWpOemd6TFRSa1lqQXRZakUyWXpSa1pHVm1OamN5 | base64 -D
The command should return N2YwMDIxZTEtNGUzNS1jNzgzLTRkYjAtYjE2YzRkZGVmNjcy
, which is indeed our API key.
Now that we have a valid API key secret, let’s go ahead and create an AuthConfig
Custom Resource (CR) with our API key authentication configuration:
kubectl apply -f - <<EOF
apiVersion: enterprise.gloo.solo.io/v1
kind: AuthConfig
metadata:
name: apikey-auth
namespace: gloo-system
spec:
configs:
- apiKeyAuth:
# This is the name of the header that is expected to contain the API key.
# This field is optional and defaults to `api-key` if not present.
headerName: api-key
labelSelector:
team: infrastructure
EOF
Once the AuthConfig
has been created, we can use it to secure our Virtual Service:
kubectl apply -f - <<EOF
apiVersion: gateway.solo.io/v1
kind: VirtualService
metadata:
name: auth-tutorial
namespace: gloo-system
spec:
virtualHost:
domains:
- 'foo'
routes:
- matchers:
- prefix: /
routeAction:
single:
upstream:
name: json-upstream
namespace: gloo-system
options:
autoHostRewrite: true
options:
extauth:
configRef:
name: apikey-auth
namespace: gloo-system
EOF
In the above example we have added the configuration to the Virtual Host. Each route belonging to a Virtual Host will inherit its AuthConfig
, unless it overwrites or disables it.
Testing denied requests
Let’s try and resend the same request we sent earlier:
curl -v -H "Host: foo" $(glooctl proxy url)/posts/1
You will see that the response now contains a 401 Unauthorized code, indicating that Gloo Edge denied the request.
> GET /posts/1 HTTP/1.1
> Host: foo
> User-Agent: curl/7.54.0
> Accept: */*
>
< HTTP/1.1 401 Unauthorized
< www-authenticate: API key is missing or invalid
< date: Mon, 07 Oct 2019 19:28:14 GMT
< server: envoy
< content-length: 0
Testing authenticated requests
For a request to be allowed, it must include a header named api-key
with the value set to the
API key we previously stored in our secret. Now let’s add the authorization headers:
curl -H "api-key: N2YwMDIxZTEtNGUzNS1jNzgzLTRkYjAtYjE2YzRkZGVmNjcy" -H "Host: foo" $(glooctl proxy url)/posts/1
We are now able to reach the Upstream again!
{
"userId": 1,
"id": 1,
"title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
"body": "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto"
}
Summary
In this tutorial, we installed Gloo Edge Enterprise and created an unauthenticated Virtual Service that routes requests to a
static upstream. We then created an API key AuthConfig
object and used it to secure our Virtual Service.
We first showed how unauthenticated requests fail with a 401 Unauthorized
response, and then showed how to send
authenticated requests successfully to the upstream.
Cleanup the resources by running:
kubectl delete ac -n gloo-system apikey-auth
kubectl delete vs -n gloo-system auth-tutorial
kubectl delete upstream -n gloo-system json-upstream
kubectl delete secret -n gloo-system infra-apikey