Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add LAN allowed and disallowed IP configurations #861

Merged
merged 1 commit into from
Dec 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 65 additions & 0 deletions adapter/inbound/ipfilter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package inbound

import (
"net"
"net/netip"

C "github.com/metacubex/mihomo/constant"
)

var lanAllowedIPs []netip.Prefix
var lanDisAllowedIPs []netip.Prefix

func SetAllowedIPs(prefixes []netip.Prefix) {
lanAllowedIPs = prefixes
}

func SetDisAllowedIPs(prefixes []netip.Prefix) {
lanDisAllowedIPs = prefixes
}

func AllowedIPs() []netip.Prefix {
return lanAllowedIPs
}

func DisAllowedIPs() []netip.Prefix {
return lanDisAllowedIPs
}

func IsRemoteAddrAllowed(addr net.Addr) bool {
m := C.Metadata{}
if err := m.SetRemoteAddr(addr); err != nil {
return false
}
return isAllowed(m.AddrPort().Addr()) && !isDisAllowed(m.AddrPort().Addr())
}

func IsRemoteAddressAllowed(addr string) bool {
m := C.Metadata{}
if err := m.SetRemoteAddress(addr); err != nil {
return false
}
return isAllowed(m.AddrPort().Addr()) && !isDisAllowed(m.AddrPort().Addr())
}

func isAllowed(addr netip.Addr) bool {
if addr.IsValid() {
for _, prefix := range lanAllowedIPs {
if prefix.Contains(addr.Unmap()) {
return true
}
}
}
return false
}

