03. Network Security with Cilium
1. The Concept (ELI5)
Think of a traditional network firewall as a toll booth on a highway. Every car (network packet) has to stop, show its ID (IP address), and wait to be processed. As traffic increases, the toll booth becomes a massive bottleneck.
Cilium is an advanced network plugin for Kubernetes. Instead of using a traditional toll booth (iptables), Cilium uses eBPF to implant instructions directly into the highway itself (the Linux kernel's networking stack). Because the kernel processes the rules at the exact moment a packet is created or received, it is incredibly fast. Furthermore, Cilium understands Kubernetes identities (like labels and namespaces) and even Layer 7 protocols (like HTTP and gRPC), allowing it to act as an incredibly intelligent, high-speed bouncer.
2. The Visual
3. The Code
Server-Side Request Forgery (SSRF) allows an attacker to force a server to make network requests on their behalf. Even if SSRF exists in the code, a strong network policy (via Cilium) can prevent the pod from reaching out to internal metadata services (like 169.254.169.254 on AWS) or external malicious domains.
Go
❌ Vulnerable Code (SSRF)
package main
import (
"io/ioutil"
"net/http"
)
func fetchURL(w http.ResponseWriter, r *http.Request) {
url := r.URL.Query().Get("url")
// Blindly fetches whatever URL the user provides
resp, _ := http.Get(url)
defer resp.Body.Close()
body, _ := ioutil.ReadAll(resp.Body)
w.Write(body)
}
✅ Secure Code (Domain Allowlisting)
package main
import (
"net/http"
"net/url"
)
func fetchURL(w http.ResponseWriter, r *http.Request) {
targetURL := r.URL.Query().Get("url")
parsed, err := url.Parse(targetURL)
// Restrict fetching to a specific, trusted domain
if err != nil || parsed.Host != "api.trusted-partner.com" {
http.Error(w, "Invalid domain", 403)
return
}
resp, _ := http.Get(targetURL)
// ... process response ...
}
Python
❌ Vulnerable Code
import requests
from flask import Flask, request
app = Flask(__name__)
@app.route('/proxy')
def proxy():
target = request.args.get('url')
# SSRF vulnerability
return requests.get(target).content
✅ Secure Code
import requests
from urllib.parse import urlparse
from flask import Flask, request, abort
app = Flask(__name__)
ALLOWED_DOMAIN = "api.internal.service"
@app.route('/proxy')
def proxy():
target = request.args.get('url')
domain = urlparse(target).hostname
if domain != ALLOWED_DOMAIN:
abort(403)
return requests.get(target).content
TypeScript / Node.js
❌ Vulnerable Code
import express from 'express';
import axios from 'axios';
const app = express();
app.get('/fetch', async (req, res) => {
const target = req.query.url as string;
// SSRF
const response = await axios.get(target);
res.send(response.data);
});
✅ Secure Code
import express from 'express';
import axios from 'axios';
const app = express();
app.get('/fetch', async (req, res) => {
const target = req.query.url as string;
const url = new URL(target);
if (url.hostname !== 'trusted-api.com') {
return res.status(403).send('Untrusted domain');
}
const response = await axios.get(target);
res.send(response.data);
});
4. The Guardrail
Regardless of the application code, we use a CiliumNetworkPolicy to enforce strict egress rules at the network layer, preventing SSRF payloads from ever leaving the node.
CiliumNetworkPolicy (YAML): Block all egress except DNS and specific trusted domain
apiVersion: "cilium.io/v2"
kind: CiliumNetworkPolicy
metadata:
name: "restrict-frontend-egress"
spec:
endpointSelector:
matchLabels:
app: frontend
egress:
- toEndpoints:
- matchLabels:
"k8s:io.kubernetes.pod.namespace": "kube-system"
"k8s:k8s-app": "kube-dns"
toPorts:
- ports:
- port: "53"
protocol: ANY
rules:
dns:
- matchPattern: "*"
- toFQDNs:
- matchName: "api.trusted-partner.com"
toPorts:
- ports:
- port: "443"
protocol: TCP