Redes Zero Trust para desarrolladores de aplicaciones
Redes zero trust desde la óptica del desarrollador: TLS mutuo, autenticación de service mesh y autorización por solicitud, sin importar la red.

El perímetro ha muerto
La seguridad de red tradicional traza una línea alrededor de tu infraestructura: todo lo que está dentro del perímetro es confiable, todo lo que está fuera no lo es. Este modelo falla cuando un solo servicio comprometido puede moverse lateralmente por toda tu red. Zero trust no asume ninguna confianza implícita: cada solicitud, sin importar su origen, debe demostrar su identidad y autorización.
TLS mutuo entre servicios
En el TLS estándar, solo el servidor presenta un certificado. El TLS mutuo (mTLS) exige que tanto el cliente como el servidor se autentiquen. Cada servicio tiene su propio certificado, y cada conexión verifica ambos extremos.
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,
});
}Identidad de servicio y autorización
mTLS demuestra la identidad. La autorización decide qué puede hacer esa identidad. Extrae la identidad del servicio a partir del certificado y compárala con una política antes de procesar la solicitud.
// ❌ 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();
}Propagación del contexto a nivel de solicitud
Zero trust va más allá de la identidad del servicio. Cada solicitud transporta contexto (identidad del usuario, scopes, IDs de traza) que los servicios posteriores validan de forma independiente, en lugar de confiar ciegamente en las afirmaciones del servicio anterior.
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;
}Políticas de red en Kubernetes
El zero trust a nivel de aplicación funciona junto con los controles a nivel de red. Los NetworkPolicies de Kubernetes restringen qué pods pueden comunicarse entre sí, aportando una defensa en profundidad.
# 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,
};
}Conclusiones clave
Zero trust significa que cada solicitud demuestra su identidad y autorización, sin excepciones para el tráfico "interno". Implementa mTLS entre servicios para que cada conexión quede mutuamente autenticada. Define políticas de autorización explícitas por endpoint, especificando qué servicios pueden llamar a qué endpoints.
Propaga el contexto de la solicitud con tokens firmados y de corta duración que cada servicio posterior verifica de forma independiente. Superpón políticas de red sobre los controles a nivel de aplicación para lograr una defensa en profundidad. La complejidad adicional es real, pero la alternativa (un solo servicio comprometido controlando toda tu red) es peor. Empieza con mTLS y autorización a nivel de endpoint, y luego añade propagación de contexto y motores de políticas a medida que crezca el número de servicios.


