TLS on Kubernetes
You can obtain TLS certificates for the OpenFaaS API Gateway and for your functions using cert-manager from JetStack.
We will use the following components:
- OpenFaaS installed via helm or (
helm template
if you can't usetiller
) - cert-manager
- Nginx IngressController
We will split this tutorial into two parts:
- 1.0 TLS for the Gateway
- 2.0 TLS and custom domains for your functions
1.0 TLS for the Gateway
This part guides you through setting up all the pre-requisite components to enable TLS for your gateway. You can then access your gateway via a URL such as https://gw.example.com
and each function such as: https://gw.example.com/function/nodeinfo
.
Configure Helm and Tiller
First install Helm and the Tiller following the instructions provided by Helm
Install OpenFaaS
Follow the instructions found in the OpenFaaS Helm Chart. As part of these instructions you will create a basic-auth password to secure the Gateway's API and UI.
Install nginx-ingress
This example will use a Kubernetes IngressController.
Add Nginx using the helm chart-ingress
:
- $ helm install stable/nginx-ingress --name nginxingress --set rbac.create=true
The full configuration options for nginx can be found here.
You will see a service created in the default
namespace with an EXTERNAL-IP
of Pending
, after a few moments it should reveal the public IP allocated by your cloud provider.
- $ kubectl get svc
- nginxingress-nginx-ingress-controller LoadBalancer 192.168.137.172 134.209.179.1
Caveats:
- If you do not have a cloud provider for your Kubernetes cluster, but have a public IP, then you can install Nginx in "host-mode" and use the IP of one or more of your nodes for the DNS record.
- $ helm install stable/nginx-ingress --name nginxingress --set rbac.create=true,controller.hostNetwork=true controller.daemonset.useHostPort=true,dnsPolicy=ClusterFirstWithHostNet,controller.kind=DaemonSet
Taken from tutorial: Setup a private Docker registry with TLS on Kubernetes
- If you do not have a public IP for your Kubernetes cluster, then you can use a project like Inlets and bypass using cert-manager. Inlets has around half a dozen examples of configurations for Kubernetes.
HTTPS for your local endpoints with inlets and Caddy
Create a DNS record
Determine the public IP address which can be used to connect to Nginx:
- If you are using a managed cloud provider, you will receive an IP address in
EXTERNAL-IP
- If you are using AWS EKS, Nginx will receive a DNS A record in
EXTERNAL-IP
- If you are using Host Mode for Nginx, then use the IP address of your node
For most people you can create a domain such as gw.example.com
using a DNS A record, for those using AWS EKS, you will have to create a DNS CNAME entry instead.
The required steps will vary depending on your domain provider and your cluster provider. For example; on Google Cloud DNS or with Route53 using AWS.
Once created, verify that what you entered into your DNS control-panel worked with ping
:
- ping gw.example.com
You should now see the value you entered. Sometimes DNS can take 1-5 minutes to propagate.
Install cert-manager
Following the recommended default installation for cert-manager, we install it into a new cert-manager
namespace using the following commands:
- # Install the CustomResourceDefinition resources separately
- kubectl apply --validate=false -f https://raw.githubusercontent.com/jetstack/cert-manager/release-0.11/deploy/manifests/00-crds.yaml
- # Create the namespace for cert-manager
- kubectl create namespace cert-manager
- # Add the Jetstack Helm repository
- helm repo add jetstack https://charts.jetstack.io
- # Update your local Helm chart repository cache
- helm repo update
- # Install the cert-manager Helm chart
- helm install \
- --name cert-manager \
- --namespace cert-manager \
- --version v0.11.0 \
- jetstack/cert-manager
This configuration will work for most deployments, but you can also see https://cert-manager.readthedocs.io/en/latest/getting-started/install.html#steps for additional instructions and options for installing cert-manager.
Configure cert-manager
In additional to the controller installed in the previous step, we must also configure an "Issuer" before cert-manager
can create certificates for our services. For convenience we will create an Issuer for both Let's Encrypt's production API and their staging API. The staging API has much higher rate limits. We will use it to issue a test certificate before switching over to a production certificate if everything works as expected.
Replace <your-email-here>
with the contact email that will be shown with the TLS certificate.
- # letsencrypt-issuer.yaml
- apiVersion: cert-manager.io/v1alpha2
- kind: Issuer
- metadata:
- name: letsencrypt-staging
- namespace: openfaas
- spec:
- acme:
- # You must replace this email address with your own.
- # Let's Encrypt will use this to contact you about expiring
- # certificates, and issues related to your account.
- email: <your-email-here>
- server: https://acme-staging-v02.api.letsencrypt.org/directory
- privateKeySecretRef:
- # Secret resource used to store the account's private key.
- name: example-issuer-account-key
- # Add a single challenge solver, HTTP01 using nginx
- solvers:
- - http01:
- ingress:
- class: nginx
- ---
- apiVersion: cert-manager.io/v1alpha2
- kind: Issuer
- metadata:
- name: letsencrypt-prod
- namespace: openfaas
- spec:
- acme:
- # You must replace this email address with your own.
- # Let's Encrypt will use this to contact you about expiring
- # certificates, and issues related to your account.
- email: <your-email-here>
- server: https://acme-v02.api.letsencrypt.org/directory
- privateKeySecretRef:
- # Secret resource used to store the account's private key.
- name: example-issuer-account-key
- # Add a single challenge solver, HTTP01 using nginx
- solvers:
- - http01:
- ingress:
- class: nginx
- $ kubectl apply -f letsencrypt-issuer.yaml
This will allow cert-manager
to automatically provision Certificates just in the openfaas
namespace.
Add TLS to openfaas
The OpenFaaS Helm Chart already supports the nginx-ingress, but we want to customize it further. This is easiest with a custom values file. Below, we enable and configure the ingress object to use our certificate and expose just the gateway
- # tls.yaml
- ingress:
- enabled: true
- annotations:
- kubernetes.io/ingress.class: "nginx"
- cert-manager.io/issuer: letsencrypt-staging
- tls:
- - hosts:
- - gw.example.com
- secretName: openfaas-crt
- hosts:
- - host: gw.example.com
- serviceName: gateway
- servicePort: 8080
- path: /
- $ helm upgrade openfaas \
- --namespace openfaas \
- --reuse-values \
- --values tls.yaml \
- openfaas/openfaas
Check the certificate
A certificate will be created automatically through "Ingress Shim", part of cert-manager. The Ingress Shim reads annotations to decide which certificates to provision for us.
You can validate that certificate has been obtained successfully using:
- $ kubectl describe certificate \
- -n openfaas \
- openfaas-crt
Switch over to the production issuer
If it was successful, then you can change to the production Let's Encrypt issuer.
- Replace
letsencrypt-staging
withletsencrypt-prod
intls.yaml
- Run the helm command again
- $ helm upgrade openfaas \
- --namespace openfaas \
- --reuse-values \
- --values tls.yaml \
- openfaas/openfaas
Deploy and Invoke a function
In your projects containing OpenFaaS functions, you can now deploy using your domain as the gateway, replace gw.example.com
with your domain as well as adding the username and password you created when you deployed OpenFaaS.
- faas-cli login --gateway https://gw.example.com --username <username> --password <password>
- faas-cli deploy --gateway https://gw.example.com
Verify and Debug
There are several commands we can use to verify that the required kubernetes objects have been created.
- To check that the cert-manager issuers were created:
- $ kubectl -n openfaas get issuer letsencrypt-prod letsencrypt-staging
- To check that your certificate was created and that cert-manager created the required secret with the actual TLS certificate:
- $ kubectl -n openfaas get certificate,secret openfaas-crt
- If you want to tail the Nginx logs, you can use
- $ kubectl logs -f $(kubectl get po -l "app=nginxingress,component=controller" -o jsonpath="{.items[0].metadata.name}")
2.0 TLS and custom domains for functions
This part builds on part 1.0 and now enables custom domains for any of your functions. You will need to have installed OpenFaaS and an IngressController. For TLS, which is optional you need to have cert-manager and at least one Issuer
.
For example, rather than accessing a function nodeinfo
via https://gw.example.com/function/nodeinfo
, you can now use a custom URL such as: https://nodeinfo.example.com
.
The IngressOperator introduces a new CRD (Custom Resource Definition) called FunctionIngress
. The role of FunctionIngress
is to create an Ingress
Kubernetes object to map a function to a domain-name, and optionally to also provision a TLS certificate using cert-manager.
Deploy the IngressOperator
- git clone https://github.com/openfaas-incubator/ingress-operator
- cd ingress-operator
- kubectl apply -f ./artifacts/operator-crd.yaml
- kubectl apply -f ./artifacts/operator-rbac.yaml
- kubectl apply -f ./artifacts/operator-amd64.yaml
Check that the Operator started correctly:
- kubectl get deploy/ingress-operator -n openfaas -o wide
If it's working, you will see AVAILABLE
showing 1
. Otherwise use kubectl logs
or kubectl get events
for more information.
Deploy a function
Let's deploy a function from the store:
- faas-cli store deploy nodeinfo
Now create a DNS A record in your DNS manager pointing to your IngressController's public IP.
Check the public IP with kubectl get svc/nginxingress-nginx-ingress-controller
, note down the EXTERNAL-IP
.
nodeinfo.example.com
pointing to theEXTERNAL-IP
Create a FunctionIngress Custom Resource (without TLS)
Now create a FunctionIngress
custom resource:
- apiVersion: openfaas.com/v1alpha2
- kind: FunctionIngress
- metadata:
- name: nodeinfo-tls
- namespace: openfaas
- spec:
- domain: "nodeinfo-tls.myfaas.club"
- function: "nodeinfo"
- ingressType: "nginx"
Verify that the Ingress
record was created:
- kubectl get ingress -n openfaas
Ingress records are always created in the same namespace as the OpenFaaS Gateway.
Create a FunctionIngress with TLS certificate
To enable TLS, we just need to add the tls
section and the following fields:
tls.enabled
- whether to create the certificateissuerRef.name
- as per the Issuer name created aboveissuerRef.kind
- optional: eitherIssuer
orClusterIssuer
Note: The
FunctionIngress
currently makes use of theHTTP01
challenge.
- apiVersion: openfaas.com/v1alpha2
- kind: FunctionIngress
- metadata:
- name: nodeinfo-tls
- namespace: openfaas
- spec:
- domain: "nodeinfo-tls.myfaas.club"
- function: "nodeinfo"
- ingressType: "nginx"
- tls:
- enabled: true
- issuerRef:
- name: "letsencrypt-staging"
- kind: "Issuer"
Verify that the Certificate
record was created:
- kubectl get cert -n openfaas
Use Zalando's skipper IngressController
Zalando's skipper IngressController is also supported. To switch over simply add the following to your YAML definition:
- spec:
- ingressType: "skipper"
What about IngressController X?
Feel free to raise a feature request for your IngressController on the GitHub repo.