To implement mutual TLS (mTLS) encryption in a Kubernetes application using Python’s FastAPI without performing TLS termination at the Ingress Controller, you’ll need to configure both the FastAPI server and clients to handle mutual authentication. This involves setting up the server to require a client certificate and the client to present a certificate that the server trusts.
Table of Contents
Overview of mTLS Implementation
- Generate TLS Certificates: Create a Certificate Authority (CA), server certificate, and client certificates.
- Configure FastAPI with mTLS: Set up the FastAPI application to require and validate client certificates.
- Deploy FastAPI with mTLS: Dockerize your application and deploy it to Kubernetes, using secrets to manage certificates.
- Set Up a Service to Expose the Application: Use a LoadBalancer or NodePort service to expose the application directly without an Ingress.
- Test the mTLS Setup: Ensure that the application properly requires client certificates and refuses connections without them.
Step-by-Step Implementation
1. Generate TLS Certificates
You’ll need to generate a CA, a server certificate, and a client certificate. Here’s how you can do it using OpenSSL:
# Create a Certificate Authority (CA)
openssl genrsa -out ca.key 4096
openssl req -x509 -new -nodes -key ca.key -sha256 -days 365 -out ca.crt -subj "/CN=my-ca"
# Generate server key and certificate signing request (CSR)
openssl genrsa -out server.key 4096
openssl req -new -key server.key -out server.csr -subj "/CN=fastapi-server"
# Sign the server CSR with the CA
openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt -days 365 -sha256
# Generate client key and certificate signing request (CSR)
openssl genrsa -out client.key 4096
openssl req -new -key client.key -out client.csr -subj "/CN=fastapi-client"
# Sign the client CSR with the CA
openssl x509 -req -in client.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out client.crt -days 365 -sha256
This will create:
ca.crt
andca.key
: The CA’s public certificate and private key.server.crt
andserver.key
: The server’s public certificate and private key.client.crt
andclient.key
: The client’s public certificate and private key.
2. Configure FastAPI with mTLS
Next, set up your FastAPI application to use mTLS by configuring Uvicorn with SSL/TLS settings.
Create a basic FastAPI application (app.py
):
from fastapi import FastAPI, Request, HTTPException
from fastapi.responses import JSONResponse
app = FastAPI()
@app.get("/")
async def root(request: Request):
return {"message": "Hello, mTLS World!"}
Configure Uvicorn to require client certificates (start.sh
):
uvicorn app:app \
--host 0.0.0.0 \
--port 443 \
--ssl-keyfile /certs/server.key \
--ssl-certfile /certs/server.crt \
--ssl-ca-certs /certs/ca.crt \
--ssl-cert-reqs=2 # Requires a client certificate
The --ssl-cert-reqs=2
option enforces that a valid client certificate must be presented for a successful connection.
3. Dockerize the FastAPI Application
Create a Dockerfile
:
FROM python:3.11-slim
WORKDIR /app
COPY . /app
RUN pip install fastapi uvicorn
# Copy your SSL certificates
COPY server.crt /certs/server.crt
COPY server.key /certs/server.key
COPY ca.crt /certs/ca.crt
# Run the start script with SSL configuration
CMD ["bash", "start.sh"]
4. Deploy FastAPI with mTLS to Kubernetes
Create Kubernetes Secrets for Certificates:
To avoid baking certificates directly into your Docker image, use Kubernetes secrets:
kubectl create secret generic fastapi-tls \
--from-file=server.crt=server.crt \
--from-file=server.key=server.key \
--from-file=ca.crt=ca.crt
Deployment YAML (fastapi-deployment.yaml
):
apiVersion: apps/v1
kind: Deployment
metadata:
name: fastapi-deployment
spec:
replicas: 2
selector:
matchLabels:
app: fastapi
template:
metadata:
labels:
app: fastapi
spec:
containers:
- name: fastapi
image: your-docker-image
ports:
- containerPort: 443
volumeMounts:
- name: tls-certs
mountPath: /certs
volumes:
- name: tls-certs
secret:
secretName: fastapi-tls
Service YAML (fastapi-service.yaml
):
apiVersion: v1
kind: Service
metadata:
name: fastapi-service
spec:
type: LoadBalancer # or NodePort if preferred
ports:
- port: 443
targetPort: 443
selector:
app: fastapi
5. Apply Deployment and Service
Deploy your application and service:
kubectl apply -f fastapi-deployment.yaml
kubectl apply -f fastapi-service.yaml
6. Test the mTLS Setup
- Client Configuration: Use
curl
or any HTTP client library to test the mTLS setup.
To test with curl
:
curl -v --key client.key --cert client.crt --cacert ca.crt https://<LoadBalancer-IP>
Ensure you replace <LoadBalancer-IP>
with the external IP of your Kubernetes service.
Benefits and Considerations
- Benefits: Full end-to-end encryption with both server and client authentication, ensuring secure communications.
- Considerations: This approach requires managing and distributing client certificates securely and handling the complexities of mTLS, such as certificate rotation and revocation.
By following these steps, you can successfully implement mTLS in a Kubernetes application using FastAPI, providing a secure communication channel that is encrypted end-to-end without relying on the Ingress Controller for TLS termination.
Please read our other blogs on Kubernetes