Contents
In the previous post we started to deploy a password generator application in AWS EKS. By the end of it, two problems came to light:
- There is no SSL certificate assuring that our domain is ours. It could be someone else, faking a connection to sniff data from your computer.
- We need a ✨remarkable✨ address.
We are going to start with registering an address. In order to follow, we need to buy a domain from a registrar [1]. In our example, we use Cloudflare.
Using a Custom Domain With Cloudflare
After buying a domain, click on the left side menu, select DNS then Records.

After that, we land in the DNS management page. Then, click on the blue button Add record.

Now, choose the type CNAME, set the required name, and finally paste the Load Balancer URL into the Target box. Be sure to disable the Proxy Status. In the image below, the CNAME senhas.leandroasaservice.com is set as an alias for the Load Balancer ad287… that is managed by the Nginx Ingress. Remember to get this address from the command kubectl get svc -n ingress-nginx.

In your preferred browser, type the address you chose. Here, it's senhas.leandroasaservice.com. Similar windows are expected to appear since the certificate isn't implemented yet.


After authorizing the insecure connection, we land on the application page:

Now, it's possible to proceed with the certificate implementation so that users will know the connection to the website is legitimate.
Implementing a Certificate Using Cert Manager
We are going to install the Cert Manager via Helm. To do so, run the following command:
helm install \
cert-manager jetstack/cert-manager \
--namespace cert-manager \
--create-namespace \
--version v1.16.3 \
--set crds.enabled=trueThe output must look similar to the following:
...
NAME: cert-manager
LAST DEPLOYED: ...
NAMESPACE: cert-manager
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
cert-manager v1.16.3 has been deployed successfully!
In order to begin issuing certificates, you will need to set up a ClusterIssuer
or Issuer resource (for example, by creating a 'letsencrypt-staging' issuer).
More information on the different types of issuers and how to configure them
can be found in our documentation:
https://cert-manager.io/docs/configuration/
For information on how to configure cert-manager to automatically provision
Certificates for Ingress resources, take a look at the `ingress-shim`
documentation:
https://cert-manager.io/docs/usage/ingress/Now, we are able to create a ClusterIssuer, which is a Kubernetes resource that represents a certificate authority (CA) [2]. Certificate Authorities are organizations responsible for validating identities of entities, like our application website [3]. In the app-prod-issuer.yaml file below, Cert Manager will create a public-private key pair and store it in a secret named letsencrypt-prod [4]. The public key will then be sent to the chosen Certificate Authority (Let's Encrypt), validating the domain identity [3]. Remember to replace the placeholder <your-email> with a proper e-mail.
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-prod
spec:
acme:
# The ACME server URL
server: https://acme-v02.api.letsencrypt.org/directory
# Email address used for ACME registration
email: <your-email>
# Name of a secret used to store the ACME account private key
privateKeySecretRef:
name: letsencrypt-prod
# Enable the HTTP-01 challenge provider
solvers:
- http01:
ingress:
ingressClassName: nginxAfter that, add the annotation cert-manager.io/cluster-issuer: letsencrypt-prod in the app-ingress.yaml file. Remember to replace the placeholder <your-domain> with a valid domain in your control:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: giropops-senhas
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
nginx.ingress.kubernetes.io/limit-rps: "2"
cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
ingressClassName: nginx
tls:
- hosts:
- <your-domain>
secretName: giropops-containers-secret
rules:
- host: <your-domain>
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: giropops-senhas
port:
number: 5000After that, create the ClusterIssuer and update the Ingress configuration by running the following commands:
kubectl apply -f app-prod-issuer.yaml
kubectl apply -f app-ingress.yamlIn order to check if the certificate was created, run the command kubectl get certificate. The result will look similar to the output below:
NAME READY SECRET AGE
giropops-containers-secret True giropops-containers-secret 58sYou can also check for the certificate details by running the command kubectl describe certificate giropops-containers-secret:
Name: giropops-containers-secret
Namespace: default
Labels: <none>
Annotations: <none>
API Version: cert-manager.io/v1
Kind: Certificate
Metadata:
Creation Timestamp: ...
Generation: 1
Owner References:
API Version: networking.k8s.io/v1
Block Owner Deletion: true
Controller: true
Kind: Ingress
Name: giropops-senhas
UID: <uid>
Resource Version: 15256
UID: <uid>
Spec:
Dns Names:
<your domain>
Issuer Ref:
Group: cert-manager.io
Kind: ClusterIssuer
Name: letsencrypt-prod
Secret Name: giropops-containers-secret
Usages:
digital signature
key encipherment
Status:
Conditions:
Last Transition Time: ...
Message: Certificate is up to date and has not expired
Observed Generation: 1
Reason: Ready
Status: True
Type: Ready
Not After: ...
Not Before: ...
Renewal Time: ...
Revision: 1
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Issuing 103s cert-manager-certificates-trigger Issuing certificate as Secret does not exist
Normal Generated 102s cert-manager-certificates-key-manager Stored new private key in temporary Secret resource "giropops-containers-secret-n6w9w"
Normal Requested 102s cert-manager-certificates-request-manager Created new CertificateRequest resource "giropops-containers-secret-1"
Normal Issuing 80s cert-manager-certificates-issuing The certificate has been successfully issuedAfter that, refresh the page and click on the lock icon in your browser. Doing this, you will be able to see the certificate details:

