|
|
@ -1,26 +1,22 @@ |
|
|
|
package core |
|
|
|
|
|
|
|
import ( |
|
|
|
"bytes" |
|
|
|
"errors" |
|
|
|
"io/ioutil" |
|
|
|
"os/user" |
|
|
|
"path/filepath" |
|
|
|
"strconv" |
|
|
|
|
|
|
|
"golang.org/x/crypto/ssh" |
|
|
|
|
|
|
|
"maze.io/gate/pkg/core/logger" |
|
|
|
"maze.io/gate/pkg/util/compact" |
|
|
|
) |
|
|
|
|
|
|
|
var ( |
|
|
|
// DefaultIdentityProvider uses the system authentication layer.
|
|
|
|
DefaultIdentityProvider systemIdentityProvider |
|
|
|
"maze.io/gate/pkg/util/compact" |
|
|
|
) |
|
|
|
|
|
|
|
// IdentityProvider provides user and groups.
|
|
|
|
type IdentityProvider interface { |
|
|
|
// Setup the IDP with a logger.
|
|
|
|
Setup(logger *logger.Logger) error |
|
|
|
|
|
|
|
// LookupUser resolves a user by login name.
|
|
|
|
LookupUser(name string) (User, error) |
|
|
|
|
|
|
|
// LookupGroup resolves a group by name.
|
|
|
|
LookupGroup(name string) (Group, error) |
|
|
|
|
|
|
|
// PasswordCallback, if non-nil, is called when a obj attempts to authenticate using a password.
|
|
|
@ -37,6 +33,7 @@ type IdentityProvider interface { |
|
|
|
PublicKeyCallback(conn ssh.ConnMetadata, key ssh.PublicKey) (*ssh.Permissions, error) |
|
|
|
} |
|
|
|
|
|
|
|
// User entity.
|
|
|
|
type User interface { |
|
|
|
ID() compact.ID |
|
|
|
Login() string |
|
|
@ -44,136 +41,9 @@ type User interface { |
|
|
|
Groups() []Group |
|
|
|
} |
|
|
|
|
|
|
|
// Group entity.
|
|
|
|
type Group interface { |
|
|
|
ID() compact.ID |
|
|
|
Name() string |
|
|
|
Members() []User |
|
|
|
} |
|
|
|
|
|
|
|
type systemIdentityProvider struct{} |
|
|
|
|
|
|
|
func (idp systemIdentityProvider) LookupUser(name string) (User, error) { |
|
|
|
u, err := user.Lookup(name) |
|
|
|
if err != nil { |
|
|
|
return nil, err |
|
|
|
} |
|
|
|
return systemUser{idp, u}, nil |
|
|
|
} |
|
|
|
|
|
|
|
func (idp systemIdentityProvider) LookupGroup(name string) (Group, error) { |
|
|
|
if _, err := strconv.Atoi(name); err == nil { |
|
|
|
g, err := user.LookupGroupId(name) |
|
|
|
if err != nil { |
|
|
|
return nil, err |
|
|
|
} |
|
|
|
return systemGroup{idp, g}, nil |
|
|
|
} |
|
|
|
g, err := user.LookupGroup(name) |
|
|
|
if err != nil { |
|
|
|
return nil, err |
|
|
|
} |
|
|
|
return systemGroup{idp, g}, nil |
|
|
|
} |
|
|
|
|
|
|
|
func (idp systemIdentityProvider) PasswordCallback(conn ssh.ConnMetadata, password []byte) (*ssh.Permissions, error) { |
|
|
|
user, err := idp.LookupUser(conn.User()) |
|
|
|
if err != nil { |
|
|
|
return nil, err |
|
|
|
} |
|
|
|
_ = user |
|
|
|
return nil, errors.New("password not implemented") |
|
|
|
} |
|
|
|
|
|
|
|
type systemKey struct { |
|
|
|
ssh.PublicKey |
|
|
|
Options []string |
|
|
|
} |
|
|
|
|
|
|
|
func (idp systemIdentityProvider) PublicKeyCallback(conn ssh.ConnMetadata, key ssh.PublicKey) (*ssh.Permissions, error) { |
|
|
|
log := logger.Default.WithFields(logger.Fields{ |
|
|
|
logger.User: conn.User(), |
|
|
|
"key": ssh.FingerprintSHA256(key), |
|
|
|
"key_type": key.Type(), |
|
|
|
}).WithSourceAddr(conn.RemoteAddr()) |
|
|
|
log.Debug("public key authentication") |
|
|
|
|
|
|
|
user, err := idp.LookupUser(conn.User()) |
|
|
|
if err != nil { |
|
|
|
return nil, err |
|
|
|
} |
|
|
|
|
|
|
|
name := filepath.Join(user.(systemUser).obj.HomeDir, ".ssh", "authorized_keys") |
|
|
|
b, err := ioutil.ReadFile(name) |
|
|
|
if err != nil { |
|
|
|
return nil, err |
|
|
|
} |
|
|
|
|
|
|
|
marshaled := key.Marshal() |
|
|
|
for len(b) > 0 { |
|
|
|
var ( |
|
|
|
authorizedKey ssh.PublicKey |
|
|
|
comment string |
|
|
|
) |
|
|
|
if authorizedKey, comment, _, b, err = ssh.ParseAuthorizedKey(b); err != nil { |
|
|
|
break |
|
|
|
} |
|
|
|
|
|
|
|
log.WithFields(logger.Fields{ |
|
|
|
"authorized_key": ssh.FingerprintSHA256(authorizedKey), |
|
|
|
"authorized_key_type": authorizedKey.Type(), |
|
|
|
"authorized_key_comment": comment, |
|
|
|
}).Debug("checking key") |
|
|
|
if bytes.Equal(authorizedKey.Marshal(), marshaled) { |
|
|
|
return &ssh.Permissions{ |
|
|
|
Extensions: map[string]string{ |
|
|
|
"gate-idp": "system", |
|
|
|
"gate-obj": user.Login(), |
|
|
|
}, |
|
|
|
}, nil |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
return nil, errors.New("key not found") |
|
|
|
} |
|
|
|
|
|
|
|
type systemUser struct { |
|
|
|
idp IdentityProvider |
|
|
|
obj *user.User |
|
|
|
} |
|
|
|
|
|
|
|
func (user systemUser) ID() compact.ID { |
|
|
|
return compact.String(user.obj.Uid) |
|
|
|
} |
|
|
|
|
|
|
|
func (user systemUser) Login() string { return user.obj.Username } |
|
|
|
func (user systemUser) Name() string { return user.obj.Name } |
|
|
|
|
|
|
|
func (user systemUser) Groups() []Group { |
|
|
|
var ( |
|
|
|
ids, _ = user.obj.GroupIds() |
|
|
|
groups = make([]Group, 0, len(ids)) |
|
|
|
) |
|
|
|
for _, id := range ids { |
|
|
|
if group, err := user.idp.LookupGroup(id); err == nil { |
|
|
|
groups = append(groups, group) |
|
|
|
} |
|
|
|
} |
|
|
|
return groups |
|
|
|
} |
|
|
|
|
|
|
|
type systemGroup struct { |
|
|
|
idp IdentityProvider |
|
|
|
obj *user.Group |
|
|
|
} |
|
|
|
|
|
|
|
func (group systemGroup) ID() compact.ID { |
|
|
|
return compact.String("gid=" + group.obj.Gid) |
|
|
|
} |
|
|
|
|
|
|
|
func (group systemGroup) Name() string { |
|
|
|
return group.obj.Name |
|
|
|
} |
|
|
|
|
|
|
|
func (group systemGroup) Members() []User { |
|
|
|
return nil |
|
|
|
} |