func isDisAllowed(addr netip.Addr) bool {
if addr.IsValid() {
for _, prefix := range lanDisAllowedIPs {
if prefix.Contains(addr.Unmap()) {
return true
}
}
}
return false
}
7 changes: 7 additions & 0 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,8 @@ type Inbound struct {
VmessConfig string `json:"vmess-config"`
Authentication []string `json:"authentication"`
SkipAuthPrefixes []netip.Prefix `json:"skip-auth-prefixes"`
LanAllowedIPs []netip.Prefix `json:"lan-allowed-ips"`
LanDisAllowedIPs []netip.Prefix `json:"lan-disallowed-ips"`
AllowLan bool `json:"allow-lan"`
BindAddress string `json:"bind-address"`
InboundTfo bool `json:"inbound-tfo"`
Expand Down Expand Up @@ -285,6 +287,8 @@ type RawConfig struct {
InboundMPTCP bool `yaml:"inbound-mptcp"`
Authentication []string `yaml:"authentication" json:"authentication"`
SkipAuthPrefixes []netip.Prefix `yaml:"skip-auth-prefixes"`
LanAllowedIPs []netip.Prefix `yaml:"lan-allowed-ips"`
LanDisAllowedIPs []netip.Prefix `yaml:"lan-disallowed-ips"`
AllowLan bool `yaml:"allow-lan" json:"allow-lan"`
BindAddress string `yaml:"bind-address" json:"bind-address"`
Mode T.TunnelMode `yaml:"mode" json:"mode"`
Expand Down Expand Up @@ -383,6 +387,7 @@ func UnmarshalRawConfig(buf []byte) (*RawConfig, error) {
rawCfg := &RawConfig{
AllowLan: false,
BindAddress: "*",
LanAllowedIPs: []netip.Prefix{netip.MustParsePrefix("0.0.0.0/0"), netip.MustParsePrefix("::/0")},
IPv6: true,
Mode: T.Rule,
GeoAutoUpdate: false,
Expand Down Expand Up @@ -643,6 +648,8 @@ func parseGeneral(cfg *RawConfig) (*General, error) {
VmessConfig: cfg.VmessConfig,
AllowLan: cfg.AllowLan,
SkipAuthPrefixes: cfg.SkipAuthPrefixes,
LanAllowedIPs: cfg.LanAllowedIPs,
LanDisAllowedIPs: cfg.LanDisAllowedIPs,
BindAddress: cfg.BindAddress,
InboundTfo: cfg.InboundTfo,
InboundMPTCP: cfg.InboundMPTCP,
Expand Down
5 changes: 5 additions & 0 deletions docs/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@ authentication: # http,socks入口的验证用户名,密码
skip-auth-prefixes: # 设置跳过验证的IP段
- 127.0.0.1/8
- ::1/128
lan-allowed-ips: # 允许连接的 IP 地址段,仅作用于 allow-lan 为 true, 默认值为0.0.0.0/0和::/0
- 0.0.0.0/0
- ::/0
lan-disallowed-ips: # 禁止连接的 IP 地址段, 黑名单优先级高于白名单, 默认值为空
- 192.168.0.3/32

# find-process-mode has 3 values:always, strict, off
# - always, 开启,强制匹配所有进程
Expand Down
4 changes: 4 additions & 0 deletions hub/executor/executor.go
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,8 @@ func GetGeneral() *config.General {
VmessConfig: ports.VmessConfig,
Authentication: authenticator,
SkipAuthPrefixes: inbound.SkipAuthPrefixes(),
LanAllowedIPs: inbound.AllowedIPs(),
LanDisAllowedIPs: inbound.DisAllowedIPs(),
AllowLan: listener.AllowLan(),
BindAddress: listener.BindAddress(),
},
Expand All @@ -165,6 +167,8 @@ func updateListeners(general *config.General, listeners map[string]C.InboundList
allowLan := general.AllowLan
listener.SetAllowLan(allowLan)
inbound.SetSkipAuthPrefixes(general.SkipAuthPrefixes)
inbound.SetAllowedIPs(general.LanAllowedIPs)
inbound.SetDisAllowedIPs(general.LanDisAllowedIPs)

bindAddress := general.BindAddress
listener.SetBindAddress(bindAddress)
Expand Down
15 changes: 12 additions & 3 deletions hub/route/configs.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import (
"github.com/metacubex/mihomo/component/dialer"
"github.com/metacubex/mihomo/component/resolver"
"github.com/metacubex/mihomo/config"
"github.com/metacubex/mihomo/constant"
C "github.com/metacubex/mihomo/constant"
"github.com/metacubex/mihomo/hub/executor"
P "github.com/metacubex/mihomo/listener"
Expand Down Expand Up @@ -50,6 +49,8 @@ type configSchema struct {
UdptunConfig *string `json:"udptun-config"`
AllowLan *bool `json:"allow-lan"`
SkipAuthPrefixes *[]netip.Prefix `json:"skip-auth-prefixes"`
LanAllowedIPs *[]netip.Prefix `json:"lan-allowed-ips"`
LanDisAllowedIPs *[]netip.Prefix `json:"lan-disallowed-ips"`
BindAddress *string `json:"bind-address"`
Mode *tunnel.TunnelMode `json:"mode"`
LogLevel *log.LogLevel `json:"log-level"`
Expand Down Expand Up @@ -252,6 +253,14 @@ func patchConfigs(w http.ResponseWriter, r *http.Request) {
inbound.SetSkipAuthPrefixes(*general.SkipAuthPrefixes)
}

if general.LanAllowedIPs != nil {
inbound.SetAllowedIPs(*general.LanAllowedIPs)
}

if general.LanDisAllowedIPs != nil {
inbound.SetDisAllowedIPs(*general.LanDisAllowedIPs)
}

if general.BindAddress != nil {
P.SetBindAddress(*general.BindAddress)
}
Expand Down Expand Up @@ -319,7 +328,7 @@ func updateConfigs(w http.ResponseWriter, r *http.Request) {
}
} else {
if req.Path == "" {
req.Path = constant.Path.Config()
req.Path = C.Path.Config()
}
if !filepath.IsAbs(req.Path) {
render.Status(r, http.StatusBadRequest)
Expand Down Expand Up @@ -364,7 +373,7 @@ func updateGeoDatabases(w http.ResponseWriter, r *http.Request) {
return
}

cfg, err := executor.ParseWithPath(constant.Path.Config())
cfg, err := executor.ParseWithPath(C.Path.Config())
if err != nil {
log.Errorln("[REST-API] update GEO databases failed: %v", err)
return
Expand Down
4 changes: 4 additions & 0 deletions listener/http/proxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,10 @@ func HandleConn(c net.Conn, tunnel C.Tunnel, cache *lru.LruCache[string, bool],

func authenticate(request *http.Request, cache *lru.LruCache[string, bool]) *http.Response {
authenticator := authStore.Authenticator()
if !inbound.IsRemoteAddressAllowed(request.RemoteAddr) {
log.Infoln("Remote address %s is not allowed", request.RemoteAddr)
return responseWith(request, http.StatusForbidden)
}
if inbound.SkipAuthRemoteAddress(request.RemoteAddr) {
authenticator = nil
}
Expand Down
8 changes: 8 additions & 0 deletions listener/socks/tcp.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,10 @@ func handleSocks(conn net.Conn, tunnel C.Tunnel, additions ...inbound.Addition)

func HandleSocks4(conn net.Conn, tunnel C.Tunnel, additions ...inbound.Addition) {
authenticator := authStore.Authenticator()
if inbound.IsRemoteAddrAllowed(conn.RemoteAddr()) {
conn.Close()
return
}
if inbound.SkipAuthRemoteAddr(conn.RemoteAddr()) {
authenticator = nil
}
Expand All @@ -100,6 +104,10 @@ func HandleSocks4(conn net.Conn, tunnel C.Tunnel, additions ...inbound.Addition)

func HandleSocks5(conn net.Conn, tunnel C.Tunnel, additions ...inbound.Addition) {
authenticator := authStore.Authenticator()
if !inbound.IsRemoteAddrAllowed(conn.RemoteAddr()) {
conn.Close()
return
}
if inbound.SkipAuthRemoteAddr(conn.RemoteAddr()) {
authenticator = nil
}
Expand Down