Zero-Trust Networking for Application Developers
Zero-trust networking from an application developer's view: mutual TLS, service mesh authentication and request-level authorization for every call.

The Perimeter Is Dead
Traditional network security draws a line around your infrastructure: everything inside the perimeter is trusted, everything outside is not. This model fails when a single compromised service can move laterally through your entire network. Zero trust assumes no implicit trust—every request, from every source, must prove its identity and authorization.
Mutual TLS Between Services
In standard TLS, only the server presents a certificate. Mutual TLS (mTLS) requires both client and server to authenticate. Every service has its own certificate, and every connection verifies both ends.
import { createServer, createSecureContext } from "node:tls";
import { readFileSync } from "node:fs";
// Server setup with mTLS
const server = createServer(
{
key: readFileSync("/certs/service-a.key"),
cert: readFileSync("/certs/service-a.crt"),
ca: readFileSync("/certs/ca.crt"),
requestCert: true, // Require client certificate
rejectUnauthorized: true, // Reject if client cert is invalid
},
(socket) => {
const clientCert = socket.getPeerCertificate();
console.log(`Authenticated client: ${clientCert.subject.CN}`);
}
);
// HTTP client with mTLS
async function callService(url: string, body: unknown): Promise<Response> {
const agent = new Agent({
cert: readFileSync("/certs/service-b.crt"),
key: readFileSync("/certs/service-b.key"),
ca: readFileSync("/certs/ca.crt"),
});
return fetch(url, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(body),
// @ts-expect-error -- Node fetch supports dispatcher
dispatcher: agent,
});
}Service Identity and Authorization
mTLS proves identity. Authorization decides what that identity can do. Extract the service identity from the certificate and check it against a policy before processing the request.
// ❌ Trusting any request from the internal network
app.post("/api/internal/process-payment", async (req, res) => {
// No identity check — any service on the network can call this
await processPayment(req.body);
res.json({ success: true });
});
// ✅ Verify service identity and check authorization
interface ServicePolicy {
allowedCallers: string[];
requiredScopes?: string[];
}
const endpointPolicies: Record<string, ServicePolicy> = {
"/api/internal/process-payment": {
allowedCallers: ["checkout-service", "subscription-service"],
requiredScopes: ["payments:write"],
},
"/api/internal/get-user": {
allowedCallers: ["*"], // Any authenticated service
},
};
function authorizeService(
req: Request,
res: Response,
next: NextFunction
): void {
const clientCert = (req.socket as TLSSocket).getPeerCertificate();
if (!clientCert || !clientCert.subject) {
res.status(401).json({ error: "No client certificate" });
return;
}
const serviceId = clientCert.subject.CN;
const policy = endpointPolicies[req.path];
if (!policy) {
res.status(403).json({ error: "No policy defined for endpoint" });
return;
}
const isAllowed =
policy.allowedCallers.includes("*") ||
policy.allowedCallers.includes(serviceId);
if (!isAllowed) {
console.warn(
`Service ${serviceId} denied access to ${req.path}`
);
res.status(403).json({ error: "Service not authorized" });
return;
}
next();
}Request-Level Context Propagation
Zero trust extends beyond service identity. Each request carries context—user identity, scopes, trace IDs—that downstream services validate independently rather than trusting upstream claims blindly.
interface RequestContext {
userId: string;
roles: string[];
scopes: string[];
traceId: string;
sourceService: string;
requestTimestamp: string;
}
// Middleware: extract and verify request context
function extractRequestContext(
req: Request,
res: Response,
next: NextFunction
): void {
const contextHeader = req.headers["x-request-context"];
if (!contextHeader || typeof contextHeader !== "string") {
res.status(401).json({ error: "Missing request context" });
return;
}
try {
// Context is signed by the API gateway
const verified = verifySignedContext(contextHeader);
req.context = verified;
next();
} catch (error) {
res.status(401).json({ error: "Invalid request context signature" });
}
}
// Every service independently verifies — never trust upstream
function verifySignedContext(token: string): RequestContext {
const decoded = jwt.verify(token, process.env.CONTEXT_PUBLIC_KEY!, {
algorithms: ["ES256"],
maxAge: "5m", // Context expires quickly
});
return decoded as RequestContext;
}Network Policies in Kubernetes
Application-level zero trust works alongside network-level controls. Kubernetes NetworkPolicies restrict which pods can talk to each other, providing defense in depth.
# Only allow checkout-service to reach payment-service
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: payment-service-ingress
namespace: production
spec:
podSelector:
matchLabels:
app: payment-service
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
app: checkout-service
- podSelector:
matchLabels:
app: subscription-service
ports:
- protocol: TCP
port: 8443// Health check for zero-trust readiness
interface ZeroTrustCheck {
name: string;
check: () => Promise<boolean>;
}
const readinessChecks: ZeroTrustCheck[] = [
{
name: "mTLS certificates valid",
check: async () => {
const cert = readFileSync("/certs/service.crt", "utf8");
const parsed = new crypto.X509Certificate(cert);
return new Date(parsed.validTo) > new Date();
},
},
{
name: "Policy engine reachable",
check: async () => {
const response = await fetch("http://policy-engine:8181/health");
return response.ok;
},
},
{
name: "Context signing key loaded",
check: async () => {
return !!process.env.CONTEXT_PUBLIC_KEY;
},
},
];
async function checkZeroTrustReadiness(): Promise<{
ready: boolean;
checks: Array<{ name: string; passed: boolean }>;
}> {
const results = await Promise.all(
readinessChecks.map(async (c) => ({
name: c.name,
passed: await c.check().catch(() => false),
}))
);
return {
ready: results.every((r) => r.passed),
checks: results,
};
}Key Takeaways
Zero trust means every request proves its identity and authorization—no exceptions for "internal" traffic. Implement mTLS between services so every connection is mutually authenticated. Define explicit authorization policies per endpoint, naming which services can call which endpoints.
Propagate request context with signed, short-lived tokens that every downstream service verifies independently. Layer network policies on top of application-level controls for defense in depth. The additional complexity is real, but the alternative—a single compromised service owning your entire network—is worse. Start with mTLS and endpoint-level authorization, then add context propagation and policy engines as your service count grows.


