mirror of
https://github.com/juanfont/headscale.git
synced 2025-06-19 01:18:37 +02:00
Fix /machine/map
endpoint vulnerability (#2642)
* Improve map auth logic * Bugfix * Add comment, improve error message * noise: make func, get by node this commit splits the additional validation into a separate function so it can be reused if we add more endpoints in the future. It swaps the check, so we still look up by NodeKey, but before accepting the connection, we validate the known machinekey from the db against the noise connection. The reason for this is that when a node logs in or out, the node key is replaced and it will no longer be possible to look it up, breaking reauthentication. Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com> Co-authored-by: Kristoffer Dalby <kristoffer@tailscale.com>
This commit is contained in:
parent
2dc2f3b3f0
commit
474ea236d0
@ -1,6 +1,12 @@
|
|||||||
# CHANGELOG
|
# CHANGELOG
|
||||||
|
|
||||||
## Next
|
## 0.26.1 (2025-06-06)
|
||||||
|
|
||||||
|
### Changes
|
||||||
|
|
||||||
|
- Ensure nodes are matching both node key and machine key
|
||||||
|
when connecting.
|
||||||
|
[#2642](https://github.com/juanfont/headscale/pull/2642)
|
||||||
|
|
||||||
## 0.26.0 (2025-05-14)
|
## 0.26.0 (2025-05-14)
|
||||||
|
|
||||||
|
@ -100,6 +100,10 @@ func (h *Headscale) NoiseUpgradeHandler(
|
|||||||
|
|
||||||
router.HandleFunc("/machine/register", noiseServer.NoiseRegistrationHandler).
|
router.HandleFunc("/machine/register", noiseServer.NoiseRegistrationHandler).
|
||||||
Methods(http.MethodPost)
|
Methods(http.MethodPost)
|
||||||
|
|
||||||
|
// Endpoints outside of the register endpoint must use getAndValidateNode to
|
||||||
|
// get the node to ensure that the MachineKey matches the Node setting up the
|
||||||
|
// connection.
|
||||||
router.HandleFunc("/machine/map", noiseServer.NoisePollNetMapHandler)
|
router.HandleFunc("/machine/map", noiseServer.NoisePollNetMapHandler)
|
||||||
|
|
||||||
noiseServer.httpBaseConfig = &http.Server{
|
noiseServer.httpBaseConfig = &http.Server{
|
||||||
@ -209,18 +213,14 @@ func (ns *noiseServer) NoisePollNetMapHandler(
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
ns.nodeKey = mapRequest.NodeKey
|
node, err := ns.getAndValidateNode(mapRequest)
|
||||||
|
|
||||||
node, err := ns.headscale.db.GetNodeByNodeKey(mapRequest.NodeKey)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
|
||||||
httpError(writer, NewHTTPError(http.StatusNotFound, "node not found", nil))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
httpError(writer, err)
|
httpError(writer, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ns.nodeKey = node.NodeKey
|
||||||
|
|
||||||
sess := ns.headscale.newMapSession(req.Context(), mapRequest, writer, node)
|
sess := ns.headscale.newMapSession(req.Context(), mapRequest, writer, node)
|
||||||
sess.tracef("a node sending a MapRequest with Noise protocol")
|
sess.tracef("a node sending a MapRequest with Noise protocol")
|
||||||
if !sess.isStreaming() {
|
if !sess.isStreaming() {
|
||||||
@ -266,8 +266,8 @@ func (ns *noiseServer) NoiseRegistrationHandler(
|
|||||||
Error: httpErr.Msg,
|
Error: httpErr.Msg,
|
||||||
}
|
}
|
||||||
return ®Req, resp
|
return ®Req, resp
|
||||||
} else {
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return ®Req, regErr(err)
|
return ®Req, regErr(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -289,3 +289,22 @@ func (ns *noiseServer) NoiseRegistrationHandler(
|
|||||||
writer.WriteHeader(http.StatusOK)
|
writer.WriteHeader(http.StatusOK)
|
||||||
writer.Write(respBody)
|
writer.Write(respBody)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// getAndValidateNode retrieves the node from the database using the NodeKey
|
||||||
|
// and validates that it matches the MachineKey from the Noise session.
|
||||||
|
func (ns *noiseServer) getAndValidateNode(mapRequest tailcfg.MapRequest) (*types.Node, error) {
|
||||||
|
node, err := ns.headscale.db.GetNodeByNodeKey(mapRequest.NodeKey)
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
|
return nil, NewHTTPError(http.StatusNotFound, "node not found", nil)
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate that the MachineKey in the Noise session matches the one associated with the NodeKey.
|
||||||
|
if ns.machineKey != node.MachineKey {
|
||||||
|
return nil, NewHTTPError(http.StatusNotFound, "node key in request does not match the one associated with this machine key", nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
return node, nil
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user