Merge pull request #44 from tobyxdd/wip-http-auth

HTTP external auth implementation
This commit is contained in:
Toby 2021-04-09 20:58:28 -07:00 committed by GitHub
commit 67f1e469d7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 150 additions and 3 deletions

View File

@ -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

View File

@ -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
{

View File

@ -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")
}

56
pkg/auth/http.go Normal file
View File

@ -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
}