Now, we can start creating passwords:

Enabling Session Stickiness
Keep testing and you are going to see a very weird behavior. At some point, the application "forgets" the generated passwords. In the image below, the passwords previously created don't appear.

If we keep creating new passwords, at some point we will land in the screen that contains the first password.

This occurs because we are using different pods each time we use the application. In order to avoid this behavior, we enable session stickiness using cookies or upstream hashing. Both mechanisms enable websites to recognize where the request comes from and direct it to the same server (or pod) in order to avert the very same behavior we have seen. When using cookies, a text file is stored in the client browser to identify where the request comes from [5] and direct it to the appropriate pod. On the other hand, when using upstream hashing, we use the request URI and create a hash without storing anything in the client browser. Then, requests that have the same hash will be directed to the same pod.
To enable stickiness we use the annotation nginx.ingress.kubernetes.io/affinity in the app-ingress.yaml file [6, day 10]. In the example below, we are using a cookie named kellercookie.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: giropops-senhas
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
nginx.ingress.kubernetes.io/limit-rps: "2"
cert-manager.io/cluster-issuer: letsencrypt-prod
nginx.ingress.kubernetes.io/affinity: cookie
nginx.ingress.kubernetes.io/session-cookie-name: kellercookie
spec:
ingressClassName: nginx
tls:
- hosts:
- <your-domain>
secretName: giropops-containers-secret
rules:
- host: <your-domain>
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: giropops-senhas
port:
number: 5000It's possible to see the cookie in the developer tools in your browser.

To see the cookie in action, keep creating new passwords. It's expected to land in the same page always. It's possible to check that by comparing the passwords being created.
Now, to enable upstream hashing, we use the annotation nginx.ingress.kubernetes.io/upstream-hash-by [6, day 10]. The content for the file app-ingress.yaml that enables this feature is presented below:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: giropops-senhas
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
nginx.ingress.kubernetes.io/limit-rps: "2"
cert-manager.io/cluster-issuer: letsencrypt-prod #comment: only for certificate
#nginx.ingress.kubernetes.io/affinity: cookie
#nginx.ingress.kubernetes.io/session-cookie-name: kellercookie
nginx.ingress.kubernetes.io/upstream-hash-by: $request_uri
spec:
ingressClassName: nginx
tls:
- hosts:
- <your-domain>
secretName: giropops-containers-secret
rules:
- host: <your-domain>
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: giropops-senhas
port:
number: 5000In your browser, delete the cookie and refresh the page:

Now, try creating new passwords. It's expected to land in the same page always, but now without cookies. It's possible to check that by comparing the passwords being created.
References
- Cloudflare, What is a domain name registrar?, Cloudflare Learning Center.
- The cert-manager Authors, Issuer, cert-manager documentation.
- Let's Encrypt, Let's Encrypt - How it works?, letsencrypt.org.
- The cert-manager Authors, ACME Issuer Type, cert-manager documentation.
- All About Cookies, What are cookies and how they are used?, allaboutcookies.org.
- Jeferson Fernando, Descomplicando Kubernetes (in Portuguese), LINUXtips.