mirror of
https://github.com/alibaba/higress.git
synced 2026-02-06 15:10:54 +08:00
doc: optimize the documentation for hmac-auth-apisix (#2912)
This commit is contained in:
@@ -81,17 +81,159 @@ allow:
|
||||
```
|
||||
|
||||
**配置说明**:
|
||||
|
||||
- 路由名称(如 route-a、route-b)对应网关路由创建时定义的名称,匹配时仅允许consumer1访问
|
||||
- 域名匹配(如 `*.example.com`、`test.com`)用于过滤请求域名,匹配时仅允许consumer2访问
|
||||
- 未在allow列表中的调用者将被拒绝访问
|
||||
|
||||
**生成签名,可以使用以下 Go 代码片段或其他技术栈**:
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/hmac"
|
||||
"crypto/sha1"
|
||||
"crypto/sha256"
|
||||
"crypto/sha512"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"hash"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// SignedHeader 定义签名头的结构
|
||||
type SignedHeader struct {
|
||||
Name string
|
||||
Value string
|
||||
}
|
||||
|
||||
func main() {
|
||||
// 配置参数
|
||||
keyID := "consumer1-key" // key id
|
||||
secretKey := "2bda943c-ba2b-11ec-ba07-00163e1250b5" // secret key
|
||||
requestMethod := "POST" // HTTP method
|
||||
requestPath := "/foo" // Route URI
|
||||
algorithm := "hmac-sha256" // algorithm
|
||||
validateRequestBody := false // 是否验证请求体,设置为true时会添加Digest头部
|
||||
|
||||
// 如果配置了 signed_headers,则需要按照顺序添加
|
||||
signedHeaders := []SignedHeader{
|
||||
//{Name: "x-custom-header-a", Value: "test1"},
|
||||
//{Name: "x-custom-header-b", Value: "test2"},
|
||||
}
|
||||
|
||||
body := []byte("{}") // request body
|
||||
|
||||
// 获取当前 GMT 时间
|
||||
gmtTime := time.Now().UTC().Format("Mon, 02 Jan 2006 15:04:05 GMT")
|
||||
|
||||
// 动态构造签名字符串(有序)
|
||||
signingStringBuilder := strings.Builder{}
|
||||
signingStringBuilder.WriteString(fmt.Sprintf("%s\n%s %s\ndate: %s\n",
|
||||
keyID,
|
||||
requestMethod,
|
||||
requestPath,
|
||||
gmtTime))
|
||||
|
||||
// 按照signedHeaders中的顺序添加header
|
||||
for _, header := range signedHeaders {
|
||||
signingStringBuilder.WriteString(fmt.Sprintf("%s: %s\n", header.Name, header.Value))
|
||||
}
|
||||
|
||||
signingString := signingStringBuilder.String()
|
||||
|
||||
// 创建签名
|
||||
signature, err := generateHmacSignature(secretKey, algorithm, signingString)
|
||||
if err != nil {
|
||||
fmt.Printf("Error generating signature: %v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
// 动态构建headers字段内容
|
||||
headersField := "@request-target date"
|
||||
for _, header := range signedHeaders {
|
||||
headersField += " " + header.Name
|
||||
}
|
||||
|
||||
// 构造请求头部
|
||||
headers := map[string]string{
|
||||
"Date": gmtTime,
|
||||
"Authorization": fmt.Sprintf(`Signature keyId="%s",algorithm="%s",headers="%s",signature="%s"`,
|
||||
keyID,
|
||||
algorithm,
|
||||
headersField,
|
||||
signature,
|
||||
),
|
||||
}
|
||||
|
||||
// 如果需要验证请求体,则添加Digest头部
|
||||
if validateRequestBody {
|
||||
headers["Digest"] = calculateBodyDigest(body)
|
||||
}
|
||||
|
||||
// 添加签名的请求头
|
||||
for _, header := range signedHeaders {
|
||||
formattedHeaderName := formatHeaderName(header.Name)
|
||||
headers[formattedHeaderName] = header.Value
|
||||
}
|
||||
|
||||
// 打印签名字符串
|
||||
fmt.Printf("signingString: %s\n", signingString)
|
||||
// 打印请求头
|
||||
fmt.Println("Headers:")
|
||||
for key, value := range headers {
|
||||
fmt.Printf("%s: %s\n", key, value)
|
||||
}
|
||||
}
|
||||
|
||||
// generateHmacSignature 生成HMAC签名
|
||||
func generateHmacSignature(secretKey, algorithm, message string) (string, error) {
|
||||
var mac hash.Hash
|
||||
|
||||
switch algorithm {
|
||||
case "hmac-sha1":
|
||||
mac = hmac.New(sha1.New, []byte(secretKey))
|
||||
case "hmac-sha256":
|
||||
mac = hmac.New(sha256.New, []byte(secretKey))
|
||||
case "hmac-sha512":
|
||||
mac = hmac.New(sha512.New, []byte(secretKey))
|
||||
default:
|
||||
return "", fmt.Errorf("unsupported algorithm: %s", algorithm)
|
||||
}
|
||||
|
||||
mac.Write([]byte(message))
|
||||
signature := mac.Sum(nil)
|
||||
return base64.StdEncoding.EncodeToString(signature), nil
|
||||
}
|
||||
|
||||
// calculateBodyDigest 计算body的摘要
|
||||
func calculateBodyDigest(body []byte) string {
|
||||
hash := sha256.Sum256(body)
|
||||
encodedDigest := base64.StdEncoding.EncodeToString(hash[:])
|
||||
return "SHA-256=" + encodedDigest
|
||||
}
|
||||
|
||||
// formatHeaderName 将header name转换为标准HTTP头格式
|
||||
func formatHeaderName(headerName string) string {
|
||||
parts := strings.Split(headerName, "-")
|
||||
for i, part := range parts {
|
||||
if len(part) > 0 {
|
||||
parts[i] = strings.ToUpper(part[:1]) + strings.ToLower(part[1:])
|
||||
}
|
||||
}
|
||||
return strings.Join(parts, "-")
|
||||
}
|
||||
```
|
||||
|
||||
**请求与响应示例**:
|
||||
|
||||
1. **验证通过场景**
|
||||
```shell
|
||||
curl -X POST 'http://localhost:8082/foo' \
|
||||
-H 'Authorization:Signature keyId="consumer1-key",algorithm="hmac-sha256",headers="@request-target date",signature="G2+60rCCHQCQDZOailnKHLCEy++P1Pa5OEP1bG4QlRo="' \
|
||||
-H 'Date:Sat, 30 Aug 2025 00:52:39 GMT' \
|
||||
-H 'Authorization:Signature keyId="consumer1-key",algorithm="hmac-sha256",headers="@request-target date",signature="746z4VISwZehUwZdzTV486ZMMbBtakmMHKPfs/A4RdU="' \
|
||||
-H 'Date:Fri, 12 Sep 2025 23:53:18 GMT' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{}'
|
||||
```
|
||||
@@ -101,8 +243,8 @@ curl -X POST 'http://localhost:8082/foo' \
|
||||
2. **请求方法修改导致验签失败**
|
||||
```shell
|
||||
curl -X PUT 'http://localhost:8082/foo' \ # 此处将POST改为PUT
|
||||
-H 'Authorization:Signature keyId="consumer1-key",algorithm="hmac-sha256",headers="@request-target date",signature="G2+60rCCHQCQDZOailnKHLCEy++P1Pa5OEP1bG4QlRo="' \
|
||||
-H 'Date:Sat, 30 Aug 2025 00:52:39 GMT' \
|
||||
-H 'Authorization:Signature keyId="consumer1-key",algorithm="hmac-sha256",headers="@request-target date",signature="746z4VISwZehUwZdzTV486ZMMbBtakmMHKPfs/A4RdU="' \
|
||||
-H 'Date:Fri, 12 Sep 2025 23:53:18 GMT' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{}'
|
||||
```
|
||||
@@ -112,8 +254,8 @@ curl -X PUT 'http://localhost:8082/foo' \ # 此处将POST改为PUT
|
||||
3. **不在允许列表中的调用者**
|
||||
```shell
|
||||
curl -X POST 'http://localhost:8082/foo' \
|
||||
-H 'Authorization:Signature keyId="consumer2-key",algorithm="hmac-sha256",headers="@request-target date",signature="5sqSbDX9b91dQsfQra2hpluM7O6/yhS7oLcKPQylyCo="' \
|
||||
-H 'Date:Sat, 30 Aug 2025 00:54:18 GMT' \
|
||||
-H 'Authorization:Signature keyId="consumer2-key",algorithm="hmac-sha256",headers="@request-target date",signature="dltotPwd4iWGGz//kuehPJlHXZemR5WKwCPAJD/KPhE="' \
|
||||
-H 'Date:Fri, 12 Sep 2025 23:59:01 GMT' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{}'
|
||||
```
|
||||
@@ -123,8 +265,8 @@ curl -X POST 'http://localhost:8082/foo' \
|
||||
4. **时间戳过期**
|
||||
```shell
|
||||
curl -X POST 'http://localhost:8082/foo' \
|
||||
-H 'Authorization: Signature keyId="consumer1-key",algorithm="hmac-sha256",headers="@request-target date",signature="gvIUwoYNiK57w6xX2g1Ntpk8lfgD7z+jgom434r5qwg="' \
|
||||
-H 'Date: Sat, 30 Aug 2025 00:40:21 GMT' \ # 过期的时间戳
|
||||
-H 'Authorization:Signature keyId="consumer1-key",algorithm="hmac-sha256",headers="@request-target date",signature="746z4VISwZehUwZdzTV486ZMMbBtakmMHKPfs/A4RdU="' \
|
||||
-H 'Date:Fri, 12 Sep 2025 23:53:18 GMT' \ # 过期的时间戳
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{}'
|
||||
```
|
||||
@@ -154,35 +296,37 @@ validate_request_body: true # 启用请求体签名校验
|
||||
1. **验证通过场景**
|
||||
```shell
|
||||
curl -X POST 'http://localhost:8082/foo' \
|
||||
-H 'Authorization:Signature keyId="consumer1-key",algorithm="hmac-sha256",headers="@request-target date x-custom-header-a x-custom-header-b",signature="+xCWYCmidq3Sisn08N54NWaau5vSY9qEanWoO9HD4mA="' \
|
||||
-H 'Date:Sat, 30 Aug 2025 01:04:06 GMT' \
|
||||
-H 'Authorization:Signature keyId="consumer1-key",algorithm="hmac-sha256",headers="@request-target date x-custom-header-a x-custom-header-b",signature="KoOlbkDIR/JzlKK47eURewnIpmhpkQU+KIyBUhqVfmo="' \
|
||||
-H 'Date:Sat, 13 Sep 2025 00:04:34 GMT' \
|
||||
-H 'Digest:SHA-256=RBNvo1WzZ4oRRq0W9+hknpT7T8If536DEMBg9hyq/4o=' \ # 请求体摘要
|
||||
-H 'X-Custom-Header-A:test1' \
|
||||
-H 'X-Custom-Header-B:test2' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{}'
|
||||
```
|
||||
|
||||
- 响应:返回后端服务正常响应
|
||||
|
||||
2. **缺少签名头**
|
||||
```shell
|
||||
curl -X POST 'http://localhost:8082/foo' \
|
||||
-H 'Authorization:Signature keyId="consumer1-key",algorithm="hmac-sha256",headers="@request-target date x-custom-header-a x-custom-header-b",signature="+xCWYCmidq3Sisn08N54NWaau5vSY9qEanWoO9HD4mA="' \
|
||||
-H 'Date:Sat, 30 Aug 2025 01:04:06 GMT' \
|
||||
-H 'Authorization:Signature keyId="consumer1-key",algorithm="hmac-sha256",headers="@request-target date x-custom-header-b",signature="KoOlbkDIR/JzlKK47eURewnIpmhpkQU+KIyBUhqVfmo="' \
|
||||
-H 'Date:Sat, 13 Sep 2025 00:04:34 GMT' \
|
||||
-H 'Digest:SHA-256=RBNvo1WzZ4oRRq0W9+hknpT7T8If536DEMBg9hyq/4o=' \
|
||||
-H 'X-Custom-Header-B:test2' \ # 缺少X-Custom-Header-A
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{}'
|
||||
```
|
||||
|
||||
- 响应:`401 Unauthorized`
|
||||
- 错误信息:`{"message":"client request can't be validated: expected header "X-Custom-Header-A" missing in signing"}`
|
||||
|
||||
3. **请求体被篡改**
|
||||
```shell
|
||||
curl -X POST 'http://localhost:8082/foo' \
|
||||
-H 'Authorization:Signature keyId="consumer1-key",algorithm="hmac-sha256",headers="@request-target date x-custom-header-a x-custom-header-b",signature="dSbv6pdQOcgkN89TmSxiT8F9nypbPUqAR2E7ELL8K2s="' \
|
||||
-H 'Date:Sat, 30 Aug 2025 01:10:17 GMT' \
|
||||
-H 'Digest:SHA-256=RBNvo1WzZ4oRRq0W9+hknpT7T8If536DEMBg9hyq/4o=' \ # 与实际body不匹配
|
||||
-H 'Authorization:Signature keyId="consumer1-key",algorithm="hmac-sha256",headers="@request-target date x-custom-header-a x-custom-header-b",signature="NcA+44FFtl2rjNvV28wSn8Rln02i4i2tFXKp3/ahyYA="' \
|
||||
-H 'Date:Sat, 13 Sep 2025 00:09:40 GMT' \
|
||||
-H 'Digest:SHA-256=RBNvo1WzZ4oRRq0W9+hknpT7T8If536DEMBg9hyq/4o=' \
|
||||
-H 'X-Custom-Header-A:test1' \
|
||||
-H 'X-Custom-Header-B:test2' \
|
||||
-H 'Content-Type: application/json' \
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
# APISIX HMAC Authentication
|
||||
---
|
||||
title: APISIX HMAC Authentication
|
||||
keywords: [higress, hmac auth, apisix]
|
||||
description: Configuration Reference for APISIX HMAC Authentication Plugin
|
||||
description: Configuration Reference for the APISIX HMAC Authentication Plugin
|
||||
---
|
||||
|
||||
## Feature Description
|
||||
@@ -50,12 +51,14 @@ The `hmac-auth-apisix` plugin is compatible with Apache APISIX's HMAC authentica
|
||||
|
||||
## Configuration Examples
|
||||
|
||||
### Global Authentication Configuration and Route-level Authorization
|
||||
The following configuration enables HMAC Auth authentication and authorization for specific routes or domains of the gateway. **Note: The `access_key` field must be unique.**
|
||||
### Global Configuration Authentication and Route-Level Authorization
|
||||
|
||||
The following configuration is used to enable Hmac Auth authentication and authorization for specific routes or domains of the gateway. **Note: The `access_key` field must be unique.**
|
||||
|
||||
|
||||
#### Example 1: Basic Route and Domain Authorization Configuration
|
||||
**Instance-level Plugin Configuration**:
|
||||
|
||||
**Instance-Level Plugin Configuration**:
|
||||
```yaml
|
||||
global_auth: false
|
||||
consumers:
|
||||
@@ -65,78 +68,222 @@ consumers:
|
||||
- name: consumer2
|
||||
access_key: consumer2-key
|
||||
secret_key: c8c8e9ca-558e-4a2d-bb62-e700dcc40e35
|
||||
```
|
||||
```
|
||||
|
||||
**Route-level Configuration** (Applicable to `route-a` and `route-b`):
|
||||
**Route-Level Configuration** (applicable to `route-a` and `route-b`):
|
||||
```yaml
|
||||
allow:
|
||||
- consumer1 # Only consumer1 is allowed to access
|
||||
```
|
||||
- consumer1 # Only consumer1 is allowed access
|
||||
```
|
||||
|
||||
**Domain-level Configuration** (Applicable to `*.example.com` and `test.com`):
|
||||
**Domain-Level Configuration** (applicable to `*.example.com` and `test.com`):
|
||||
```yaml
|
||||
allow:
|
||||
- consumer2 # Only consumer2 is allowed to access
|
||||
```
|
||||
- consumer2 # Only consumer2 is allowed access
|
||||
```
|
||||
|
||||
**Configuration Description**:
|
||||
- Route names (e.g., `route-a`, `route-b`) correspond to the names defined when creating gateway routes. Only `consumer1` is allowed to access when the route matches.
|
||||
- Domain matching (e.g., `*.example.com`, `test.com`) is used to filter request domains. Only `consumer2` is allowed to access when the domain matches.
|
||||
|
||||
#### Configuration Instructions:
|
||||
- **Route Names** (e.g., `route-a`, `route-b`): Correspond to the names defined when creating gateway routes. Only `consumer1` is allowed access when matched.
|
||||
- **Domain Matching** (e.g., `*.example.com`, `test.com`): Used to filter request domains. Only `consumer2` is allowed access when matched.
|
||||
- Callers not in the `allow` list will be denied access.
|
||||
|
||||
|
||||
**Request and Response Examples**:
|
||||
#### To Generate a Signature, Use the Following Go Code Snippet or Other Tech Stacks:
|
||||
|
||||
1. **Successful Verification Scenario**
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/hmac"
|
||||
"crypto/sha1"
|
||||
"crypto/sha256"
|
||||
"crypto/sha512"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"hash"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// SignedHeader defines the structure of signed headers
|
||||
type SignedHeader struct {
|
||||
Name string
|
||||
Value string
|
||||
}
|
||||
|
||||
func main() {
|
||||
// Configuration parameters
|
||||
keyID := "consumer1-key" // Key ID
|
||||
secretKey := "2bda943c-ba2b-11ec-ba07-00163e1250b5" // Secret key
|
||||
requestMethod := "POST" // HTTP method
|
||||
requestPath := "/foo" // Route URI
|
||||
algorithm := "hmac-sha256" // Algorithm
|
||||
validateRequestBody := false // Whether to validate the request body; set to true to add the Digest header
|
||||
|
||||
// If signed_headers is configured, add them in order
|
||||
signedHeaders := []SignedHeader{
|
||||
//{Name: "x-custom-header-a", Value: "test1"},
|
||||
//{Name: "x-custom-header-b", Value: "test2"},
|
||||
}
|
||||
|
||||
body := []byte("{}") // Request body
|
||||
|
||||
// Get current GMT time
|
||||
gmtTime := time.Now().UTC().Format("Mon, 02 Jan 2006 15:04:05 GMT")
|
||||
|
||||
// Dynamically construct the signing string (in order)
|
||||
signingStringBuilder := strings.Builder{}
|
||||
signingStringBuilder.WriteString(fmt.Sprintf("%s\n%s %s\ndate: %s\n",
|
||||
keyID,
|
||||
requestMethod,
|
||||
requestPath,
|
||||
gmtTime))
|
||||
|
||||
// Add headers in the order specified in signedHeaders
|
||||
for _, header := range signedHeaders {
|
||||
signingStringBuilder.WriteString(fmt.Sprintf("%s: %s\n", header.Name, header.Value))
|
||||
}
|
||||
|
||||
signingString := signingStringBuilder.String()
|
||||
|
||||
// Generate signature
|
||||
signature, err := generateHmacSignature(secretKey, algorithm, signingString)
|
||||
if err != nil {
|
||||
fmt.Printf("Error generating signature: %v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Dynamically build the content of the headers field
|
||||
headersField := "@request-target date"
|
||||
for _, header := range signedHeaders {
|
||||
headersField += " " + header.Name
|
||||
}
|
||||
|
||||
// Construct request headers
|
||||
headers := map[string]string{
|
||||
"Date": gmtTime,
|
||||
"Authorization": fmt.Sprintf(`Signature keyId="%s",algorithm="%s",headers="%s",signature="%s"`,
|
||||
keyID,
|
||||
algorithm,
|
||||
headersField,
|
||||
signature,
|
||||
),
|
||||
}
|
||||
|
||||
// Add Digest header if request body validation is required
|
||||
if validateRequestBody {
|
||||
headers["Digest"] = calculateBodyDigest(body)
|
||||
}
|
||||
|
||||
// Add signed request headers
|
||||
for _, header := range signedHeaders {
|
||||
formattedHeaderName := formatHeaderName(header.Name)
|
||||
headers[formattedHeaderName] = header.Value
|
||||
}
|
||||
|
||||
// Print the signing string
|
||||
fmt.Printf("signingString: %s\n", signingString)
|
||||
// Print request headers
|
||||
fmt.Println("Headers:")
|
||||
for key, value := range headers {
|
||||
fmt.Printf("%s: %s\n", key, value)
|
||||
}
|
||||
}
|
||||
|
||||
// generateHmacSignature generates an HMAC signature
|
||||
func generateHmacSignature(secretKey, algorithm, message string) (string, error) {
|
||||
var mac hash.Hash
|
||||
|
||||
switch algorithm {
|
||||
case "hmac-sha1":
|
||||
mac = hmac.New(sha1.New, []byte(secretKey))
|
||||
case "hmac-sha256":
|
||||
mac = hmac.New(sha256.New, []byte(secretKey))
|
||||
case "hmac-sha512":
|
||||
mac = hmac.New(sha512.New, []byte(secretKey))
|
||||
default:
|
||||
return "", fmt.Errorf("unsupported algorithm: %s", algorithm)
|
||||
}
|
||||
|
||||
mac.Write([]byte(message))
|
||||
signature := mac.Sum(nil)
|
||||
return base64.StdEncoding.EncodeToString(signature), nil
|
||||
}
|
||||
|
||||
// calculateBodyDigest calculates the digest of the request body
|
||||
func calculateBodyDigest(body []byte) string {
|
||||
hash := sha256.Sum256(body)
|
||||
encodedDigest := base64.StdEncoding.EncodeToString(hash[:])
|
||||
return "SHA-256=" + encodedDigest
|
||||
}
|
||||
|
||||
// formatHeaderName converts the header name to standard HTTP header format
|
||||
func formatHeaderName(headerName string) string {
|
||||
parts := strings.Split(headerName, "-")
|
||||
for i, part := range parts {
|
||||
if len(part) > 0 {
|
||||
parts[i] = strings.ToUpper(part[:1]) + strings.ToLower(part[1:])
|
||||
}
|
||||
}
|
||||
return strings.Join(parts, "-")
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
#### Request and Response Examples:
|
||||
|
||||
1. **Validation Passed Scenario**
|
||||
```shell
|
||||
curl -X POST 'http://localhost:8082/foo' \
|
||||
-H 'Authorization:Signature keyId="consumer1-key",algorithm="hmac-sha256",headers="@request-target date",signature="G2+60rCCHQCQDZOailnKHLCEy++P1Pa5OEP1bG4QlRo="' \
|
||||
-H 'Date:Sat, 30 Aug 2025 00:52:39 GMT' \
|
||||
-H 'Authorization:Signature keyId="consumer1-key",algorithm="hmac-sha256",headers="@request-target date",signature="746z4VISwZehUwZdzTV486ZMMbBtakmMHKPfs/A4RdU="' \
|
||||
-H 'Date:Fri, 12 Sep 2025 23:53:18 GMT' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{}'
|
||||
```
|
||||
- Response: Returns a normal response from the backend service.
|
||||
- Additional Info: After successful authentication, the request header `X-Mse-Consumer: consumer1` is automatically added and passed to the backend.
|
||||
```
|
||||
- **Response**: Returns a normal response from the backend service.
|
||||
- **Additional Info**: After successful authentication, the request header `X-Mse-Consumer: consumer1` is automatically added and passed to the backend.
|
||||
|
||||
|
||||
2. **Signature Verification Failure Due to Modified Request Method**
|
||||
```shell
|
||||
curl -X PUT 'http://localhost:8082/foo' \ # Changed from POST to PUT here
|
||||
-H 'Authorization:Signature keyId="consumer1-key",algorithm="hmac-sha256",headers="@request-target date",signature="G2+60rCCHQCQDZOailnKHLCEy++P1Pa5OEP1bG4QlRo="' \
|
||||
-H 'Date:Sat, 30 Aug 2025 00:52:39 GMT' \
|
||||
curl -X PUT 'http://localhost:8082/foo' \ # POST is modified to PUT here
|
||||
-H 'Authorization:Signature keyId="consumer1-key",algorithm="hmac-sha256",headers="@request-target date",signature="746z4VISwZehUwZdzTV486ZMMbBtakmMHKPfs/A4RdU="' \
|
||||
-H 'Date:Fri, 12 Sep 2025 23:53:18 GMT' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{}'
|
||||
```
|
||||
- Response: `401 Unauthorized`
|
||||
- Error Message: `{"message":"client request can't be validated: Invalid signature"}`
|
||||
```
|
||||
- **Response**: `401 Unauthorized`
|
||||
- **Error Message**: `{"message":"client request can't be validated: Invalid signature"}`
|
||||
|
||||
|
||||
3. **Caller Not in the Allow List**
|
||||
3. **Caller Not in Allow List**
|
||||
```shell
|
||||
curl -X POST 'http://localhost:8082/foo' \
|
||||
-H 'Authorization:Signature keyId="consumer2-key",algorithm="hmac-sha256",headers="@request-target date",signature="5sqSbDX9b91dQsfQra2hpluM7O6/yhS7oLcKPQylyCo="' \
|
||||
-H 'Date:Sat, 30 Aug 2025 00:54:18 GMT' \
|
||||
-H 'Authorization:Signature keyId="consumer2-key",algorithm="hmac-sha256",headers="@request-target date",signature="dltotPwd4iWGGz//kuehPJlHXZemR5WKwCPAJD/KPhE="' \
|
||||
-H 'Date:Fri, 12 Sep 2025 23:59:01 GMT' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{}'
|
||||
```
|
||||
- Response: `401 Unauthorized`
|
||||
- Error Message: `{"message":"client request can't be validated: consumer 'consumer2' is not allowed"}`
|
||||
```
|
||||
- **Response**: `401 Unauthorized`
|
||||
- **Error Message**: `{"message":"client request can't be validated: consumer 'consumer2' is not allowed"}`
|
||||
|
||||
|
||||
4. **Expired Timestamp**
|
||||
```shell
|
||||
curl -X POST 'http://localhost:8082/foo' \
|
||||
-H 'Authorization: Signature keyId="consumer1-key",algorithm="hmac-sha256",headers="@request-target date",signature="gvIUwoYNiK57w6xX2g1Ntpk8lfgD7z+jgom434r5qwg="' \
|
||||
-H 'Date: Sat, 30 Aug 2025 00:40:21 GMT' \ # Expired timestamp
|
||||
-H 'Authorization:Signature keyId="consumer1-key",algorithm="hmac-sha256",headers="@request-target date",signature="746z4VISwZehUwZdzTV486ZMMbBtakmMHKPfs/A4RdU="' \
|
||||
-H 'Date:Fri, 12 Sep 2025 23:53:18 GMT' \ # Expired timestamp
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{}'
|
||||
```
|
||||
- Response: `401 Unauthorized`
|
||||
- Error Message: `{"message":"client request can't be validated: Clock skew exceeded"}`
|
||||
```
|
||||
- **Response**: `401 Unauthorized`
|
||||
- **Error Message**: `{"message":"client request can't be validated: Clock skew exceeded"}`
|
||||
|
||||
|
||||
#### Example 2: Configuration with Custom Signature Headers and Request Body Verification
|
||||
**Instance-level Plugin Configuration**:
|
||||
#### Example 2: Configuration with Custom Signed Headers and Request Body Validation
|
||||
|
||||
**Instance-Level Plugin Configuration**:
|
||||
```yaml
|
||||
global_auth: false
|
||||
consumers:
|
||||
@@ -150,56 +297,57 @@ signed_headers: # Custom request headers to be included in the signature
|
||||
- X-Custom-Header-A
|
||||
- X-Custom-Header-B
|
||||
validate_request_body: true # Enable request body signature verification
|
||||
```
|
||||
```
|
||||
|
||||
|
||||
**Request and Response Examples**:
|
||||
#### Request and Response Examples:
|
||||
|
||||
1. **Successful Verification Scenario**
|
||||
1. **Validation Passed Scenario**
|
||||
```shell
|
||||
curl -X POST 'http://localhost:8082/foo' \
|
||||
-H 'Authorization:Signature keyId="consumer1-key",algorithm="hmac-sha256",headers="@request-target date x-custom-header-a x-custom-header-b",signature="+xCWYCmidq3Sisn08N54NWaau5vSY9qEanWoO9HD4mA="' \
|
||||
-H 'Date:Sat, 30 Aug 2025 01:04:06 GMT' \
|
||||
-H 'Authorization:Signature keyId="consumer1-key",algorithm="hmac-sha256",headers="@request-target date x-custom-header-a x-custom-header-b",signature="KoOlbkDIR/JzlKK47eURewnIpmhpkQU+KIyBUhqVfmo="' \
|
||||
-H 'Date:Sat, 13 Sep 2025 00:04:34 GMT' \
|
||||
-H 'Digest:SHA-256=RBNvo1WzZ4oRRq0W9+hknpT7T8If536DEMBg9hyq/4o=' \ # Request body digest
|
||||
-H 'X-Custom-Header-A:test1' \
|
||||
-H 'X-Custom-Header-B:test2' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{}'
|
||||
```
|
||||
- Response: Returns a normal response from the backend service.
|
||||
```
|
||||
- **Response**: Returns a normal response from the backend service.
|
||||
|
||||
|
||||
2. **Missing Signature Header**
|
||||
2. **Missing Signed Header**
|
||||
```shell
|
||||
curl -X POST 'http://localhost:8082/foo' \
|
||||
-H 'Authorization:Signature keyId="consumer1-key",algorithm="hmac-sha256",headers="@request-target date x-custom-header-a x-custom-header-b",signature="+xCWYCmidq3Sisn08N54NWaau5vSY9qEanWoO9HD4mA="' \
|
||||
-H 'Date:Sat, 30 Aug 2025 01:04:06 GMT' \
|
||||
-H 'Authorization:Signature keyId="consumer1-key",algorithm="hmac-sha256",headers="@request-target date x-custom-header-b",signature="KoOlbkDIR/JzlKK47eURewnIpmhpkQU+KIyBUhqVfmo="' \
|
||||
-H 'Date:Sat, 13 Sep 2025 00:04:34 GMT' \
|
||||
-H 'Digest:SHA-256=RBNvo1WzZ4oRRq0W9+hknpT7T8If536DEMBg9hyq/4o=' \
|
||||
-H 'X-Custom-Header-B:test2' \ # Missing X-Custom-Header-A
|
||||
-H 'X-Custom-Header-B:test2' \ # X-Custom-Header-A is missing
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{}'
|
||||
```
|
||||
- Response: `401 Unauthorized`
|
||||
- Error Message: `{"message":"client request can't be validated: expected header \"X-Custom-Header-A\" missing in signing"}`
|
||||
```
|
||||
- **Response**: `401 Unauthorized`
|
||||
- **Error Message**: `{"message":"client request can't be validated: expected header \"X-Custom-Header-A\" missing in signing"}`
|
||||
|
||||
|
||||
3. **Tampered Request Body**
|
||||
```shell
|
||||
curl -X POST 'http://localhost:8082/foo' \
|
||||
-H 'Authorization:Signature keyId="consumer1-key",algorithm="hmac-sha256",headers="@request-target date x-custom-header-a x-custom-header-b",signature="dSbv6pdQOcgkN89TmSxiT8F9nypbPUqAR2E7ELL8K2s="' \
|
||||
-H 'Date:Sat, 30 Aug 2025 01:10:17 GMT' \
|
||||
-H 'Digest:SHA-256=RBNvo1WzZ4oRRq0W9+hknpT7T8If536DEMBg9hyq/4o=' \ # Mismatches the actual body
|
||||
-H 'Authorization:Signature keyId="consumer1-key",algorithm="hmac-sha256",headers="@request-target date x-custom-header-a x-custom-header-b",signature="NcA+44FFtl2rjNvV28wSn8Rln02i4i2tFXKp3/ahyYA="' \
|
||||
-H 'Date:Sat, 13 Sep 2025 00:09:40 GMT' \
|
||||
-H 'Digest:SHA-256=RBNvo1WzZ4oRRq0W9+hknpT7T8If536DEMBg9hyq/4o=' \
|
||||
-H 'X-Custom-Header-A:test1' \
|
||||
-H 'X-Custom-Header-B:test2' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{"key":"value"}' # Tampered request body
|
||||
```
|
||||
- Response: `401 Unauthorized`
|
||||
- Error Message: `{"message":"client request can't be validated: Invalid digest"}`
|
||||
```
|
||||
- **Response**: `401 Unauthorized`
|
||||
- **Error Message**: `{"message":"client request can't be validated: Invalid digest"}`
|
||||
|
||||
|
||||
### Enabling Global Authentication at the Gateway Instance Level
|
||||
The following configuration enables HMAC Auth authentication at the gateway instance level. **All requests must pass authentication to access**:
|
||||
### Enable Global Authentication at the Gateway Instance Level
|
||||
|
||||
The following configuration enables Hmac Auth authentication at the gateway instance level. **All requests must be authenticated to access the gateway**:
|
||||
|
||||
```yaml
|
||||
global_auth: true # Enable global authentication
|
||||
@@ -210,6 +358,6 @@ consumers:
|
||||
- name: consumer2
|
||||
access_key: consumer2-key
|
||||
secret_key: c8c8e9ca-558e-4a2d-bb62-e700dcc40e35
|
||||
```
|
||||
```
|
||||
|
||||
**Description**: When `global_auth: true`, all requests accessing the gateway must carry valid authentication information. Unauthenticated requests will be directly rejected.
|
||||
**Description**: When `global_auth: true`, all requests to the gateway must carry valid authentication information. Unauthenticated requests will be rejected directly.
|
||||
Reference in New Issue
Block a user