Skip to content

Commit b8ae374

Browse files
authored
Merge pull request #334 from IbrahimAhmed8/fix_auth_bypass
enforce biscuit expiry and revocation in node relay
2 parents e8af068 + 23b76ff commit b8ae374

2 files changed

Lines changed: 38 additions & 52 deletions

File tree

internal/node/node.go

Lines changed: 25 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -891,27 +891,22 @@ func (n *SamNode) performRouterAuthHandshake(s network.Stream, biscuitBytes []by
891891
return false, fmt.Errorf("%w: remote router returned empty biscuit", ErrFatalAuth)
892892
}
893893

894-
n.keysMu.RLock()
895-
var trustedKeys []ed25519.PublicKey
896-
for _, tk := range n.trustedKeys {
897-
trustedKeys = append(trustedKeys, tk.Key)
898-
}
899-
n.keysMu.RUnlock()
894+
trustedKeys := n.getTrustedPublicKeys()
900895

901896
if len(trustedKeys) == 0 {
902897
return false, fmt.Errorf("%w: no trusted control plane keys loaded", ErrFatalAuth)
903898
}
904899

905900
// Verify the router's biscuit using the control plane keys
906-
b, verifiedKey, err := identity.VerifyBiscuitAndGetKey(resp.Biscuit, expectedRouter, trustedKeys, n.BiscuitTimeout)
901+
b, verifyingKey, err := identity.VerifyBiscuitAndGetKey(resp.Biscuit, expectedRouter, trustedKeys, n.BiscuitTimeout)
907902
if err != nil {
908903
return false, fmt.Errorf("%w: failed to verify router biscuit: %w", ErrFatalAuth, err)
909904
}
910905

911906
// Enforce role("router") inside the biscuit, under the key that verified:
912907
// with several valid keys loaded (rotation grace) the first is not
913908
// necessarily the signer.
914-
authorizer, err := b.Authorizer(verifiedKey, identity.AuthorizerOptions(n.BiscuitTimeout)...)
909+
authorizer, err := b.Authorizer(verifyingKey, identity.AuthorizerOptions(n.BiscuitTimeout)...)
915910
if err != nil {
916911
return false, fmt.Errorf("authorizer instantiation failed: %w", err)
917912
}
@@ -1508,6 +1503,16 @@ func (n *SamNode) startDiscovery(ctx context.Context, meshID string, interval ti
15081503
}
15091504
}
15101505

