mirror of
https://gitclone.com/github.com/MetaCubeX/Clash.Meta
synced 2025-02-23 07:43:13 +08:00
chore: improve authentication parsing logic in http listener (#1336)
This commit is contained in:
parent
cc7823dad8
commit
4051ea522a
@ -69,3 +69,5 @@ func WithDSCP(dscp uint8) Addition {
|
|||||||
metadata.DSCP = dscp
|
metadata.DSCP = dscp
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Placeholder(metadata *C.Metadata) {}
|
||||||
|
@ -13,7 +13,7 @@ import (
|
|||||||
"github.com/metacubex/mihomo/transport/socks5"
|
"github.com/metacubex/mihomo/transport/socks5"
|
||||||
)
|
)
|
||||||
|
|
||||||
func newClient(srcConn net.Conn, tunnel C.Tunnel, additions ...inbound.Addition) *http.Client {
|
func newClient(srcConn net.Conn, tunnel C.Tunnel, additions []inbound.Addition) *http.Client { // additions using slice let caller can change its value (without size) after newClient return
|
||||||
return &http.Client{
|
return &http.Client{
|
||||||
Transport: &http.Transport{
|
Transport: &http.Transport{
|
||||||
// from http.DefaultTransport
|
// from http.DefaultTransport
|
||||||
|
@ -10,10 +10,9 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/metacubex/mihomo/adapter/inbound"
|
"github.com/metacubex/mihomo/adapter/inbound"
|
||||||
"github.com/metacubex/mihomo/common/lru"
|
|
||||||
N "github.com/metacubex/mihomo/common/net"
|
N "github.com/metacubex/mihomo/common/net"
|
||||||
|
"github.com/metacubex/mihomo/component/auth"
|
||||||
C "github.com/metacubex/mihomo/constant"
|
C "github.com/metacubex/mihomo/constant"
|
||||||
authStore "github.com/metacubex/mihomo/listener/auth"
|
|
||||||
"github.com/metacubex/mihomo/log"
|
"github.com/metacubex/mihomo/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -31,8 +30,10 @@ func (b *bodyWrapper) Read(p []byte) (n int, err error) {
|
|||||||
return n, err
|
return n, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func HandleConn(c net.Conn, tunnel C.Tunnel, cache *lru.LruCache[string, bool], additions ...inbound.Addition) {
|
func HandleConn(c net.Conn, tunnel C.Tunnel, authenticator auth.Authenticator, additions ...inbound.Addition) {
|
||||||
client := newClient(c, tunnel, additions...)
|
additions = append(additions, inbound.Placeholder) // Add a placeholder for InUser
|
||||||
|
inUserIdx := len(additions) - 1
|
||||||
|
client := newClient(c, tunnel, additions)
|
||||||
defer client.CloseIdleConnections()
|
defer client.CloseIdleConnections()
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
defer cancel()
|
defer cancel()
|
||||||
@ -41,7 +42,8 @@ func HandleConn(c net.Conn, tunnel C.Tunnel, cache *lru.LruCache[string, bool],
|
|||||||
conn := N.NewBufferedConn(c)
|
conn := N.NewBufferedConn(c)
|
||||||
|
|
||||||
keepAlive := true
|
keepAlive := true
|
||||||
trusted := cache == nil // disable authenticate if lru is nil
|
trusted := authenticator == nil // disable authenticate if lru is nil
|
||||||
|
lastUser := ""
|
||||||
|
|
||||||
for keepAlive {
|
for keepAlive {
|
||||||
peekMutex.Lock()
|
peekMutex.Lock()
|
||||||
@ -57,12 +59,10 @@ func HandleConn(c net.Conn, tunnel C.Tunnel, cache *lru.LruCache[string, bool],
|
|||||||
|
|
||||||
var resp *http.Response
|
var resp *http.Response
|
||||||
|
|
||||||
if !trusted {
|
var user string
|
||||||
var user string
|
resp, user = authenticate(request, authenticator) // always call authenticate function to get user
|
||||||
resp, user = authenticate(request, cache)
|
trusted = trusted || resp == nil
|
||||||
additions = append(additions, inbound.WithInUser(user))
|
additions[inUserIdx] = inbound.WithInUser(user)
|
||||||
trusted = resp == nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if trusted {
|
if trusted {
|
||||||
if request.Method == http.MethodConnect {
|
if request.Method == http.MethodConnect {
|
||||||
@ -89,6 +89,13 @@ func HandleConn(c net.Conn, tunnel C.Tunnel, cache *lru.LruCache[string, bool],
|
|||||||
return // hijack connection
|
return // hijack connection
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ensure there is a client with correct additions
|
||||||
|
// when the authenticated user changed, outbound client should close idle connections
|
||||||
|
if user != lastUser {
|
||||||
|
client.CloseIdleConnections()
|
||||||
|
lastUser = user
|
||||||
|
}
|
||||||
|
|
||||||
removeHopByHopHeaders(request.Header)
|
removeHopByHopHeaders(request.Header)
|
||||||
removeExtraHTTPHostPort(request)
|
removeExtraHTTPHostPort(request)
|
||||||
|
|
||||||
@ -138,34 +145,24 @@ func HandleConn(c net.Conn, tunnel C.Tunnel, cache *lru.LruCache[string, bool],
|
|||||||
_ = conn.Close()
|
_ = conn.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
func authenticate(request *http.Request, cache *lru.LruCache[string, bool]) (resp *http.Response, u string) {
|
func authenticate(request *http.Request, authenticator auth.Authenticator) (resp *http.Response, user string) {
|
||||||
authenticator := authStore.Authenticator()
|
|
||||||
if inbound.SkipAuthRemoteAddress(request.RemoteAddr) {
|
if inbound.SkipAuthRemoteAddress(request.RemoteAddr) {
|
||||||
authenticator = nil
|
authenticator = nil
|
||||||
}
|
}
|
||||||
if authenticator != nil {
|
credential := parseBasicProxyAuthorization(request)
|
||||||
credential := parseBasicProxyAuthorization(request)
|
if credential == "" && authenticator != nil {
|
||||||
if credential == "" {
|
resp = responseWith(request, http.StatusProxyAuthRequired)
|
||||||
resp := responseWith(request, http.StatusProxyAuthRequired)
|
resp.Header.Set("Proxy-Authenticate", "Basic")
|
||||||
resp.Header.Set("Proxy-Authenticate", "Basic")
|
return
|
||||||
return resp, ""
|
|
||||||
}
|
|
||||||
|
|
||||||
authed, exist := cache.Get(credential)
|
|
||||||
if !exist {
|
|
||||||
user, pass, err := decodeBasicProxyAuthorization(credential)
|
|
||||||
authed = err == nil && authenticator.Verify(user, pass)
|
|
||||||
u = user
|
|
||||||
cache.Set(credential, authed)
|
|
||||||
}
|
|
||||||
if !authed {
|
|
||||||
log.Infoln("Auth failed from %s", request.RemoteAddr)
|
|
||||||
|
|
||||||
return responseWith(request, http.StatusForbidden), u
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
user, pass, err := decodeBasicProxyAuthorization(credential)
|
||||||
return nil, u
|
authed := authenticator == nil || (err == nil && authenticator.Verify(user, pass))
|
||||||
|
if !authed {
|
||||||
|
log.Infoln("Auth failed from %s", request.RemoteAddr)
|
||||||
|
return responseWith(request, http.StatusForbidden), user
|
||||||
|
}
|
||||||
|
log.Debugln("Auth success from %s -> %s", request.RemoteAddr, user)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func responseWith(request *http.Request, statusCode int) *http.Response {
|
func responseWith(request *http.Request, statusCode int) *http.Response {
|
||||||
|
@ -4,9 +4,10 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
|
|
||||||
"github.com/metacubex/mihomo/adapter/inbound"
|
"github.com/metacubex/mihomo/adapter/inbound"
|
||||||
"github.com/metacubex/mihomo/common/lru"
|
"github.com/metacubex/mihomo/component/auth"
|
||||||
C "github.com/metacubex/mihomo/constant"
|
C "github.com/metacubex/mihomo/constant"
|
||||||
"github.com/metacubex/mihomo/constant/features"
|
"github.com/metacubex/mihomo/constant/features"
|
||||||
|
authStore "github.com/metacubex/mihomo/listener/auth"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Listener struct {
|
type Listener struct {
|
||||||
@ -32,10 +33,10 @@ func (l *Listener) Close() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func New(addr string, tunnel C.Tunnel, additions ...inbound.Addition) (*Listener, error) {
|
func New(addr string, tunnel C.Tunnel, additions ...inbound.Addition) (*Listener, error) {
|
||||||
return NewWithAuthenticate(addr, tunnel, true, additions...)
|
return NewWithAuthenticate(addr, tunnel, authStore.Authenticator(), additions...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewWithAuthenticate(addr string, tunnel C.Tunnel, authenticate bool, additions ...inbound.Addition) (*Listener, error) {
|
func NewWithAuthenticate(addr string, tunnel C.Tunnel, authenticator auth.Authenticator, additions ...inbound.Addition) (*Listener, error) {
|
||||||
isDefault := false
|
isDefault := false
|
||||||
if len(additions) == 0 {
|
if len(additions) == 0 {
|
||||||
isDefault = true
|
isDefault = true
|
||||||
@ -50,11 +51,6 @@ func NewWithAuthenticate(addr string, tunnel C.Tunnel, authenticate bool, additi
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var c *lru.LruCache[string, bool]
|
|
||||||
if authenticate {
|
|
||||||
c = lru.New[string, bool](lru.WithAge[string, bool](30))
|
|
||||||
}
|
|
||||||
|
|
||||||
hl := &Listener{
|
hl := &Listener{
|
||||||
listener: l,
|
listener: l,
|
||||||
addr: addr,
|
addr: addr,
|
||||||
@ -79,7 +75,7 @@ func NewWithAuthenticate(addr string, tunnel C.Tunnel, authenticate bool, additi
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
go HandleConn(conn, tunnel, c, additions...)
|
go HandleConn(conn, tunnel, authenticator, additions...)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
@ -4,9 +4,9 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
|
|
||||||
"github.com/metacubex/mihomo/adapter/inbound"
|
"github.com/metacubex/mihomo/adapter/inbound"
|
||||||
"github.com/metacubex/mihomo/common/lru"
|
|
||||||
N "github.com/metacubex/mihomo/common/net"
|
N "github.com/metacubex/mihomo/common/net"
|
||||||
C "github.com/metacubex/mihomo/constant"
|
C "github.com/metacubex/mihomo/constant"
|
||||||
|
authStore "github.com/metacubex/mihomo/listener/auth"
|
||||||
"github.com/metacubex/mihomo/listener/http"
|
"github.com/metacubex/mihomo/listener/http"
|
||||||
"github.com/metacubex/mihomo/listener/socks"
|
"github.com/metacubex/mihomo/listener/socks"
|
||||||
"github.com/metacubex/mihomo/transport/socks4"
|
"github.com/metacubex/mihomo/transport/socks4"
|
||||||
@ -16,7 +16,6 @@ import (
|
|||||||
type Listener struct {
|
type Listener struct {
|
||||||
listener net.Listener
|
listener net.Listener
|
||||||
addr string
|
addr string
|
||||||
cache *lru.LruCache[string, bool]
|
|
||||||
closed bool
|
closed bool
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -53,7 +52,6 @@ func New(addr string, tunnel C.Tunnel, additions ...inbound.Addition) (*Listener
|
|||||||
ml := &Listener{
|
ml := &Listener{
|
||||||
listener: l,
|
listener: l,
|
||||||
addr: addr,
|
addr: addr,
|
||||||
cache: lru.New[string, bool](lru.WithAge[string, bool](30)),
|
|
||||||
}
|
}
|
||||||
go func() {
|
go func() {
|
||||||
for {
|
for {
|
||||||
@ -70,14 +68,14 @@ func New(addr string, tunnel C.Tunnel, additions ...inbound.Addition) (*Listener
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
go handleConn(c, tunnel, ml.cache, additions...)
|
go handleConn(c, tunnel, additions...)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
return ml, nil
|
return ml, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleConn(conn net.Conn, tunnel C.Tunnel, cache *lru.LruCache[string, bool], additions ...inbound.Addition) {
|
func handleConn(conn net.Conn, tunnel C.Tunnel, additions ...inbound.Addition) {
|
||||||
N.TCPKeepAlive(conn)
|
N.TCPKeepAlive(conn)
|
||||||
|
|
||||||
bufConn := N.NewBufferedConn(conn)
|
bufConn := N.NewBufferedConn(conn)
|
||||||
@ -92,6 +90,6 @@ func handleConn(conn net.Conn, tunnel C.Tunnel, cache *lru.LruCache[string, bool
|
|||||||
case socks5.Version:
|
case socks5.Version:
|
||||||
socks.HandleSocks5(bufConn, tunnel, additions...)
|
socks.HandleSocks5(bufConn, tunnel, additions...)
|
||||||
default:
|
default:
|
||||||
http.HandleConn(bufConn, tunnel, cache, additions...)
|
http.HandleConn(bufConn, tunnel, authStore.Authenticator(), additions...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user