Skip to main content

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:

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:

  1. Plugins with additional functionality out of the box, such as support for Open Policy Agent (OPA) authorization policies
  2. Native integration with SlashID identity, meaning more powerful and configurable identity policies
info

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:

EndpointAuthentication?Authorization?
/homeNoNo
/profileYesNo
/profile/manageYesNo
/profile/vipYesMust belong to vip group
/adminYesMust belong to admin group
/admin/diagYesMust 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.

info

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
info

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 and admins
  • 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 [email protected]).

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:

backend_service.yaml
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.

info

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:

deploy_edge_stack.sh
# 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:

edge_stack_listener.yaml
---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:

deploy_emissary.sh
# 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:

emissary_listener.yaml
---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.

mapping.yaml
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:

gate_configmap.yaml
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.

danger

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:

gate_service.yaml
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.

external_filter.yaml
---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 an External 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 the default 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 to grpc, and protocol_version to v3 - 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.

authservice.yaml
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 the default 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 to grpc, and protocol_version to v3 - 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.

CallAuthn challenge?Authenticate as ... ?Request allowed?Reason
GET /homeNoN/AYesNo plugins configured for endpoint
GET /profileYes[email protected]YesNo authorization policies configured for endpoint
GET /profile/manageYes[email protected]YesNo authorization policies configured for endpoint
GET /profile/vipYes[email protected]NoAuthorization policy requires vips group
GET /adminYes[email protected]NoAuthorization policy requires admins group
GET /admin/diagYes[email protected]NoAuthorization policy requires admins group
GET /profileYes[email protected]YesNo authorization policies configured for endpoint
GET /profile/manageYes[email protected]YesNo authorization policies configured for endpoint
GET /profile/vipYes[email protected]YesUser belongs to vips group
GET /adminYes[email protected]NoAuthorization policy requires admins group
GET /admin/diagYes[email protected]NoAuthorization policy requires admins group
GET /profileYes[email protected]YesNo authorization policies configured for endpoint
GET /profile/manageYes[email protected]YesNo authorization policies configured for endpoint
GET /profile/vipYes[email protected]NoAuthorization policy requires vips group
GET /adminYes[email protected]YesUser belongs to admins group
GET /admin/diagYes[email protected]YesUser 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 or AuthService 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!