Documentation PortalBack to Self Assist PortalBack
Documentation Portal
Contents

Authentication with Ambassador - V 1

CloudOne DevOps: Authentication with Ambassador

Background:

Edge Stack ships with an authentication service that is enabled to perform OAuth, JWT validation, and custom authentication schemes. It can perform different authentication schemes on different requests allowing you to enforce authentication as your application needs.

The Filter and FilterPolicy resources are used to configure authentication. This doc focuses on how to deploy and manage the authentication extension. CloudOne do not provide default configurations or HELM chart templates required, instead this document provides steps in case if wish to do.

Notes and Requirements:

  1. Application must be using Ambassador.
  2. Recommended to have ambassador mappings with the host value configured.
  3. Ambassador redirection endpoint should be registered into IAM as a return URL against the Application's client ID.
  4. (Advance Users) Example shown here uses restructured ambassaodor.yaml template and ambassaodor section of the values.yaml file.

This is done intentionally to make a provision for multiple hosts (application URLs) configurations for an application. You may continue using existing format, but template files need to be modified accordingly. You can either add a section for filter abd filterpolicy into current ambassaodor.yaml template or optionally can create separate HELM template file.

Filters and FilterPolicies:

Filters are used to extend the Ambassador Edge Stack to modify or intercept a request before sending to your backend service. The most common use case for Filters is authentication, and Edge Stack includes a number of built-in filters for this purpose. Edge Stack also supports developing custom filters.

Filters are managed using a FilterPolicy resource. The FilterPolicy resource specifies a particular host or URL to match, along with a set of filters to run when an request matches the host/URL.

Filters are created with the Filter resource type, which contains global arguments to that filter. Which Filter(s) to use for which HTTP requests is then configured in FilterPolicy resources, which may contain path-specific arguments to the filter. FilterPolicy resources specify which filters (if any) to apply to which HTTP requests.

Edge Stack supports the multiple filter types: JWT, OAuth2, Plugin and External. You can read more about filters and FilterPolicies here.

Example uses OAuth2.

The OAuth2 Filter

The OAuth2 filter type performs OAuth2 authorization against an identity provider implementing OIDC Discovery. The filter is both:

  1. An OAuth Client, which fetches resources from the Resource Server on the user's behalf.
  2. Half of a Resource Server, validating the Access Token before allowing the request through to the upstream service, which implements the other half of the Resource Server.

This is different from most OAuth implementations where the Authorization Server and the Resource Server are in the same security domain. With the Ambassador Edge Stack, the Client and the Resource Server are in the same security domain, and there is an independent Authorization Server

You can read more about OAuth2 and The Ambassador authentication flow here.

Simplest HELM template for a filter could be:

apiVersion: getambassador.io/v2
kind: Filter
metadata:
  name: {{ $namePrefix }}
spec:
  OAuth2:
    authorizationURL: {{ .authorizationURL }}
    clientID: {{ .clientId }}
    secret: {{ .clientSecret }}
    protectedOrigins:
      - origin: https://{{ $host }}

And HELM template for a fileterpolicy could be:

apiVersion: getambassador.io/v2
kind: FilterPolicy
metadata:
  name: {{ $namePrefix }}
spec:
  rules:
  {{- range $ambassadorConfig.mappings }}
  - host: {{ $host | quote }}
    path: {{ .prefix | default '/' }}
    filters:
    - name: {{ $hostPrefix }}

Ambassador redirection endpoint and Return URL

You will need to register each origin in protectedOrigins as an authorized callback endpoint with your identity provider. The URL will look like {{ORIGIN}}/.ambassador/oauth2/redirection-endpoint.

Example HELM template ambassador.yaml and values.yaml

As mentioned earlier complete re-structured template ambassador.yaml that includes filter and filter policy both could be:

{{- range $hostIndex, $hostConfig := .Values.ambassadorConfig }}
{{- $hostPrefix := printf "%s-%d" (include "app.fullname" $) $hostIndex }}
{{- $secretName := printf "%s-tls" $hostPrefix }}

{{- /*----- TLS Secret for Host (1/host) -----*/}}
{{- if $hostConfig.tls }}
{{- with $hostConfig.tls }}
---
apiVersion: v1
kind: Secret
metadata:
  name: {{ $secretName }}
type: kubernetes.io/tls
data:
  tls.crt: {{ .crt | b64enc }}
  tls.key: {{ .key | b64enc }}
  ca.crt: {{ .ca | b64enc }}
{{- end }}
{{- end }}

