From 461b16f07fb9fec65d8a800d222c490e9faea782 Mon Sep 17 00:00:00 2001 From: Toby Date: Sun, 4 Apr 2021 14:47:07 -0700 Subject: [PATCH 1/2] External HTTP auth implementation --- cmd/server.go | 19 ++++++++++++++++ pkg/auth/http.go | 56 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 75 insertions(+) create mode 100644 pkg/auth/http.go diff --git a/cmd/server.go b/cmd/server.go index fe18332..b588b85 100644 --- a/cmd/server.go +++ b/cmd/server.go @@ -6,12 +6,15 @@ import ( "github.com/lucas-clemente/quic-go/congestion" "github.com/sirupsen/logrus" "github.com/tobyxdd/hysteria/pkg/acl" + "github.com/tobyxdd/hysteria/pkg/auth" hyCongestion "github.com/tobyxdd/hysteria/pkg/congestion" "github.com/tobyxdd/hysteria/pkg/core" "github.com/tobyxdd/hysteria/pkg/obfs" "github.com/yosuke-furukawa/json5/encoding/json5" "io" "net" + "net/http" + "time" ) func server(config *serverConfig) { @@ -72,6 +75,22 @@ func server(config *serverConfig) { return false, "Wrong password" } } + case "external": + logrus.Info("External authentication enabled") + var extConfig map[string]string + err = json5.Unmarshal(config.Auth.Config, &extConfig) + if err != nil || len(extConfig["http"]) == 0 { + logrus.WithFields(logrus.Fields{ + "error": err, + }).Fatal("Invalid external authentication config") + } + provider := &auth.HTTPAuthProvider{ + Client: &http.Client{ + Timeout: 10 * time.Second, + }, + URL: extConfig["http"], + } + authFunc = provider.Auth default: logrus.WithField("mode", config.Auth.Mode).Fatal("Unsupported authentication mode") } diff --git a/pkg/auth/http.go b/pkg/auth/http.go new file mode 100644 index 0000000..be55836 --- /dev/null +++ b/pkg/auth/http.go @@ -0,0 +1,56 @@ +package auth + +import ( + "bytes" + "encoding/json" + "io/ioutil" + "net" + "net/http" +) + +type HTTPAuthProvider struct { + Client *http.Client + URL string +} + +type authReq struct { + Addr string `json:"addr"` + Payload []byte `json:"payload"` + Send uint64 `json:"send"` + Recv uint64 `json:"recv"` +} + +type authResp struct { + OK bool `json:"ok"` + Msg string `json:"msg"` +} + +func (p *HTTPAuthProvider) Auth(addr net.Addr, auth []byte, sSend uint64, sRecv uint64) (bool, string) { + jbs, err := json.Marshal(&authReq{ + Addr: addr.String(), + Payload: auth, + Send: sSend, + Recv: sRecv, + }) + if err != nil { + return false, "Internal error" + } + resp, err := p.Client.Post(p.URL, "application/json", bytes.NewBuffer(jbs)) + if err != nil { + return false, "Internal error" + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusOK { + return false, "Auth endpoint error" + } + data, err := ioutil.ReadAll(resp.Body) + if err != nil { + return false, "Auth endpoint error" + } + var ar authResp + err = json.Unmarshal(data, &ar) + if err != nil { + return false, "Auth endpoint error" + } + return ar.OK, ar.Msg +} From 22ad36dabb273403e3ba14a17995cfd10f2cc844 Mon Sep 17 00:00:00 2001 From: Toby Date: Sun, 4 Apr 2021 15:10:23 -0700 Subject: [PATCH 2/2] Update README --- README.md | 38 +++++++++++++++++++++++++++++++++++++- README.zh.md | 40 ++++++++++++++++++++++++++++++++++++++-- 2 files changed, 75 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 3b5bfb6..ac4f932 100644 --- a/README.md +++ b/README.md @@ -125,7 +125,7 @@ Proxy Server: AWS US West Oregon (us-west-2) "acl": "my_list.acl", // See ACL below "obfs": "AMOGUS", // Obfuscation password "auth": { // Authentication - "mode": "password", // Mode, only supports "password" and "none" for now + "mode": "password", // Mode, supports "password" "none" and "external" for now "config": { "password": "yubiyubi" } @@ -136,6 +136,42 @@ Proxy Server: AWS US West Oregon (us-west-2) } ``` +#### External authentication integration + +If you are a commercial proxy provider, you may want to connect Hysteria to your own authentication backend. + +```json5 +{ + // ... + "auth": { + "mode": "external", + "config": { + "http": "https://api.example.com/auth" // Both HTTP and HTTPS are supported + } + } +} +``` + +For the above config, Hysteria sends a POST request to `https://api.example.com/auth` upon each client's connection: + +```json5 +{ + "addr": "111.222.111.222:52731", + "payload": "[BASE64]", // auth or auth_str of the client + "send": 12500000, // Negotiated server send speed for this client (Bps) + "recv": 12500000 // Negotiated server recv speed for this client (Bps) +} +``` + +The endpoint must return results with HTTP status code 200 (even if the authentication failed): + +```json5 +{ + "ok": false, + "msg": "No idea who you are" +} +``` + ### Client ```json5 diff --git a/README.zh.md b/README.zh.md index 1d28e91..1ba74a6 100644 --- a/README.zh.md +++ b/README.zh.md @@ -100,7 +100,7 @@ Hysteria 是专门针对恶劣网络环境(常见于卫星网络、在中国 ## 高级用法 -### Server +### 服务器 ```json5 { @@ -124,7 +124,43 @@ Hysteria 是专门针对恶劣网络环境(常见于卫星网络、在中国 } ``` -### Client +#### 接入外部验证 + +如果你是商业代理服务提供商,可以这样把 Hysteria 接入到自己的验证后端: + +```json5 +{ + // ... + "auth": { + "mode": "external", + "config": { + "http": "https://api.example.com/auth" // 支持 HTTP 和 HTTPS + } + } +} +``` + +对于上述配置,Hysteria 会把验证请求通过 HTTP POST 发送到 `https://api.example.com/auth` + +```json5 +{ + "addr": "111.222.111.222:52731", + "payload": "[BASE64]", // 对应客户端配置的 auth 或 auth_str 字段 + "send": 12500000, // 协商后的服务端最大发送速率 (Bps) + "recv": 12500000 // 协商后的服务端最大接收速率 (Bps) +} +``` + +后端必须用 HTTP 200 状态码返回验证结果(即使验证不通过): + +```json5 +{ + "ok": false, + "msg": "No idea who you are" +} +``` + +### 客户端 ```json5 {