Deploying a Secure Contact Form Application with Kubernetes and Cloudflare Tunnel
Introduction
In this comprehensive guide, we'll walk through setting up and deploying a secure, scalable contact-form backend using Python Flask and Kubernetes and exposing it securely to the Internet using Cloudflare Tunnel. By the end of this tutorial, you'll have a fully working backend service accessible globally, ideal for integrating with any front-end application.
Prerequisites
Basic knowledge of Kubernetes, Docker, and Python
Cloudflare account and a registered domain
Docker and Kubernetes installed locally or on your server
setup a SendGrid account and get the API key
Step 1: Create Your Flask Contact Form Application
Create a Flask app named app.py with email integration using SendGrid.
app.py:
from flask import Flask, request, jsonify
from flask_cors import CORS
import os
import logging
from sendgrid import SendGridAPIClient
from sendgrid.helpers.mail import Mail
app = Flask(__name__)
CORS(app)
@app.route('/contact-form', methods=['POST', 'OPTIONS'])
def contact_form():
if request.method == "OPTIONS":
response = jsonify({"message": "CORS Preflight OK"})
response.headers.add("Access-Control-Allow-Origin", "*")
response.headers.add("Access-Control-Allow-Methods", "POST, OPTIONS")
response.headers.add("Access-Control-Allow-Headers", "Content-Type, Authorization")
return response, 200
data = request.get_json()
required_fields = ['name', 'email', 'subject', 'message']
if not all(field in data for field in required_fields):
return jsonify({"error": "Missing fields"}), 400
sender_email = os.getenv("SENDER_EMAIL")
receiver_email = os.getenv("RECEIVER_EMAIL")
email_body = f"""
<p><strong>Name:</strong> {data['name']}</p>
<p><strong>Email:</strong> {data['email']}</p>
<p><strong>Subject:</strong> {data['subject']}</p>
<p><strong>Message:</strong></p><p>{data['message']}</p>
"""
sg = SendGridAPIClient(os.getenv("SENDGRID_API_KEY"))
email = Mail(from_email=sender_email, to_emails=receiver_email,
subject=data['subject'], html_content=email_body)
sg.send(email)
return jsonify({"message": "Form submitted successfully"}), 200
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)
requirements.txt:
flask
flask_cors
gunicorn
sendgrid
Step 2: Dockerizing Your Application
Create a Dockerfile in the root directory:
FROM python:3.9
WORKDIR /app
COPY . .
RUN pip install -r requirements.txt
EXPOSE 5000
CMD ["gunicorn", "-w", "2", "-b", "0.0.0.0:5000", "app:app"]
Build and push your Docker image:
docker build -t yourdockerhubusername/contact-form-app:latest .
docker push yourdockerhubusername/contact-form-app:latest
Step 3: Deploying Application to Kubernetes
Create Kubernetes Deployment and Service files:
deployment.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
name: contact-form-app
namespace: contact-form
spec:
replicas: 1
selector:
matchLabels:
app: contact-form-app
template:
metadata:
labels:
app: contact-form-app
spec:
containers:
- name: contact-form
image: yourdockerhubusername/contact-form-app:latest
ports:
- containerPort: 5000
env:
- name: SENDGRID_API_KEY
valueFrom:
secretKeyRef:
name: sendgrid-secret
key: sendgrid_api_key
- name: SENDER_EMAIL
value: "your-sender-email@example.com"
- name: RECEIVER_EMAIL
value: "your-receiver-email@example.com"
service.yaml:
apiVersion: v1
kind: Service
metadata:
name: contact-form-service
namespace: contact-form
spec:
selector:
app: contact-form-app
ports:
- protocol: TCP
port: 3344
targetPort: 5000
type: ClusterIP
Kubernetes Secret
Create Kubernetes secret for SendGrid API:
kubectl create secret generic sendgrid-secret -n contact-form --from-literal=sendgrid_api_key='your-sendgrid-api-key'
Deploy:
kubectl apply -f deployment.yaml
kubectl apply -f service.yaml
Step 4: Test Application using ClusterIP (Local Test)
Create a temporary debug pod:
kubectl run testcurl -n contact-form --image=radial/busyboxplus:curl -i --tty --rm
Inside pod run:
curl -X POST http://contact-form-service:3344/contact-form -H 'Content-Type: application/json' -d '{"name":"test","email":"test@test.com","subject":"test","message":"hello"}'
Step 5: Setting Up Cloudflare Tunnel
- Install Cloudflared:
sudo apt install cloudflared
- Authenticate Cloudflare:
cloudflared tunnel login
- Create a Tunnel:
cloudflared tunnel create contact-form-tunnel
Configure DNS via Cloudflare Dashboard:
Log into Cloudflare > select domain > DNS tab
Add a CNAME record:
Name:
contact(your subdomain)Target:
<tunnel-uuid>.cfargotunnel.com
Step 6: Cloudflare Kubernetes Deployment
cloudflared-deployment.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
name: cloudflare-tunnel
namespace: contact-form
spec:
replicas: 1
selector:
matchLabels:
app: cloudflare-tunnel
template:
metadata:
labels:
app: cloudflare-tunnel
spec:
containers:
- name: cloudflare-tunnel
image: cloudflare/cloudflared:latest
args:
- "tunnel"
- "--no-autoupdate"
- "run"
- "--token"
- "$(TUNNEL_TOKEN)"
env:
- name: TUNNEL_TOKEN
valueFrom:
secretKeyRef:
name: cloudflare-tunnel-secret
key: token
volumeMounts:
- name: config-volume
mountPath: /etc/cloudflared
volumes:
- name: config-volume
configMap:
name: cloudflare-config
cloudflared-config.yaml:
apiVersion: v1
kind: ConfigMap
metadata:
name: cloudflared-config
namespace: contact-form
data:
config.yml: |
ingress:
- hostname: contact.yourdomain.com
service: http://contact-form-service.contact-form.svc.cluster.local:3344
- service: http_status:404
Cloudflare secret:
kubectl create secret generic cloudflare-tunnel-secret --from-literal=token='your-tunnel-token' -n contact-form
Deploy Cloudflared:
kubectl apply -f cloudflared-config.yaml -f cloudflared-deployment.yaml
Step 7: Final Test
curl -X POST https://contact.yourdomain.com/contact-form -H 'Content-Type: application/json' -d '{"name":"test","email":"test@test.com","subject":"hello","message":"world"}'
Frontend Integration
Use the above URL as an API endpoint for form submissions from your front end.
Future Scope
Integrate a database to store submissions.
Set up analytics and monitoring.
Conclusion
You now have a secure, scalable contact form backend ready for production use.