#57: add multi user auth support for HTTP and SOCKS5
This commit is contained in:
parent
e22229b5b1
commit
c5fabde5f8
10
chain.go
10
chain.go
@ -86,9 +86,9 @@ func (c *ProxyChain) Init() {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
glog.V(LWARNING).Infoln("[kcp]", err)
|
glog.V(LWARNING).Infoln("[kcp]", err)
|
||||||
}
|
}
|
||||||
if c.nodes[0].User != nil {
|
if c.nodes[0].Users != nil {
|
||||||
config.Crypt = c.nodes[0].User.Username()
|
config.Crypt = c.nodes[0].Users[0].Username()
|
||||||
config.Key, _ = c.nodes[0].User.Password()
|
config.Key, _ = c.nodes[0].Users[0].Password()
|
||||||
}
|
}
|
||||||
c.kcpConfig = config
|
c.kcpConfig = config
|
||||||
return
|
return
|
||||||
@ -349,9 +349,9 @@ func (c *ProxyChain) http2Connect(addr string) (net.Conn, error) {
|
|||||||
|
|
||||||
header := make(http.Header)
|
header := make(http.Header)
|
||||||
header.Set("Gost-Target", addr) // Flag header to indicate the address that server connected to
|
header.Set("Gost-Target", addr) // Flag header to indicate the address that server connected to
|
||||||
if http2Node.User != nil {
|
if http2Node.Users != nil {
|
||||||
header.Set("Proxy-Authorization",
|
header.Set("Proxy-Authorization",
|
||||||
"Basic "+base64.StdEncoding.EncodeToString([]byte(http2Node.User.String())))
|
"Basic "+base64.StdEncoding.EncodeToString([]byte(http2Node.Users[0].String())))
|
||||||
}
|
}
|
||||||
return c.getHttp2Conn(header)
|
return c.getHttp2Conn(header)
|
||||||
}
|
}
|
||||||
|
@ -46,7 +46,7 @@ func init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if printVersion {
|
if printVersion {
|
||||||
fmt.Fprintf(os.Stderr, "GOST %s (%s)\n", gost.Version, runtime.Version())
|
fmt.Fprintf(os.Stderr, "gost %s (%s)\n", gost.Version, runtime.Version())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
4
cmd/gost/secrets.txt
Normal file
4
cmd/gost/secrets.txt
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
# username password
|
||||||
|
|
||||||
|
test001 123456
|
||||||
|
test002 12345678
|
20
conn.go
20
conn.go
@ -95,7 +95,10 @@ func (c *ProxyConn) handshake() error {
|
|||||||
gosocks5.MethodUserPass,
|
gosocks5.MethodUserPass,
|
||||||
//MethodTLS,
|
//MethodTLS,
|
||||||
},
|
},
|
||||||
user: c.Node.User,
|
}
|
||||||
|
|
||||||
|
if len(c.Node.Users) > 0 {
|
||||||
|
selector.user = c.Node.Users[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
if !tlsUsed { // if transport is not security, enable security socks5
|
if !tlsUsed { // if transport is not security, enable security socks5
|
||||||
@ -112,9 +115,9 @@ func (c *ProxyConn) handshake() error {
|
|||||||
}
|
}
|
||||||
c.conn = conn
|
c.conn = conn
|
||||||
case "ss": // shadowsocks
|
case "ss": // shadowsocks
|
||||||
if c.Node.User != nil {
|
if len(c.Node.Users) > 0 {
|
||||||
method := c.Node.User.Username()
|
method := c.Node.Users[0].Username()
|
||||||
password, _ := c.Node.User.Password()
|
password, _ := c.Node.Users[0].Password()
|
||||||
cipher, err := shadowsocks.NewCipher(method, password)
|
cipher, err := shadowsocks.NewCipher(method, password)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -191,9 +194,14 @@ func (c *ProxyConn) Connect(addr string) error {
|
|||||||
Header: make(http.Header),
|
Header: make(http.Header),
|
||||||
}
|
}
|
||||||
req.Header.Set("Proxy-Connection", "keep-alive")
|
req.Header.Set("Proxy-Connection", "keep-alive")
|
||||||
if c.Node.User != nil {
|
if len(c.Node.Users) > 0 {
|
||||||
|
user := c.Node.Users[0]
|
||||||
|
s := user.String()
|
||||||
|
if _, set := user.Password(); !set {
|
||||||
|
s += ":"
|
||||||
|
}
|
||||||
req.Header.Set("Proxy-Authorization",
|
req.Header.Set("Proxy-Authorization",
|
||||||
"Basic "+base64.StdEncoding.EncodeToString([]byte(c.Node.User.String())))
|
"Basic "+base64.StdEncoding.EncodeToString([]byte(s)))
|
||||||
}
|
}
|
||||||
if err := req.Write(c); err != nil {
|
if err := req.Write(c); err != nil {
|
||||||
return err
|
return err
|
||||||
|
2
gost.go
2
gost.go
@ -11,7 +11,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
Version = "2.3-dev"
|
Version = "2.3-rc1"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Log level for glog
|
// Log level for glog
|
||||||
|
53
http.go
53
http.go
@ -44,15 +44,21 @@ func (s *HttpServer) HandleRequest(req *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var username, password string
|
valid := false
|
||||||
if s.Base.Node.User != nil {
|
u, p, _ := basicProxyAuth(req.Header.Get("Proxy-Authorization"))
|
||||||
username = s.Base.Node.User.Username()
|
glog.V(LINFO).Infoln(u, p)
|
||||||
password, _ = s.Base.Node.User.Password()
|
for _, user := range s.Base.Node.Users {
|
||||||
|
username := user.Username()
|
||||||
|
password, _ := user.Password()
|
||||||
|
if (u == username && p == password) ||
|
||||||
|
(u == username && password == "") ||
|
||||||
|
(username == "" && p == password) {
|
||||||
|
valid = true
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
u, p, _ := basicProxyAuth(req.Header.Get("Proxy-Authorization"))
|
if len(s.Base.Node.Users) > 0 && !valid {
|
||||||
req.Header.Del("Proxy-Authorization")
|
|
||||||
if (username != "" && u != username) || (password != "" && p != password) {
|
|
||||||
glog.V(LWARNING).Infof("[http] %s <- %s : proxy authentication required", s.conn.RemoteAddr(), req.Host)
|
glog.V(LWARNING).Infof("[http] %s <- %s : proxy authentication required", s.conn.RemoteAddr(), req.Host)
|
||||||
resp := "HTTP/1.1 407 Proxy Authentication Required\r\n" +
|
resp := "HTTP/1.1 407 Proxy Authentication Required\r\n" +
|
||||||
"Proxy-Authenticate: Basic realm=\"gost\"\r\n" +
|
"Proxy-Authenticate: Basic realm=\"gost\"\r\n" +
|
||||||
@ -61,6 +67,8 @@ func (s *HttpServer) HandleRequest(req *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
req.Header.Del("Proxy-Authorization")
|
||||||
|
|
||||||
// forward http request
|
// forward http request
|
||||||
lastNode := s.Base.Chain.lastNode
|
lastNode := s.Base.Chain.lastNode
|
||||||
if lastNode != nil && (lastNode.Protocol == "http" || lastNode.Protocol == "") {
|
if lastNode != nil && (lastNode.Protocol == "http" || lastNode.Protocol == "") {
|
||||||
@ -117,9 +125,14 @@ func (s *HttpServer) forwardRequest(req *http.Request) {
|
|||||||
}
|
}
|
||||||
defer cc.Close()
|
defer cc.Close()
|
||||||
|
|
||||||
if last.User != nil {
|
if len(last.Users) > 0 {
|
||||||
|
user := last.Users[0]
|
||||||
|
s := user.String()
|
||||||
|
if _, set := user.Password(); !set {
|
||||||
|
s += ":"
|
||||||
|
}
|
||||||
req.Header.Set("Proxy-Authorization",
|
req.Header.Set("Proxy-Authorization",
|
||||||
"Basic "+base64.StdEncoding.EncodeToString([]byte(last.User.String())))
|
"Basic "+base64.StdEncoding.EncodeToString([]byte(s)))
|
||||||
}
|
}
|
||||||
|
|
||||||
cc.SetWriteDeadline(time.Now().Add(WriteTimeout))
|
cc.SetWriteDeadline(time.Now().Add(WriteTimeout))
|
||||||
@ -184,20 +197,26 @@ func (s *Http2Server) HandleRequest(w http.ResponseWriter, req *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var username, password string
|
valid := false
|
||||||
if s.Base.Node.User != nil {
|
|
||||||
username = s.Base.Node.User.Username()
|
|
||||||
password, _ = s.Base.Node.User.Password()
|
|
||||||
}
|
|
||||||
|
|
||||||
u, p, _ := basicProxyAuth(req.Header.Get("Proxy-Authorization"))
|
u, p, _ := basicProxyAuth(req.Header.Get("Proxy-Authorization"))
|
||||||
req.Header.Del("Proxy-Authorization")
|
for _, user := range s.Base.Node.Users {
|
||||||
if (username != "" && u != username) || (password != "" && p != password) {
|
username := user.Username()
|
||||||
|
password, _ := user.Password()
|
||||||
|
if (u == username && p == password) ||
|
||||||
|
(u == username && password == "") ||
|
||||||
|
(username == "" && p == password) {
|
||||||
|
valid = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(s.Base.Node.Users) > 0 && !valid {
|
||||||
glog.V(LWARNING).Infof("[http2] %s <- %s : proxy authentication required", req.RemoteAddr, target)
|
glog.V(LWARNING).Infof("[http2] %s <- %s : proxy authentication required", req.RemoteAddr, target)
|
||||||
w.WriteHeader(http.StatusProxyAuthRequired)
|
w.WriteHeader(http.StatusProxyAuthRequired)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
req.Header.Del("Proxy-Authorization")
|
||||||
|
|
||||||
c, err := s.Base.Chain.Dial(target)
|
c, err := s.Base.Chain.Dial(target)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
glog.V(LWARNING).Infof("[http2] %s -> %s : %s", req.RemoteAddr, target, err)
|
glog.V(LWARNING).Infof("[http2] %s -> %s : %s", req.RemoteAddr, target, err)
|
||||||
|
54
node.go
54
node.go
@ -1,20 +1,23 @@
|
|||||||
package gost
|
package gost
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bufio"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/golang/glog"
|
||||||
"net"
|
"net"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Proxy node represent a proxy
|
// Proxy node represent a proxy
|
||||||
type ProxyNode struct {
|
type ProxyNode struct {
|
||||||
Addr string // [host]:port
|
Addr string // [host]:port
|
||||||
Protocol string // protocol: http/socks5/ss
|
Protocol string // protocol: http/socks5/ss
|
||||||
Transport string // transport: ws/wss/tls/http2/tcp/udp/rtcp/rudp
|
Transport string // transport: ws/wss/tls/http2/tcp/udp/rtcp/rudp
|
||||||
Remote string // remote address, used by tcp/udp port forwarding
|
Remote string // remote address, used by tcp/udp port forwarding
|
||||||
User *url.Userinfo // authentication for proxy
|
Users []*url.Userinfo // authentication for proxy
|
||||||
values url.Values
|
values url.Values
|
||||||
serverName string
|
serverName string
|
||||||
conn net.Conn
|
conn net.Conn
|
||||||
@ -34,11 +37,22 @@ func ParseProxyNode(s string) (node ProxyNode, err error) {
|
|||||||
|
|
||||||
node = ProxyNode{
|
node = ProxyNode{
|
||||||
Addr: u.Host,
|
Addr: u.Host,
|
||||||
User: u.User,
|
|
||||||
values: u.Query(),
|
values: u.Query(),
|
||||||
serverName: u.Host,
|
serverName: u.Host,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if u.User != nil {
|
||||||
|
node.Users = append(node.Users, u.User)
|
||||||
|
}
|
||||||
|
|
||||||
|
users, er := parseUsers(node.Get("secrets"))
|
||||||
|
if users != nil {
|
||||||
|
node.Users = append(node.Users, users...)
|
||||||
|
}
|
||||||
|
if er != nil {
|
||||||
|
glog.V(LWARNING).Infoln("secrets:", er)
|
||||||
|
}
|
||||||
|
|
||||||
if strings.Contains(u.Host, ":") {
|
if strings.Contains(u.Host, ":") {
|
||||||
node.serverName, _, _ = net.SplitHostPort(u.Host)
|
node.serverName, _, _ = net.SplitHostPort(u.Host)
|
||||||
if node.serverName == "" {
|
if node.serverName == "" {
|
||||||
@ -78,6 +92,34 @@ func ParseProxyNode(s string) (node ProxyNode, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func parseUsers(authFile string) (users []*url.Userinfo, err error) {
|
||||||
|
if authFile == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
file, err := os.Open(authFile)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
scanner := bufio.NewScanner(file)
|
||||||
|
for scanner.Scan() {
|
||||||
|
line := strings.TrimSpace(scanner.Text())
|
||||||
|
if line == "" || strings.HasPrefix(line, "#") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
s := strings.SplitN(line, " ", 2)
|
||||||
|
if len(s) == 1 {
|
||||||
|
users = append(users, url.User(strings.TrimSpace(s[0])))
|
||||||
|
} else if len(s) == 2 {
|
||||||
|
users = append(users, url.UserPassword(strings.TrimSpace(s[0]), strings.TrimSpace(s[1])))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err = scanner.Err()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Get get node parameter by key
|
// Get get node parameter by key
|
||||||
func (node *ProxyNode) Get(key string) string {
|
func (node *ProxyNode) Get(key string) string {
|
||||||
return node.values.Get(key)
|
return node.values.Get(key)
|
||||||
|
14
server.go
14
server.go
@ -28,10 +28,10 @@ func NewProxyServer(node ProxyNode, chain *ProxyChain, config *tls.Config) *Prox
|
|||||||
}
|
}
|
||||||
|
|
||||||
var cipher *ss.Cipher
|
var cipher *ss.Cipher
|
||||||
if node.Protocol == "ss" && node.User != nil {
|
if node.Protocol == "ss" && node.Users != nil {
|
||||||
var err error
|
var err error
|
||||||
method := node.User.Username()
|
method := node.Users[0].Username()
|
||||||
password, _ := node.User.Password()
|
password, _ := node.Users[0].Password()
|
||||||
cipher, err = ss.NewCipher(method, password)
|
cipher, err = ss.NewCipher(method, password)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
glog.Fatal(err)
|
glog.Fatal(err)
|
||||||
@ -49,7 +49,7 @@ func NewProxyServer(node ProxyNode, chain *ProxyChain, config *tls.Config) *Prox
|
|||||||
MethodTLS,
|
MethodTLS,
|
||||||
MethodTLSAuth,
|
MethodTLSAuth,
|
||||||
},
|
},
|
||||||
user: node.User,
|
users: node.Users,
|
||||||
tlsConfig: config,
|
tlsConfig: config,
|
||||||
},
|
},
|
||||||
cipher: cipher,
|
cipher: cipher,
|
||||||
@ -90,9 +90,9 @@ func (s *ProxyServer) Serve() error {
|
|||||||
glog.V(LWARNING).Infoln("[kcp]", err)
|
glog.V(LWARNING).Infoln("[kcp]", err)
|
||||||
}
|
}
|
||||||
// override crypt and key if specified explicitly
|
// override crypt and key if specified explicitly
|
||||||
if s.Node.User != nil {
|
if s.Node.Users != nil {
|
||||||
config.Crypt = s.Node.User.Username()
|
config.Crypt = s.Node.Users[0].Username()
|
||||||
config.Key, _ = s.Node.User.Password()
|
config.Key, _ = s.Node.Users[0].Password()
|
||||||
}
|
}
|
||||||
return NewKCPServer(s, config).ListenAndServe()
|
return NewKCPServer(s, config).ListenAndServe()
|
||||||
default:
|
default:
|
||||||
|
21
socks.go
21
socks.go
@ -81,7 +81,7 @@ func (selector *clientSelector) OnSelected(method uint8, conn net.Conn) (net.Con
|
|||||||
|
|
||||||
type serverSelector struct {
|
type serverSelector struct {
|
||||||
methods []uint8
|
methods []uint8
|
||||||
user *url.Userinfo
|
users []*url.Userinfo
|
||||||
tlsConfig *tls.Config
|
tlsConfig *tls.Config
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -101,7 +101,7 @@ func (selector *serverSelector) Select(methods ...uint8) (method uint8) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// when user/pass is set, auth is mandatory
|
// when user/pass is set, auth is mandatory
|
||||||
if selector.user != nil {
|
if selector.users != nil {
|
||||||
if method == gosocks5.MethodNoAuth {
|
if method == gosocks5.MethodNoAuth {
|
||||||
method = gosocks5.MethodUserPass
|
method = gosocks5.MethodUserPass
|
||||||
}
|
}
|
||||||
@ -132,13 +132,18 @@ func (selector *serverSelector) OnSelected(method uint8, conn net.Conn) (net.Con
|
|||||||
}
|
}
|
||||||
glog.V(LDEBUG).Infoln("[socks5]", req.String())
|
glog.V(LDEBUG).Infoln("[socks5]", req.String())
|
||||||
|
|
||||||
var username, password string
|
valid := false
|
||||||
if selector.user != nil {
|
for _, user := range selector.users {
|
||||||
username = selector.user.Username()
|
username := user.Username()
|
||||||
password, _ = selector.user.Password()
|
password, _ := user.Password()
|
||||||
|
if (req.Username == username && req.Password == password) ||
|
||||||
|
(req.Username == username && password == "") ||
|
||||||
|
(username == "" && req.Password == password) {
|
||||||
|
valid = true
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
if len(selector.users) > 0 && !valid {
|
||||||
if (username != "" && req.Username != username) || (password != "" && req.Password != password) {
|
|
||||||
resp := gosocks5.NewUserPassResponse(gosocks5.UserPassVer, gosocks5.Failure)
|
resp := gosocks5.NewUserPassResponse(gosocks5.UserPassVer, gosocks5.Failure)
|
||||||
if err := resp.Write(conn); err != nil {
|
if err := resp.Write(conn); err != nil {
|
||||||
glog.V(LWARNING).Infoln("[socks5-auth]", err)
|
glog.V(LWARNING).Infoln("[socks5-auth]", err)
|
||||||
|
Loading…
Reference in New Issue
Block a user