Day-5-Kubernetes
Securing Communication Between Users and Services with Kubernetes Ingress
Experienced DevOps Engineer with expertise in CI/CD automation, cloud infrastructure, Kubernetes, and GitOps. Provisioned a Jenkins server on AWS EC2 for automated deployments, integrating Terraform to provision VPCs and EKS clusters. Configured a Jump Server for secure Kubernetes access and implemented ArgoCD for GitOps-driven deployments. Integrated SonarQube for static code analysis and enforced quality gates in Jenkins pipelines. Built AWS ECR repositories and automated Docker image management. Ensured security by managing secrets in Jenkins Credentials Manager and implementing IAM policies for AWS resources. Configured Kubernetes Ingress via ArgoCD and deployed MongoDB with persistence strategies. Designed multi-branch Jenkins pipelines for different environments. Installed Prometheus and Grafana for monitoring with automated alerts. Optimized costs using AWS CloudWatch and Lambda for unused resource cleanup. Ensured end-to-end automation, security, and observability.
TLDR: This blog post discusses the importance of Kubernetes Ingress for managing secure communication between services in a Kubernetes environment. It covers the setup of TLS keys, the use of private Docker repositories, and the configuration of Ingress resources to streamline service communication while ensuring security.
In today's digital landscape, ensuring secure communication between users and services is paramount. This blog post delves into the concept of Kubernetes Ingress, a powerful tool that simplifies the management of service communication while enhancing security. We will explore the practical steps involved in setting up Ingress, generating TLS keys, and managing Docker images in a private repository.
Kubernetes Ingress is a resource that manages external access to services within a Kubernetes cluster. It provides a way to route traffic from outside the cluster to the appropriate services, allowing for better management of incoming requests. Ingress can also handle SSL termination, which is crucial for securing communications.
Why Use Ingress?
When deploying applications, especially those that consist of multiple services, managing communication can become complex. For instance, consider an application with three services: User Service, Order Service, and Payment Service. Without Ingress, each service would require its own load balancer and SSL configuration, leading to increased management overhead and potential security vulnerabilities.