{{- range $ambassadorId, $ambassadorConfig := dict "internal" $hostConfig.internal "external" $hostConfig.external }}
{{- if $ambassadorConfig }}
{{- $namePrefix := printf "%s-%s" $hostPrefix $ambassadorId }}
{{- $domain := required (printf "Value for ambassador.%s.domain is required" $ambassadorId) (index $.Values.ambassador $ambassadorId).domain -}}
{{- $defaultHost := printf "%s-%s.%s" (include "app.fullname" $) $.Release.Namespace $domain -}}
{{- $host := $hostConfig.host | default $defaultHost }}

{{- /*----- TLSContexts for Host (1/ambassadorId) -----*/}}
{{- if $hostConfig.tls }}
---
apiVersion: getambassador.io/v2
kind: TLSContext
metadata:
  name: {{ $namePrefix }}
spec:
  ambassador_id: {{ $ambassadorId }}
  hosts:
  - {{ $host | quote }}
  secret: {{ $secretName }}
{{- end }}

{{- /*----- Mappings for Host (many/ambassadorId) -----*/}}
{{- range $mappingIndex, $mappingConfig := $ambassadorConfig.mappings }}
{{- $scheme := required "service.scheme is required"  $.Values.service.scheme }}
---
apiVersion: getambassador.io/v2
kind: Mapping
metadata:
  name: {{ printf "%s-%d" $namePrefix $mappingIndex }}
  labels:
  {{- include "app.labels" $ | nindent 4 }}
spec:
  ambassador_id: {{ $ambassadorId }}
  prefix: {{ $mappingConfig.prefix | default '/' }}
  {{- if $mappingConfig.rewrite }}
  rewrite: {{ $mappingConfig.rewrite }}
  {{- end }}
  host: {{ $host | quote }}
  service: {{ printf "%s://%s:%s" $scheme (include "app.fullname" $) ( $.Values.service.port | toString ) | quote }}
  weight: {{ coalesce $mappingConfig.weight $.Values.ambassador.traffic.weight 100 }}
{{- end }}

{{- /*----- Filter for Host (1/ambassadorId) -----*/}}
{{- if $hostConfig.oauth2 }}
{{- with $hostConfig.oauth2 }}
---
apiVersion: getambassador.io/v2
kind: Filter
metadata:
  name: {{ $namePrefix }}
spec:
  OAuth2:
    authorizationURL: {{ .authorizationURL }}
    clientID: {{ .clientId }}
    secret: {{ .clientSecret }}
    protectedOrigins:
    - origin: https://{{ $host }}
{{- end }}

{{- /*----- FilterPolicy for mappings (1/ambassadorId) -----*/}}
---
apiVersion: getambassador.io/v2
kind: FilterPolicy
metadata:
  name: {{ $namePrefix }}
spec:
  rules:
  {{- range $ambassadorConfig.mappings }}
  - host: {{ $host | quote }}
    path: {{ .prefix | default '/' }}
    filters:
    - name: {{ $hostPrefix }}
  {{- end }}
{{- end }}
{{- end }}
{{- end }}
{{- end }}

And respective values.yaml which provides an option to specify multiple hosts:

ambassadorConfig:
  # - host: something.else.com
  #   # tls: # Specify be in securefile
  #   #   ca:
  #   #   crt:
  #   #   key:
  #   # oauth2:
  #   #   authorizationURL:
  #   #   clientId:
  #   #   clientSecret:
  #   internal:
  #     mappings:
  #     - prefix: /
  #       rewrite: /admin
  #     - prefix: /admin
  #       rewrite: /admin
  #       weight: 10
  #   external:
  #     mappings:
  #     - prefix: /
  #       rewrite: /admin
  - host: auth-example.netapp.com
    # tls: # Specify be in securefile
    #   ca:
    #   crt:
    #   key:
    oauth2:
      authorizationURL: https://login.microsoftonline.com/<GET THIS FROM IDENTITY PROVIDER>/v2.0
      clientId: Application-Client-ID-
      clientSecret: Client~Secret.-FromIDENTITYProvider
    internal:
      mappings:
        - prefix: /
          rewrite: /login
        - prefix: /admin
          rewrite: /admin
    external:
      mappings:
        - prefix: /
          rewrite: /login
        - prefix: /admin
          rewrite: /admin

Please feel free to use separate template for filter and filter policy as per your preference.