KrakenD API Gateway Security Hardening: A Comprehensive Guide
KrakenD is a high-performance, stateless API gateway designed for microservices architectures. Its declarative configuration model and extensive middleware ecosystem make it a powerful entry point for your backend services. However, as the single ingress point for all client traffic, the gateway becomes a critical security boundary — a compromise here exposes everything behind it. This tutorial walks you through practical security hardening techniques, from basic configuration hygiene to advanced threat mitigation, with complete code examples you can deploy today.
What Is KrakenD API Gateway Security Hardening?
Security hardening in KrakenD means systematically reducing the attack surface of the gateway itself and enforcing strict policies on every request that passes through it. This includes:
- Authentication & Authorization — verifying caller identity at the gateway edge
- Transport Security — enforcing TLS, HSTS, and secure cipher suites
- Rate Limiting & DoS Protection — preventing resource exhaustion attacks
- Input Validation — blocking malicious payloads before they reach backends
- Header & Response Hardening — stripping sensitive data and adding security headers
- Backend Isolation — circuit breakers, timeouts, and TLS to upstream services
- Secrets Management — keeping credentials out of configuration files
- Operational Security — logging, monitoring, and secure deployment practices
Why Security Hardening Matters
The API gateway sits at a privileged network position. Without hardening, it becomes a single point of failure and a high-value target. Attackers can exploit weak gateways to:
- Bypass authentication and access internal services directly
- Extract sensitive data through information leakage in error responses
- Execute denial-of-service attacks that cascade to all downstream systems
- Abuse excessive data exposure from unvalidated backend responses
Hardening the gateway means you stop these threats at the perimeter, protecting both your infrastructure and your users' data. It also helps meet compliance requirements like PCI-DSS, GDPR, and SOC2 by enforcing consistent security controls across all API traffic.
1. Authentication & Authorization at the Gateway Edge
🚀 Deploy your AI agent in 10 minutes
Managed Hermes hosting. Zero DevOps. 100M tokens/mo included.
Try it free →KrakenD supports multiple auth mechanisms. The best practice is to offload authentication to the gateway so backends never handle raw credentials. Here are the most common patterns.
JWT Validation with jwks-url
This configuration validates JWTs against a JWKS endpoint (like Auth0, Okta, or a custom OIDC provider). Invalid tokens are rejected before any backend receives the request.
{
"version": 3,
"name": "Secure API Gateway",
"timeout": "3000ms",
"endpoints": [
{
"endpoint": "/api/v1/{service}",
"method": "GET",
"output_mode": "noauth",
"input_headers": ["Authorization"],
"extra_config": {
"auth/validator": {
"alg": "RS256",
"jwks_url": "https://auth.example.com/.well-known/jwks.json",
"audience": ["api://example"],
"issuer": "https://auth.example.com/",
"roles": ["user", "admin"],
"roles_key": "custom:roles",
"propagate": true
}
},
"backend": [
{
"url_pattern": "/v1/{service}",
"host": ["http://backend-service:8080"],
"encoding": "noauth"
}
]
}
]
}
Key security settings in this block:
- alg — explicitly pin the signing algorithm to prevent algorithm confusion attacks. Never use
"alg": "*"in production. - audience — validate the token was issued for your API specifically
- issuer — verify the token comes from your trusted identity provider
- roles — enforce coarse-grained authorization at the gateway; requests without matching roles get a 403 immediately
- propagate: true — forward validated claims to backends so they can make fine-grained decisions without re-validating the token
API Key Validation (For Internal Services or Legacy Systems)
For machine-to-machine communication where JWT overhead is undesirable, use API keys. Store them in a backend or local registry.
{
"endpoint": "/internal/events",
"method": "POST",
"input_headers": ["X-API-Key"],
"extra_config": {
"auth/apikey": {
"keys": ["{{ .env.EVENTS_API_KEY }}"],
"header": "X-API-Key",
"cache_duration": "3600s"
}
},
"backend": [
{
"url_pattern": "/events",
"host": ["http://event-processor:8080"]
}
]
}
Notice the use of {{ .env.EVENTS_API_KEY }} — this pulls the key from environment variables at startup, never hardcoded in the config file. The cache_duration reduces lookup overhead for high-frequency endpoints.
OAuth2 Client Credentials (Introspection)
For opaque tokens, use token introspection against the authorization server. This is heavier but works with any OAuth2-compliant provider.
{
"extra_config": {
"auth/oauth2-introspection": {
"url": "https://auth.example.com/introspect",
"endpoint_params": {
"token": "{{ .token }}"
},
"endpoint_headers": {
"Authorization": ["Bearer {{ .env.INTROSPECTION_TOKEN }}"]
},
"cache_duration": "300s",
"required_scopes": ["read:events"]
}
}
}
The cache_duration is critical — without it, every request triggers an introspection call, creating a potential DoS vector against your auth server. Always cache introspection results.
2. Rate Limiting and DoS Protection
Unchecked traffic can overwhelm both the gateway and your backends. KrakenD's rate limiting works at the endpoint level and uses Redis for distributed counters in multi-instance deployments.
Endpoint-Level Rate Limiting with Redis
{
"endpoint": "/api/v1/public/{resource}",
"method": "GET",
"extra_config": {
"qos/ratelimit/router": {
"max_rate": 100,
"client_max_rate": 10,
"every": "1s",
"strategy": "redis",
"redis_host": "redis-cluster:6379",
"redis_db": 0,
"redis_prefix": "ratelimit-public",
"tokenizer": "header:X-User-Id"
}
},
"backend": [
{
"url_pattern": "/public/{resource}",
"host": ["http://content-service:8080"]
}
]
}
This configuration:
- max_rate: 100 — the endpoint handles at most 100 total requests per second across all clients
- client_max_rate: 10 — any single client (identified by X-User-Id header) gets capped at 10 req/s
- strategy: redis — counters are shared across all gateway instances, preventing split-brain rate limit bypass
- tokenizer — identifies clients by a header value rather than IP, which is more reliable behind proxies and prevents IP spoofing attacks
Bot Detection and Challenge
For endpoints prone to scraping, add a proof-of-work challenge using KrakenD's bot detection plugin:
{
"extra_config": {
"plugin/req-resp-modifier": {
"name": ["bot-detector"],
"bot-detector": {
"challenge": "proof-of-work",
"difficulty": 4,
"endpoints": ["/api/v1/public/*"],
"whitelist": ["10.0.0.0/8", "172.16.0.0/12"]
}
}
}
}
This requires clients to solve a computational puzzle before their request reaches backends, effectively rate-limiting automated scrapers without impacting legitimate users.
3. TLS Configuration and Transport Security
Enforcing HTTPS on the Gateway
KrakenD can terminate TLS natively. The minimal secure configuration looks like this:
{
"version": 3,
"name": "TLS-Secured Gateway",
"tls": {
"public_key": "/etc/krakend/certs/fullchain.pem",
"private_key": "/etc/krakend/certs/privkey.pem",
"min_version": "TLSv1.2",
"max_version": "TLSv1.3",
"cipher_suites": [
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384"
],
"prefer_server_cipher_suites": true,
"disable_ssl3": true
},
"endpoints": []
}
Security decisions here:
- TLSv1.2 minimum, TLSv1.3 preferred — eliminates all legacy protocol vulnerabilities (POODLE, BEAST, etc.)
- Explicit cipher suites — only forward-secrecy AEAD ciphers; no CBC-mode ciphers that are vulnerable to timing attacks
- prefer_server_cipher_suites: true — the server chooses the cipher, preventing client downgrade attacks
TLS to Backends (mTLS)
Don't just secure ingress — encrypt traffic to your upstream services too. This prevents lateral movement if an attacker compromises the internal network.
{
"backend": [
{
"url_pattern": "/secure-data",
"host": ["https://internal-service:8443"],
"tls": {
"ca_cert": "/etc/krakend/certs/ca-cert.pem",
"client_cert": "/etc/krakend/certs/client-cert.pem",
"client_key": "/etc/krakend/certs/client-key.pem",
"disable_system_ca": true
}
}
]
}
Setting disable_system_ca: true ensures KrakenD only trusts your private CA, not the public PKI, preventing certificate injection attacks against backend connections.
4. CORS Configuration — Restrict, Don't Relax
Overly permissive CORS settings expose your APIs to cross-origin attacks. The principle is: allow only what you need.
{
"endpoint": "/api/v1/data",
"extra_config": {
"github.com/devopsfaith/krakend-cors": {
"allow_origins": ["https://app.example.com", "https://admin.example.com"],
"allow_methods": ["GET", "POST"],
"allow_headers": ["Content-Type", "Authorization", "X-Request-ID"],
"expose_headers": ["X-Request-ID", "X-RateLimit-Remaining"],
"max_age": "3600s",
"allow_credentials": true
}
}
}
Never use "allow_origins": ["*"] in production. Wildcard origins combined with allow_credentials: true are blocked by browsers, but some implementations mishandle this — explicitly list your origins. Also, restrict allow_methods to only the HTTP verbs your API actually uses; this prevents unexpected state-changing requests via OPTIONS probes.
5. Input Validation and Header Security
Rejecting Malformed Requests
Use KrakenD's validation components to enforce strict input schemas before any backend processing occurs.
{
"endpoint": "/api/v1/users",
"method": "POST",
"extra_config": {
"validation/json-schema": {
"type": "object",
"required": ["name", "email"],
"properties": {
"name": {
"type": "string",
"minLength": 1,
"maxLength": 100
},
"email": {
"type": "string",
"format": "email",
"pattern": "^[a-zA-Z0-9._%+-]+@example\\.com$"
},
"age": {
"type": "integer",
"minimum": 0,
"maximum": 150
}
},
"additionalProperties": false
}
},
"backend": [
{
"url_pattern": "/users",
"host": ["http://user-service:8080"]
}
]
}
The "additionalProperties": false constraint is especially important — it rejects payloads with unexpected fields, blocking mass-assignment attacks where attackers inject fields like "role": "admin" that your backend might blindly accept.
Header Hardening — What You Strip and What You Add
KrakenD lets you control exactly which headers flow in each direction. Use this to prevent information leakage.
{
"endpoint": "/api/v1/secure",
"input_headers": [
"Authorization",
"Content-Type",
"X-Request-ID",
"Accept"
],
"output_headers": [
"Content-Type",
"X-Request-ID",
"X-RateLimit-Remaining"
],
"extra_config": {
"modifier/header": {
"name": "X-Content-Type-Options",
"value": "nosniff"
},
"modifier/header-2": {
"name": "X-Frame-Options",
"value": "DENY"
},
"modifier/header-3": {
"name": "Content-Security-Policy",
"value": "default-src 'none'; frame-ancestors 'none'"
},
"modifier/header-4": {
"name": "Strict-Transport-Security",
"value": "max-age=31536000; includeSubDomains; preload"
}
}
}
By whitelisting only Authorization, Content-Type, X-Request-ID, and Accept as input headers, you prevent header injection attacks and strip unnecessary metadata (like User-Agent, Referer, or custom tracking headers) that could be used for fingerprinting. On the output side, only three headers plus security headers are returned — backends that leak internal headers (like X-Powered-By, Server, or stack traces) are automatically sanitized.
6. Backend Isolation and Resilience
Circuit Breakers Prevent Cascading Failures
A compromised or overloaded backend shouldn't take down the entire gateway. Circuit breakers isolate failures:
{
"backend": [
{
"url_pattern": "/risky-third-party",
"host": ["http://external-api:9090"],
"extra_config": {
"qos/circuit-breaker": {
"interval": "60s",
"timeout": "10s",
"max_errors": 5,
"half_open_max_requests": 3,
"closed_to_half_open_after": "30s",
"open_to_half_open_after": "60s"
}
}
}
]
}
After 5 consecutive failures within 60 seconds, the circuit opens and requests fail immediately (fast-fail) without touching the backend for 60 seconds. This protects the gateway thread pool and prevents backpressure from propagating.
Strict Timeouts
Every backend must have a timeout. Without it, a slow backend can consume all gateway connections.
{
"timeout": "2000ms",
"backend": [
{
"url_pattern": "/data",
"host": ["http://slow-service:8080"],
"timeout": "1500ms",
"max_retries": 0
}
]
}
Set max_retries: 0 for idempotent-safe backends when combined with circuit breakers — retries amplify load during partial outages. If you must retry, always use exponential backoff and limit attempts.
Backend Connection Hardening
Limit how KrakenD connects to backends to prevent resource exhaustion:
{
"extra_config": {
"backend/http": {
"max_connections": 50,
"max_connections_per_host": 10,
"idle_connection_timeout": "30s",
"disable_keep_alives": false,
"connection_idle_timeout": "60s",
"dial_timeout": "3s",
"response_header_timeout": "2s"
}
}
}
These settings prevent connection leaks and limit the blast radius of any single misbehaving backend. The response_header_timeout is particularly effective against slow-loris style attacks where a backend sends headers byte-by-byte to hold connections open.
7. Secrets Management
Environment Variable Injection
KrakenD supports Go templates in configuration files. Use this to keep secrets out of your config repository:
{
"extra_config": {
"auth/apikey": {
"keys": ["{{ .env.SERVICE_API_KEY }}"],
"header": "X-API-Key"
}
},
"backend": [
{
"host": ["{{ .env.BACKEND_HOST }}"],
"url_pattern": "/internal",
"extra_config": {
"modifier/header": {
"name": "X-Service-Token",
"value": "{{ .env.BACKEND_AUTH_TOKEN }}"
}
}
}
]
}
At startup, inject these via the environment:
# Production environment
export SERVICE_API_KEY="sk-prod-9a8b7c6d5e4f3a2b1c0d"
export BACKEND_HOST="https://secure-backend.internal:8443"
export BACKEND_AUTH_TOKEN="Bearer eyJhbGciOi..."
# Then start KrakenD
krakend run -c krakend.json
Never commit actual secret values to configuration files. Use a secrets manager like HashiCorp Vault, AWS Secrets Manager, or Kubernetes Secrets with a sidecar that populates environment variables before KrakenD starts.
Partial Configuration with Secrets Templates
For complex deployments, use KrakenD's flexible configuration to split secrets into a separate file that's never checked into version control:
{
"version": 3,
"name": "Production Gateway",
"import": [
"endpoints.json",
"rate_limits.json",
"circuit_breakers.json",
"/run/secrets/krakend_secrets.json"
]
}
The /run/secrets/ path is typical in Docker/Kubernetes environments where secrets are mounted as tmpfs volumes. The secrets file contains only the sensitive portions of configuration and is mounted read-only at runtime.
8. Logging and Monitoring for Security
Audit Logging Configuration
Security-relevant events must be logged for incident response and compliance:
{
"extra_config": {
"telemetry/logging": {
"level": "INFO",
"format": "json",
"fields": {
"request_id": true,
"remote_ip": true,
"method": true,
"path": true,
"status": true,
"latency": true,
"user_agent": true
},
"filter": {
"status_codes": [401, 403, 429, 500]
}
}
}
}
JSON-formatted logs integrate with centralized logging systems (ELK, Splunk, Loki). Filtering to security-relevant status codes (401 unauthorized, 403 forbidden, 429 rate limited, 500 server errors) reduces noise while capturing all authentication failures and potential attack indicators.
Metrics for Anomaly Detection
Expose metrics that help detect attacks in real-time:
{
"extra_config": {
"telemetry/metrics": {
"collection_period": "30s",
"endpoint": "/__metrics",
"expose": {
"requests_total": true,
"requests_per_second": true,
"response_time_p99": true,
"response_time_p95": true,
"status_codes": true,
"auth_errors": true,
"rate_limit_hits": true,
"circuit_breaker_trips": true
}
}
}
}
Sudden spikes in auth_errors indicate credential stuffing. Increases in rate_limit_hits suggest automated scanning. Frequent circuit_breaker_trips may signal backend exploitation attempts. Pipe these metrics to Prometheus/Grafana with alerting thresholds.
9. Deployment Hardening
KrakenD as Non-Root User
Always run KrakenD as a non-privileged user. In Docker:
FROM devopsfaith/krakend:latest
COPY krakend.json /etc/krakend/krakend.json
COPY certs/ /etc/krakend/certs/
RUN addgroup -S krakend && adduser -S krakend -G krakend
USER krakend:krakend
EXPOSE 8080 8443
ENTRYPOINT ["krakend", "run", "-c", "/etc/krakend/krakend.json"]
In Kubernetes, add a security context:
apiVersion: v1
kind: Pod
spec:
containers:
- name: krakend
image: devopsfaith/krakend:latest
securityContext:
runAsNonRoot: true
runAsUser: 1000
readOnlyRootFilesystem: true
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
volumes:
- name: config
configMap:
name: krakend-config
- name: secrets
secret:
secretName: krakend-secrets
defaultMode: 0400
The readOnlyRootFilesystem: true and allowPrivilegeEscalation: false settings prevent attackers from writing files or escalating privileges if they somehow compromise the gateway process.
Network Segmentation
Place KrakenD in a DMZ network segment. It should be able to reach backends but backends should not be able to initiate connections to the gateway:
# Kubernetes NetworkPolicy example
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: krakend-isolation
spec:
podSelector:
matchLabels:
app: krakend
policyTypes:
- Ingress
- Egress
ingress:
- from:
- ipBlock:
cidr: 10.100.0.0/24 # External load balancer subnet only
ports:
- port: 443
protocol: TCP
egress:
- to:
- podSelector:
matchLabels:
app: backend-service
ports:
- port: 8080
protocol: TCP
This ensures KrakenD only accepts traffic from your load balancer and can only communicate with designated backend pods — lateral movement is contained.
10. Complete Security-Hardened Configuration Example
Below is a production-ready configuration combining multiple hardening techniques. Adapt it to your environment:
{
"version": 3,
"name": "Hardened API Gateway",
"timeout": "3000ms",
"tls": {
"public_key": "/etc/krakend/certs/fullchain.pem",
"private_key": "/etc/krakend/certs/privkey.pem",
"min_version": "TLSv1.2",
"max_version": "TLSv1.3",
"cipher_suites": [
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384"
],
"prefer_server_cipher_suites": true
},
"extra_config": {
"backend/http": {
"max_connections": 100,
"max_connections_per_host": 20,
"idle_connection_timeout": "30s",
"response_header_timeout": "2s",
"dial_timeout": "3s"
},
"telemetry/logging": {
"level": "INFO",
"format": "json",
"fields": {
"request_id": true,
"remote_ip": true,
"method": true,
"path": true,
"status": true,
"latency": true
}
}
},
"endpoints": [
{
"endpoint": "/api/v1/users/{id}",
"method": "GET",
"input_headers": ["Authorization", "X-Request-ID"],
"output_headers": ["Content-Type", "X-Request-ID"],
"extra_config": {
"auth/validator": {
"alg": "RS256",
"jwks_url": "{{ .env.JWKS_URL }}",
"audience": ["api://production"],
"issuer": "{{ .env.OIDC_ISSUER }}",
"roles": ["user"],
"propagate": true
},
"qos/ratelimit/router": {
"max_rate": 200,
"client_max_rate": 20,
"every": "1s",
"strategy": "redis",
"redis_host": "{{ .env.REDIS_HOST }}",
"tokenizer": "header:X-User-Id"
},
"modifier/header": {
"name": "Strict-Transport-Security",
"value": "max-age=31536000; includeSubDomains; preload"
},
"modifier/header-2": {
"name": "X-Content-Type-Options",
"value": "nosniff"
}
},
"backend": [
{
"url_pattern": "/users/{id}",
"host": ["{{ .env.USER_SERVICE_HOST }}"],
"timeout": "1500ms",
"max_retries": 0,
"extra_config": {
"qos/circuit-breaker": {
"interval": "60s",
"timeout": "10s",
"max_errors": 5
}
}
}
]
},
{
"endpoint": "/api/v1/admin/{resource}",
"method": "POST",
"input_headers": ["Authorization", "Content-Type"],
"output_headers": ["Content-Type"],
"extra_config": {
"auth/validator": {
"alg": "RS256",
"jwks_url": "{{ .env.JWKS_URL }}",
"audience": ["api://production"],
"issuer": "{{ .env.OIDC_ISSUER }}",
"roles": ["admin"],
"propagate": true
},
"validation/json-schema": {
"type": "object",
"required": ["action"],
"properties": {
"action": {
"type": "string",
"enum": ["refresh_cache", "reload_config", "health_check"]
}
},
"additionalProperties": false
}
},
"backend": [
{
"url_pattern": "/admin/{resource}",
"host": ["{{ .env.ADMIN_SERVICE_HOST }}"],
"encoding": "noauth"
}
]
}
]
}
This configuration demonstrates defense in depth: TLS termination at the gateway, JWT validation with role enforcement, rate limiting tied to user identity, input validation with strict schemas, security headers on all responses, circuit breakers on backends, and all secrets pulled from environment variables. Each layer independently contributes to security, so a failure in one doesn't compromise the entire system.
Conclusion
KrakenD API gateway security hardening isn't a single checkbox — it's a layered approach that spans authentication, transport security, input validation, rate limiting, backend isolation, secrets management, and operational practices. By implementing the techniques in this tutorial, you transform the gateway from a simple reverse proxy into a robust security enforcement point that protects your microservices ecosystem. Start with the fundamentals: TLS everywhere, JWT validation at the edge, and strict header controls. Then layer on rate limiting, input schemas, and circuit breakers as your threat model demands. Finally, ensure your deployment pipeline runs KrakenD as a non-root user on a read-only filesystem with minimal network privileges. The complete configuration example above gives you a production-ready starting point — adapt it, test it against your specific requirements, and deploy with confidence that your API gateway is genuinely hardened against real-world threats.