
Securing Kubernetes Ingress with Ambassador and Let's Encrypt
In addition to routing the incoming requests or exposing service API's through a single endpoint, the ingress gateways does other tasks, such as rate limiting, SSL termination, load balancing, authentication, circuit breaking and more. In this article I will show you how to install the Ambassador Gateway and other components to be able to obtain an SSL certificate for your application.
Introduction
What is SSL?
helm version to make sure Helm is installed:$ helm version
version.BuildInfo{Version:"v3.2.4", GitCommit:"0ad800ef43d3b826f31a5ad8dfbb4fe05d143688", GitTreeState:"dirty", GoVersion:"go1.14.3"}
Note
Helm is a package manager for Kubernetes. Instead of dealing with individual deployments, services, configuration maps, secrets, and other Kubernetes resources, Helm packages them into "charts". A chart is a collection of different Kubernetes resource files. You can then take thee charts and version, deploy, upgrade, and manage them as a single unit.
Deploying the sample application
apiVersion: apps/v1
kind: Deployment
metadata:
name: dogpic-web
labels:
app.kubernetes.io/name: dogpic-web
spec:
replicas: 1
selector:
matchLabels:
app.kubernetes.io/name: dogpic-web
template:
metadata:
labels:
app.kubernetes.io/name: dogpic-web
spec:
containers:
- name: dogpic-container
image: learncloudnative/dogpic-service:0.1.0
ports:
- containerPort: 3000
---
kind: Service
apiVersion: v1
metadata:
name: dogpic-service
labels:
app.kubernetes.io/name: dogpic-web
spec:
selector:
app.kubernetes.io/name: dogpic-web
ports:
- port: 3000
name: http
dogpic-app.yaml file and use kubectl apply -f dogpic-app.yaml to create the deployment and service.Deploying cert-manager
$ kubectl create ns cert-manager
namespace/cert-manager created
jetstack Helm repository and refresh the local repository cache:$ helm repo add jetstack https://charts.jetstack.io
"jetstack" has been added to your repositories
$ helm repo update
Hang tight while we grab the latest from your chart repositories...
...Successfully got an update from the "jetstack" chart repository
Update Complete. ⎈ Happy Helming!⎈
helm install \
cert-manager jetstack/cert-manager \
--namespace cert-manager \
--version v0.15.1 \
--set installCRDs=true
ClusterIssuer or an Issuer resource and configure it. This resource represents a certificate signing authority (CA) and allows cert-manager to issue certificates.ClusterIssuer and an Issuer is that the ClusterIssuer operates at the cluster level, while an Issuer resource works on a namespace. For example, you could configure different Issuer resources for each namespace. Alternatively, you could create a ClusterIssuer to issue certificates in any namespace.Challenges
HTTP-01 and DNS-01 challenge. You can read more details about each one of these on Let's Encrypt website.http://[my-cool-domain]/.well-known/acme-challenge/[token-file].ClusterIssuer we will be using. Make sure you replace the email with your email address:apiVersion: cert-manager.io/v1alpha2
kind: ClusterIssuer
metadata:
name: letsencrypt-prod
spec:
acme:
email: hello@example.com
server: https://acme-v02.api.letsencrypt.org/directory
privateKeySecretRef:
name: letsencrypt-prod
solvers:
- http01:
ingress:
class: nginx
selector: {}
kubectl describe clusterissuer and confirming that the ACME account was registered (i.e. the email address you provided):...
Status:
Acme:
Last Registered Email: hello@example.com
Uri: https://acme-v02.api.letsencrypt.org/acme/acct/89498526
Conditions:
Last Transition Time: 2020-06-22T20:36:04Z
Message: The ACME account was registered with the ACME server
Reason: ACMEAccountRegistered
Status: True
Type: Ready
Events: <none>
kubectl get clusterissuer you should see the indication that the ClusterIssuer is ready:$ kubectl get clusterissuer
NAME READY AGE
letsencrypt-prod True 2m30s
Certificate resource.Ambassador Gateway
$ kubectl apply -f https://www.getambassador.io/yaml/ambassador/ambassador-crds.yaml
...
$ kubectl apply -f https://www.getambassador.io/yaml/ambassador/ambassador-rbac.yaml
apiVersion: v1
kind: Service
metadata:
name: ambassador
spec:
type: LoadBalancer
ports:
- name: http
port: 80
targetPort: 8080
- name: https
port: 443
targetPort: 8443
selector:
service: ambassador
ambassador-svc.yaml file and run kubectl apply -f ambassador-svc.yaml.Note
Deploying the above service will create a load balancer in your cloud providers' account.
ambassador service:$ kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
ambassador LoadBalancer 10.0.78.66 51.143.120.54 80:31365/TCP 97s
ambassador-admin NodePort 10.0.65.191 <none> 8877:30189/TCP 4m20s
kubernetes ClusterIP 10.0.0.1 <none> 443/TCP 30d
http://[my-domain.com] in your browser and it will resolve to the above IP address (the ingress controller inside the cluster).startkubernetes.com. I will set up a subdomain dogs.startkubernetes.com to point to the IP address of my load balancer (e.g. 51.143.120.54) using an A record. Regardless of where you registered your domain, you should be able to update the DNS records. Check the documentation on your domain registrars website on how to do that.dogs.startkubernetes.com with your domain or subdomain name):apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: my-ingress
annotations:
kubernetes.io/ingress.class: ambassador
spec:
rules:
- host: dogs.startkubernetes.com
http:
paths:
- backend:
serviceName: dogpic-service
servicePort: 3000
http://dogs.startkubernetes.com you should see the Dog Pic website as shown below.
Requesting a certificate
Certificate resource. This resource includes the issuer reference (ClusterIssuer we created earlier), DNS names we want to request certificates for (dogs.startkubernetes.com), and the Secret name where the certificate will be stored.apiVersion: cert-manager.io/v1alpha2
kind: Certificate
metadata:
name: ambassador-certs
namespace: default
spec:
secretName: ambassador-certs
issuerRef:
name: letsencrypt-prod
kind: ClusterIssuer
dnsNames:
- dogs.startkubernetes.com
dogs.startkubernetes.com with your domain name. Once you've done that, save the YAML in cert.yaml and create the certificate using kubectl apply -f -cert.yaml.cm-acme-http-solver:$ kubectl get po
NAME READY STATUS RESTARTS AGE
ambassador-9db7b5d76-jlcdg 1/1 Running 0 22h
ambassador-9db7b5d76-qcwgk 1/1 Running 0 22h
ambassador-9db7b5d76-xsfw4 1/1 Running 0 22h
cm-acme-http-solver-qzh6l 1/1 Running 0 25m
dogpic-web-7bf547bd54-f2pff 1/1 Running 0 22h
$ kubectl logs cm-acme-http-solver-qzh6l
I0622 20:39:26.712391 1 solver.go:39] cert-manager/acmesolver "msg"="starting listener" "expected_domain"="dogs.startkubernetes.com" "expected_key"="iqUZlG9v1K8czpAKaTpLfL278piwf-mN4VZNvuwD0Ks.xonKHFvEQg2Ox_mI0cPM7UpCUHfu6H4aKtRcdrpiLik" "expected_token"="iqUZlG9v1K8czpAKaTpLfL278piwf-mN4VZNvuwD0Ks" "listen_port"=8089
Mapping resource from Ambassador. This resource defines a mapping to redirect requests with prefix ./well-known/acme-challenge to the Kubernetes service that goes to the pod.apiVersion: getambassador.io/v2
kind: Mapping
metadata:
name: challenge-mapping
spec:
prefix: /.well-known/acme-challenge/
rewrite: ''
service: challenge-service
---
apiVersion: v1
kind: Service
metadata:
name: challenge-service
spec:
ports:
- port: 80
targetPort: 8089
selector:
acme.cert-manager.io/http01-solver: 'true'
challenge.yaml and deploy it using kubectl apply -f challenge.yaml. The cert-manager will retry the challenge and issue the certificate.kubectl get cert and confirm the READY column shows True, like this:$ kubectl get cert
NAME READY SECRET AGE
ambassador-certs True ambassador-certs 35m
- Request the certificate by creating the
Certificateresource. - Cert-manager creates the
http-solverpod (exposed through thechallenge-servicewe created) - Cert-manager uses the issuer referenced in the
Certificateand requests the certificates for thednsNamesfrom the authority (Let's Encrypt) - The authority sends the challenge for the
http-solverto prove that we own the domains and checks that the challenges are solved (i.e. downloads the file from/.well-known/acme-challenge/) - Issued certificate and key are stored in the secret, referenced by the Issuer resource

Configuring TLS in Ingress
ambassador-certs secret name in the Certificate resource we created earlier.apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: my-ingress
annotations:
kubernetes.io/ingress.class: ambassador
spec:
tls:
- hosts:
- dogs.startkubernetes.com
secretName: ambassador-certs
rules:
- host: dogs.startkubernetes.com
http:
paths:
- path: /
backend:
serviceName: dogpic-service
servicePort: 3000
spec), we use the tls key to specify the hosts and the secret name where the certificate and private key are stored.ingress-tls.yaml and apply it with kubectl apply -f ingress-tls.yaml.https://dogs.startkubernetes.com) you will see that the connection is secure and it is using a valid certificate from Let's Encrypt.
Cleanup
kubectl delete cert ambassador-certs
kubectl delete secret ambassador-certs
kubectl delete -f https://www.getambassador.io/yaml/ambassador/ambassador-crds.yaml
kubectl delete -f https://www.getambassador.io/yaml/ambassador/ambassador-rbac.yaml
kubectl delete svc ambassador
helm uninstall cert-manager -n cert-manager
kubectl delete svc dogpic-service challenge-service
kubectl delete deploy dogpic-web
kubectl delete ing my-ingress