1506+
func (n *SamNode) getTrustedPublicKeys() []ed25519.PublicKey {
1507+
n.keysMu.RLock()
1508+
defer n.keysMu.RUnlock()
1509+
keys := make([]ed25519.PublicKey, 0, len(n.trustedKeys))
1510+
for _, tk := range n.trustedKeys {
1511+
keys = append(keys, tk.Key)
1512+
}
1513+
return keys
1514+
}
1515+
15111516
// HandleAuthHandshake is the core libp2p stream handler for /sam/auth/1.0.0.
15121517
// This is the "Admission Office" of the mesh node.
15131518
func (n *SamNode) HandleAuthHandshake(s network.Stream) {
@@ -1539,7 +1544,7 @@ func (n *SamNode) HandleAuthHandshake(s network.Stream) {
15391544
return
15401545
}
15411546

1542-
b, expiry, err := n.verifyBiscuit(exchange.Biscuit, remotePeer)
1547+
b, verifyingKey, err := identity.VerifyBiscuitAndGetKey(exchange.Biscuit, remotePeer, n.getTrustedPublicKeys(), n.BiscuitTimeout)
15431548
if err != nil {
15441549
logger.Warnf("[AuthN] Authorization failed for %s: %v", remotePeer, err)
15451550
return
@@ -1551,6 +1556,17 @@ func (n *SamNode) HandleAuthHandshake(s network.Stream) {
15511556
return
15521557
}
15531558

1559+
expiry := time.Now().Add(n.BiscuitTimeout)
1560+
if authorizer, authErr := b.Authorizer(verifyingKey, identity.AuthorizerOptions(n.BiscuitTimeout)...); authErr == nil {
1561+
identity.EnforceExpiration(authorizer)
1562+
authorizer.AddPolicy(api.AllowIfTruePolicy)
1563+
if authErr := authorizer.Authorize(); authErr == nil {
1564+
if e, expErr := identity.ExpirationOf(authorizer); expErr == nil {
1565+
expiry = e
1566+
}
1567+
}
1568+
}
1569+
15541570
n.authPeers.Store(remotePeer, expiry)
15551571
logger.Infof("[AuthN] Successfully authenticated peer %s", remotePeer)
15561572

@@ -1563,47 +1579,6 @@ func (n *SamNode) HandleAuthHandshake(s network.Stream) {
15631579
}
15641580
}
15651581

1566-
func (n *SamNode) verifyBiscuit(biscuitData []byte, remotePeer peer.ID) (*biscuit.Biscuit, time.Time, error) {
1567-
b, err := biscuit.Unmarshal(biscuitData)
1568-
if err != nil {
1569-
return nil, time.Time{}, fmt.Errorf("malformed biscuit: %w", err)
1570-
}
1571-
1572-
n.keysMu.RLock()
1573-
keys := n.trustedKeys
1574-
n.keysMu.RUnlock()
1575-
1576-
var lastErr error
1577-
for _, tk := range keys {
1578-
if len(tk.Key) != ed25519.PublicKeySize {
1579-
continue
1580-
}
1581-
authorizer, err := b.Authorizer(tk.Key, identity.AuthorizerOptions(n.BiscuitTimeout)...)
1582-
if err != nil {
1583-
lastErr = fmt.Errorf("authorizer error: %w", err)
1584-
continue
1585-
}
1586-
1587-
identity.EnforceExpiration(authorizer)
1588-
authorizer.AddPolicy(api.AllowIfTruePolicy)
1589-
1590-
if err := authorizer.Authorize(); err == nil {
1591-
expiry, err := identity.ExpirationOf(authorizer)
1592-
if err != nil {
1593-
return nil, time.Time{}, err
1594-
}
1595-
return b, expiry, nil
1596-
} else {
1597-
lastErr = fmt.Errorf("authorize error: %w", err)
1598-
}
1599-
}
1600-
1601-
if lastErr != nil {
1602-
return nil, time.Time{}, fmt.Errorf("no valid key found (last error: %v)", lastErr)
1603-
}
1604-
return nil, time.Time{}, fmt.Errorf("no valid key found")
1605-
}
1606-
16071582
func (n *SamNode) RegisterService(ctx context.Context, req *api.RegisterServiceRequest) error {
16081583
svc, err := NewServiceFromRequest(req)
16091584
if err != nil {

internal/node/node_test.go

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -295,16 +295,27 @@ func TestVerifyBiscuitRejectsExpiredToken(t *testing.T) {
295295
}
296296

297297
want := time.Now().Add(time.Hour)
298-
_, expiry, err := node.verifyBiscuit(mint(want), remotePeer)
298+
trustedKeys := node.getTrustedPublicKeys()
299+
b, verifyingKey, err := identity.VerifyBiscuitAndGetKey(mint(want), remotePeer, trustedKeys, node.BiscuitTimeout)
299300
if err != nil {
300301
t.Fatalf("valid token rejected: %v", err)
301302
}
303+
expiry := time.Now().Add(node.BiscuitTimeout)
304+
if authorizer, authErr := b.Authorizer(verifyingKey, identity.AuthorizerOptions(node.BiscuitTimeout)...); authErr == nil {
305+
identity.EnforceExpiration(authorizer)
306+
authorizer.AddPolicy(api.AllowIfTruePolicy)
307+
if authErr := authorizer.Authorize(); authErr == nil {
308+
if e, expErr := identity.ExpirationOf(authorizer); expErr == nil {
309+
expiry = e
310+
}
311+
}
312+
}
302313
// The admission is cached against this instant, so it has to be the token's.
303314
if skew := expiry.Sub(want); skew < -time.Second || skew > time.Second {
304315
t.Errorf("reported expiry %v, want ~%v", expiry, want)
305316
}
306317

307-
if _, _, err := node.verifyBiscuit(mint(time.Now().Add(-time.Hour)), remotePeer); err == nil {
318+
if _, _, err := identity.VerifyBiscuitAndGetKey(mint(time.Now().Add(-time.Hour)), remotePeer, trustedKeys, node.BiscuitTimeout); err == nil {
308319
t.Fatal("expired token admitted on the peer-authentication path")
309320
}
310321
}

0 commit comments

Comments
 (0)