You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
* Add hmac signed http check
* Add new http-check parameters for hmac signed requests
* Add documentation for hmac signed http requests
---------
Co-authored-by: Tarun Menon <tarunmenon95@gmail.com>
Copy file name to clipboardExpand all lines: docs/custom_checks/http.md
+131-2Lines changed: 131 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -13,7 +13,7 @@ Resources:
13
13
StatusCode: 200
14
14
# enables the SSL check
15
15
Ssl: true
16
-
# boolean tp request a compressed response
16
+
# boolean to request a compressed response
17
17
Compressed: true
18
18
- Id: https://www.example.com
19
19
StatusCode: 301
@@ -38,6 +38,55 @@ Resources:
38
38
Payload: '{"name": "john"}'
39
39
```
40
40
41
+
## HMAC signed requests
42
+
43
+
For health endpoints that require authenticated requests, Guardian can send HMAC-signed headers so your application can verify the request came from Guardian and resist replay attacks.
44
+
45
+
When enabled, the HTTP check Lambda (see [aws-lambda-http-check](https://github.com/base2/aws-lambda-http-check)) adds these headers to each request:
| `X-Health-Timestamp` | Unix epoch timestamp in seconds |
52
+
| `X-Health-Nonce` | Random value (e.g. UUID hex) to prevent replay |
53
+
54
+
**Configuration (Public or Internal HTTP):**
55
+
56
+
| Key | Required | Description |
57
+
|-----|----------|-------------|
58
+
| `HmacSecretSsm` | Yes | SSM Parameter Store path to the HMAC secret (SecureString). The Guardian-generated IAM role grants the Lambda `ssm:GetParameter` for this path. |
59
+
| `HmacKeyId` | No | Key id sent in the `Key-Id` header. Default: `default`. |
60
+
| `HmacHeaderPrefix` | No | Prefix for all HMAC header names. Default: `X-Health`(yields `X-Health-Signature`, `X-Health-Key-Id`, etc.). |
61
+
62
+
**Example:**
63
+
64
+
```yaml
65
+
Resources:
66
+
Http:
67
+
- Id: https://api.example.com/health
68
+
StatusCode: 200
69
+
HmacSecretSsm: /guardian/myapp/hmac-secret
70
+
HmacKeyId: default
71
+
HmacHeaderPrefix: X-Health
72
+
```
73
+
74
+
Internal HTTP checks support the same keys under each host:
75
+
76
+
```yaml
77
+
Resources:
78
+
InternalHttp:
79
+
- Environment: Prod
80
+
VpcId: vpc-1234
81
+
Subnets: [subnet-abcd]
82
+
Hosts:
83
+
- Id: http://api.example.com/health
84
+
StatusCode: 200
85
+
HmacSecretSsm: /guardian/myapp/hmac-secret
86
+
```
87
+
88
+
Create the secret in SSM (e.g. SecureString) and use the same value in your application when verifying the signature.
89
+
41
90
## Private HTTP Check
42
91
43
92
Cloudwatch NameSpace: `InternalHttpCheck`
@@ -58,4 +107,84 @@ Resources:
58
107
# Array of resources defining the http endpoint with the Id: key
59
108
# All the same options as Http including ssl check on the internal endpoint
60
109
- Id: http://api.example.com
61
-
```
110
+
```
111
+
112
+
## Supporting HMAC-signed health checks in your application
113
+
114
+
If you want Guardian to call a health endpoint that only accepts HMAC-authenticated requests, your server must verify the same scheme the Lambda uses.
115
+
116
+
**Canonical string (what gets signed):**
117
+
118
+
The Lambda signs this string (newline-separated, no trailing newline):
119
+
120
+
```
121
+
METHOD\nPATH\nTIMESTAMP\nNONCE\nQUERY\nBODY_HASH
122
+
```
123
+
124
+
- `METHOD`– HTTP method (e.g. `GET`).
125
+
- `PATH`– URL path (e.g. `/health`), no query.
126
+
- `TIMESTAMP`– Same value as the `{prefix}-Timestamp` header (Unix seconds).
127
+
- `NONCE`– Same value as the `{prefix}-Nonce` header.
128
+
- `QUERY`– Raw query string (e.g. `foo=bar` or empty).
129
+
- `BODY_HASH`– SHA-256 hex digest of the raw request body (empty string for GET; for POST/PUT, hash the body as sent).
130
+
131
+
**Verification steps (pseudo code):**
132
+
133
+
1. Read headers (default prefix `X-Health`):
134
+
`signature`, `key_id`, `timestamp`, `nonce`.
135
+
2. **Optional – replay protection:**
136
+
Reject if `timestamp` is too old (e.g. outside last 5 minutes).
137
+
Reject if `nonce` has been seen before (e.g. cache or DB) and treat as replay.
138
+
3. Look up the shared secret for `key_id` (e.g. from config or secrets store – same value as in SSM for Guardian).
139
+
4. Build the canonical string from the incoming request:
140
+
- Use request method, path, and query.
141
+
- Use `timestamp` and `nonce` from the headers.
142
+
- For body: compute SHA-256 hex of the raw request body (empty string for no body).
143
+
5. Compute `expected = HMAC-SHA256(secret, canonical_string)` and compare to `signature` (constant-time).
Use the same HMAC secret in SSM (Guardian config) and in your app’s config or secrets store. Keep the secret in SecureString or equivalent and restrict access appropriately.
0 commit comments