# Istio

  • Istio provides mutual TLS via sidecars and to make Istio play well with Pomerium we need to disable TLS on the Pomerium side.
  • We need to provide Istio with information on how to route requests via Pomerium to their destinations.
  • The following example shows how to make Grafana's auth proxy work with Pomerium inside of an Istio mesh.

# Gateway

We are using the standard istio-ingressgateway that comes configured with Istio and attach a Gateway to it that deals with a subset of our ingress traffic based on the Host header (in this case *.yourcompany.com). This is the Gateway to which we will later attach VirtualServices for more granular routing decisions. Along with the Gateway, because we care about TLS, we are using Certmanager to provision a self-signed certificate (see Certmanager docs for setup instructions).

apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
  name: internal-gateway
  namespace: istio-system
spec:
  selector:
    istio: ingressgateway
  servers:
  - port:
      number: 443
      protocol: HTTPS
      name: https-default
    tls:
      mode: SIMPLE
      serverCertificate: "sds"
      privateKey: "sds"
      credentialName: internal-cert
    hosts:
    - *.yourcompany.com
---
apiVersion: cert-manager.io/v1alpha2
kind: Certificate
metadata:
  name: internal-cert
  namespace: istio-system
spec:
  secretName: internal-cert
  issuerRef:
    name: self-signed-issuer
    kind: ClusterIssuer
  commonName: *.yourcompany.com
  dnsNames:
    - *.yourcompany.com
---
apiVersion: cert-manager.io/v1alpha2
kind: ClusterIssuer
metadata:
  name: self-signed-issuer
spec:
  selfSigned: {}

# Virtual Services

Here we are configuring two Virtual Services. One to route from the Gateway to the Authenticate service and one to route from the Gateway to the Pomerium Proxy, which will route the request to Grafana according to the configured Pomerium policy.

apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: grafana-virtual-service
  namespace: pomerium
spec:
  gateways:
    - istio-system/internal-gateway
  hosts:
    - grafana.yourcompany.com
  http:
    - route:
        - destination:
            host: pomerium-proxy
            port:
              number: 80
---
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: authenticate-virtual-service
  namespace: pomerium
spec:
  gateways:
    - istio-system/internal-gateway
  hosts:
    - authenticate.yourcompany.com
  http:
    - route:
        - destination:
            host: pomerium-authenticate
            port:
              number: 80
---

# Service Entry

If you are enforcing mutual TLS in your service mesh you will need to add a ServiceEntry for your identity provider so that Istio knows not to expect a mutual TLS connection with, for example https://yourcompany.okta.com.

apiVersion: networking.istio.io/v1alpha3
kind: ServiceEntry
metadata:
  name: external-idp
  namespace: pomerium
spec:
  hosts:
    - yourcompany.okta.com
  location: MESH_EXTERNAL
  ports:
    - number: 443
      name: https
      protocol: TLS
  resolution: DNS

# Pomerium Configuration

For this example we're using the Pomerium Helm chart with the following values.yaml file. Things to note here are the insecure flag, where we are disabling TLS in Pomerium in favor of the Istio-provided TLS via sidecars. Also note the extaEnv arguments where we are asking Pomerium to extract the email property from the JWT and pass it on to Grafana in a header called X-Pomerium-Claim-Email. We need to do this because Grafana does not know how to read the Pomerium JWT but its auth-proxy authentication method can be configured to read user information from headers. The policy document contains a single route that will send all requests with a host header of https://grafana.yourcompany.com to the Grafana instance running in the monitoring namespace. We disable ingress because we are using the Istio ingressgateway for ingress traffic and don't need the Pomerium helm chart to create ingress objects for us.

config:
  insecure: true
  policy:
    - from: https://grafana.yourcompany.com
      to: "http://prometheus-grafana.monitoring.svc.cluster.local"
      timeout: 30s
      allowed_domains:
        - yourcompany.com
ingress:
  enabled: false

extraEnv:
  JWT_CLAIMS_HEADERS: email

# Grafana ini

On the Grafana side we are using the Grafana Helm chart and what follows is the relevant section of the values.yml file. The most important thing here is that we need to tell Grafana from which request header to grab the username. In this case that's X-Pomerium-Claim-Email because we will be using the user's email (provided by your identity provider) as their username in Grafana. For all the configuration options check out the Grafana documentation about its auth-proxy authentication method.

grafana.ini:
  users:
    allow_sign_up: false
    auto_assign_org: true
    auto_assign_org_role: Editor
  auth.proxy:
    enabled: true
    header_name: X-Pomerium-Claim-Email
    header_property: username
    auto_sign_up: true
    sync_ttl: 60
    enable_login_token: false