HTTP Authorization Middleware¶
| Status | Implemented |
|---|---|
| Revision | 2026-10-27, skywire 4a8cc24ca |
| Code | pkg/httpauth, pkg/httpauthclient |
Abstract¶
The authorization middleware authenticates HTTP requests to deployment services by a secp256k1 public key. A request carries a signature over its body and a per-key security nonce that prevents replay. Requests that arrive over dmsg are identified by the dmsg peer key instead.
Overview¶
Deployment services such as the address resolver, transport discovery and
service discovery wrap their authenticated handlers in httpauth.WithAuth.
Visors call them through httpauthclient.Client. Both ends keep a counter of
the next expected nonce for the key.
Addressing¶
The server exposes the nonce recovery endpoint at /security/nonces/{pk}
through httpauth.NonceHandler. The key is the last path segment.
Data Formats¶
httpauth.Nonce and httpauthclient.Nonce are uint64, rendered as decimal.
Request headers, parsed by httpauth.AuthFromHeaders:
| Header | Value |
|---|---|
SW-Public |
Public key, hexadecimal. Required. |
SW-Nonce |
Nonce of this request, unsigned decimal. Required. |
SW-Sig |
Signature, hexadecimal. Required when verification is on. |
The client also sets SW-PublicIP when it was given a public address, and the
build version header (buildinfo.VersionHeader). The server does not use them
for authentication.
The signed value is SHA256(body || nonce) where nonce is its decimal ASCII
rendering appended to the verbatim body (httpauth.PayloadWithNonce,
httpauth.Sign, cipher.SignPayload). For body {"a":1} and nonce 7 the
payload is the string {"a":1}7.
Error responses are httpauth.HTTPResponse:
| JSON name | Type | Meaning |
|---|---|---|
error.message |
string | Reason. The status text when empty. |
error.code |
integer | HTTP status code. |
httpauth.NextNonceResponse:
| JSON name | Type | Meaning |
|---|---|---|
edge |
public key | The queried key. |
next_nonce |
uint64 | The nonce the next request MUST carry. |
Interfaces¶
HTTP API¶
GET /security/nonces/{pk} is not authenticated.
| Status | Condition |
|---|---|
| 200 | NextNonceResponse. The nonce is zero for an unknown key. |
| 400 | The last path segment is not a public key, or it is the zero key. |
| 500 | The nonce store failed. |
Behaviour¶
Server¶
The nonce store (httpauth.NonceStore) provides Nonce, IncrementNonce and
Count. IncrementNonce is atomic and returns the new value. Implementations
are an in-memory map and Redis (INCR on nonces:<pk>), selected by
httpauth.NewNonceStore.
For a request that does not arrive over dmsg, WithAuth runs these steps.
AuthFromHeadersrejects a missing or malformed header with 401.- The stored nonce is read. A store failure returns 500.
- A
SW-Noncethat differs from the stored nonce returns 401 with the messageSW-Nonce does not match. - The body is read in full and the signature is verified. A bad signature
returns 401. A body above the
httputil.LimitBodycap returns 413. - The wrapped handler runs with the key in the request context
(
httpauth.ContextAuthKey, read withhttpauth.PKFromCtx). - When the handler wrote a 2xx status, the server calls
IncrementNonce. Any other status leaves the nonce unchanged.
The middleware has no key whitelist. Handlers that need one check
PKFromCtx themselves.
MakeLoadTestingMiddleware skips the nonce and signature checks and still
requires SW-Public and SW-Nonce.
Dmsg callers¶
A request whose RemoteAddr parses as a public key arrived over dmsg HTTP.
The dmsg handshake authenticated that key. WithAuth takes the identity from
RemoteAddr (dmsgRemotePK), ignores any SW-* header, performs no nonce or
signature check, never increments a nonce, and calls the handler.
Client¶
httpauthclient.Client keeps one nonce counter per server address and key,
shared by every client in the process that uses that pair, and serializes
their requests.
NewClientfetches the initial nonce from/security/nonces/{pk}.- Each request carries the three
SW-*headers computed from the local counter. - When the response is a JSON error with code 401 and message
SW-Nonce does not match, the client fetches the nonce again, stores it and retries the request once. - After the final response the client increments its counter when the status is 2xx.
Deregistration whitelist¶
The bulk deregistration endpoints of transport discovery, dmsg discovery, the
address resolver and service discovery do not use the nonce middleware. They
authorize the caller with httpauth.Whitelist.Authorize against the keys in
the service's whitelist_keys setting.
The caller is the authenticated remote key of the request's dmsg stream. A stream a host visor hands to the service from skynet carries the key of its originator, which signs the stream request, so a relay cannot stand in for it. The caller is authorized when its key is on the whitelist.
A plain HTTP request has no authenticated key and answers 403 (ErrNotDmsg).
A key that is not listed answers 403 (ErrNotWhitelisted). Three of the
endpoints take a JSON array of keys in hex as the body, read with
httpauth.ReadKeyList.
Security Considerations¶
The nonce is per key and per service. It orders requests and prevents replay of a signed body; it does not encrypt. Over dmsg the stream is authenticated and ordered by the dmsg session, so the application level nonce is not used.