mirror of
https://github.com/netbirdio/netbird.git
synced 2026-10-01 19:19:07 +02:00
Describe session auth as SSH and VNC, cover the conflicting key path, fix de and ru wording
This commit is contained in:
@@ -1076,7 +1076,7 @@
|
|||||||
"message": "Eingehende Verbindung zulassen?"
|
"message": "Eingehende Verbindung zulassen?"
|
||||||
},
|
},
|
||||||
"approval.field.user": {
|
"approval.field.user": {
|
||||||
"message": "Von Benutzer"
|
"message": "Vom Benutzer"
|
||||||
},
|
},
|
||||||
"approval.field.keyFingerprint": {
|
"approval.field.keyFingerprint": {
|
||||||
"message": "Schlüssel-Fingerabdruck"
|
"message": "Schlüssel-Fingerabdruck"
|
||||||
|
|||||||
@@ -1427,15 +1427,15 @@
|
|||||||
"message": "Ожидание авторизации…"
|
"message": "Ожидание авторизации…"
|
||||||
},
|
},
|
||||||
"connect.activeSession.badge": {
|
"connect.activeSession.badge": {
|
||||||
"message": "Экран доступен",
|
"message": "Экран транслируется",
|
||||||
"description": "Short label on the badge shown on the main screen while somebody is attached to this machine over VNC."
|
"description": "Short label on the badge shown on the main screen while somebody is attached to this machine over VNC."
|
||||||
},
|
},
|
||||||
"connect.activeSession.tooltip": {
|
"connect.activeSession.tooltip": {
|
||||||
"message": "Этот экран просматривается по VNC ({sessionCount} сеанс(ов)). Отключение завершит его, и если вы подключены через VNC, вы потеряете доступ.",
|
"message": "Этот экран сейчас просматривают по VNC ({sessionCount} сеанс(ов)). Отключение завершит VNC-сеанс, и если вы подключены через VNC, вы потеряете доступ.",
|
||||||
"description": "Tooltip on the screen-shared badge. {sessionCount} is how many VNC sessions are attached; wording covers any number since the bundle has no plural forms."
|
"description": "Tooltip on the screen-shared badge. {sessionCount} is how many VNC sessions are attached; wording covers any number since the bundle has no plural forms."
|
||||||
},
|
},
|
||||||
"connect.activeSession.tooltipNamed": {
|
"connect.activeSession.tooltipNamed": {
|
||||||
"message": "Этот экран просматривает {who} по VNC ({sessionCount} сеанс(ов)). Отключение завершит его, и если вы подключены через VNC, вы потеряете доступ.",
|
"message": "Этот экран сейчас просматривает {who} по VNC ({sessionCount} сеанс(ов)). Отключение завершит VNC-сеанс, и если вы подключены через VNC, вы потеряете доступ.",
|
||||||
"description": "As connect.activeSession.tooltip, with {who} naming the dashboard users who started the sessions."
|
"description": "As connect.activeSession.tooltip, with {who} naming the dashboard users who started the sessions."
|
||||||
},
|
},
|
||||||
"settings.privilege.hint": {
|
"settings.privilege.hint": {
|
||||||
|
|||||||
@@ -27,7 +27,9 @@ var (
|
|||||||
ErrSessionKeyNotKnown = errors.New("session pubkey not registered")
|
ErrSessionKeyNotKnown = errors.New("session pubkey not registered")
|
||||||
)
|
)
|
||||||
|
|
||||||
// Authorizer handles SSH fine-grained access control authorization
|
// Authorizer handles fine-grained access control authorization for the
|
||||||
|
// remote-access servers: SSH by hashed user identity and OS login name, VNC by
|
||||||
|
// the session public key management issued for a temporary-access grant.
|
||||||
type Authorizer struct {
|
type Authorizer struct {
|
||||||
// UserIDClaim is the JWT claim to extract the user ID from
|
// UserIDClaim is the JWT claim to extract the user ID from
|
||||||
userIDClaim string
|
userIDClaim string
|
||||||
@@ -53,7 +55,7 @@ type Authorizer struct {
|
|||||||
mu sync.RWMutex
|
mu sync.RWMutex
|
||||||
}
|
}
|
||||||
|
|
||||||
// Config contains configuration for the SSH authorizer
|
// Config contains configuration for the session authorizer
|
||||||
type Config struct {
|
type Config struct {
|
||||||
// UserIDClaim is the JWT claim to extract the user ID from (e.g., "sub", "email")
|
// UserIDClaim is the JWT claim to extract the user ID from (e.g., "sub", "email")
|
||||||
UserIDClaim string
|
UserIDClaim string
|
||||||
@@ -80,7 +82,7 @@ type SessionPubKey struct {
|
|||||||
DisplayName string
|
DisplayName string
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewAuthorizer creates a new SSH authorizer with empty configuration
|
// NewAuthorizer creates a new session authorizer with empty configuration
|
||||||
func NewAuthorizer() *Authorizer {
|
func NewAuthorizer() *Authorizer {
|
||||||
a := &Authorizer{
|
a := &Authorizer{
|
||||||
userIDClaim: DefaultUserIDClaim,
|
userIDClaim: DefaultUserIDClaim,
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package sessionauth
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
@@ -399,11 +400,27 @@ func TestAuthorizer_ConcurrentAuthorization(t *testing.T) {
|
|||||||
}(i)
|
}(i)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Reapply the same config while the reads above are in flight: Update
|
||||||
|
// replaces authorizedUsers, machineUsers and sessionPubKeys wholesale, so
|
||||||
|
// the race detector has something to catch if any of them is read unguarded.
|
||||||
|
const numUpdaters = 10
|
||||||
|
var updaters sync.WaitGroup
|
||||||
|
updaters.Add(numUpdaters)
|
||||||
|
for range numUpdaters {
|
||||||
|
go func() {
|
||||||
|
defer updaters.Done()
|
||||||
|
for range 50 {
|
||||||
|
authorizer.Update(config)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
// Wait for all goroutines to complete and collect errors
|
// Wait for all goroutines to complete and collect errors
|
||||||
for i := 0; i < numGoroutines; i++ {
|
for i := 0; i < numGoroutines; i++ {
|
||||||
err := <-errChan
|
err := <-errChan
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
}
|
}
|
||||||
|
updaters.Wait()
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAuthorizer_Wildcard_AllowsAllAuthorizedUsers(t *testing.T) {
|
func TestAuthorizer_Wildcard_AllowsAllAuthorizedUsers(t *testing.T) {
|
||||||
@@ -662,6 +679,31 @@ func TestAuthorizer_LookupSessionKey_UpdateClears(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A session pubkey configured twice with different user hashes cannot be
|
||||||
|
// resolved to one identity, so Update drops the binding rather than picking a
|
||||||
|
// winner: the key must not authenticate anybody.
|
||||||
|
func TestAuthorizer_SessionKey_ConflictingDuplicateIsDropped(t *testing.T) {
|
||||||
|
pub := bytesRepeat(0x44, sessionPubKeyLen)
|
||||||
|
aliceHash, err := sshauth.HashUserID("alice")
|
||||||
|
require.NoError(t, err)
|
||||||
|
bobHash, err := sshauth.HashUserID("bob")
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
a := NewAuthorizer()
|
||||||
|
a.Update(&Config{
|
||||||
|
AuthorizedUsers: []sshauth.UserIDHash{aliceHash, bobHash},
|
||||||
|
MachineUsers: map[string][]uint32{Wildcard: {0, 1}},
|
||||||
|
SessionPubKeys: []SessionPubKey{
|
||||||
|
{PubKey: pub, UserIDHash: aliceHash, DisplayName: "Alice"},
|
||||||
|
{PubKey: pub, UserIDHash: bobHash, DisplayName: "Bob"},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
_, err = a.LookupSessionKey(pub)
|
||||||
|
require.ErrorIs(t, err, ErrSessionKeyNotKnown, "a conflicting key must authenticate nobody")
|
||||||
|
assert.Empty(t, a.LookupSessionDisplayName(pub), "the display name goes with the dropped binding")
|
||||||
|
}
|
||||||
|
|
||||||
func bytesRepeat(b byte, n int) []byte {
|
func bytesRepeat(b byte, n int) []byte {
|
||||||
out := make([]byte, n)
|
out := make([]byte, n)
|
||||||
for i := range out {
|
for i := range out {
|
||||||
|
|||||||
Reference in New Issue
Block a user