Installing and securing OpenFaaS on an AKS cluster
A few months back, I wrote a guide for installing and locking down OpenFaaS in a Docker Swarm running on Google Cloud Platform virtual machines. Today I want to share a step-by-step guide that shows how to install OpenFaaS on a new Azure Kubernetes Service (AKS) cluster using an Nginx ingress controller to lock it down with basic authentication and free Let's Encrypt TLS certificates.
Before we begin
If you just want to do a quick deployment of OpenFaaS on AKS, there's a guide in the official AKS documentation here, however it does not show how to implement TLS encryption or authentication.
All the Azure configuration I'll show today is done via the command line, so if you don't already have the Azure CLI installed on your local system, install it from here. You can do it through the Azure portal, but it's much faster to do with the CLI.
You will also need Git command-line tools installed so you can pull down the latest version of OpenFaaS from its repository.
Finally, in order to secure your OpenFaaS installation with TLS, you will need a domain and access to your DNS provider so you can point a hostname to your cluster's public IP address
Ready? Let's get started.
Basic Azure configuration
- From the command line, log in to Azure using
az login
. Follow the prompts to complete your authentication. - If you don't have an existing resource group you want to use for your AKS cluster, create a new one with
az group create -l REGIONNAME -n RESOURCEGROUP
. Replace REGIONNAME and RESOURCEGROUP with appropriate values, but make sure you use a region where AKS is currently available. - Create a new AKS cluster with
az aks create -g RESOURCEGROUP -n CLUSTERNAME --generate-ssh-keys
. The RESOURCEGROUP value is the same as before, and CLUSTERNAME is whatever you want it to be called. Note that the default virtual machine size for your cluster is Standard_DS1_v2. You can change this by setting the--node-vm-size
, and I am personally using burstable Standard_B2s VMs for my AKS cluster. - Once the AKS cluster creation completes, use this command to get the credentials you need to manage the cluster with the Kubernetes CLI
az aks get-credentials --resource-group RESOURCEGROUP --name CLUSTERNAME
. - Install the Kubernetes CLI (kubectl) with
az aks install-cli
. - Get the name of the node resource group that was created for your AKS cluster with this command
az resource show --resource-group RESOURCEGROUP --name CLUSTERNAME --resource-type Microsoft.ContainerService/managedClusters --query properties.nodeResourceGroup -o tsv
. You should get an output that looks like MC_resourcegroup_clustername_regionname. You will use this return value in the next step. - Create a public IP address in the node resource groupe with this command
az network public-ip create --resource-group MC_RESOURCEGROUP --name IPADDRESSNAME --allocation-method static
. You will get a JSON response that contains a "publicIp" object. Copy its "ipAddress" value and save it for later.
Note: you might be tempted to create a DNS name label for this IP address so you can avoid using a custom domain name, but *.cloudapp.azure.com host names don't work with Let's Encrypt. - Go to your DNS provider and register a new A record for your a hostname that points to the external IP you reserved in the previous step (mine is akskube.alexanderdevelopment.net). This will be the hostname you use to access OpenFaaS. You may also need to create a new CAA record to explicitly allow Let's Encrypt to issue certificates for your domain.
Basic cluster configuration
Once the basic Azure configuration work is done, it's time to configure the AKS cluster.
- If you don't already have the Helm client installed on your local system, install it by following the directions here. I am using a Windows dev workstation, so I installed Chocolatey and then installed the Helm client with
choco install kubernetes-helm
. - Install Helm components on your AKS cluster with
helm init --upgrade --service-account default
. - Install the Nginx ingress controller on your AKS cluster with
helm install stable/nginx-ingress --namespace kube-system --set rbac.create=false --set rbac.createRole=false --set rbac.createClusterRole=false --set controller.service.loadBalancerIP=STATICIPADDRESS
. Replace STATICIPADDRESS with the public IP address you created previously. - Install cert-manager to request and manage your TLS certificates
helm install --name cert-manager --namespace kube-system stable/cert-manager --set rbac.create=false
.
Installing OpenFaas
It's relatively easy to install OpenFaaS on your AKS cluster using Helm, and a detailed readme is available here. Basically you need to download the faas-netes Git repository to your local system, create a couple of namespaces on the AKS cluser and use the Helm chart in the repo you downloaded. Here's how I set it up on my AKS cluster.
kubectl create ns openfaas
kubectl create ns openfaas-fn
git clone https://github.com/openfaas/faas-netes
cd faas-netes
helm install --namespace openfaas -n openfaas --set functionNamespace=openfaas-fn --set ingress.enabled=true --set rbac=false chart/openfaas/
Once OpenFaaS is installed, you need to create ingress resources to make it available externally.
Creating the ingress resources
Before creating your ingress resources, you need to create certificate issuer resources to get TLS certificates. Here's the YAML for a Let's Encrypt staging issuer:
apiVersion: certmanager.k8s.io/v1alpha1
kind: Issuer
metadata:
name: letsencrypt-staging
spec:
acme:
# The ACME server URL
server: https://acme-staging-v02.api.letsencrypt.org/directory
# Email address used for ACME registration
email: EMAILADDRESS
# Name of a secret used to store the ACME account private key
privateKeySecretRef:
name: letsencrypt-staging
# Enable the HTTP-01 challenge provider
http01: {}
Copy it, replace EMAILADDRESS with your email address and save it as faas-staging-issuer.yml. Then run kubectl apply -f faas-staging-issuer.yml -n openfaas
.
Here's a corresponding production issuer:
apiVersion: certmanager.k8s.io/v1alpha1
kind: Issuer
metadata:
name: letsencrypt-production
spec:
acme:
# The ACME server URL
server: https://acme-v02.api.letsencrypt.org/directory
# Email address used for ACME registration
email: EMAILADDRESS
# Name of a secret used to store the ACME account private key
privateKeySecretRef:
name: letsencrypt-production
# Enable the HTTP-01 challenge provider
http01: {}
Copy it, replace EMAILADDRESS with your email address and save it as faas-production-issuer.yml. Then run kubectl apply -f faas-production-issuer.yml -n openfaas
.
Next you need to create a password file to implement basic authentication. If you are working on a system with apache2-utils installed, you can just use the htpasswd command. Otherwise, you can use a tool like http://aspirine.org/htpasswd_en.html to generate your htpasswd content. Once you have your htpasswd content generated, save it in a file named "auth" and run the following command kubectl create secret generic basic-auth --from-file=auth -n openfaas
Now you can use the following YAML to create an ingress resource that exposes your OpenFaaS instance:
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: faas-ingress
annotations:
nginx.ingress.kubernetes.io/auth-realm: "Authentication Required"
nginx.ingress.kubernetes.io/auth-secret: basic-auth
nginx.ingress.kubernetes.io/auth-type: basic
kubernetes.io/tls-acme: "true"
certmanager.k8s.io/issuer: letsencrypt-staging
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
tls:
- hosts:
- HOSTNAME
secretName: faas-letsencrypt-staging
rules:
- host: HOSTNAME
http:
paths:
- path: /faas-admin
backend:
serviceName: gateway
servicePort: 8080
Copy it, replace both instances of HOSTNAME with the hostname you created earlier and save it as faas-ingress.yml. Deploy it to your cluser with this command kubectl apply -f faas-ingress.yml -n openfaas
.
As the ingress starts up, it will request a staging certificate from Let's Encrypt, and then it will start listening for requests. It may take a few minutes, so now might be a good time to take a short break. Once everything is complete, you will be able to access your OpenFaaS UI from https://HOSTNAME/faas-admin/ui/, and once you deploy functions, they will be available at https://HOSTNAME/faas-admin/functions/FUNCTIONNAME. You should get a browser warning about the certificate because it's using a Let's Encrypt production certificate, but that's OK for now. You should also be prompted for basic authentication credentials, which will be the username and password you created earlier.
If everything looks good, you can switch over to using a production TLS certificate. Take the faas-ingress YAML and replace the "letsencrypt-staging" in the secretname and certmanager.k8s.io issuer values with "letsencrypt-production" instead. Save it and deploy the update with kubectl apply -f faas-ingress.yml -n openfaas
. Like before, the ingress will take a few minutes to restart and request a production TLS certificate from Let's Encrypt. Once that's done, you can access the your OpenFaaS UI via the same URL, but now you should not get a warning about an invalid certificate.
At this point you have a locked-down OpenFaaS installation, but you might not want to use basic authentication to restrict access to your OpenFaaS functions. If that's the case you can create another ingress resource that exposes them outside the "/faas-admin" path. Here's the YAML for that resource:
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: faas-function-ingress
annotations:
kubernetes.io/tls-acme: "true"
certmanager.k8s.io/issuer: letsencrypt-production
nginx.ingress.kubernetes.io/rewrite-target: /functions
spec:
tls:
- hosts:
- HOSTNAME
secretName: faas-letsencrypt-production
rules:
- host: HOSTNAME
http:
paths:
- path: /functions
backend:
serviceName: gateway
servicePort: 8080
Copy it, replace both instances of HOSTNAME with your DNS hostname from earlier and save it as faas-function-ingress.yml. Deploy it to your cluser with this command kubectl apply -f faas-function-ingress.yml -n openfaas
.
Once the ingress starts up and applies the TLS certificate, you will be able to access your functions at https://HOSTNAME/functions/FUNCTIONNAME without authenticating.
Wrapping up
A few closing thoughts:
- I am still extremely new to AKS and Kubernetes, and I've tried to simplify this guide as much as possible for other newbies. In figuring out how to set this up, I relied heavily on the official AKS docs, and I encourage you to take a look at them if you want to dig in deeper.
- This configuration does not expose the OpenFaaS Prometheus montitoring service. If you want to set that up, you will need to create a different DNS entry (either an A record or CNAME record) and create another ingress resource in the openfaas namespace for that host name that points to the "prometheus" service on service port 9090.
- The Nginx ingress controller configuration I showed here is extremely simple. If you want to use a more sophisticated configurations to enable advanced features like rate limiting, for example, take a look at https://github.com/kubernetes/charts/tree/master/stable/nginx-ingress#configuration and https://github.com/kubernetes/ingress-nginx/blob/master/docs/user-guide/nginx-configuration/configmap.md.