97 lines
2.1 KiB
Go
97 lines
2.1 KiB
Go
package auth
|
|
|
|
import (
|
|
"bytes"
|
|
"errors"
|
|
"strings"
|
|
"time"
|
|
|
|
"golang.org/x/crypto/ssh"
|
|
|
|
"git.maze.io/maze/conduit/logger"
|
|
)
|
|
|
|
var ErrUnauthorized = errors.New("unauthorized")
|
|
|
|
// Provider implements zero or more of the [Password], [PublicKey] interfaces.
|
|
type Provider any
|
|
|
|
// Password authenticator.
|
|
type Password interface {
|
|
VerifyPassword(meta ssh.ConnMetadata, password string) (Principal, error)
|
|
}
|
|
|
|
// Token authenticator.
|
|
type Token interface {
|
|
Instruction() string
|
|
|
|
Prompt() string
|
|
|
|
// Multiline accepts multiline input and wait for the user to supply
|
|
// an empty line.
|
|
Multiline() bool
|
|
|
|
VerifyToken(meta ssh.ConnMetadata, token string) (Principal, error)
|
|
}
|
|
|
|
// PublicKey authenticator.
|
|
type PublicKey interface {
|
|
VerifyPublicKey(meta ssh.ConnMetadata, publicKey ssh.PublicKey) (Principal, error)
|
|
}
|
|
|
|
type CertificateAuthority struct {
|
|
checker *ssh.CertChecker
|
|
keys []ssh.PublicKey
|
|
blob [][]byte
|
|
user bool
|
|
host bool
|
|
}
|
|
|
|
func NewUserCertificateAuthority(keys ...ssh.PublicKey) *CertificateAuthority {
|
|
var blob [][]byte
|
|
for _, key := range keys {
|
|
blob = append(blob, key.Marshal())
|
|
}
|
|
auth := &CertificateAuthority{
|
|
checker: &ssh.CertChecker{
|
|
Clock: time.Now,
|
|
},
|
|
keys: keys,
|
|
blob: blob,
|
|
user: true,
|
|
}
|
|
auth.checker.IsUserAuthority = auth.isUserAuthority
|
|
return auth
|
|
}
|
|
|
|
func (auth *CertificateAuthority) isUserAuthority(key ssh.PublicKey) bool {
|
|
if !auth.user {
|
|
return false
|
|
}
|
|
blob := key.Marshal()
|
|
for _, other := range auth.blob {
|
|
if bytes.Equal(blob, other) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func (auth *CertificateAuthority) VerifyPublicKey(meta ssh.ConnMetadata, publicKey ssh.PublicKey) (Principal, error) {
|
|
log := logger.StandardLog.Values(logger.Values{
|
|
"client": meta.RemoteAddr().String(),
|
|
"key": strings.TrimSpace(string(ssh.MarshalAuthorizedKey(publicKey))),
|
|
"user": meta.User(),
|
|
"version": string(meta.ClientVersion()),
|
|
})
|
|
log.Debug("Verifying user certificate")
|
|
|
|
_, err := auth.checker.Authenticate(meta, publicKey)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
cert := publicKey.(*ssh.Certificate)
|
|
return UserCertificatePrincipal{cert}, nil
|
|
}
|