HMAC Auth in Kong
HMAC Authentication proves that a request was created by a client that knows a shared secret without sending that secret in the request. In addition to authenticating the client, the signature detects changes to the signed elements while they are in transit.
This article explains how an HMAC signature is built and validated by Kong Gateway, and how the Consumer relates to its credential. We then build a reproducible Kong Ingress Controller lab and protect /hmac-auth.
This lab continues directly from Installing KIC. It reuses the kong Gateway, the echo Service, and the 192.168.121.200 address assigned by MetalLB to kong-gateway-proxy.
The tests were run with Kong Gateway 3.10.0.16 and Kong Ingress Controller 3.5.
1. What is an HMAC signature?
HMAC, or Hash-based Message Authentication Code, combines a hash function with a shared secret. Both the client and Kong know the secret, but they never need to send it over the network.
The client selects the request components to protect and builds a canonical string. This lab signs:
date, to limit how long the request can be reused.@request-target, containing the HTTP method and requested path.host, identifying the destination host.
For a GET request to /hmac-auth, the string has this form:
date: Sun, 26 Jul 2026 10:00:00 GMT
@request-target: get /hmac-auth
host: echo.javiercd.es
The client processes that string with HMAC-SHA256 and the shared secret:
Canonical string + secret
|
v
HMAC-SHA256
|
v
Base64-encoded signature
It sends the result in Authorization:
Authorization: hmac username="hmac-client", algorithm="hmac-sha256", headers="date @request-target host", signature="<signature>"
HMAC does not encrypt the request. An observer can still read it over HTTP, so HTTPS remains mandatory: TLS provides confidentiality, while HMAC proves the integrity of the signed data and knowledge of the secret.
The date also reduces replay attacks. Kong checks that the client’s clock is within the configured window, which defaults to 300 seconds. Clients and Kong nodes must keep their clocks synchronized.
2. How does HMAC Auth work in Kong Gateway?
Kong implements this mechanism through the official hmac-auth plugin. It supports traditional, hybrid, and DB-less topologies and can be applied globally, to a Gateway Service, or to a Route.
This lab applies it only to the Route generated from /hmac-auth.
When a request matches the Route, Kong runs the plugin before contacting the upstream:
- It looks for the signature first in
Proxy-Authorizationand then inAuthorization. - It reads the
usernamedeclared by the client. - It finds the HMAC credential associated with that name.
- It checks that the signature covers
date,@request-target, andhost. - It reconstructs the canonical string from the request.
- It calculates the signature with the stored secret and compares both values.
- It verifies that the date is inside the allowed clock window.
- If every check passes, it identifies the Consumer and continues.
The diagram shows the two resulting branches. Kong rejects a signature that is incorrect, incomplete, or outside the allowed time window. If the calculated signature matches and the date is valid, Kong associates the request with the Consumer and proxies it upstream.
The resulting behavior is:
- A missing signature returns
401 Unauthorized. - An unknown user, mismatched signature, or missing required header returns
401 Unauthorized. - A date outside the allowed window is rejected.
- A valid signature identifies the Consumer and forwards the request upstream.
- Rejected requests never reach
echo.
hide_credentials: true removes the authentication header before proxying. Kong can instead add X-Consumer-ID, X-Consumer-Username, and X-Credential-Identifier.
HMAC Auth authenticates the Consumer but does not select the backend or authorize individual resources. Routing belongs to the Route and Service. Additional authorization can be implemented with ACL.
3. Consumers and HMAC credentials
A Consumer represents the API client. The HMAC credential contains the name declared in the signature and the shared secret used to calculate it.
Do not confuse these lab values:
hmac-auth-clientis the KubernetesKongConsumerresource name.hmac-clientis the Consumer username in Kong.hmac-clientis also the HMAC credential username.hmac-shared-secretis the shared secret.
Their relationship is:
Consumer
|
+-- HMAC credential
|
+-- username
+-- secret
The Consumer provides identity. The credential provides the data Kong needs to verify the signature. Although both username fields match in this lab, they belong to different objects.
The example secret makes the lab reproducible. In production it must be generated randomly, stored in a secrets manager, and distributed only to the client that signs requests.
4. Configuring HMAC Auth in Kong with KIC
We publish the echo Service through an HTTPRoute and protect it with HMAC Auth.
The KIC installation already created the kong Gateway, the echo Service, and the proxy exposed by MetalLB. This lab adds only a KongPlugin, a Secret, a KongConsumer, and an HTTPRoute.
4.1. Relationship between Kubernetes and Kong resources
| Kubernetes resource | Internal Kong resource |
|---|---|
KongPlugin | plugins |
Secret | hmacauth_credentials |
KongConsumer | consumers |
HTTPRoute | routes, services, upstreams, and targets |
In DB-less mode these translations form part of Kong’s in-memory configuration. In traditional mode they correspond to database entities.
4.2. Create the hmac-auth plugin
enforce_headers requires the signature to protect the three recommended minimum elements. Order also matters because the client must construct the string in the same order declared in Authorization.
apiVersion: configuration.konghq.com/v1
kind: KongPlugin
metadata:
name: hmac-auth
namespace: javier
plugin: hmac-auth
config:
enforce_headers:
- date
- "@request-target"
- host
hide_credentials: true
The KongPlugin does not protect a Route by itself. The HTTPRoute annotation attaches it.
4.3. Create the credential
The konghq.com/credential: hmac-auth label tells KIC to translate the Secret into an HMAC credential.
apiVersion: v1
kind: Secret
metadata:
name: hmac-auth-credential
namespace: javier
labels:
konghq.com/credential: hmac-auth
stringData:
username: hmac-client
secret: hmac-shared-secret
Kubernetes Base64-encodes stringData when storing the Secret. That encoding is not encryption.
4.4. Create the KongConsumer
The KongConsumer creates the hmac-client identity and references the credential:
apiVersion: configuration.konghq.com/v1
kind: KongConsumer
metadata:
name: hmac-auth-client
namespace: javier
annotations:
kubernetes.io/ingress.class: kong
username: hmac-client
credentials:
- hmac-auth-credential
KIC creates the Consumer and associates the HMAC credential. In traditional mode they are represented through consumers and hmacauth_credentials.
4.5. Create the HTTPRoute
The HTTPRoute publishes /hmac-auth and applies the plugin through konghq.com/plugins: hmac-auth.
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: hmac-auth
namespace: javier
annotations:
konghq.com/plugins: hmac-auth
spec:
parentRefs:
- name: kong
namespace: kong
hostnames:
- echo.javiercd.es
rules:
- matches:
- path:
type: PathPrefix
value: /hmac-auth
backendRefs:
- name: echo
port: 80
4.6. Apply the manifests
sudo kubectl apply -f 12-hmac-auth-plugin.yaml
sudo kubectl apply -f 12-hmac-auth-secret.yaml
sudo kubectl apply -f 12-hmac-auth-consumer.yaml
sudo kubectl apply -f 12-hmac-auth-httproute.yaml
Check the resources:
sudo kubectl get kongplugin hmac-auth -n javier
sudo kubectl get kongconsumer hmac-auth-client -n javier
sudo kubectl get httproute hmac-auth -n javier
sudo kubectl describe httproute hmac-auth -n javier
5. Testing HMAC Authentication
The following responses were captured directly in the lab VM.
5.1. Request without a signature
curl -i http://echo.javiercd.es/hmac-auth
Kong returns 401 Unauthorized without contacting the upstream:
HTTP/1.1 401 Unauthorized
Date: Sun, 26 Jul 2026 09:01:48 GMT
Content-Type: application/json; charset=utf-8
Connection: keep-alive
WWW-Authenticate: hmac
Content-Length: 81
X-Kong-Response-Latency: 0
Server: kong/3.10.0.16-enterprise-edition
X-Kong-Request-Id: 1a794543e980ec54d493cd1edd88800c
{
"message":"Unauthorized",
"request_id":"1a794543e980ec54d493cd1edd88800c"
}
5.2. Create a valid signature
Run the following block on the host. It builds the date, canonical string, and signature with the secret declared in Kubernetes:
HOST='echo.javiercd.es'
TARGET='/hmac-auth'
DATE="$(LC_ALL=C date -u '+%a, %d %b %Y %H:%M:%S GMT')"
SIGNING_STRING="date: ${DATE}\n@request-target: get ${TARGET}\nhost: ${HOST}"
SIGNATURE="$(echo -en "${SIGNING_STRING}" \
| openssl dgst -sha256 -hmac 'hmac-shared-secret' -binary \
| base64 -w0)"
Inspect the exact values being signed:
echo -e "${SIGNING_STRING}"
echo "${SIGNATURE}"
5.3. Send the signed request
curl -i \
-H "Date: ${DATE}" \
-H "Authorization: hmac username=\"hmac-client\", algorithm=\"hmac-sha256\", headers=\"date @request-target host\", signature=\"${SIGNATURE}\"" \
"http://${HOST}${TARGET}"
Kong rebuilds the string, validates the signature, and identifies hmac-client. The real response is:
HTTP/1.1 200 OK
Content-Type: text/plain; charset=utf-8
Content-Length: 28
Connection: keep-alive
X-App-Name: http-echo
X-App-Version: 1.0.0
Date: Sun, 26 Jul 2026 09:01:48 GMT
Server: kong/3.10.0.16-enterprise-edition
X-Kong-Upstream-Latency: 0
X-Kong-Proxy-Latency: 0
Via: 1.1 kong/3.10.0.16-enterprise-edition
X-Kong-Request-Id: 0dc5cb6b1fb32f20969e215ce2c97015
Hola desde Kong Gateway KIC
5.4. Verify that the signature protects the path
Reuse the same signature but change the destination:
curl -i \
-H "Date: ${DATE}" \
-H "Authorization: hmac username=\"hmac-client\", algorithm=\"hmac-sha256\", headers=\"date @request-target host\", signature=\"${SIGNATURE}\"" \
"http://${HOST}/hmac-auth/otra-ruta"
The signature is invalid because @request-target no longer matches the signed value. Kong explains the failure without contacting the upstream:
HTTP/1.1 401 Unauthorized
Date: Sun, 26 Jul 2026 09:01:48 GMT
Content-Type: application/json; charset=utf-8
Connection: keep-alive
WWW-Authenticate: hmac
Content-Length: 98
X-Kong-Response-Latency: 1
Server: kong/3.10.0.16-enterprise-edition
X-Kong-Request-Id: 9a78552fb81cd9672ef9e3668d00dc11
{
"message":"HMAC signature does not match",
"request_id":"9a78552fb81cd9672ef9e3668d00dc11"
}
The request is also rejected if reused after the clock window expires. Clients must therefore generate a fresh date and signature for every request.
