Merge pull request #211 from HyNetwork/wip-more-auth

feat: multi-password & cmd auth
This commit is contained in:
Toby 2022-01-24 17:27:08 -08:00 committed by GitHub
commit 1430c81f90
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 114 additions and 34 deletions

30
cmd/auth/cmd.go Normal file
View File

@ -0,0 +1,30 @@
package auth
import (
"github.com/sirupsen/logrus"
"net"
"os/exec"
"strconv"
"strings"
)
type CmdAuthProvider struct {
Cmd string
}
func (p *CmdAuthProvider) Auth(addr net.Addr, auth []byte, sSend uint64, sRecv uint64) (bool, string) {
cmd := exec.Command(p.Cmd, addr.String(), string(auth), strconv.Itoa(int(sSend)), strconv.Itoa(int(sRecv)))
out, err := cmd.Output()
if err != nil {
if _, ok := err.(*exec.ExitError); ok {
return false, strings.TrimSpace(string(out))
} else {
logrus.WithFields(logrus.Fields{
"error": err,
}).Error("Failed to execute auth command")
return false, "internal error"
}
} else {
return true, strings.TrimSpace(string(out))
}
}

View File

@ -3,6 +3,7 @@ package auth
import ( import (
"bytes" "bytes"
"encoding/json" "encoding/json"
"github.com/sirupsen/logrus"
"io/ioutil" "io/ioutil"
"net" "net"
"net/http" "net/http"
@ -33,24 +34,39 @@ func (p *HTTPAuthProvider) Auth(addr net.Addr, auth []byte, sSend uint64, sRecv
Recv: sRecv, Recv: sRecv,
}) })
if err != nil { if err != nil {
return false, "Internal error" logrus.WithFields(logrus.Fields{
"error": err,
}).Error("Failed to marshal auth request")
return false, "internal error"
} }
resp, err := p.Client.Post(p.URL, "application/json", bytes.NewBuffer(jbs)) resp, err := p.Client.Post(p.URL, "application/json", bytes.NewBuffer(jbs))
if err != nil { if err != nil {
return false, "Internal error" logrus.WithFields(logrus.Fields{
"error": err,
}).Error("Failed to send auth request")
return false, "internal error"
} }
defer resp.Body.Close() defer resp.Body.Close()
if resp.StatusCode != http.StatusOK { if resp.StatusCode != http.StatusOK {
return false, "Auth endpoint error" logrus.WithFields(logrus.Fields{
"code": resp.StatusCode,
}).Error("Invalid status code from auth server")
return false, "internal error"
} }
data, err := ioutil.ReadAll(resp.Body) data, err := ioutil.ReadAll(resp.Body)
if err != nil { if err != nil {
return false, "Auth endpoint error" logrus.WithFields(logrus.Fields{
"error": err,
}).Error("Failed to read auth response")
return false, "internal error"
} }
var ar authResp var ar authResp
err = json.Unmarshal(data, &ar) err = json.Unmarshal(data, &ar)
if err != nil { if err != nil {
return false, "Auth endpoint error" logrus.WithFields(logrus.Fields{
"error": err,
}).Error("Failed to unmarshal auth response")
return false, "internal error"
} }
return ar.OK, ar.Msg return ar.OK, ar.Msg
} }

View File

@ -131,7 +131,7 @@ func initApp(c *cli.Context) error {
"version", "url", "version", "url",
"config", "file", "mode", "config", "file", "mode",
"addr", "src", "dst", "session", "action", "addr", "src", "dst", "session", "action",
"msg", "error", "code", "msg", "error",
}, },
TimestampFormat: c.String("log-timestamp"), TimestampFormat: c.String("log-timestamp"),
}) })

View File

