Guide: Gate as an ExtAuth Service for Ambassador in Kubernetes
Introduction
This guide describes how to deploy Gate alongside Ambassador Edge Stack or Emissary-ingress in Kubernetes, with Gate acting as an External Filter (for Edge Stack) or an Auth Service (for Emissary). It demonstrates how to configure Gate to run as an External Filter/Auth Service, and how to configure Edge Stack/Emissary to use Gate.
If you are already familiar with Ambassador Edge Stack/Emissary, you can skip to step 4, or refer to our standalone examples.
What is Gate?
Gate is an identity-aware service deployed at the edge of your infrastructure, performing identity, authentication, and authorization (authN/authZ) operations on incoming traffic.
Gate is highly configurable, and comes with a range of ready-to-use plugins for common authentication and authorization use cases. Gate plugins can be combined to create complex authN/authZ policies with a couple of simple lines of configuration.
Gate's deployment is also highly flexible, and can be used with just about any infrastructure.
What are Ambassador Edge Stack and Emissary-ingress?
Ambassador provides two different API gateway products:
- Emissary-ingress, an open-source API Gateway based on Envoy Proxy
- Edge Stack, Ambassador's proprietary API Gateway.
Both are Kubernetes native and offer a variety of features. In particular, both can integrate with an external service using the Envoy ExtAuth protocol.
In this case, each request will be evaluated by the external service, which will return an allow
or deny
response.
The external service may also modify the request before it is forwarded to the target server (if allowed), or write
a response to be returned to the client (if denied).
Why use Gate with Edge Stack/Emissary?
Edge Stack and Emissary-ingress both include a variety of useful features for authN/authZ. Gate can augment these in two ways:
- Plugins with additional functionality out of the box, such as support for Open Policy Agent (OPA) authorization policies
- Native integration with SlashID identity, meaning more powerful and configurable identity policies
While integration with SlashID identity and authentication unlocks Gate's full potential, many of its plugins work with standalone Gate and still provide flexible and powerful authN/authZ features.
Gate is easy to set up and configure. This guide will demonstrate all the steps required to get Gate up and running in Kubernetes with Ambassador Edge Stack or Emissary.
Scenario
For this guide, suppose you have a backend exposing multiple endpoints, with different authentication and authorization requirements. These are summarised in the table below:
Endpoint | Authentication? | Authorization? |
---|---|---|
/home | No | No |
/profile | Yes | No |
/profile/manage | Yes | No |
/profile/vip | Yes | Must belong to vip group |
/admin | Yes | Must belong to admin group |
/admin/diag | Yes | Must belong to admin group |
You have decided to use Ambassador Edge Stack or Emissary-ingress as your API gateway, and you want to have all authN/authZ logic at the edge. However, you want to extend the auth capabilities of Edge Stack/Emissary, and so you are using Gate to complement the existing features, as well as SlashID Identity Management for secure and easy-to-use identity and user management.
You can easily use Gate without SlashID Identity Management, and Gate has been designed to work with other identity providers. However, some of Gate's plugins require SlashID Identity Management to function correctly. Check the plugins documentation for more information.
The steps below will demonstrate how to configure Edge Stack/Emissary and Gate to easily enforce these requirements.
Guide
Prerequisites
Before you get started, we recommend the following:
- install minikube or prepare another Kubernetes environment to work in
- install kubectl and Helm
- if you don't have one already, sign up for a free SlashID organization, and make a note of your organization ID and API key
While we are using minikube for this guide, the majority of these steps will apply regardless of how you have deployed Kubernetes. Steps that are specific to minikube will be noted as such.
Step 0: Prepare users and groups
To be able to make the most of Gate's plugins, we recommend taking a couple of minutes to create some groups and users in the SlashID Dashboard. This will help if you are following along with each step. In particular, we recommend creating:
- two groups:
vips
andadmins
- three users:
- a standard user
- a VIP user, who should be added to the
vips
group - an admin user, who should be added to the
admins
group
For all three users, use an email address you can access, so you can authenticate as each one (for example, using plus addressing like my.name+vip@example.com
).
Step 1: Prepare your Kubernetes cluster
If you are using minikube or another local Kubernetes tool, make sure it is running. For minikube run minikube start
.
It may also be useful to run minikube dashboard
so you can easily check on the status of your deployments. If you
are using another Kubernetes deployment, make sure it is running, and you have tooling to manage it.
Step 2: Deploy the backend
To begin with, we will deploy the backend service. For this demo, we will use a simple echo server that simply returns a description of any request it receives:
apiVersion: apps/v1kind: Deploymentmetadata: name: backendspec: replicas: 2 selector: matchLabels: app: backend template: metadata: labels: app: backend spec: containers: - name: backend image: kicbase/echo-server:1.0 ports: - containerPort: 8080---apiVersion: v1kind: Servicemetadata: labels: app: backend name: backendspec: externalTrafficPolicy: Local ports: - port: 80 targetPort: 8080 selector: app: backend sessionAffinity: None type: LoadBalancer
Create this service with kubectl apply -f backend_service.yaml
.
If you are using minikube, you can create a tunnel to the new service using minikube service backend --url
.
Making requests to the localhost
URL for the tunnel should respond with 200
status codes and details of the request.
Step 3: Deploy Edge Stack or Emissary-ingress
Now we will deploy Edge Stack or Emissary-ingress. These steps are based on the relevant quickstart guides provided by Ambassador. For alternative installation instructions or more details, please check the quickstarts for Edge Stack and Emissary-ingress.
Now we will deploy Edge Stack or Emissary-ingress. These steps are based on the relevant quickstart guides provided by Ambassador for Edge Stack and Emissary-ingress. Please check the Ambassador documentation for more details and alternative installation instructions.
Step 3a: Edge Stack
If you are using Edge Stack, follow these steps to deploy it:
# Add the Ambassador helm repohelm repo add datawire https://app.getambassador.iohelm repo update# Create a Namespace for the deploymentkubectl create namespace ambassador# Apply the Edge Stack custom resource definitionskubectl apply -f https://app.getambassador.io/yaml/edge-stack/latest/aes-crds.yaml# Check the deploymentkubectl wait --timeout=90s --for=condition=available deployment emissary-apiext -n emissary-system# Install Edge Stack using the Helm chart# Note that this may have succeeded even if it logs some error messages - you can verify the status using the minikube# dashboard or other Kubernetes monitoring products.helm install edge-stack --namespace ambassador datawire/edge-stack# Check the Edge Stack installation succeededkubectl -n ambassador wait --for condition=available --timeout=90s deploy -lproduct=aes
This will deploy Edge Stack in evaluation mode by default. If you want to connect your deployment to Ambassador Cloud, refer to the Ambassador documentation.
If you are using the minikube dashboard or similar, you should now see multiple services in the ambassador
namespace.
We also need to create a Listener
to tell Edge Stack which port(s) to listen on:
---apiVersion: getambassador.io/v3alpha1kind: Listenermetadata: name: edge-stack-listener-8080 namespace: ambassadorspec: port: 8080 protocol: HTTP securityModel: XFP hostBinding: namespace: from: ALL---apiVersion: getambassador.io/v3alpha1kind: Listenermetadata: name: edge-stack-listener-8443 namespace: ambassadorspec: port: 8443 protocol: HTTPS securityModel: XFP hostBinding: namespace: from: ALL
Use kubectl apply -f edge_stack_listener.yaml
to create this resource.
Step 3b: Emissary-ingress
If you are using Emissary-ingress, follow these steps to deploy it:
# Add the Ambassador helm repohelm repo add datawire https://app.getambassador.iohelm repo update# Create a Namespace for the deploymentkubectl create namespace emissary# Apply the Emissary custom resource definitionskubectl apply -f https://app.getambassador.io/yaml/emissary/latest/emissary-crds.yaml# Check the deploymentkubectl wait --timeout=90s --for=condition=available deployment emissary-apiext -n emissary-system# Install Emissary using the Helm charthelm install emissary-ingress --namespace emissary datawire/emissary-ingress# Check the Emissary installation succeededkubectl -n emissary wait --for condition=available --timeout=90s deploy -lapp.kubernetes.io/instance=emissary-ingress
If you want to connect your deployment to Ambassador Cloud, refer to the Ambassador documentation.
If you are using the minikube dashboard or similar, you should now see multiple services in the emissary
namespace.
We also need to create a Listener
to tell Emissary which port(s) to listen on:
---apiVersion: getambassador.io/v3alpha1kind: Listenermetadata: name: emissary-listener-8080 namespace: emissaryspec: port: 8080 protocol: HTTP securityModel: XFP hostBinding: namespace: from: ALL---apiVersion: getambassador.io/v3alpha1kind: Listenermetadata: name: emissary-listener-8443 namespace: emissaryspec: port: 8443 protocol: HTTPS securityModel: XFP hostBinding: namespace: from: ALL
Use kubectl apply -f emissary_listener.yaml
to create this resource.
Step 3c: Mapping
For both Edge Stack and Emissary-ingress, we need to create a Mapping
resource so that Edge Stack/Emissary will
route inbound traffic to the backend service.
apiVersion: getambassador.io/v3alpha1kind: Mappingmetadata: name: backend-mapping namespace: defaultspec: service: backend.default:80 timeout_ms: 3000 connect_timeout_ms: 3000 retry_policy: retry_on: 5xx num_retries: 1 per_try_timeout: 0.150s hostname: "*" prefix: /
Create this resource with kubectl apply -f mapping.yaml
.
Step 4: Deploy Gate
Deploying Gate requires two resources: a ConfigMap
with Gate's configuration, and a Deployment
for deploying
Gate itself.
Gate ConfigMap
First, let's look at the ConfigMap
:
apiVersion: v1kind: ConfigMapmetadata: name: gate-configdata: gate.yaml: | slashid_config: &slashid_config slashid_org_id: <your SlashID organization ID> slashid_api_key: <your SlashID API key> slashid_base_url: https://api.slashid.com/ gate: mode: ext_auth default: ext_auth_response: deny port: 5000 tls: enabled: false log: format: text level: debug plugins_http_cache: - pattern: "*" cache_control_override: private, max-age=600, stale-while-revalidate=300 plugins: - id: authn_proxy type: authentication-proxy enabled: false parameters: <<: *slashid_config login_page_factors: - method: email_link jwks_url: https://api.slashid.com/.well-known/jwks.json jwks_refresh_interval: 15m jwt_expected_issuer: https://api.slashid.com online_tokens_validation_timeout: "24h" - id: authz_admin type: opa enabled: false parameters: <<: *slashid_config cookie_with_token: SID-AuthnProxy-Token policy_decision_path: /authz/allow policy: | package authz import future.keywords.if default allow := false allow if input.request.parsed_token.payload.groups[_] == "admins" - id: authz_vip type: opa enabled: false parameters: <<: *slashid_config cookie_with_token: SID-AuthnProxy-Token policy_decision_path: /authz/allow policy: | package authz import future.keywords.if default allow := false allow if input.request.parsed_token.payload.groups[_] == "vips" urls: - pattern: "*/home" - pattern: "*/profile" plugins: authn_proxy: enabled: true - pattern: "*/profile/manage" plugins: authn_proxy: enabled: true - pattern: "*/profile/vip" plugins: authn_proxy: enabled: true authz_vip: enabled: true - pattern: "*/admin" plugins: authn_proxy: enabled: true authz_admin: enabled: true - pattern: "*/admin/*" plugins: authn_proxy: enabled: true authz_admin: enabled: true
Before we move on, let's take a look at some key parts of this ConfigMap
, and in particular the gate.yaml
data.
This data contains the configuration for Gate. The relevant lines are highlighted.
First, lines 7-10 contain the configuration needed to communicate with SlashID. You should set the slashid_org_id
and
slashid_api_key
fields to the relevant values for your SlashID organization.
Lines 13-18 configure how Gate should run.
Here, mode
is set to extauth
- meaning Gate will run a gRPC server that implements the ExtAuth protocol, so it
can be used an external auth service with Edge Stack and Emissary.
The default
block determines how Gate behaves if no other configuration applies, and we have set ext_auth_response
to deny
- so in the absence of other configuration (i.e., a plugin), Gate will deny requests it receives.
The port
field determines what port Gate will expose - in this case, 5000
.
Finally, in the tls
block, enabled
is set to false
. This is to simplify the guide, so we do not have to provide
certificates for Gate (which will generate self-signed certificates automatically), nor configure Edge Stack/Emissary
to accept self-signed certificates.
Disabling TLS is suitable only for development purposes. In production environments, it is essential that you enable TLS and properly configure certificates.
Now let's look at the plugins
block. This configures the plugins that Gate will apply to incoming requests. In this
case, we have configured three plugins: an authentication proxy, and two Open Policy Agent (OPA) authorization plugins.
The authentication proxy will check incoming requests for cookies with a SlashID user token: if not found or the token is not valid, it will deny the request and return a SlashID authentication page; if found and is valid, it will allow the request.
The OPA authorization plugins will apply an OPA policy, which is provided inline in the plugin configuration. Each one
includes a simple policy that checks the groups
claim in the token to see if the person belongs to a specific group.
If they do, the request is allowed; otherwise the request is denied.
Last, let's look at the urls
block. This tells Gate which plugins to apply based on the URL of the incoming request.
Each entry in the urls
block must include a pattern
, which is matched against the URL of the incoming request.
If the pattern matches, the plugins for that pattern are applied in the order they are specified in the config.
You will see that the patterns and their plugins match the requirements for the endpoints in the table above: those that
need authentication have the authentication proxy plugin, and those needing authorization have one of the OPA plugins.
You can create the ConfigMap
resource by downloading/copying this file and running kubectl apply -f gate_configmap.yaml
.
Gate Deployment
Now that the ConfigMap
is in place, we can deploy Gate itself using this YAML file:
apiVersion: apps/v1kind: Deploymentmetadata: name: gatespec: replicas: 2 selector: matchLabels: app: gate template: metadata: labels: app: gate spec: containers: - name: gate image: slashid/gate-free:latest command: [gate, --yaml, /etc/gate/gate.yaml] ports: - containerPort: 5000 volumeMounts: - mountPath: /etc/gate name: gate-config volumes: - name: gate-config configMap: name: gate-config---apiVersion: v1kind: Servicemetadata: labels: app: gate name: gatespec: externalTrafficPolicy: Local ports: - port: 5000 targetPort: 5000 selector: app: gate sessionAffinity: None type: LoadBalancer
Note that:
- the ports are set to
5000
to match the port in the Gate configuration - the volume mounts are set up to use the
ConfigMap
you created above - the command uses YAML to load the configuration into Gate, matching the name and format of the
ConfigMap
data
You can deploy Gate by downloading/copying this file and running kubectl apply -f gate_service.yaml
.
You should now see Gate in the default
namespace of your Kubernetes cluster.
Step 5: Use Gate as External Filter/Auth Service
You now have the backend, Gate, and Edge Stack/Emissary deployed in your Kubernetes cluster - the last piece is to tell Edge Stack/Emissary to use Gate as the external auth service. This is slightly different depending on whether you are using Edge Stack or Emissary.
Step 5a: External Filter
and FilterPolicy
for Edge Stack
For Edge Stack, we need to create an ExternalFilter
resource and corresponding FilterPolicy
.
---apiVersion: getambassador.io/v3alpha1kind: Filtermetadata: name: "gate-filter" namespace: "ambassador"spec: External: auth_service: http://gate.default:5000 proto: grpc protocol_version: v3---apiVersion: getambassador.io/v3alpha1kind: FilterPolicymetadata: name: "gate-filter-policy" namespace: "ambassador"spec: rules: - host: "*" path: /* filters: - name: gate-filter
Note that in the Filter
:
- the
spec
describes anExternal
Filter
- meaning it tells Edge Stack to delegate filtering to an external service - the
auth_service
field has the internal URL for the Gate service, which is in thedefault
namespace - the Gate URL uses the
http
scheme, because TLS is disabled - the Gate URL uses the
5000
port, as in the Gate configuration and service - the
proto
field must be set togrpc
, andprotocol_version
tov3
- Gate only supports the Envoy gRPC v3 ExtAuth protocol
Note that in the FilterPolicy
, we are applying the policy to all hosts and all paths, using wildcards. The filter
must refer to the Filter
defined above.
Create the Filter
and FilterPolicy
with kubectl apply -f external_filter.yaml
.
Step 5b: AuthService
for Emissary-ingress
For Emissary-ingress, we need to create an AuthService
resource.
apiVersion: getambassador.io/v3alpha1kind: AuthServicemetadata: name: "gate-auth-svc" namespace: emissaryspec: auth_service: http://gate.default:5000 proto: grpc protocol_version: v3
Note that:
- the
auth_service
field has the internal URL for the Gate service, which is in thedefault
namespace - the Gate URL uses the
http
scheme, because TLS is disabled - the Gate URL uses the
5000
port, as in the Gate configuration and service - the
proto
field must be set togrpc
, andprotocol_version
tov3
- Gate only supports the Envoy gRPC v3 ExtAuth protocol
Create the AuthService
with kubectl apply -f authservice.yaml
.
Step 6: Testing
All the pieces are now in place to try out the service. Before we can do this, we should make sure we can access the
services from outside Kubernetes. If you are using minikube, we can use a tunnel to expose services on localhost
:
minikube service -n ambassador edge-stack --url
or
minikube service -n emissary emissary-ingress --url
This will print a localhost
URL that you can use to make HTTP calls to the Edge Stack or Emissary service.
If you are using a different deployment, refer to the appropriate documentation on exposing services.
Here are some sample calls that you can make to see Edge Stack/Emissary and Gate working together to enforce
authentication and authorization on the endpoints. We recommend making these calls in a browser, so you can see the
authentication proxy. Denied requests will return a 403 Forbidden
status code, while allowed requests will return
an echo of the incoming request. In order to authenticate as a different user you will need to simulate logging out
by deleting the relevant cookie from your browser.
Call | Authn challenge? | Authenticate as ... ? | Request allowed? | Reason |
---|---|---|---|---|
GET /home | No | N/A | Yes | No plugins configured for endpoint |
GET /profile | Yes | standard@example.com | Yes | No authorization policies configured for endpoint |
GET /profile/manage | Yes | standard@example.com | Yes | No authorization policies configured for endpoint |
GET /profile/vip | Yes | standard@example.com | No | Authorization policy requires vips group |
GET /admin | Yes | standard@example.com | No | Authorization policy requires admins group |
GET /admin/diag | Yes | standard@example.com | No | Authorization policy requires admins group |
GET /profile | Yes | vip@example.com | Yes | No authorization policies configured for endpoint |
GET /profile/manage | Yes | vip@example.com | Yes | No authorization policies configured for endpoint |
GET /profile/vip | Yes | vip@example.com | Yes | User belongs to vips group |
GET /admin | Yes | vip@example.com | No | Authorization policy requires admins group |
GET /admin/diag | Yes | vip@example.com | No | Authorization policy requires admins group |
GET /profile | Yes | admin@example.com | Yes | No authorization policies configured for endpoint |
GET /profile/manage | Yes | admin@example.com | Yes | No authorization policies configured for endpoint |
GET /profile/vip | Yes | admin@example.com | No | Authorization policy requires vips group |
GET /admin | Yes | admin@example.com | Yes | User belongs to admins group |
GET /admin/diag | Yes | admin@example.com | Yes | User belongs to admins group |
You can experiment with these endpoints and see how Edge Stack/Emissary works together with Gate to enforce the authN/authZ rules we described at the beginning of this guide.
Summary
In this guide, we have been through the steps required to set up Ambassador Edge Stack or Emissary-ingress with Gate as an external auth service. The key points are:
- configuring Gate to act as an
ExtAuth
service - creating
Filter
/FilterPolicy
orAuthService
resources for Edge Stack/Emissary
You can now start to adapt the example from this guide to your own purposes, experimenting with Gate's range of plugins, and using your own backend services and endpoints.
Ready to try SlashID? Register here!
Want to learn more about Gate? Check out the rest of our documentation.
Is there a feature you’d like to see, or have you tried out Gate and have some feedback? Let us know!