wisski-cloud-distillery/internal/dis/component/ssh2/server_auth.go
Tom Wiesing 3455f491ca
Add context
This commit adds and passes context around to (almost) every function.
This allows cancelling (almost) every function call globally.
2022-11-29 15:32:31 +01:00

110 lines
2.5 KiB
Go

package ssh2
import (
"time"
"github.com/gliderlabs/ssh"
)
func (ssh2 *SSH2) setupAuth(server *ssh.Server) {
server.PublicKeyHandler = ssh2.handleAuth
}
// ssh2Key is a type of context keys for this package
type ssh2Key int
const (
// permissions represents the permissions for the given session
permission ssh2Key = iota
)
func setPermissions(context ssh.Context, permissions map[string]bool) {
context.SetValue(permission, permissions)
}
// hasPermission checks if the given context permits access to the given slug.
// The empty slug checks for global access.
func hasPermission(context ssh.Context, slug string) bool {
value, ok := context.Value(permission).(map[string]bool)
return ok && value[slug]
}
// getAnyPermission gets some instance the user has access to.
// If the user does not have access to anything, returns "", false.
// If the user has superuser access, but there are no instances, returns "", true.
func getAnyPermission(context ssh.Context) (string, bool) {
value, ok := context.Value(permission).(map[string]bool)
if !ok {
return "", false
}
for slug, ok := range value {
if ok && slug != "" {
return slug, true
}
}
return "", (false || value[""])
}
const authDelay = time.Second / 10
func (ssh2 *SSH2) handleAuth(ctx ssh.Context, key ssh.PublicKey) bool {
return slowdown(func() (ok bool) {
permissions := make(map[string]bool)
// grab the global permissions
{
globalKeys, err := ssh2.GlobalKeys()
if err != nil {
return false
}
permissions[""] = isKey(globalKeys, key)
ok = permissions[""]
}
// grab permissions for each instance
{
instances, err := ssh2.Instances.All(ctx)
if err != nil {
return false
}
for _, instance := range instances {
ikeys, err := instance.SSH().Keys()
if err != nil {
continue
}
access := isKey(ikeys, key)
permissions[instance.Slug] = access || permissions[""]
ok = ok || access
}
}
setPermissions(ctx, permissions)
return
}, authDelay)
}
// slowdown invokes f immediatly, but only returns the result to the caller after at least duration.
// It can be used to prevent timing attacks
func slowdown[T any](f func() T, duration time.Duration) T {
result := make(chan T, 1)
go func() {
result <- f()
}()
time.Sleep(duration)
return <-result
}
// isKey checks if keys contains key in O(len(keys))
func isKey(keys []ssh.PublicKey, key ssh.PublicKey) bool {
var res bool
for _, ak := range keys {
if ssh.KeysEqual(ak, key) {
res = true
}
}
return res
}