mirror of
https://github.com/cmz0228/hysteria-dev.git
synced 2025-06-09 05:49:54 +00:00
feat: HTTP auth
This commit is contained in:
parent
cbfb1998a5
commit
d3db1e4a1d
@ -88,10 +88,16 @@ type serverConfigBandwidth struct {
|
|||||||
Down string `mapstructure:"down"`
|
Down string `mapstructure:"down"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type serverConfigAuthHTTP struct {
|
||||||
|
URL string `mapstructure:"url"`
|
||||||
|
Insecure bool `mapstructure:"insecure"`
|
||||||
|
}
|
||||||
|
|
||||||
type serverConfigAuth struct {
|
type serverConfigAuth struct {
|
||||||
Type string `mapstructure:"type"`
|
Type string `mapstructure:"type"`
|
||||||
Password string `mapstructure:"password"`
|
Password string `mapstructure:"password"`
|
||||||
UserPass map[string]string `mapstructure:"userpass"`
|
UserPass map[string]string `mapstructure:"userpass"`
|
||||||
|
HTTP serverConfigAuthHTTP `mapstructure:"http"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type serverConfigResolverTCP struct {
|
type serverConfigResolverTCP struct {
|
||||||
@ -393,6 +399,12 @@ func (c *serverConfig) fillAuthenticator(hyConfig *server.Config) error {
|
|||||||
}
|
}
|
||||||
hyConfig.Authenticator = &auth.UserPassAuthenticator{Users: c.Auth.UserPass}
|
hyConfig.Authenticator = &auth.UserPassAuthenticator{Users: c.Auth.UserPass}
|
||||||
return nil
|
return nil
|
||||||
|
case "http", "https":
|
||||||
|
if c.Auth.HTTP.URL == "" {
|
||||||
|
return configError{Field: "auth.http.url", Err: errors.New("empty auth http url")}
|
||||||
|
}
|
||||||
|
hyConfig.Authenticator = auth.NewHTTPAuthenticator(c.Auth.HTTP.URL, c.Auth.HTTP.Insecure)
|
||||||
|
return nil
|
||||||
default:
|
default:
|
||||||
return configError{Field: "auth.type", Err: errors.New("unsupported auth type")}
|
return configError{Field: "auth.type", Err: errors.New("unsupported auth type")}
|
||||||
}
|
}
|
||||||
|
@ -66,6 +66,10 @@ func TestServerConfig(t *testing.T) {
|
|||||||
"lol": "kek",
|
"lol": "kek",
|
||||||
"foo": "bar",
|
"foo": "bar",
|
||||||
},
|
},
|
||||||
|
HTTP: serverConfigAuthHTTP{
|
||||||
|
URL: "http://127.0.0.1:5000/auth",
|
||||||
|
Insecure: true,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
Resolver: serverConfigResolver{
|
Resolver: serverConfigResolver{
|
||||||
Type: "udp",
|
Type: "udp",
|
||||||
|
@ -46,6 +46,9 @@ auth:
|
|||||||
yolo: swag
|
yolo: swag
|
||||||
lol: kek
|
lol: kek
|
||||||
foo: bar
|
foo: bar
|
||||||
|
http:
|
||||||
|
url: http://127.0.0.1:5000/auth
|
||||||
|
insecure: true
|
||||||
|
|
||||||
resolver:
|
resolver:
|
||||||
type: udp
|
type: udp
|
||||||
|
90
extras/auth/http.go
Normal file
90
extras/auth/http.go
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
package auth
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto/tls"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/apernet/hysteria/core/server"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
httpAuthTimeout = 10 * time.Second
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ server.Authenticator = &HTTPAuthenticator{}
|
||||||
|
|
||||||
|
var errInvalidStatusCode = errors.New("invalid status code")
|
||||||
|
|
||||||
|
type HTTPAuthenticator struct {
|
||||||
|
Client *http.Client
|
||||||
|
URL string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewHTTPAuthenticator(url string, insecure bool) *HTTPAuthenticator {
|
||||||
|
tr := http.DefaultTransport.(*http.Transport).Clone()
|
||||||
|
tr.TLSClientConfig = &tls.Config{
|
||||||
|
InsecureSkipVerify: insecure,
|
||||||
|
}
|
||||||
|
return &HTTPAuthenticator{
|
||||||
|
Client: &http.Client{
|
||||||
|
Transport: tr,
|
||||||
|
Timeout: httpAuthTimeout,
|
||||||
|
},
|
||||||
|
URL: url,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type httpAuthRequest struct {
|
||||||
|
Addr string `json:"addr"`
|
||||||
|
Auth string `json:"auth"`
|
||||||
|
Tx uint64 `json:"tx"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type httpAuthResponse struct {
|
||||||
|
OK bool `json:"ok"`
|
||||||
|
ID string `json:"id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *HTTPAuthenticator) post(req *httpAuthRequest) (*httpAuthResponse, error) {
|
||||||
|
bs, err := json.Marshal(req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
resp, err := a.Client.Post(a.URL, "application/json", bytes.NewReader(bs))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
return nil, errInvalidStatusCode
|
||||||
|
}
|
||||||
|
respData, err := io.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var authResp httpAuthResponse
|
||||||
|
err = json.Unmarshal(respData, &authResp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &authResp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *HTTPAuthenticator) Authenticate(addr net.Addr, auth string, tx uint64) (ok bool, id string) {
|
||||||
|
req := &httpAuthRequest{
|
||||||
|
Addr: addr.String(),
|
||||||
|
Auth: auth,
|
||||||
|
Tx: tx,
|
||||||
|
}
|
||||||
|
resp, err := a.post(req)
|
||||||
|
if err != nil {
|
||||||
|
return false, ""
|
||||||
|
}
|
||||||
|
return resp.OK, resp.ID
|
||||||
|
}
|
36
extras/auth/http_test.go
Normal file
36
extras/auth/http_test.go
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
package auth
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"os/exec"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestHTTPAuthenticator(t *testing.T) {
|
||||||
|
// Run the Python test auth server
|
||||||
|
cmd := exec.Command("python", "http_test.py")
|
||||||
|
err := cmd.Start()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
defer cmd.Process.Kill()
|
||||||
|
|
||||||
|
time.Sleep(1 * time.Second) // Wait for the server to start
|
||||||
|
|
||||||
|
auth := NewHTTPAuthenticator("http://127.0.0.1:5000/auth", false)
|
||||||
|
|
||||||
|
ok, id := auth.Authenticate(&net.UDPAddr{
|
||||||
|
IP: net.ParseIP("1.2.3.4"),
|
||||||
|
Port: 34567,
|
||||||
|
}, "idk", 123)
|
||||||
|
assert.False(t, ok)
|
||||||
|
assert.Equal(t, "", id)
|
||||||
|
|
||||||
|
ok, id = auth.Authenticate(&net.UDPAddr{
|
||||||
|
IP: net.ParseIP("123.123.123.123"),
|
||||||
|
Port: 5566,
|
||||||
|
}, "wahaha", 12345)
|
||||||
|
assert.True(t, ok)
|
||||||
|
assert.Equal(t, "some_unique_id", id)
|
||||||
|
}
|
24
extras/auth/http_test.py
Normal file
24
extras/auth/http_test.py
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
from flask import Flask, request, jsonify
|
||||||
|
|
||||||
|
app = Flask(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
@app.route("/auth", methods=["POST"])
|
||||||
|
def auth():
|
||||||
|
data = request.json
|
||||||
|
|
||||||
|
if data is None:
|
||||||
|
return jsonify({"ok": False, "id": ""}), 400
|
||||||
|
|
||||||
|
addr = data.get("addr", "")
|
||||||
|
auth = data.get("auth", "")
|
||||||
|
tx = data.get("tx", 0)
|
||||||
|
|
||||||
|
if addr == "123.123.123.123:5566" and auth == "wahaha" and tx == 12345:
|
||||||
|
return jsonify({"ok": True, "id": "some_unique_id"})
|
||||||
|
else:
|
||||||
|
return jsonify({"ok": False, "id": ""})
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
app.run()
|
Loading…
x
Reference in New Issue
Block a user