@ -2,14 +2,15 @@ package main
import ( import (
"crypto/tls" "crypto/tls"
"errors"
"github.com/lucas-clemente/quic-go" "github.com/lucas-clemente/quic-go"
"github.com/lucas-clemente/quic-go/congestion" "github.com/lucas-clemente/quic-go/congestion"
"github.com/oschwald/geoip2-golang" "github.com/oschwald/geoip2-golang"
"github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp" "github.com/prometheus/client_golang/prometheus/promhttp"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
"github.com/tobyxdd/hysteria/cmd/auth"
"github.com/tobyxdd/hysteria/pkg/acl" "github.com/tobyxdd/hysteria/pkg/acl"
"github.com/tobyxdd/hysteria/pkg/auth"
hyCongestion "github.com/tobyxdd/hysteria/pkg/congestion" hyCongestion "github.com/tobyxdd/hysteria/pkg/congestion"
"github.com/tobyxdd/hysteria/pkg/core" "github.com/tobyxdd/hysteria/pkg/core"
"github.com/tobyxdd/hysteria/pkg/obfs" "github.com/tobyxdd/hysteria/pkg/obfs"
@ -84,7 +85,7 @@ func server(config *serverConfig) {
quicConfig.MaxIncomingStreams = DefaultMaxIncomingStreams quicConfig.MaxIncomingStreams = DefaultMaxIncomingStreams
} }
// Auth // Auth
var authFunc func(addr net.Addr, auth []byte, sSend uint64, sRecv uint64) (bool, string) var authFunc core.ConnectFunc
var err error var err error
switch authMode := config.Auth.Mode; authMode { switch authMode := config.Auth.Mode; authMode {
case "", "none": case "", "none":
@ -95,39 +96,24 @@ func server(config *serverConfig) {
authFunc = func(addr net.Addr, auth []byte, sSend uint64, sRecv uint64) (bool, string) { authFunc = func(addr net.Addr, auth []byte, sSend uint64, sRecv uint64) (bool, string) {
return true, "Welcome" return true, "Welcome"
} }
case "password": case "password", "passwords":
logrus.Info("Password authentication enabled") authFunc, err = passwordAuthFunc(config.Auth.Config)
var pwdConfig map[string]string if err != nil {
err = json5.Unmarshal(config.Auth.Config, &pwdConfig)
if err != nil || len(pwdConfig["password"]) == 0 {
logrus.WithFields(logrus.Fields{ logrus.WithFields(logrus.Fields{
"error": err, "error": err,
}).Fatal("Invalid password authentication config") }).Fatal("Failed to enable password authentication")
} } else {
pwd := pwdConfig["password"] logrus.Info("Password authentication enabled")
authFunc = func(addr net.Addr, auth []byte, sSend uint64, sRecv uint64) (bool, string) {
if string(auth) == pwd {
return true, "Welcome"
} else {
return false, "Wrong password"
}
} }
case "external": case "external":
logrus.Info("External authentication enabled") authFunc, err = externalAuthFunc(config.Auth.Config)
var extConfig map[string]string if err != nil {
err = json5.Unmarshal(config.Auth.Config, &extConfig)
if err != nil || len(extConfig["http"]) == 0 {
logrus.WithFields(logrus.Fields{ logrus.WithFields(logrus.Fields{
"error": err, "error": err,
}).Fatal("Invalid external authentication config") }).Fatal("Failed to enable external authentication")
} else {
logrus.Info("External authentication enabled")
} }
provider := &auth.HTTPAuthProvider{
Client: &http.Client{
Timeout: 10 * time.Second,
},
URL: extConfig["http"],
}
authFunc = provider.Auth
default: default:
logrus.WithField("mode", config.Auth.Mode).Fatal("Unsupported authentication mode") logrus.WithField("mode", config.Auth.Mode).Fatal("Unsupported authentication mode")
} }
@ -199,6 +185,54 @@ func server(config *serverConfig) {
logrus.WithField("error", err).Fatal("Server shutdown") logrus.WithField("error", err).Fatal("Server shutdown")
} }
func passwordAuthFunc(rawMsg json5.RawMessage) (core.ConnectFunc, error) {
var pwds []string
err := json5.Unmarshal(rawMsg, &pwds)
if err != nil {
// not a string list, legacy format?
var pwdConfig map[string]string
err = json5.Unmarshal(rawMsg, &pwdConfig)
if err != nil || len(pwdConfig["password"]) == 0 {
// still no, invalid config
return nil, errors.New("invalid config")
}
// yes it is
pwds = []string{pwdConfig["password"]}
}
return func(addr net.Addr, auth []byte, sSend uint64, sRecv uint64) (bool, string) {
for _, pwd := range pwds {
if string(auth) == pwd {
return true, "Welcome"
}
}
return false, "Wrong password"
}, nil
}
func externalAuthFunc(rawMsg json5.RawMessage) (core.ConnectFunc, error) {
var extConfig map[string]string
err := json5.Unmarshal(rawMsg, &extConfig)
if err != nil {
return nil, errors.New("invalid config")
}
if len(extConfig["http"]) != 0 {
hp := &auth.HTTPAuthProvider{
Client: &http.Client{
Timeout: 10 * time.Second,
},
URL: extConfig["http"],
}
return hp.Auth, nil
} else if len(extConfig["cmd"]) != 0 {
cp := &auth.CmdAuthProvider{
Cmd: extConfig["cmd"],
}
return cp.Auth, nil
} else {
return nil, errors.New("invalid config")
}
}
func disconnectFunc(addr net.Addr, auth []byte, err error) { func disconnectFunc(addr net.Addr, auth []byte, err error) {
logrus.WithFields(logrus.Fields{ logrus.WithFields(logrus.Fields{
"src": addr, "src": addr,