Setting Up TLS Keys
To secure communication, we first need to generate TLS keys. This process involves creating a private key and a certificate that will be used for SSL termination. Here’s a brief overview of the steps:
Create a T2 Micro Instance: This instance will be used to generate the TLS keys.
Install Certbot: This tool will help in generating the SSL certificates.
Generate Certificates: Use Certbot to create the necessary certificates for your domain.
Configure Route 53: Update your DNS records to include the generated certificates.
Practical Steps for Generating TLS Keys
Log into your T2 Micro instance and install Certbot.
Run the command to generate the certificates, ensuring you enter your domain name correctly.
Update your Route 53 hosted zones with the generated records.
# Create a T2 Micro Instance -> generate the TLS keys
# Log into your T2 Micro instance and install Certbot.
sudo snap install --classic certbot
# <your_dns_name> --> cloudvishwakarma.in
certbot certonly --manual --preferred-challenges=dns --key-type rsa --email <your_email> \
--server https://acme-v02.api.letsencrypt.org/directory --agree-tos -d *.<your_dns_name>.
yes
# copy key value pair for creating new record:
key -> _acme-challenge
value ->
# search -> Route53 -> Hosted Zone -> <your_dns_name>
# create record -> name : value -> [_acme-challenge : ""]
# only after setting new record name and value --> after the record is in sync (view-status)
# then press enter
Certificate is saved at: /etc/letsencrypt/live/cloudvishwakarma.in/fullchain.pem
Key is saved at: /etc/letsencrypt/live/cloudvishwakarma.in/privkey.pem
# to view secrete
cat /etc/letsencrypt/live/cloudvishwakarma.in/fullchain.pem # copy it and paste it in tls.crt
cat /etc/letsencrypt/live/cloudvishwakarma.in/privkey.pem # copy it and paste it in tls.key
kubectl cluster-info # cluster is up in running
kubectl get nodes -o wide # nodes is up in running
# configure ingress controller
# One LoadBalancer will be created by the Ingress Controller.-> 1LB for distributing traffic (vote,result)
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.3.1/deploy/static/prov ider/aws/deploy.yaml
cd /tmp
nano tls.crt # copy tls.crt and paste here -> ctrl+x,y
nano tls.key # copy tls.key and paste here -> ctrl+x,y
kubectl create secret tls nginx-tls-default --key="tls.key" --cert="tls.crt"
ku get secrets
# tls.key: 1704 bytes
# tls.crt: 3050 bytes
# if bytes is not visible / 0 then something is wrong
Managing Docker Images in a Private Repository
In a production environment, it is advisable to store Docker images in a private repository rather than a public one. This enhances security by controlling access to the images used in your deployments.
Steps to Push Docker Images to a Private Repository
Create a Private Repository: On Docker Hub, create a new repository and set it to private.
Tag Your Images: Use Docker commands to tag your public images for the private repository.
Push Images: Push the tagged images to your private repository.
# create private docker repo
# pull/download public image
docker pull kiran2361993/testing:latestappresults
docker pull kiran2361993/testing:latestappvote
docker pull kiran2361993/testing:latestappworker
# tag public image to private repo
docker tag kiran2361993/testing:latestappresults kiran2361993/votingapp:results
docker tag kiran2361993/testing:latestappvote kiran2361993/votingapp:vote
docker tag kiran2361993/testing:latestappworker kiran2361993/votingapp:worker
# push private image to private repo
docker push kiran2361993/votingapp:results
docker push kiran2361993/votingapp:vote
docker push kiran2361993/votingapp:worker
# docker downloaded images
docker images
# docker remove all downloaded images
docker rmi $(docker images -aq) --force
docker system prune
# redis
---
apiVersion: v1
kind: Service
metadata:
labels:
app: redis
name: redis
spec:
clusterIP: None
ports:
- name: redis-service
port: 6379
targetPort: 6379
selector:
app: redis
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: redis
labels:
app: redis
spec:
replicas: 1
selector:
matchLabels:
app: redis
template:
metadata:
labels:
app: redis
spec:
containers:
- name: redis
image: redis:alpine
ports:
- containerPort: 6379
name: redis
# db
---
apiVersion: v1
kind: Service
metadata:
labels:
app: db
name: db
spec:
clusterIP: None
ports:
- name: db
port: 5432
targetPort: 5432
selector:
app: db
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: db
labels:
app: db
spec:
replicas: 1
selector:
matchLabels:
app: db
template:
metadata:
labels:
app: db
spec:
containers:
- name: db
image: postgres:9.4
env:
- name: PGDATA
value: /var/lib/postgresql/data/pgdata
- name: POSTGRES_USER
value: postgres
- name: POSTGRES_PASSWORD
value: postgres
ports:
- containerPort: 5432
name: db
volumeMounts:
- name: db-data
mountPath: /var/lib/postgresql/data
volumes:
- name: db-data
emptyDir: {}
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: postgres-pv-claim
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
# result
---
apiVersion: v1
kind: Service
metadata:
name: result
labels:
app: result
spec:
#type: LoadBalancer
ports:
- port: 80
targetPort: 80
name: result-service
selector:
app: result
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: result
labels:
app: result
spec:
replicas: 1
selector:
matchLabels:
app: result
template:
metadata:
labels:
app: result
spec:
containers:
- name: result
image: kiran2361993/votingapp:results
ports:
- containerPort: 80
name: result
# vote
---
apiVersion: v1
kind: Service
metadata:
name: vote
labels:
apps: vote
spec:
#type: LoadBalancer
ports:
- port: 80
targetPort: 80
name: vote-service
selector:
app: vote
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: vote
labels:
app: vote
spec:
replicas: 2
selector:
matchLabels:
app: vote
template:
metadata:
labels:
app: vote
spec:
containers:
- name: vote
image: kiran2361993/votingapp:vote
ports:
- containerPort: 80
name: vote
# worker
---
apiVersion: v1
kind: Service
metadata:
labels:
apps: worker
name: worker
spec:
clusterIP: None
selector:
app: worker
---
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: worker
name: worker
spec:
replicas: 1
selector:
matchLabels:
app: worker
template:
metadata:
labels:
app: worker
spec:
containers:
- image: kiran2361993/votingapp:worker
name: worker
nano voting.yaml # paste above code here -> crtl+x,y
kubectl apply -f voting.yaml
kubectl get pods # error in votting,result and worker -> status : ErrImagePull -> cant pull img from privite repo without secret
# click on profile -> account setting -> personal access token -> Generate token(read,write,Delete)
# copy the token and save it
# Create Docker registry secret
kubectl create secret docker-registry docker-pwd --docker-username=<your-username> --docker-password=<your-token> --docker-email=<your-email>
kubectl get secrets
# imagePullSecrets:
# - name: docker-pwd
# redis
---
apiVersion: v1
kind: Service
metadata:
labels:
app: redis
name: redis
spec:
clusterIP: None
ports:
- name: redis-service
port: 6379
targetPort: 6379
selector:
app: redis
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: redis
labels:
app: redis
spec:
replicas: 1
selector:
matchLabels:
app: redis
template:
metadata:
labels:
app: redis
spec:
containers:
- name: redis
image: redis:alpine
ports:
- containerPort: 6379
name: redis
# db
---
apiVersion: v1
kind: Service
metadata:
labels:
app: db
name: db
spec:
clusterIP: None
ports:
- name: db
port: 5432
targetPort: 5432
selector:
app: db
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: db
labels:
app: db
spec:
replicas: 1
selector:
matchLabels:
app: db
template:
metadata:
labels:
app: db
spec:
containers:
- name: db
image: postgres:9.4
env:
- name: PGDATA
value: /var/lib/postgresql/data/pgdata
- name: POSTGRES_USER
value: postgres
- name: POSTGRES_PASSWORD
value: postgres
ports:
- containerPort: 5432
name: db
volumeMounts:
- name: db-data
mountPath: /var/lib/postgresql/data
volumes:
- name: db-data
emptyDir: {}
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: postgres-pv-claim
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
# result
---
apiVersion: v1
kind: Service
metadata:
name: result
labels:
app: result
spec:
#type: LoadBalancer
ports:
- port: 80
targetPort: 80
name: result-service
selector:
app: result
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: result
labels:
app: result
spec:
replicas: 1
selector:
matchLabels:
app: result
template:
metadata:
labels:
app: result
spec:
containers:
- name: result
image: kiran2361993/votingapp:results
ports:
- containerPort: 80
name: result
imagePullSecrets:
- name: docker-pwd
# vote
---
apiVersion: v1
kind: Service
metadata:
name: vote
labels:
apps: vote
spec:
#type: LoadBalancer
ports:
- port: 80
targetPort: 80
name: vote-service
selector:
app: vote
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: vote
labels:
app: vote
spec:
replicas: 2
selector:
matchLabels:
app: vote
template:
metadata:
labels:
app: vote
spec:
containers:
- name: vote
image: kiran2361993/votingapp:vote
ports:
- containerPort: 80
name: vote
imagePullSecrets:
- name: docker-pwd
# worker
---
apiVersion: v1
kind: Service
metadata:
labels:
apps: worker
name: worker
spec:
clusterIP: None
selector:
app: worker
---
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: worker
name: worker
spec:
replicas: 1
selector:
matchLabels:
app: worker
template:
metadata:
labels:
app: worker
spec:
containers:
- image: kiran2361993/votingapp:worker
name: worker
imagePullSecrets:
- name: docker-pwd
echo "" > voting.yaml
nano voting.yaml # paste above code
kubectl apply -f voting.yaml
kubectl describe pod
kubectl get pods
ls -al
rm -rf votingapp.yaml
# 1 LB created by ingress controller -> we cant share Dns name directly so
# Route53 -> Hosted Zone -> cloudvishwakarma.in -> create record -> simple routing -> define simple record
# define simple record
record name : www.cloudvishwakarma.in
record type : A - route traffic to IPV4 add and some AWS resources
value/Route traffic to : alias - NLB
Region : us north (virginia)
select NLB (created by ingress controller):
# define simple record
record name : vote.cloudvishwakarma.in
record type : A - route traffic to IPV4 add and some AWS resources
value/Route traffic to : alias - NLB
Region : us north (virginia)
select NLB (created by ingress controller):
# define simple record
record name : result.cloudvishwakarma.in
record type : A - route traffic to IPV4 add and some AWS resources
value/Route traffic to : alias - NLB
Region : us north (virginia)
select NLB (created by ingress controller):
# create records
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: result-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec.ingressClassName: nginx
# changed - as it is depracted "kubernetes.io/ingress.class"
spec:
ingressClassName: nginx
tls:
- hosts:
- "result.cloudvishwakarma.in"
secretName: nginx-tls-default
rules:
- host: result.cloudvishwakarma.in
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: result
port:
number: 80
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: vote-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec.ingressClassName: nginx
spec:
ingressClassName: nginx
tls:
- hosts:
- "vote.cloudvishwakarma.in"
- "www.cloudvishwakarma.in"
secretName: nginx-tls-default
rules:
- host: vote.cloudvishwakarma.in
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: vote
port:
number: 80
- host: www.cloudvishwakarma.in
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: vote
port:
number: 80
---
# www.cloudvishwakarma.in , vote.cloudvishwakarma.in -> vote
# result.cloudvishwakarma.in -> result
nano ingress.yaml # paste above code
kubectl apply -f ingress.yaml
# open in browser
www.cloudvishwakarma.in -> vote app is running
vote.cloudvishwakarma.in -> vote app is running
result.cloudvishwakarma.in -> result app is running
Imp
Configuring Ingress Resources
Once the TLS keys are set up and Docker images are managed, the next step is to configure Ingress resources. This involves creating rules that dictate how incoming traffic should be routed to the various services.
Steps to Configure Ingress
Deploy Ingress Controller: This is a piece of software that manages the Ingress resources.
Create Ingress Resources: Define the routing rules in a YAML manifest file, specifying which domain names correspond to which services.
Apply the Ingress Configuration: Use Kubernetes commands to apply the Ingress configuration.
Conclusion
Kubernetes Ingress is an essential component for managing secure communication between services in a Kubernetes environment. By generating TLS keys, managing Docker images in a private repository, and configuring Ingress resources, you can significantly enhance the security and efficiency of your applications.
As you embark on your Kubernetes journey, remember that practical experience is invaluable. Engage with the tools and processes discussed in this post to solidify your understanding and skills in managing Kubernetes environments effectively.