diff --git a/adapter/inbound/ipfilter.go b/adapter/inbound/ipfilter.go new file mode 100644 index 00000000..7fa218c1 --- /dev/null +++ b/adapter/inbound/ipfilter.go @@ -0,0 +1,57 @@ +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 IsRemoteAddrDisAllowed(addr net.Addr) bool { + m := C.Metadata{} + if err := m.SetRemoteAddr(addr); err != nil { + return false + } + return isAllowed(m.AddrPort().Addr().Unmap()) && !isDisAllowed(m.AddrPort().Addr().Unmap()) +} + +func isAllowed(addr netip.Addr) bool { + if addr.IsValid() { + for _, prefix := range lanAllowedIPs { + if prefix.Contains(addr) { + return true + } + } + } + return false +} + +func isDisAllowed(addr netip.Addr) bool { + if addr.IsValid() { + for _, prefix := range lanDisAllowedIPs { + if prefix.Contains(addr) { + return true + } + } + } + return false +} diff --git a/config/config.go b/config/config.go index 5f4995fe..469a58ca 100644 --- a/config/config.go +++ b/config/config.go @@ -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"` @@ -289,6 +291,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"` @@ -387,6 +391,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, @@ -647,6 +652,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, diff --git a/docs/config.yaml b/docs/config.yaml index 43bbe0ac..f69ab41f 100644 --- a/docs/config.yaml +++ b/docs/config.yaml @@ -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, 开启,强制匹配所有进程 diff --git a/hub/executor/executor.go b/hub/executor/executor.go index edab400f..b9e27bfa 100644 --- a/hub/executor/executor.go +++ b/hub/executor/executor.go @@ -141,6 +141,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(), }, @@ -166,6 +168,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) diff --git a/hub/route/configs.go b/hub/route/configs.go index 7b579cb4..ec0b464c 100644 --- a/hub/route/configs.go +++ b/hub/route/configs.go @@ -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" @@ -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"` @@ -268,6 +269,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) } @@ -335,7 +344,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) @@ -380,7 +389,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 diff --git a/listener/http/server.go b/listener/http/server.go index be397d9a..3ff1679d 100644 --- a/listener/http/server.go +++ b/listener/http/server.go @@ -71,6 +71,12 @@ func NewWithAuthenticate(addr string, tunnel C.Tunnel, authenticate bool, additi t.SetKeepAlive(false) } } + if len(additions) == 0 { // only apply on default listener + if inbound.IsRemoteAddrDisAllowed(conn.RemoteAddr()) { + _ = conn.Close() + continue + } + } go HandleConn(conn, tunnel, c, additions...) } }() diff --git a/listener/mixed/mixed.go b/listener/mixed/mixed.go index 0f1b3aab..94613039 100644 --- a/listener/mixed/mixed.go +++ b/listener/mixed/mixed.go @@ -62,6 +62,12 @@ func New(addr string, tunnel C.Tunnel, additions ...inbound.Addition) (*Listener } continue } + if len(additions) == 0 { // only apply on default listener + if inbound.IsRemoteAddrDisAllowed(c.RemoteAddr()) { + _ = c.Close() + continue + } + } go handleConn(c, tunnel, ml.cache, additions...) } }() diff --git a/listener/socks/tcp.go b/listener/socks/tcp.go index c8c33e7b..8016e958 100644 --- a/listener/socks/tcp.go +++ b/listener/socks/tcp.go @@ -59,6 +59,12 @@ func New(addr string, tunnel C.Tunnel, additions ...inbound.Addition) (*Listener } continue } + if len(additions) == 0 { // only apply on default listener + if inbound.IsRemoteAddrDisAllowed(c.RemoteAddr()) { + _ = c.Close() + continue + } + } go handleSocks(c, tunnel, additions...) } }()