Introduction to Kong API Gateway Security Hardening
Kong API Gateway is a high-performance, extensible gateway that sits at the edge of your microservice infrastructure, handling cross-cutting concerns like authentication, rate limiting, and logging. Out of the box, Kong ships with sensible defaults, but in production environments, those defaults are rarely sufficient. Security hardening is the process of systematically locking down Kong's configuration, plugins, admin interface, and underlying infrastructure to minimize the attack surface and protect your backend services from malicious actors.
This tutorial walks you through a complete security hardening checklist for Kong Gateway, covering every layer from the Admin API to plugin-specific configurations, with practical code examples you can apply directly to your deployment.
Why Kong Security Hardening Matters
🚀 Deploy your AI agent in 10 minutes
Managed Hermes hosting. Zero DevOps. 100M tokens/mo included.
Try it free →An unsecured API gateway is a single point of failure that can expose all your backend services. Consider these real-world risks:
- Admin API exposure: Kong's Admin API (default port 8001) can create, read, update, and delete all gateway configuration—including routes, services, and consumers. If left unprotected, an attacker can reconfigure your entire gateway.
- Credential leakage: Kong stores consumer credentials and plugin configurations in its database. Weak database access controls can lead to mass credential exposure.
- DDoS amplification: Without rate limiting, an attacker can flood your gateway with requests that cascade to all backend services, causing infrastructure-wide outages.
- Man-in-the-middle attacks: Unencrypted traffic between clients and Kong, or between Kong and upstreams, can be intercepted and tampered with.
- Plugin vulnerabilities: Third-party plugins or misconfigured built-in plugins can introduce injection vectors, bypass authentication, or leak sensitive data in error responses.
Hardening Kong is not optional—it's a fundamental requirement for any production deployment handling sensitive data or business-critical traffic.
1. Securing the Admin API
1.1 Disable the Admin API on Public Interfaces
The most critical hardening step is ensuring Kong's Admin API is never exposed to the public internet or even your internal corporate network without strict access controls. In Kong's configuration file (kong.conf), bind the admin listener exclusively to localhost:
# kong.conf — Admin API hardening
admin_listen = 127.0.0.1:8001
# Optionally, use a Unix socket instead of a TCP port for maximum isolation
# admin_listen = unix:/var/run/kong-admin.sock
# Disable the Admin API GUI (Kong Manager) if not needed
admin_gui = off
# Disable the Admin API development sandbox endpoints
admin_gui_auth_conf = {}
admin_gui_auth_header = off
If you must expose the Admin API remotely, front it with a tightly restricted reverse proxy (like Nginx) that enforces mutual TLS, or use Kong's built-in loopback-only restriction combined with SSH tunneling for administrative access.
1.2 Enforce Authentication on the Admin API
Kong supports multiple authentication methods for the Admin API. The recommended approach for production is to use RBAC (Role-Based Access Control) with a strong authentication backend:
# kong.conf — Admin API authentication
admin_gui_auth = basic-auth
# or for LDAP-backed authentication:
# admin_gui_auth = ldap-auth
# If using Kong Manager with RBAC (Enterprise feature)
enforce_rbac = on
admin_gui_auth_conf = {
"session_secret": "generate-a-cryptographically-random-64-char-string-here",
"session_cookie_name": "kong_admin_session",
"session_cookie_secure": true,
"session_cookie_httponly": true,
"session_cookie_samesite": "Strict"
}
For the Admin API itself (not just the GUI), add client certificate validation or a shared secret header that only your automation tools know:
# Require a secret header on every Admin API call
# This is enforced at the reverse proxy layer in front of Kong
# Example Nginx configuration snippet:
location /admin/ {
proxy_pass http://127.0.0.1:8001/;
proxy_set_header Host $host;
# Require a pre-shared secret header
set $admin_secret "$http_x_admin_secret";
if ($admin_secret != "your-256-bit-random-secret") {
return 403;
}
# Require mutual TLS
ssl_verify_client on;
ssl_client_certificate /etc/ssl/certs/admin_ca.crt;
}
1.3 Disable Unnecessary Admin API Routes
Not every environment needs all Admin API endpoints. Use a firewall or reverse proxy to block routes you never use, such as the /tags endpoint or the developer playground:
# Block unnecessary Admin API routes at the proxy layer
location ~ ^/admin/(tags|debug|dev) {
return 404;
}
2. Hardening Kong Plugins
2.1 Rate Limiting for DDoS Protection
Rate limiting is your first line of defense against volumetric attacks. Configure the rate-limiting or rate-limiting-advanced plugin globally, then fine-tune per-route:
# Apply a global rate limit via Kong's declarative configuration (kong.yml)
# This example uses the rate-limiting-advanced plugin for more granular control
_format_version: "3.0"
services:
- name: public-api
url: http://backend-service:8080
routes:
- name: public-route
paths:
- /api/public
plugins:
- name: rate-limiting-advanced
config:
# Limit to 100 requests per second per consumer
limit:
- second=100
# Burst size of 20 requests
window_size:
- 10
# Return 429 with a Retry-After header
retry_after_jitter_max: 10
# Use Redis for synchronized counters across nodes
strategy: redis
redis:
host: redis-cluster.internal
port: 6379
database: 0
password: "${REDIS_PASSWORD}"
ssl: true
server_name: "redis.internal"
For critical endpoints like authentication or payment processing, apply stricter, per-route limits:
# Per-route strict rate limiting for sensitive endpoints
routes:
- name: auth-route
paths:
- /api/auth
plugins:
- name: rate-limiting-advanced
config:
limit:
- minute=10
- hour=50
# Sync counters across all Kong nodes via Redis
strategy: redis
# Hide rate limit headers to avoid leaking limits to attackers
hide_client_headers: true
2.2 Authentication Plugins: Key-Auth, OIDC, and JWT
Never leave routes unprotected. Kong supports multiple authentication mechanisms. Here's how to harden each one:
# Key Authentication — simple but requires careful key management
plugins:
- name: key-auth
config:
# Hide credentials from upstream services
hide_credentials: true
# Require keys to be sent in headers, not query strings
key_in_header: true
key_in_query: false
key_in_body: false
# Custom header name to avoid "Authorization" header conflicts
key_names:
- X-API-Key
# Run authentication before other plugins
run_before: []
# JWT (JSON Web Token) Authentication hardening
plugins:
- name: jwt
config:
# Require tokens in Authorization header only
header_names:
- Authorization
# Reject tokens passed in query parameters (log leakage risk)
cookie_names: []
# Validate the issuer claim
claims_to_verify:
- iss
- aud
# Enforce algorithm constraints — never allow 'none'
algorithms:
- RS256
- ES256
# Maximum token lifetime override
maximum_expiration: 3600
# Hide credentials from upstream
hide_credentials: true
# URI sources disabled to prevent token leakage in logs
uri_param_names: []
# OpenID Connect (OIDC) hardening with enterprise plugin
plugins:
- name: openid-connect
config:
# Require PKCE (Proof Key for Code Exchange) for public clients
proof_of_possession: true
# Validate the nonce to prevent replay attacks
verify_nonce: true
# Use state parameter to prevent CSRF
use_state: true
# Redirect only to whitelisted URIs after logout
logout_redirect_uri: "https://app.example.com/post-logout"
# Enforce HTTPS for all redirect URIs
enforce_https: true
# Reject tokens without a valid 'aud' claim matching this gateway
audience_claim: "kong-gateway"
# Scopes must be explicitly whitelisted
scopes:
- openid
- profile
# Session cookie hardening
session_cookie_httponly: true
session_cookie_secure: true
session_cookie_samesite: "Strict"
2.3 CORS Hardening
The CORS plugin is frequently misconfigured. Never use origins: * in production. Always whitelist specific origins:
plugins:
- name: cors
config:
# Whitelist specific origins — NEVER use "*"
origins:
- https://app.example.com
- https://admin.example.com
# Only allow necessary methods
methods:
- GET
- POST
- PUT
# Restrict headers to those your app actually uses
headers:
- Content-Type
- Authorization
- X-Request-ID
# Expose only safe headers to client-side JavaScript
exposed_headers:
- X-Request-ID
- X-RateLimit-Remaining
# Credentials support
credentials: true
# Cache preflight results for a reasonable window
max_age: 3600
# Preflight continues only if all conditions pass
preflight_continue: false
2.4 IP Restriction for Internal Endpoints
Some routes should only be accessible from internal networks or specific IP ranges. Use the ip-restriction plugin:
# Restrict admin-oriented endpoints to internal IPs only
routes:
- name: internal-metrics
paths:
- /internal/metrics
plugins:
- name: ip-restriction
config:
# Allow only internal networks
allow:
- 10.0.0.0/8
- 172.16.0.0/12
- 192.168.0.0/16
# Explicitly deny everything else
deny: []
# Return 403 with no additional info
status: 403
message: "Access denied"
2.5 Bot Detection and Advanced Threat Protection
Kong's enterprise Bot Detection plugin can identify and block automated scrapers, credential-stuffing bots, and vulnerability scanners:
plugins:
- name: bot-detection
config:
# Allow known good bots (search engines)
allow:
- "Googlebot"
- "Bingbot"
# Block based on regex patterns in User-Agent
deny:
- ".*scanner.*"
- ".*sqlmap.*"
- ".*nikto.*"
# Enable machine-learning-based detection (requires Kong Brain)
use_ml: true
3. TLS/SSL Configuration
3.1 Terminate TLS at Kong
Kong should terminate TLS at the proxy level. Use strong cipher suites and modern TLS versions only:
# kong.conf — TLS hardening
proxy_listen = 0.0.0.0:443 ssl
proxy_listen = 0.0.0.0:80 # Only if you need HTTP-to-HTTPS redirect
# TLS certificate and key paths
ssl_cert = /etc/kong/ssl/kong-cert.pem
ssl_cert_key = /etc/kong/ssl/kong-key.pem
# Enforce TLS 1.2 minimum (disable TLS 1.0, 1.1)
ssl_protocols = TLSv1.2 TLSv1.3
# Strong cipher suites — no weak ciphers, no export-grade ciphers
ssl_ciphers = ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256
# Prefer server cipher suite order over client's
ssl_prefer_server_ciphers = on
# Enable session tickets for performance, but rotate keys regularly
ssl_session_tickets = on
ssl_session_timeout = 1d
3.2 Mutual TLS (mTLS) for Service-to-Service Communication
For zero-trust architectures, enforce mTLS between Kong and upstream services:
# Upstream mTLS configuration in service definition
services:
- name: secure-upstream
url: https://upstream.internal:8443
client_certificate:
# The certificate Kong presents to the upstream
id: "certificate-uuid-here"
routes:
- name: secure-route
paths:
- /secure
# Create the client certificate via Admin API
# curl -X POST http://localhost:8001/certificates \
# -d 'cert=@client-cert.pem' \
# -d 'key=@client-key.pem' \
# -d 'snis=upstream.internal'
3.3 HTTP Strict Transport Security (HSTS)
Add the HSTS response header to force browsers to always use HTTPS:
# Using the response-transformer plugin to inject HSTS header
plugins:
- name: response-transformer
config:
add:
headers:
- "Strict-Transport-Security:max-age=31536000; includeSubDomains; preload"
- "X-Content-Type-Options:nosniff"
- "X-Frame-Options:DENY"
- "X-XSS-Protection:1; mode=block"
- "Referrer-Policy:strict-origin-when-cross-origin"
- "Permissions-Policy:geolocation=(), microphone=(), camera=()"
4. Database Security
Kong stores its configuration in PostgreSQL (or Cassandra for older deployments). The database connection must be hardened:
# kong.conf — Database connection hardening
pg_host = 127.0.0.1 # Bind to localhost if DB is co-located
pg_port = 5432
pg_user = kong_readonly # Use least-privilege principle
pg_password = "${DB_PASSWORD}" # Use environment variables, never hardcode
pg_database = kong
pg_ssl = on # Always encrypt database connections
pg_ssl_verify = on # Verify the database server certificate
pg_max_concurrent_queries = 50 # Prevent connection exhaustion
Never expose the PostgreSQL port to the public internet. Use cloud-native secrets management (AWS Secrets Manager, HashiCorp Vault) to inject DB_PASSWORD at runtime rather than storing it in configuration files.
5. Logging and Monitoring for Security Events
5.1 Enable Audit Logging
Kong Enterprise includes an audit logging plugin that records all Admin API activity. In the open-source version, you can achieve similar results with the file-log or http-log plugins:
# Log all requests with security-relevant metadata
plugins:
- name: file-log
config:
path: /var/log/kong/security_audit.log
# Log only requests that are blocked or have authentication failures
# This is done via a custom logging criteria in the plugin
custom_fields:
- name: security_event
value: true
- name: client_ip
value_from: "$remote_addr"
- name: user_agent
value_from: "$http_user_agent"
- name: authenticated_user
value_from: "$authenticated_consumer_id"
# Ship security logs to a centralized SIEM via HTTP
plugins:
- name: http-log
config:
endpoint: https://siem.internal.example.com/ingest
method: POST
timeout: 3000
headers:
- "Authorization:Bearer ${SIEM_INGEST_TOKEN}"
- "Content-Type:application/json"
# Include all security-relevant fields
custom_fields:
- name: kong_route
value_from: "$route_name"
- name: kong_service
value_from: "$service_name"
- name: status_code
value_from: "$status"
- name: latency_ms
value_from: "$latency"
5.2 Alert on Anomalous Patterns
Use Kong's Prometheus plugin to export metrics, then configure alerts for security anomalies:
# kong.conf — Enable Prometheus metrics
prometheus = on
prometheus_metrics = all
Then in your Prometheus alerting rules:
# Prometheus alert rules for Kong security anomalies
groups:
- name: kong_security
rules:
- alert: KongHighRateLimitHits
expr: rate(kong_http_requests_total{status="429"}[5m]) > 10
for: 2m
labels:
severity: warning
annotations:
summary: "High rate of rate-limited requests on {{ $labels.route }}"
- alert: KongAuthFailures
expr: rate(kong_http_requests_total{status="401"}[5m]) > 5
for: 2m
labels:
severity: critical
annotations:
summary: "Elevated authentication failures — possible credential stuffing attack"
6. Network and Infrastructure Hardening
6.1 Run Kong as a Non-Root User
By default, Kong may run as the nobody user, but you should explicitly set a dedicated, unprivileged user:
# kong.conf — Run as unprivileged user
kong_env:
user: kong
group: kong
# In your Dockerfile or systemd service file:
# USER kong
# Or in systemd:
# [Service]
# User=kong
# Group=kong
# NoNewPrivileges=yes
# CapabilityBoundingSet=CAP_NET_BIND_SERVICE
# ProtectSystem=strict
# ProtectHome=yes
6.2 Enable Linux Kernel Hardening Features
If running Kong directly on Linux (not in containers), leverage kernel security features:
# Systemd service unit hardening for Kong
[Service]
# Restrict capabilities to only what's needed for binding to port 8000/8443
CapabilityBoundingSet=CAP_NET_BIND_SERVICE
AmbientCapabilities=CAP_NET_BIND_SERVICE
# Prevent privilege escalation
NoNewPrivileges=yes
# Mount /usr, /etc read-only after startup
ReadOnlyPaths=/usr /etc/kong
# Restrict write access to log and PID directories only
ReadWritePaths=/var/log/kong /var/run/kong /tmp
# Private /tmp directory
PrivateTmp=yes
# Protect kernel tunables
ProtectKernelTunables=yes
ProtectKernelModules=yes
# Restrict system calls with a seccomp filter
SystemCallFilter=~@clock @debug @module @reboot @swap
# Disable network namespace access
RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX AF_NETLINK
6.3 Container-Specific Hardening (Docker/Kubernetes)
When running Kong in containers, apply these security contexts:
# Kubernetes Pod Security Context for Kong
apiVersion: v1
kind: Pod
spec:
securityContext:
runAsNonRoot: true
runAsUser: 1000
runAsGroup: 1000
fsGroup: 1000
seccompProfile:
type: RuntimeDefault
containers:
- name: kong
image: kong:3.6
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop:
- ALL
add:
- NET_BIND_SERVICE
# volumes for writable paths
volumeMounts:
- name: tmp
mountPath: /tmp
- name: logs
mountPath: /var/log/kong
- name: runtime
mountPath: /var/run/kong
volumes:
- name: tmp
emptyDir: {}
- name: logs
emptyDir: {}
- name: runtime
emptyDir: {}
7. Secrets Management and Configuration Hardening
7.1 Never Hardcode Secrets in Configuration
Kong supports environment variable interpolation in its configuration files. Always inject secrets at runtime:
# kong.conf — Use environment variables for all secrets
pg_password = ${KONG_PG_PASSWORD}
admin_gui_auth_conf = {
"session_secret": "${KONG_ADMIN_SESSION_SECRET}"
}
# Declarative config also supports env var interpolation
# kong.yml
services:
- name: external-api
url: https://third-party-api.example.com
plugins:
- name: aws-lambda
config:
aws_key: "${AWS_ACCESS_KEY_ID}"
aws_secret: "${AWS_SECRET_ACCESS_KEY}"
# NEVER put actual keys here
Inject these environment variables from a secrets manager at container startup:
# Docker run with secrets injection
docker run -d \
--name kong \
-e KONG_PG_PASSWORD=$(aws secretsmanager get-secret-value --secret-id kong/db/password --query SecretString --output text) \
-e KONG_ADMIN_SESSION_SECRET=$(openssl rand -hex 32) \
kong:latest
7.2 Rotate Secrets Regularly
Implement automated rotation for:
- Admin API session secrets
- Database credentials
- TLS certificates (use cert-manager or AWS ACM)
- Consumer API keys (enforce expiry via Kong's key-auth plugin)
- JWT signing keys
# Enforce API key expiry in key-auth plugin
plugins:
- name: key-auth
config:
hide_credentials: true
key_in_header: true
# Keys expire after 90 days
key_expiration: 7776000
# Require keys to be at least 32 characters
key_min_length: 32
8. Hardening Declarative Configuration and GitOps
When using Kong's declarative configuration (kong.yml) in a GitOps workflow, protect the configuration pipeline:
# .github/workflows/kong-security-scan.yml
# Automated security scanning of Kong configuration before deployment
name: Kong Config Security Scan
on:
pull_request:
paths:
- 'kong.yml'
jobs:
scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Validate Kong Config
run: |
# Check for dangerous patterns in kong.yml
! grep -q 'origins: \*' kong.yml && echo "CORS OK" || exit 1
! grep -q 'algorithms:.*none' kong.yml && echo "JWT algorithms OK" || exit 1
! grep -q 'key_in_query: true' kong.yml && echo "Key in query disabled OK" || exit 1
- name: Kong Ingress Controller Validation
run: |
# Dry-run the configuration through Kong
kong check config kong.yml
9. Complete Hardening Checklist
Use this checklist as a pre-deployment verification for every Kong instance:
- Admin API: Bound to localhost or protected by mTLS + RBAC authentication
- Admin GUI: Disabled if not needed; otherwise, secured with strong session cookies
- Database: TLS-encrypted connection, least-privilege user, password in secrets manager
- TLS: Minimum TLS 1.2, strong cipher suites, HSTS header injected
- Authentication: Every public route has an authentication plugin; no query-string tokens
- CORS: Specific origins whitelisted, never wildcard
- Rate Limiting: Applied globally and strictly on sensitive endpoints
- IP Restriction: Internal/admin routes restricted to internal networks
- Logging: Security events shipped to SIEM; audit logs retained
- Container/OS: Non-root user, read-only filesystem, dropped capabilities
- Secrets: Zero hardcoded secrets; all injected via environment variables from a vault
- Plugins: Unnecessary plugins disabled; all plugin configurations reviewed for security
- Monitoring: Prometheus metrics exported; alerts configured for 401/429/5xx spikes
- Headers: Security headers (HSTS, X-Frame-Options, X-Content-Type-Options) injected on all responses
10. Testing Your Hardened Configuration
After applying hardening measures, validate your configuration with penetration testing:
# Basic security validation script for Kong
#!/bin/bash
# Test 1: Admin API should be unreachable from external network
curl -v --max-time 3 http://public-ip:8001/status && echo "FAIL: Admin API exposed" || echo "PASS: Admin API protected"
# Test 2: Unauthenticated requests should receive 401
curl -v --max-time 3 http://public-ip:8000/api/protected && echo "FAIL: Route not authenticated" || echo "PASS: Authentication enforced"
# Test 3: CORS should not echo wildcard origin
curl -v -H "Origin: https://evil.example.com" http://public-ip:8000/api/public -s -o /dev/null -w "%header{access-control-allow-origin}" | grep -q "evil" && echo "FAIL: CORS allows any origin" || echo "PASS: CORS restricted"
# Test 4: Security headers present
curl -s -I http://public-ip:8000/api/public | grep -q "Strict-Transport-Security" && echo "PASS: HSTS present" || echo "FAIL: HSTS missing"
# Test 5: Rate limiting triggers after threshold
for i in $(seq 1 150); do
curl -s -o /dev/null -w "%{http_code}\n" http://public-ip:8000/api/public
done | sort | uniq -c | grep 429 && echo "PASS: Rate limiting active" || echo "FAIL: Rate limiting not enforcing"
Conclusion
Kong API Gateway security hardening is a multi-layered discipline that spans network configuration, plugin hardening, secrets management, operating system controls, and continuous validation. The steps outlined in this tutorial form a defense-in-depth strategy: even if one layer is compromised, subsequent layers continue to protect your backend services. Start with the highest-impact items—locking down the Admin API and enforcing authentication on every route—then progressively work through the checklist. Integrate security scanning into your CI/CD pipeline so that every configuration change is validated before deployment. With these hardening measures in place, Kong becomes not just a gateway, but a robust security enforcement point that actively defends your infrastructure against the most common attack vectors in modern API-driven architectures.