support challenger as plugin

This commit is contained in:
Boshi Lian 2018-02-19 18:21:43 +08:00
parent c2d67f63fa
commit f312057e85
10 changed files with 154 additions and 80 deletions

View file

@ -1,41 +1,36 @@
package challenger
import (
"fmt"
"sort"
"golang.org/x/crypto/ssh"
"github.com/tg123/sshpiper/sshpiperd/registry"
)
type Challenger func(conn ssh.ConnMetadata, client ssh.KeyboardInteractiveChallenge) (bool, error)
type ChallengerHandler func(conn ssh.ConnMetadata, client ssh.KeyboardInteractiveChallenge) (bool, error)
var challengers = make(map[string]Challenger)
type Challenger interface {
registry.Plugin
// copied from database/sql
func Register(name string, challenger Challenger) {
if challenger == nil {
panic("challenger is nil")
}
if _, dup := challengers[name]; dup {
panic("Register twice for challenger" + name)
}
challengers[name] = challenger
GetChallengerHandler() ChallengerHandler
}
func Challengers() []string {
var list []string
for name := range challengers {
list = append(list, name)
}
sort.Strings(list)
return list
var (
drivers = registry.NewRegistry()
)
func Register(name string, driver Challenger) {
drivers.Register(name, driver)
}
func GetChallenger(name string) (Challenger, error) {
challenger, ok := challengers[name]
if !ok {
return nil, fmt.Errorf("no such challenger:" + name)
}
return challenger, nil
func All() []string {
return drivers.Drivers()
}
func Get(name string) Challenger {
if d, ok := drivers.Get(name).(Challenger); ok {
return d
}
return nil
}

View file

@ -0,0 +1,6 @@
package loader
import (
_ "github.com/tg123/sshpiper/sshpiperd/challenger/pam"
_ "github.com/tg123/sshpiper/sshpiperd/challenger/welcometext"
)

View file

@ -0,0 +1 @@
package pam

View file

@ -1,12 +1,16 @@
// +build pam
package challenger
package pam
import (
"fmt"
pam "github.com/vvanpo/golang-pam"
"golang.org/x/crypto/ssh"
"os"
"golang.org/x/crypto/ssh"
pam "github.com/vvanpo/golang-pam"
"github.com/tg123/sshpiperd/challenger"
)
const (
@ -61,10 +65,10 @@ func pamChallenger(conn ssh.ConnMetadata, client ssh.KeyboardInteractiveChalleng
}
func init() {
if _, err := os.Stat(SSHPIPER_PAM_SERVICE_FILE); os.IsNotExist(err) {
return
}
Register("pam", pamChallenger)
challenger.Registry(challenger.NewFromHandler("pam", pamChallenger, nil, nil))
}

View file

@ -0,0 +1,42 @@
package challenger
import (
"log"
)
type plugin struct {
name string
init func(logger *log.Logger) error
opts interface{}
handler ChallengerHandler
}
func (p *plugin) GetName() string {
return p.name
}
func (p *plugin) GetOpts() interface{} {
return p.opts
}
func (p *plugin) GetChallengerHandler() ChallengerHandler {
return p.handler
}
func (p *plugin) Init(logger *log.Logger) error {
logger.Printf("challenger: %v init", p.name)
if p.init != nil {
return p.init(logger)
}
return nil
}
func NewFromHandler(name string, handler ChallengerHandler, opts interface{}, init func(glogger *log.Logger) error) Challenger {
return &plugin{
name: name,
init: init,
opts: opts,
handler: handler,
}
}

View file

@ -1,17 +0,0 @@
package challenger
import (
"golang.org/x/crypto/ssh"
)
// piper.AdditionalChallenge = challenger.MakeWelcomeChallenger("Please Use your phone to do the authentication")
func MakeWelcomeChallenger(text string) Challenger {
return func(conn ssh.ConnMetadata, client ssh.KeyboardInteractiveChallenge) (bool, error) {
client(conn.User(), text, nil, nil)
return true, nil
}
}

View file

@ -0,0 +1,32 @@
package challenger
import (
"log"
"golang.org/x/crypto/ssh"
"github.com/tg123/sshpiper/sshpiperd/challenger"
)
func makeWelcomeChallenger(text string) challenger.ChallengerHandler {
return func(conn ssh.ConnMetadata, client ssh.KeyboardInteractiveChallenge) (bool, error) {
client(conn.User(), text, nil, nil)
return true, nil
}
}
func init() {
var h challenger.ChallengerHandler
config := &struct {
WelcomeText string `long:"challenger-welcometext" description:"Show a welcome text when connect to sshpiper server" ini-name:"challenger-welcometext"`
}{}
challenger.Register("welcometext", challenger.NewFromHandler("welcometext", h, config, func(logger *log.Logger) error {
h = makeWelcomeChallenger(config.WelcomeText)
return nil
}))
}

View file

@ -6,6 +6,9 @@ import (
"github.com/jessevdk/go-flags"
"github.com/tg123/sshpiper/sshpiperd/challenger"
_ "github.com/tg123/sshpiper/sshpiperd/challenger/loader"
"github.com/tg123/sshpiper/sshpiperd/registry"
"github.com/tg123/sshpiper/sshpiperd/upstream"
_ "github.com/tg123/sshpiper/sshpiperd/upstream/loader"
)
@ -32,6 +35,25 @@ func addOpt(parser *flags.Parser, name string, data interface{}) {
}
}
func addPlugins(parser *flags.Parser, name string, pluginNames []string, getter func(n string) registry.Plugin) {
for _, n := range pluginNames {
p := getter(n)
opt := p.GetOpts()
if opt == nil {
continue
}
_, err := parser.AddGroup(name+"."+p.GetName(), "", opt)
if err != nil {
panic(err)
}
}
}
func main() {
parser := flags.NewNamedParser("sshpiperd", flags.Default)
@ -60,28 +82,11 @@ func main() {
Logfile string `long:"log" description:"Logfile path. Leave empty or any error occurs will fall back to stdout" env:"SSHPIPERD_LOG_PATH" ini-name:"log-path"`
ConfigFile flags.Filename `long:"config" description:"Config file path. Higher priority than arg options and environment variables" default:"/etc/sshpiperd.ini" no-ini:"true"`
}{}
addOpt(parser, "sshpiperd", config)
// registry upstream
//upstreamOpt := make(map[string]interface{})
for _, n := range upstream.All() {
u := upstream.Get(n)
opt := u.GetOpts()
if opt == nil {
continue
}
_, err := parser.AddGroup("upstream."+u.GetName(), "", opt)
if err != nil {
panic(err)
}
//upstreamOpt[u.GetName()] = opt
}
addPlugins(parser, "upstream", upstream.All(), func(n string) registry.Plugin { return upstream.Get(n) })
addPlugins(parser, "challenger", challenger.All(), func(n string) registry.Plugin { return challenger.Get(n) })
if _, err := parser.Parse(); err != nil {
return

View file

@ -16,18 +16,18 @@ type piperdConfig struct {
Port uint `short:"p" long:"port" description:"Listening Port" default:"2222" env:"SSHPIPERD_PORT" ini-name:"listen-port"`
PiperKeyFile string `short:"i" long:"server-key" description:"Server key file for SSH Piper" default:"/etc/ssh/ssh_host_rsa_key" env:"SSHPIPERD_SERVER_KEY" ini-name:"server-key"`
UpstreamDriver string `short:"d" long:"upstream-driver" description:"Upstream provider driver" default:"workingdir" env:"SSHPIPERD_UPSTREAM_DRIVER"`
ChallengerDriver string `short:"c" long:"challenger-driver" description:"Additional challenger name, e.g. pam, empty for no additional challenge" env:"SSHPIPERD_CHALLENGER"`
UpstreamDriver string `short:"u" long:"upstream-driver" description:"Upstream provider driver" default:"workingdir" env:"SSHPIPERD_UPSTREAM_DRIVER" ini-name:"upstream-driver"`
ChallengerDriver string `short:"c" long:"challenger-driver" description:"Additional challenger name, e.g. pam, empty for no additional challenge" env:"SSHPIPERD_CHALLENGER" ini-name:"challenger-driver"`
}
func startPiper(config *piperdConfig) {
logger.Println("sshpiper is about to start")
// init upstream
// install upstream driver
upstream := upstream.Get(config.UpstreamDriver)
if upstream == nil {
logger.Fatal("upstream driver %v not found", config.UpstreamDriver)
logger.Fatalf("upstream driver %v not found", config.UpstreamDriver)
}
upstream.Init(logger)
@ -35,15 +35,16 @@ func startPiper(config *piperdConfig) {
FindUpstream: upstream.GetFindUpstreamHandle(),
}
// TODO move to plugin
// install challenger
if config.ChallengerDriver != "" {
ac, err := challenger.GetChallenger(config.ChallengerDriver)
if err != nil {
logger.Fatalln("failed to load challenger", err)
ac := challenger.Get(config.ChallengerDriver)
if ac == nil {
logger.Fatalf("challenger driver %v not found", config.ChallengerDriver)
}
logger.Printf("using additional challenger %s", config.ChallengerDriver)
piper.AdditionalChallenge = ac
ac.Init(logger)
piper.AdditionalChallenge = ac.GetChallengerHandler()
}
privateBytes, err := ioutil.ReadFile(config.PiperKeyFile)

View file

@ -29,5 +29,10 @@ func All() []string {
}
func Get(name string) UpstreamProvider {
return drivers.Get(name).(UpstreamProvider)
if d, ok := drivers.Get(name).(UpstreamProvider); ok {
return d
}
return nil
}