Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
G
gost
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Locked Files
Issues
0
Issues
0
List
Boards
Labels
Service Desk
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Security & Compliance
Security & Compliance
Dependency List
License Compliance
Packages
Packages
List
Container Registry
Analytics
Analytics
CI / CD
Code Review
Insights
Issues
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
nanahira
gost
Commits
c5fabde5
Commit
c5fabde5
authored
Dec 15, 2016
by
rui.zheng
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
#57: add multi user auth support for HTTP and SOCKS5
parent
e22229b5
Changes
9
Hide whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
129 additions
and
51 deletions
+129
-51
chain.go
chain.go
+5
-5
cmd/gost/main.go
cmd/gost/main.go
+1
-1
cmd/gost/secrets.txt
cmd/gost/secrets.txt
+4
-0
conn.go
conn.go
+14
-6
gost.go
gost.go
+1
-1
http.go
http.go
+36
-17
node.go
node.go
+48
-6
server.go
server.go
+7
-7
socks.go
socks.go
+13
-8
No files found.
chain.go
View file @
c5fabde5
...
@@ -86,9 +86,9 @@ func (c *ProxyChain) Init() {
...
@@ -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
]
.
User
s
!=
nil
{
config
.
Crypt
=
c
.
nodes
[
0
]
.
User
.
Username
()
config
.
Crypt
=
c
.
nodes
[
0
]
.
User
s
[
0
]
.
Username
()
config
.
Key
,
_
=
c
.
nodes
[
0
]
.
User
.
Password
()
config
.
Key
,
_
=
c
.
nodes
[
0
]
.
User
s
[
0
]
.
Password
()
}
}
c
.
kcpConfig
=
config
c
.
kcpConfig
=
config
return
return
...
@@ -349,9 +349,9 @@ func (c *ProxyChain) http2Connect(addr string) (net.Conn, error) {
...
@@ -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
.
User
s
!=
nil
{
header
.
Set
(
"Proxy-Authorization"
,
header
.
Set
(
"Proxy-Authorization"
,
"Basic "
+
base64
.
StdEncoding
.
EncodeToString
([]
byte
(
http2Node
.
User
.
String
())))
"Basic "
+
base64
.
StdEncoding
.
EncodeToString
([]
byte
(
http2Node
.
User
s
[
0
]
.
String
())))
}
}
return
c
.
getHttp2Conn
(
header
)
return
c
.
getHttp2Conn
(
header
)
}
}
cmd/gost/main.go
View file @
c5fabde5
...
@@ -46,7 +46,7 @@ func init() {
...
@@ -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
}
}
}
}
...
...
cmd/gost/secrets.txt
0 → 100644
View file @
c5fabde5
# username password
test001 123456
test002 12345678
\ No newline at end of file
conn.go
View file @
c5fabde5
...
@@ -95,7 +95,10 @@ func (c *ProxyConn) handshake() error {
...
@@ -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 {
...
@@ -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
.
User
s
[
0
]
.
Username
()
password
,
_
:=
c
.
Node
.
User
.
Password
()
password
,
_
:=
c
.
Node
.
User
s
[
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 {
...
@@ -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
...
...
gost.go
View file @
c5fabde5
...
@@ -11,7 +11,7 @@ import (
...
@@ -11,7 +11,7 @@ import (
)
)
const
(
const
(
Version
=
"2.3-
dev
"
Version
=
"2.3-
rc1
"
)
)
// Log level for glog
// Log level for glog
...
...
http.go
View file @
c5fabde5
...
@@ -44,15 +44,21 @@ func (s *HttpServer) HandleRequest(req *http.Request) {
...
@@ -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) {
...
@@ -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) {
...
@@ -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) {
...
@@ -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
)
...
...
node.go
View file @
c5fabde5
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
User
s
[]
*
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) {
...
@@ -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) {
...
@@ -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
)
...
...
server.go
View file @
c5fabde5
...
@@ -28,10 +28,10 @@ func NewProxyServer(node ProxyNode, chain *ProxyChain, config *tls.Config) *Prox
...
@@ -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
.
User
s
!=
nil
{
var
err
error
var
err
error
method
:=
node
.
User
.
Username
()
method
:=
node
.
User
s
[
0
]
.
Username
()
password
,
_
:=
node
.
User
.
Password
()
password
,
_
:=
node
.
User
s
[
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
...
@@ -49,7 +49,7 @@ func NewProxyServer(node ProxyNode, chain *ProxyChain, config *tls.Config) *Prox
MethodTLS
,
MethodTLS
,
MethodTLSAuth
,
MethodTLSAuth
,
},
},
user
:
node
.
User
,
user
s
:
node
.
Users
,
tlsConfig
:
config
,
tlsConfig
:
config
,
},
},
cipher
:
cipher
,
cipher
:
cipher
,
...
@@ -90,9 +90,9 @@ func (s *ProxyServer) Serve() error {
...
@@ -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
.
User
s
!=
nil
{
config
.
Crypt
=
s
.
Node
.
User
.
Username
()
config
.
Crypt
=
s
.
Node
.
User
s
[
0
]
.
Username
()
config
.
Key
,
_
=
s
.
Node
.
User
.
Password
()
config
.
Key
,
_
=
s
.
Node
.
User
s
[
0
]
.
Password
()
}
}
return
NewKCPServer
(
s
,
config
)
.
ListenAndServe
()
return
NewKCPServer
(
s
,
config
)
.
ListenAndServe
()
default
:
default
:
...
...
socks.go
View file @
c5fabde5
...
@@ -81,7 +81,7 @@ func (selector *clientSelector) OnSelected(method uint8, conn net.Conn) (net.Con
...
@@ -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
user
s
[]
*
url
.
Userinfo
tlsConfig
*
tls
.
Config
tlsConfig
*
tls
.
Config
}
}
...
@@ -101,7 +101,7 @@ func (selector *serverSelector) Select(methods ...uint8) (method uint8) {
...
@@ -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
.
user
s
!=
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
...
@@ -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
)
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment