Pomerium Ingress
This tutorial covers installing the Pomerium Ingress Controller and securing it with cert-manager. Pomerium is an identity-aware proxy that can also provide a custom ingress controller for your Kubernetes services.
Prerequisites
-
Install Kubectl and set the context to the cluster you'll be working with.
-
Install Helm on your local computer. See Installing Helm for the best installation method for your operating system.
-
Pomerium connects to an identity provider (IdP) to authenticate users. See one of their guides to learn how to set up your IdP of choice to provide oauth2 validation.
-
This tutorial assumes you have a domain space reserved for this cluster (such as
*.cluster.example.com
). You will need access to DNS for this domain to assign A and CNAME records as needed.
Install cert-manager
-
Create a namespace for cert-manager:
kubectl create namespace cert-manager -
Add the
jetstack
repository and update Helm:helm repo add jetstack https://charts.jetstack.iohelm repo update -
Install cert-manager to your cluster:
helm install cert-manager jetstack/cert-manager --namespace cert-manager --create-namespace \--set installCRDs=true -
Confirm deployment with
kubectl get pods --namespace cert-manager
:kubectl get pods --namespace cert-managerNAME READY STATUS RESTARTS AGEcert-manager-5d7f97b46d-8g942 1/1 Running 0 33scert-manager-cainjector-69d885bf55-6x5v2 1/1 Running 0 33scert-manager-webhook-8d7495f4-s5s6p 1/1 Running 0 33s
Configure a Private Certificate Issuer
For secure communication between Pomerium services, create a private certificate issuer. This issuer will reside in the pomerium
namespace, which we will use when creating the Ingress Controller. The certificates issued will only be used for communication between Pomerium components.
-
Define an issuer in the file
pomerium-issuer.yaml
:apiVersion: cert-manager.io/v1kind: Issuermetadata:name: pomerium-canamespace: pomeriumspec:selfSigned: {}---apiVersion: cert-manager.io/v1kind: Certificatemetadata:name: pomerium-canamespace: pomeriumspec:isCA: truesecretName: pomerium-cacommonName: pomerium caissuerRef:name: pomerium-cakind: Issuer---apiVersion: cert-manager.io/v1kind: Issuermetadata:name: pomerium-issuernamespace: pomeriumspec:ca:secretName: pomerium-ca -
Deploy the issuer:
kubectl apply -f pomerium-issuer.yaml
Configure Let's Encrypt Issuer
For communication between the Ingresses and the internet, we'll want to use certificates signed by a trusted certificate authority like Let's Encrypt. This example creates two Let's Encrypt issuers, one for staging and one for production.
The Let's Encrypt production issuer has strict rate limits. Before your configuration is finalized you may have to recreate services several times, hitting those limits. It's easy to confuse rate limiting with errors in configuration or operation while building your stack.
Because of this, we will start with the Let's Encrypt staging issuer. Once your configuration is all but finalized, we will switch to a production issuer. Both of these issuers are configured to use the HTTP01
challenge provider.
The following YAML defines a staging certificate issuer. You must update the email address to your own. The
email
field is required by Let's Encrypt and used to notify you of certificate expiration and updates.apiVersion: cert-manager.io/v1kind: Issuermetadata:name: letsencrypt-stagingspec:acme:# The ACME server URLserver: https://acme-staging-v02.api.letsencrypt.org/directory# Email address used for ACME registrationemail: user@example.com# Name of a secret used to store the ACME account private keyprivateKeySecretRef:name: letsencrypt-staging# Enable the HTTP-01 challenge providersolvers:- http01:ingress:class: pomeriumYou can download and edit the example and apply it with
kubectl apply -f
, or edit, and apply the custom resource in one command:kubectl create --edit -f https://cert-manager.io/docs/tutorials/acme/example/pomerium-staging-issuer.yamlCreate a production issuer and deploy it. As with the staging issuer, update this example with your own email address:
apiVersion: cert-manager.io/v1kind: Issuermetadata:name: letsencrypt-prodspec:acme:# The ACME server URLserver: https://acme-v02.api.letsencrypt.org/directory# Email address used for ACME registrationemail: user@example.com# Name of a secret used to store the ACME account private keyprivateKeySecretRef:name: letsencrypt-prod# Enable the HTTP-01 challenge providersolvers:- http01:ingress:class: pomeriumkubectl create --edit -f https://cert-manager.io/docs/tutorials/acme/example/pomerium-production-issuer.yaml
You can confirm on the status of the issuers after you create them:
kubectl describe issuer letsencrypt-stagingkubectl describe issuer letsencrypt-prod
You should see the issuer listed with a registered account.
Install The Pomerium Ingress Controller
Set your
kubectl
context to the Pomerium namespace:kubectl config set-context --current --namespace=pomeriumCreate certificate configurations for Pomerium. Our example is named
pomerium-certificates.yaml
:apiVersion: cert-manager.io/v1kind: Certificatemetadata:name: pomerium-certnamespace: pomeriumspec:secretName: pomerium-tlsissuerRef:name: pomerium-issuerkind: Issuerusages:- server auth- client authdnsNames:- pomerium-proxy.pomerium.svc.cluster.local- pomerium-authorize.pomerium.svc.cluster.local- pomerium-databroker.pomerium.svc.cluster.local- pomerium-authenticate.pomerium.svc.cluster.local---apiVersion: cert-manager.io/v1kind: Certificatemetadata:name: pomerium-redis-certnamespace: pomeriumspec:secretName: pomerium-redis-tlsissuerRef:name: pomerium-issuerkind: Issuerusages:- server auth- client authdnsNames:- pomerium-redis-master.pomerium.svc.cluster.local- pomerium-redis-headless.pomerium.svc.cluster.local- pomerium-redis-replicas.pomerium.svc.cluster.localReplace
localhost.pomerium.io
with the domain name you'll assign to the Ingress. Keep the subdomainauthenticate
.This example defines 2 certificates:
- One using the self-signed
pomerium-issuer
to encrypt traffic between Pomerium's services, - One using the self-signed
pomerium-issuer
to encrypt traffic to and from the Redis service(s) used by Pomerium.
Additional certificates will be issued as new Ingresses are created.
- One using the self-signed
Apply the certificate configuration, and confirm:
kubectl apply -f pomerium-certificates.yamlkubectl get certificateNAME READY SECRET AGEpomerium-ca True pomerium-ca 10spomerium-cert True pomerium-tls 10spomerium-redis-cert True pomerium-redis-tls 10sCreate a values file for Helm to use when installing Pomerium. Our example is named
pomerium-values.yaml
.authenticate:existingTLSSecret: pomerium-tlsidp:provider: "google"clientID: YOUR_CLIENT_IDclientSecret: YOUR_SECRETserviceAccount: YOUR_SERVICE_ACCOUNTingress:annotations:cert-manager.io/issuer: letsencrypt-stagingtls:secretName: authenticate.localhost.pomerium.io-tlsproxy:existingTLSSecret: pomerium-tlsdatabroker:existingTLSSecret: pomerium-tlsstorage:clientTLS:existingSecretName: pomerium-redis-tlsexistingCASecretKey: ca.crtauthorize:existingTLSSecret: pomerium-tlsredis:enabled: truegenerateTLS: falsetls:certificateSecret: pomerium-redis-tlsingressController:enabled: trueconfig:rootDomain: localhost.pomerium.io #Change this to your reserved domain space.existingCASecret: pomerium-tlsgenerateTLS: false # On by default, disabled when cert-manager or another solution is in place.The options required in the
authenticate.idp
block will vary depending on your identity provider.Update
config.rootDomain
to match your domain space.Add Pomerium's Helm repo:
helm repo add pomerium https://helm.pomerium.ioInstall Pomerium to the cluster:
helm upgrade --install pomerium pomerium/pomerium --values ./pomerium-values.yamlUse
kubectl
to confirm that the Pomerium Proxy has stood up, and get the external IP needed to route your domain space to the cluster:kubectl get svc pomerium-proxyNAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGEpomerium-proxy LoadBalancer 10.128.117.25 192.0.2.20 443:30006/TCP,9090:30707/TCP 2m37s
Define a Test Service
To test our new Ingress Controller, we will add the kuard app to our cluster and define an Ingress for it.
Define the kuard deployment and associated service:
apiVersion: apps/v1kind: Deploymentmetadata:name: kuardspec:selector:matchLabels:app: kuardreplicas: 1template:metadata:labels:app: kuardspec:containers:- image: gcr.io/kuar-demo/kuard-amd64:1imagePullPolicy: Alwaysname: kuardports:- containerPort: 8080apiVersion: v1kind: Servicemetadata:name: kuardspec:ports:- port: 80targetPort: 8080protocol: TCPselector:app: kuardYou can download and reference these files locally, or you can reference them from the GitHub source repository for this documentation.
To install the example service from the tutorial files straight from GitHub:
kubectl apply -f https://cert-manager.io/docs/tutorials/acme/example/deployment.yamlkubectl apply -f https://netlify.cert-manager.io/docs/tutorials/acme/example/service.yamlCreate a new Ingress manifest (
example-ingress.yaml
) for our test service:apiVersion: networking.k8s.io/v1kind: Ingressmetadata:name: kuardannotations:cert-manager.io/issuer: letsencrypt-stagingingress.pomerium.io/policy: '[{"allow":{"and":[{"domain":{"is":"example.com"}}]}}]'spec:ingressClassName: pomeriumrules:- host: kuard.localhost.pomerium.iohttp:paths:- path: /pathType: Prefixbackend:service:name: kuardport:number: 80tls:- hosts:- kuard.localhost.pomerium.iosecretName: kuard.localhost.pomerium.io-tlsAgain, change the references to
localhost.pomerium.io
to match your domain space.Apply the Ingress manifest to the cluster:
kubectl apply -f example-ingress.yaml
The Pomerium Ingress Controller will use cert-manager to automatically provision a certificate from the letsencrypt-staging
issuer for the route to kuard.localhost.pomerium.io
.
Once you've configured all your application services correctly in the cluster, adjust the issuer for your Ingresses (including the Authenticate service) to use letsencrypt-prod
.