mirror of
https://github.com/cmz0228/hysteria-dev.git
synced 2025-06-09 05:49:54 +00:00
Implement server side ACL & an example script
This commit is contained in:
parent
ea7b596f79
commit
2ee4f245a8
2
ACL.md
2
ACL.md
@ -16,6 +16,8 @@ hijack cidr 192.168.1.1/24 127.0.0.1
|
||||
direct all
|
||||
```
|
||||
|
||||
A real-life ACL example of directly connecting to all China IPs (and its generator Python script) [can be found here](docs/acl).
|
||||
|
||||
Hysteria acts according to the first matching rule in the file for each request. When there is no match, the default behavior is to proxy all connections. You can override this by adding a rule at the end of the file with the condition "all".
|
||||
|
||||
4 actions:
|
||||
|
@ -1,4 +1,4 @@
|
||||
# ACL File Format
|
||||
# ACL 文件格式
|
||||
|
||||
ACL 文件描述如何处理传入请求。服务器和客户端都支持 ACL,并且遵循相同的语法。
|
||||
|
||||
@ -16,6 +16,8 @@ hijack cidr 192.168.1.1/24 127.0.0.1
|
||||
direct all
|
||||
```
|
||||
|
||||
一个直连所有中国 IP 的规则和 Python 生成脚本 [在这里](docs/acl)。
|
||||
|
||||
Hysteria 根据文件中第一个匹配到规则对每个请求进行操作。当没有匹配时默认的行为是代理连接。可以通过在文件的末尾添加一个规则加上条件 "all" 来设置默认行为。
|
||||
|
||||
4 种处理方式:
|
||||
|
@ -67,6 +67,7 @@ The command line program supports loading configurations from both JSON files an
|
||||
| Description | JSON config field | CLI argument |
|
||||
| --- | --- | --- |
|
||||
| Server listen address | listen | -listen |
|
||||
| Access control list | acl | -acl |
|
||||
| TLS certificate file | cert | -cert |
|
||||
| TLS key file | key | -key |
|
||||
| Authentication file | auth | -auth |
|
||||
@ -83,6 +84,7 @@ The command line program supports loading configurations from both JSON files an
|
||||
| --- | --- | --- |
|
||||
| SOCKS5 listen address | socks5_addr | -socks5-addr |
|
||||
| SOCKS5 connection timeout in seconds | socks5_timeout | -socks5-timeout |
|
||||
| Access control list | acl | -acl |
|
||||
| Server address | server | -server |
|
||||
| Authentication username | username | -username |
|
||||
| Authentication password | password | -password |
|
||||
@ -98,6 +100,10 @@ The command line program supports loading configurations from both JSON files an
|
||||
|
||||
Supports TCP (CONNECT) and UDP (ASSOCIATE) commands. BIND is not supported and is not planned to be supported.
|
||||
|
||||
#### About ACL
|
||||
|
||||
[ACL File Format](ACL.md)
|
||||
|
||||
#### About proxy authentication
|
||||
|
||||
Proxy supports username and password authentication (sent encrypted with TLS). If the server starts with an authentication file, it will check for the existence of the corresponding username and password in this file when each user connects. A valid authentication file is a text file with a pair of username and password per line (separated by a space). Example:
|
||||
|
@ -65,6 +65,7 @@ Hysteria 是专门针对恶劣网络环境(常见于在中国访问海外服
|
||||
| 描述 | JSON 字段 | 命令行参数 |
|
||||
| --- | --- | --- |
|
||||
| 服务端监听地址 | listen | -listen |
|
||||
| ACL 规则文件 | acl | -acl |
|
||||
| TLS 证书文件 | cert | -cert |
|
||||
| TLS 密钥文件 | key | -key |
|
||||
| 用户名密码验证文件 | auth | -auth |
|
||||
@ -81,6 +82,7 @@ Hysteria 是专门针对恶劣网络环境(常见于在中国访问海外服
|
||||
| --- | --- | --- |
|
||||
| SOCKS5 监听地址 | socks5_addr | -socks5-addr |
|
||||
| SOCKS5 超时时间(秒) | socks5_timeout | -socks5-timeout |
|
||||
| ACL 规则文件 | acl | -acl |
|
||||
| 服务端地址 | server | -server |
|
||||
| 验证用户名 | username | -username |
|
||||
| 验证密码 | password | -password |
|
||||
@ -96,6 +98,10 @@ Hysteria 是专门针对恶劣网络环境(常见于在中国访问海外服
|
||||
|
||||
支持 TCP (CONNECT) 和 UDP (ASSOCIATE),不支持 BIND 也无计划支持。
|
||||
|
||||
#### 关于 ACL
|
||||
|
||||
[ACL 文件格式](ACL.zh.md)
|
||||
|
||||
#### 关于用户名密码验证
|
||||
|
||||
代理支持用户名和密码认证(经过 TLS 加密发送)。如果服务器启动时指定了一个验证文件,当每个用户连接时,服务器会检查该文件中是否存在相应的用户名和密码。验证文件是一个文本文件,每行有一对用户名和密码(用空格分割)。比如:
|
||||
|
@ -42,6 +42,7 @@ func (c *proxyClientConfig) Check() error {
|
||||
|
||||
type proxyServerConfig struct {
|
||||
ListenAddr string `json:"listen" desc:"Server listen address"`
|
||||
ACLFile string `json:"acl" desc:"Access control list"`
|
||||
CertFile string `json:"cert" desc:"TLS certificate file"`
|
||||
KeyFile string `json:"key" desc:"TLS key file"`
|
||||
AuthFile string `json:"auth" desc:"Authentication file"`
|
||||
|
@ -5,6 +5,7 @@ import (
|
||||
"crypto/tls"
|
||||
"github.com/lucas-clemente/quic-go"
|
||||
"github.com/lucas-clemente/quic-go/congestion"
|
||||
"github.com/tobyxdd/hysteria/pkg/acl"
|
||||
hyCongestion "github.com/tobyxdd/hysteria/pkg/congestion"
|
||||
"github.com/tobyxdd/hysteria/pkg/core"
|
||||
"github.com/tobyxdd/hysteria/pkg/obfs"
|
||||
@ -61,6 +62,15 @@ func proxyServer(args []string) {
|
||||
obfuscator = obfs.XORObfuscator(config.Obfs)
|
||||
}
|
||||
|
||||
var aclEngine *acl.Engine
|
||||
if len(config.ACLFile) > 0 {
|
||||
aclEngine, err = acl.LoadFromFile(config.ACLFile)
|
||||
if err != nil {
|
||||
log.Fatalln("Unable to parse ACL:", err)
|
||||
}
|
||||
aclEngine.DefaultAction = acl.ActionDirect
|
||||
}
|
||||
|
||||
server, err := core.NewServer(config.ListenAddr, tlsConfig, quicConfig,
|
||||
uint64(config.UpMbps)*mbpsToBps, uint64(config.DownMbps)*mbpsToBps,
|
||||
func(refBPS uint64) congestion.SendAlgorithmWithDebugInfos {
|
||||
@ -93,9 +103,24 @@ func proxyServer(args []string) {
|
||||
log.Printf("%s (%s) disconnected: %s\n", addr.String(), username, err.Error())
|
||||
},
|
||||
func(addr net.Addr, username string, id int, packet bool, reqAddr string) (core.ConnectResult, string, io.ReadWriteCloser) {
|
||||
host, port, err := net.SplitHostPort(reqAddr)
|
||||
if err != nil {
|
||||
return core.ConnFailed, err.Error(), nil
|
||||
}
|
||||
ip := net.ParseIP(host)
|
||||
if ip != nil {
|
||||
// IP request, clear host for ACL engine
|
||||
host = ""
|
||||
}
|
||||
action, arg := acl.ActionDirect, ""
|
||||
if aclEngine != nil {
|
||||
action, arg = aclEngine.Lookup(host, ip)
|
||||
}
|
||||
switch action {
|
||||
case acl.ActionDirect, acl.ActionProxy: // Treat proxy as direct on server side
|
||||
if !packet {
|
||||
// TCP
|
||||
log.Printf("%s (%s): [TCP] %s\n", addr.String(), username, reqAddr)
|
||||
log.Printf("%s (%s): [TCP] [Direct] %s\n", addr.String(), username, reqAddr)
|
||||
conn, err := net.DialTimeout("tcp", reqAddr, dialTimeout)
|
||||
if err != nil {
|
||||
log.Printf("TCP error %s: %s\n", reqAddr, err.Error())
|
||||
@ -104,7 +129,7 @@ func proxyServer(args []string) {
|
||||
return core.ConnSuccess, "", conn
|
||||
} else {
|
||||
// UDP
|
||||
log.Printf("%s (%s): [UDP] %s\n", addr.String(), username, reqAddr)
|
||||
log.Printf("%s (%s): [UDP] [Direct] %s\n", addr.String(), username, reqAddr)
|
||||
conn, err := net.Dial("udp", reqAddr)
|
||||
if err != nil {
|
||||
log.Printf("UDP error %s: %s\n", reqAddr, err.Error())
|
||||
@ -112,6 +137,40 @@ func proxyServer(args []string) {
|
||||
}
|
||||
return core.ConnSuccess, "", conn
|
||||
}
|
||||
case acl.ActionBlock:
|
||||
if !packet {
|
||||
// TCP
|
||||
log.Printf("%s (%s): [TCP] [Block] %s\n", addr.String(), username, reqAddr)
|
||||
return core.ConnBlocked, "blocked by ACL", nil
|
||||
} else {
|
||||
// UDP
|
||||
log.Printf("%s (%s): [UDP] [Block] %s\n", addr.String(), username, reqAddr)
|
||||
return core.ConnBlocked, "blocked by ACL", nil
|
||||
}
|
||||
case acl.ActionHijack:
|
||||
hijackAddr := net.JoinHostPort(arg, port)
|
||||
if !packet {
|
||||
// TCP
|
||||
log.Printf("%s (%s): [TCP] [Hijack to %s] %s\n", addr.String(), username, arg, reqAddr)
|
||||
conn, err := net.DialTimeout("tcp", hijackAddr, dialTimeout)
|
||||
if err != nil {
|
||||
log.Printf("TCP error %s: %s\n", hijackAddr, err.Error())
|
||||
return core.ConnFailed, err.Error(), nil
|
||||
}
|
||||
return core.ConnSuccess, "", conn
|
||||
} else {
|
||||
// UDP
|
||||
log.Printf("%s (%s): [UDP] [Hijack to %s] %s\n", addr.String(), username, arg, reqAddr)
|
||||
conn, err := net.Dial("udp", hijackAddr)
|
||||
if err != nil {
|
||||
log.Printf("UDP error %s: %s\n", hijackAddr, err.Error())
|
||||
return core.ConnFailed, err.Error(), nil
|
||||
}
|
||||
return core.ConnSuccess, "", conn
|
||||
}
|
||||
default:
|
||||
return core.ConnFailed, "server ACL error", nil
|
||||
}
|
||||
},
|
||||
func(addr net.Addr, username string, id int, packet bool, reqAddr string, err error) {
|
||||
if !packet {
|
||||
|
7030
docs/acl/chnroutes.acl
Normal file
7030
docs/acl/chnroutes.acl
Normal file
File diff suppressed because it is too large
Load Diff
20
docs/acl/chnroutes.py
Normal file
20
docs/acl/chnroutes.py
Normal file
@ -0,0 +1,20 @@
|
||||
#! /usr/bin/env python3
|
||||
|
||||
import urllib.request
|
||||
from itertools import chain
|
||||
from datetime import date
|
||||
|
||||
data_ipv4 = urllib.request.urlopen(
|
||||
'https://www.ipdeny.com/ipblocks/data/aggregated/cn-aggregated.zone')
|
||||
data_ipv6 = urllib.request.urlopen(
|
||||
'https://www.ipdeny.com/ipv6/ipaddresses/aggregated/cn-aggregated.zone')
|
||||
|
||||
data = chain(data_ipv4, data_ipv6)
|
||||
|
||||
with open('chnroutes.acl', 'w') as out:
|
||||
out.write('# chnroutes\n# Generated on %s\n\n' %
|
||||
date.today().strftime("%B %d, %Y"))
|
||||
for l in data:
|
||||
ls = str(l, 'UTF8').strip()
|
||||
if ls:
|
||||
out.write('direct cidr %s\n' % ls)
|
Loading…
x
Reference in New Issue
Block a user