Skip to main content

Command Palette

Search for a command to run...

Day-5-Kubernetes

Securing Communication Between Users and Services with Kubernetes Ingress

Updated
10 min read
G

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:

  1. Create a T2 Micro Instance: This instance will be used to generate the TLS keys.

  2. Install Certbot: This tool will help in generating the SSL certificates.

  3. Generate Certificates: Use Certbot to create the necessary certificates for your domain.

  4. 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

  1. Create a Private Repository: On Docker Hub, create a new repository and set it to private.

  2. Tag Your Images: Use Docker commands to tag your public images for the private repository.

  3. 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
Ingress.service.name === Service.metadata.name # for correct route Service.spec.selector.app === Deployment.metadata.labels.app Service.spec.port.targetport ====Deployment.spec.containers.ports.containerPort

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

  1. Deploy Ingress Controller: This is a piece of software that manages the Ingress resources.

  2. Create Ingress Resources: Define the routing rules in a YAML manifest file, specifying which domain names correspond to which services.

  3. 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.