mirror of
https://github.com/tobychui/zoraxy.git
synced 2025-06-03 06:07:20 +02:00
Fixed #573
- Added whitelist loopback quick toggle - Fixed plugin exit stuck bug
This commit is contained in:
parent
23d4df1ed7
commit
3e57a90bb6
@ -78,8 +78,8 @@ func main() {
|
|||||||
Dynamic Captures
|
Dynamic Captures
|
||||||
*/
|
*/
|
||||||
pathRouter.RegisterDynamicSniffHandler("/d_sniff", http.DefaultServeMux, func(dsfr *plugin.DynamicSniffForwardRequest) plugin.SniffResult {
|
pathRouter.RegisterDynamicSniffHandler("/d_sniff", http.DefaultServeMux, func(dsfr *plugin.DynamicSniffForwardRequest) plugin.SniffResult {
|
||||||
fmt.Println("Dynamic Capture Sniffed Request:")
|
//fmt.Println("Dynamic Capture Sniffed Request:")
|
||||||
fmt.Println("Request URI: " + dsfr.RequestURI)
|
//fmt.Println("Request URI: " + dsfr.RequestURI)
|
||||||
|
|
||||||
//In this example, we want to capture all URI
|
//In this example, we want to capture all URI
|
||||||
//that start with /test_ and forward it to the dynamic capture handler
|
//that start with /test_ and forward it to the dynamic capture handler
|
||||||
|
@ -547,6 +547,38 @@ func handleWhitelistEnable(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func handleWhitelistAllowLoopback(w http.ResponseWriter, r *http.Request) {
|
||||||
|
enable, _ := utils.PostPara(r, "enable")
|
||||||
|
ruleID, err := utils.PostPara(r, "id")
|
||||||
|
if err != nil {
|
||||||
|
ruleID = "default"
|
||||||
|
}
|
||||||
|
|
||||||
|
rule, err := accessController.GetAccessRuleByID(ruleID)
|
||||||
|
if err != nil {
|
||||||
|
utils.SendErrorResponse(w, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if enable == "" {
|
||||||
|
//Return the current enabled state
|
||||||
|
currentEnabled := rule.WhitelistAllowLocalAndLoopback
|
||||||
|
js, _ := json.Marshal(currentEnabled)
|
||||||
|
utils.SendJSONResponse(w, string(js))
|
||||||
|
} else {
|
||||||
|
if enable == "true" {
|
||||||
|
rule.ToggleAllowLoopback(true)
|
||||||
|
} else if enable == "false" {
|
||||||
|
rule.ToggleAllowLoopback(false)
|
||||||
|
} else {
|
||||||
|
utils.SendErrorResponse(w, "invalid enable state: only true and false is accepted")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
utils.SendOK(w)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// List all quick ban ip address
|
// List all quick ban ip address
|
||||||
func handleListQuickBan(w http.ResponseWriter, r *http.Request) {
|
func handleListQuickBan(w http.ResponseWriter, r *http.Request) {
|
||||||
currentSummary := statisticCollector.GetCurrentDailySummary()
|
currentSummary := statisticCollector.GetCurrentDailySummary()
|
||||||
|
@ -114,7 +114,7 @@ func RegisterAccessRuleAPIs(authRouter *auth.RouterDef) {
|
|||||||
authRouter.HandleFunc("/api/whitelist/ip/add", handleIpWhitelistAdd)
|
authRouter.HandleFunc("/api/whitelist/ip/add", handleIpWhitelistAdd)
|
||||||
authRouter.HandleFunc("/api/whitelist/ip/remove", handleIpWhitelistRemove)
|
authRouter.HandleFunc("/api/whitelist/ip/remove", handleIpWhitelistRemove)
|
||||||
authRouter.HandleFunc("/api/whitelist/enable", handleWhitelistEnable)
|
authRouter.HandleFunc("/api/whitelist/enable", handleWhitelistEnable)
|
||||||
|
authRouter.HandleFunc("/api/whitelist/allowLocal", handleWhitelistAllowLoopback)
|
||||||
/* Quick Ban List */
|
/* Quick Ban List */
|
||||||
authRouter.HandleFunc("/api/quickban/list", handleListQuickBan)
|
authRouter.HandleFunc("/api/quickban/list", handleListQuickBan)
|
||||||
}
|
}
|
||||||
|
13
src/def.go
13
src/def.go
@ -62,12 +62,13 @@ const (
|
|||||||
STATISTIC_AUTO_SAVE_INTERVAL = 600 /* Seconds */
|
STATISTIC_AUTO_SAVE_INTERVAL = 600 /* Seconds */
|
||||||
|
|
||||||
/* Configuration Folder Storage Path Constants */
|
/* Configuration Folder Storage Path Constants */
|
||||||
CONF_HTTP_PROXY = "./conf/proxy"
|
CONF_HTTP_PROXY = "./conf/proxy"
|
||||||
CONF_STREAM_PROXY = "./conf/streamproxy"
|
CONF_STREAM_PROXY = "./conf/streamproxy"
|
||||||
CONF_CERT_STORE = "./conf/certs"
|
CONF_CERT_STORE = "./conf/certs"
|
||||||
CONF_REDIRECTION = "./conf/redirect"
|
CONF_REDIRECTION = "./conf/redirect"
|
||||||
CONF_ACCESS_RULE = "./conf/access"
|
CONF_ACCESS_RULE = "./conf/access"
|
||||||
CONF_PATH_RULE = "./conf/rules/pathrules"
|
CONF_PATH_RULE = "./conf/rules/pathrules"
|
||||||
|
CONF_PLUGIN_GROUPS = "./conf/plugin_groups.json"
|
||||||
)
|
)
|
||||||
|
|
||||||
/* System Startup Flags */
|
/* System Startup Flags */
|
||||||
|
@ -94,7 +94,7 @@ func NewAccessController(options *Options) (*Controller, error) {
|
|||||||
thisAccessRule := AccessRule{}
|
thisAccessRule := AccessRule{}
|
||||||
err = json.Unmarshal(configContent, &thisAccessRule)
|
err = json.Unmarshal(configContent, &thisAccessRule)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
options.Logger.PrintAndLog("Access", "Unable to parse config "+filepath.Base(configFile), err)
|
options.Logger.PrintAndLog("access", "Unable to parse config "+filepath.Base(configFile), err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
thisAccessRule.parent = &thisController
|
thisAccessRule.parent = &thisController
|
||||||
@ -102,6 +102,19 @@ func NewAccessController(options *Options) (*Controller, error) {
|
|||||||
}
|
}
|
||||||
thisController.ProxyAccessRule = &ProxyAccessRules
|
thisController.ProxyAccessRule = &ProxyAccessRules
|
||||||
|
|
||||||
|
//Start the public ip ticker
|
||||||
|
if options.PublicIpCheckInterval <= 0 {
|
||||||
|
options.PublicIpCheckInterval = 12 * 60 * 60 //12 hours
|
||||||
|
}
|
||||||
|
thisController.ServerPublicIP = "127.0.0.1"
|
||||||
|
go func() {
|
||||||
|
err = thisController.UpdatePublicIP()
|
||||||
|
if err != nil {
|
||||||
|
options.Logger.PrintAndLog("access", "Unable to update public IP address", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
thisController.StartPublicIPUpdater()
|
||||||
|
}()
|
||||||
return &thisController, nil
|
return &thisController, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -147,11 +160,7 @@ func (c *Controller) ListAllAccessRules() []*AccessRule {
|
|||||||
// Check if an access rule exists given the rule id
|
// Check if an access rule exists given the rule id
|
||||||
func (c *Controller) AccessRuleExists(ruleID string) bool {
|
func (c *Controller) AccessRuleExists(ruleID string) bool {
|
||||||
r, _ := c.GetAccessRuleByID(ruleID)
|
r, _ := c.GetAccessRuleByID(ruleID)
|
||||||
if r != nil {
|
return r != nil
|
||||||
//An access rule with identical ID exists
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add a new access rule to runtime and save it to file
|
// Add a new access rule to runtime and save it to file
|
||||||
@ -219,3 +228,7 @@ func (c *Controller) RemoveAccessRuleByID(ruleID string) error {
|
|||||||
//Remove it
|
//Remove it
|
||||||
return c.DeleteAccessRuleByID(ruleID)
|
return c.DeleteAccessRuleByID(ruleID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Controller) Close() {
|
||||||
|
c.StopPublicIPUpdater()
|
||||||
|
}
|
||||||
|
@ -25,18 +25,24 @@ func (s *AccessRule) AllowConnectionAccess(conn net.Conn) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Toggle black list
|
// Toggle blacklist
|
||||||
func (s *AccessRule) ToggleBlacklist(enabled bool) {
|
func (s *AccessRule) ToggleBlacklist(enabled bool) {
|
||||||
s.BlacklistEnabled = enabled
|
s.BlacklistEnabled = enabled
|
||||||
s.SaveChanges()
|
s.SaveChanges()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Toggel white list
|
// Toggel whitelist
|
||||||
func (s *AccessRule) ToggleWhitelist(enabled bool) {
|
func (s *AccessRule) ToggleWhitelist(enabled bool) {
|
||||||
s.WhitelistEnabled = enabled
|
s.WhitelistEnabled = enabled
|
||||||
s.SaveChanges()
|
s.SaveChanges()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Toggle whitelist loopback
|
||||||
|
func (s *AccessRule) ToggleAllowLoopback(enabled bool) {
|
||||||
|
s.WhitelistAllowLocalAndLoopback = enabled
|
||||||
|
s.SaveChanges()
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Check if a IP address is blacklisted, in either country or IP blacklist
|
Check if a IP address is blacklisted, in either country or IP blacklist
|
||||||
IsBlacklisted default return is false (allow access)
|
IsBlacklisted default return is false (allow access)
|
||||||
|
134
src/mod/access/loopback.go
Normal file
134
src/mod/access/loopback.go
Normal file
@ -0,0 +1,134 @@
|
|||||||
|
package access
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
PUBLIC_IP_CHECK_URL = "http://checkip.amazonaws.com/"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Start the public IP address updater
|
||||||
|
func (c *Controller) StartPublicIPUpdater() {
|
||||||
|
stopChan := make(chan bool)
|
||||||
|
c.publicIpTickerStop = stopChan
|
||||||
|
ticker := time.NewTicker(time.Duration(c.Options.PublicIpCheckInterval) * time.Second)
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-stopChan:
|
||||||
|
ticker.Stop()
|
||||||
|
return
|
||||||
|
case <-ticker.C:
|
||||||
|
err := c.UpdatePublicIP()
|
||||||
|
if err != nil {
|
||||||
|
c.Options.Logger.PrintAndLog("access", "Unable to update public IP address", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
c.publicIpTicker = ticker
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop the public IP address updater
|
||||||
|
func (c *Controller) StopPublicIPUpdater() {
|
||||||
|
// Stop the public IP address updater
|
||||||
|
if c.publicIpTickerStop != nil {
|
||||||
|
c.publicIpTickerStop <- true
|
||||||
|
}
|
||||||
|
c.publicIpTicker = nil
|
||||||
|
c.publicIpTickerStop = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the public IP address of the server
|
||||||
|
func (c *Controller) UpdatePublicIP() error {
|
||||||
|
req, err := http.NewRequest("GET", PUBLIC_IP_CHECK_URL, nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3")
|
||||||
|
req.Header.Set("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8")
|
||||||
|
req.Header.Set("Accept-Language", "en-US,en;q=0.5")
|
||||||
|
req.Header.Set("Connection", "keep-alive")
|
||||||
|
req.Header.Set("Upgrade-Insecure-Requests", "1")
|
||||||
|
req.Header.Set("sec-ch-ua", `"Chromium";v="91", " Not;A Brand";v="99", "Google Chrome";v="91"`)
|
||||||
|
req.Header.Set("sec-ch-ua-platform", `"Windows"`)
|
||||||
|
req.Header.Set("sec-ch-ua-mobile", "?0")
|
||||||
|
|
||||||
|
client := &http.Client{}
|
||||||
|
resp, err := client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
ip, err := io.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate if the returned byte is a valid IP address
|
||||||
|
pubIP := net.ParseIP(strings.TrimSpace(string(ip)))
|
||||||
|
if pubIP == nil {
|
||||||
|
return errors.New("invalid IP address")
|
||||||
|
}
|
||||||
|
|
||||||
|
c.ServerPublicIP = pubIP.String()
|
||||||
|
c.Options.Logger.PrintAndLog("access", "Public IP address updated to: "+c.ServerPublicIP, nil)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Controller) IsLoopbackRequest(ipAddr string) bool {
|
||||||
|
loopbackIPs := []string{
|
||||||
|
"localhost",
|
||||||
|
"::1",
|
||||||
|
"127.0.0.1",
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the request is loopback from public IP
|
||||||
|
if ipAddr == c.ServerPublicIP {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the request is from localhost or loopback IPv4 or 6
|
||||||
|
for _, loopbackIP := range loopbackIPs {
|
||||||
|
if ipAddr == loopbackIP {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the IP address is in private IP range
|
||||||
|
func (c *Controller) IsPrivateIPRange(ipAddr string) bool {
|
||||||
|
privateIPBlocks := []string{
|
||||||
|
"10.0.0.0/8",
|
||||||
|
"172.16.0.0/12",
|
||||||
|
"192.168.0.0/16",
|
||||||
|
"169.254.0.0/16",
|
||||||
|
"127.0.0.0/8",
|
||||||
|
"::1/128",
|
||||||
|
"fc00::/7",
|
||||||
|
"fe80::/10",
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, cidr := range privateIPBlocks {
|
||||||
|
_, block, err := net.ParseCIDR(cidr)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
ip := net.ParseIP(ipAddr)
|
||||||
|
if block.Contains(ip) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
@ -2,6 +2,7 @@ package access
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"sync"
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
"imuslab.com/zoraxy/mod/database"
|
"imuslab.com/zoraxy/mod/database"
|
||||||
"imuslab.com/zoraxy/mod/geodb"
|
"imuslab.com/zoraxy/mod/geodb"
|
||||||
@ -13,14 +14,18 @@ type Options struct {
|
|||||||
ConfigFolder string //Path for storing config files
|
ConfigFolder string //Path for storing config files
|
||||||
GeoDB *geodb.Store //For resolving country code
|
GeoDB *geodb.Store //For resolving country code
|
||||||
Database *database.Database //System key-value database
|
Database *database.Database //System key-value database
|
||||||
|
|
||||||
|
/* Public IP monitoring */
|
||||||
|
PublicIpCheckInterval int64 //in Seconds
|
||||||
}
|
}
|
||||||
|
|
||||||
type AccessRule struct {
|
type AccessRule struct {
|
||||||
ID string
|
ID string
|
||||||
Name string
|
Name string
|
||||||
Desc string
|
Desc string
|
||||||
BlacklistEnabled bool
|
BlacklistEnabled bool
|
||||||
WhitelistEnabled bool
|
WhitelistEnabled bool
|
||||||
|
WhitelistAllowLocalAndLoopback bool //Allow local and loopback address to bypass whitelist
|
||||||
|
|
||||||
/* Whitelist Blacklist Table, value is comment if supported */
|
/* Whitelist Blacklist Table, value is comment if supported */
|
||||||
WhiteListCountryCode *map[string]string
|
WhiteListCountryCode *map[string]string
|
||||||
@ -32,7 +37,12 @@ type AccessRule struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Controller struct {
|
type Controller struct {
|
||||||
|
ServerPublicIP string
|
||||||
DefaultAccessRule *AccessRule
|
DefaultAccessRule *AccessRule
|
||||||
ProxyAccessRule *sync.Map
|
ProxyAccessRule *sync.Map
|
||||||
Options *Options
|
Options *Options
|
||||||
|
|
||||||
|
//Internal
|
||||||
|
publicIpTicker *time.Ticker
|
||||||
|
publicIpTickerStop chan bool
|
||||||
}
|
}
|
||||||
|
@ -93,6 +93,13 @@ func (s *AccessRule) IsIPWhitelisted(ipAddr string) bool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Check for loopback match
|
||||||
|
if s.WhitelistAllowLocalAndLoopback {
|
||||||
|
if s.parent.IsLoopbackRequest(ipAddr) || s.parent.IsPrivateIPRange(ipAddr) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -151,6 +151,7 @@ func (m *Manager) handlePluginSTDOUT(pluginID string, line string) {
|
|||||||
m.Log("["+thisPlugin.Spec.Name+":"+strconv.Itoa(processID)+"] "+line, nil)
|
m.Log("["+thisPlugin.Spec.Name+":"+strconv.Itoa(processID)+"] "+line, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// StopPlugin stops a plugin, it is garanteed that the plugin is stopped after this function
|
||||||
func (m *Manager) StopPlugin(pluginID string) error {
|
func (m *Manager) StopPlugin(pluginID string) error {
|
||||||
plugin, ok := m.LoadedPlugins.Load(pluginID)
|
plugin, ok := m.LoadedPlugins.Load(pluginID)
|
||||||
if !ok {
|
if !ok {
|
||||||
@ -224,15 +225,3 @@ func (m *Manager) PluginStillRunning(pluginID string) bool {
|
|||||||
}
|
}
|
||||||
return plugin.(*Plugin).process.ProcessState == nil
|
return plugin.(*Plugin).process.ProcessState == nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// BlockUntilAllProcessExited blocks until all the plugins processes have exited
|
|
||||||
func (m *Manager) BlockUntilAllProcessExited() {
|
|
||||||
m.LoadedPlugins.Range(func(key, value interface{}) bool {
|
|
||||||
plugin := value.(*Plugin)
|
|
||||||
if m.PluginStillRunning(value.(*Plugin).Spec.ID) {
|
|
||||||
//Wait for the plugin to exit
|
|
||||||
plugin.process.Wait()
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
@ -10,6 +10,7 @@ package plugins
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
@ -34,14 +35,24 @@ func NewPluginManager(options *ManagerOptions) *Manager {
|
|||||||
os.MkdirAll(options.PluginDir, 0755)
|
os.MkdirAll(options.PluginDir, 0755)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Create the plugin config file if not exists
|
||||||
|
if !utils.FileExists(options.PluginGroupsConfig) {
|
||||||
|
js, _ := json.Marshal(map[string][]string{})
|
||||||
|
err := os.WriteFile(options.PluginGroupsConfig, js, 0644)
|
||||||
|
if err != nil {
|
||||||
|
options.Logger.PrintAndLog("plugin-manager", "Failed to create plugin group config file", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//Create database table
|
//Create database table
|
||||||
options.Database.NewTable("plugins")
|
options.Database.NewTable("plugins")
|
||||||
|
|
||||||
return &Manager{
|
return &Manager{
|
||||||
LoadedPlugins: sync.Map{},
|
LoadedPlugins: sync.Map{},
|
||||||
tagPluginMap: sync.Map{},
|
tagPluginMap: sync.Map{},
|
||||||
tagPluginList: make(map[string][]*Plugin),
|
tagPluginListMutex: sync.RWMutex{},
|
||||||
Options: options,
|
tagPluginList: make(map[string][]*Plugin),
|
||||||
|
Options: options,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -76,6 +87,14 @@ func (m *Manager) LoadPluginsFromDisk() error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if m.Options.PluginGroupsConfig != "" {
|
||||||
|
//Load the plugin groups from the config file
|
||||||
|
err = m.LoadPluginGroupsFromConfig()
|
||||||
|
if err != nil {
|
||||||
|
m.Log("Failed to load plugin groups", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//Generate the static forwarder radix tree
|
//Generate the static forwarder radix tree
|
||||||
m.UpdateTagsToPluginMaps()
|
m.UpdateTagsToPluginMaps()
|
||||||
|
|
||||||
@ -156,9 +175,6 @@ func (m *Manager) Close() {
|
|||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
|
|
||||||
//Wait until all loaded plugin process are terminated
|
|
||||||
m.BlockUntilAllProcessExited()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Plugin Functions */
|
/* Plugin Functions */
|
||||||
|
@ -24,6 +24,7 @@ func (m *Manager) UpdateTagsToPluginMaps() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//build the plugin list for each tag
|
//build the plugin list for each tag
|
||||||
|
m.tagPluginListMutex.Lock()
|
||||||
m.tagPluginList = make(map[string][]*Plugin)
|
m.tagPluginList = make(map[string][]*Plugin)
|
||||||
for tag, pluginIds := range m.Options.PluginGroups {
|
for tag, pluginIds := range m.Options.PluginGroups {
|
||||||
for _, pluginId := range pluginIds {
|
for _, pluginId := range pluginIds {
|
||||||
@ -35,6 +36,7 @@ func (m *Manager) UpdateTagsToPluginMaps() {
|
|||||||
m.tagPluginList[tag] = append(m.tagPluginList[tag], plugin)
|
m.tagPluginList[tag] = append(m.tagPluginList[tag], plugin)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
m.tagPluginListMutex.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
// GenerateForwarderRadixTree generates the radix tree for static forwarders
|
// GenerateForwarderRadixTree generates the radix tree for static forwarders
|
||||||
|
99
src/mod/plugins/tags.go
Normal file
99
src/mod/plugins/tags.go
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
package plugins
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
/*
|
||||||
|
Plugin Tags
|
||||||
|
|
||||||
|
This file contains the tags that are used to match the plugin tag
|
||||||
|
to the one on HTTP proxy rule. Once the tag is matched, the plugin
|
||||||
|
will be enabled on that given rule.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// LoadTagPluginMap loads the plugin map into the manager
|
||||||
|
// This will only load the plugin tags to option.PluginGroups map
|
||||||
|
// to push the changes to runtime, call UpdateTagsToPluginMaps()
|
||||||
|
func (m *Manager) LoadPluginGroupsFromConfig() error {
|
||||||
|
m.Options.pluginGroupsMutex.RLock()
|
||||||
|
defer m.Options.pluginGroupsMutex.RUnlock()
|
||||||
|
|
||||||
|
//Read the config file
|
||||||
|
rawConfig, err := os.ReadFile(m.Options.PluginGroupsConfig)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var config map[string][]string
|
||||||
|
err = json.Unmarshal(rawConfig, &config)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
//Reset m.tagPluginList
|
||||||
|
m.Options.PluginGroups = config
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddPluginToTag adds a plugin to a tag
|
||||||
|
func (m *Manager) AddPluginToTag(tag string, pluginID string) error {
|
||||||
|
m.Options.pluginGroupsMutex.RLock()
|
||||||
|
defer m.Options.pluginGroupsMutex.RUnlock()
|
||||||
|
|
||||||
|
//Check if the plugin exists
|
||||||
|
_, err := m.GetPluginByID(pluginID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
//Add to m.Options.PluginGroups
|
||||||
|
pluginList, ok := m.Options.PluginGroups[tag]
|
||||||
|
if !ok {
|
||||||
|
pluginList = []string{}
|
||||||
|
}
|
||||||
|
pluginList = append(pluginList, pluginID)
|
||||||
|
m.Options.PluginGroups[tag] = pluginList
|
||||||
|
|
||||||
|
//Update to runtime
|
||||||
|
m.UpdateTagsToPluginMaps()
|
||||||
|
|
||||||
|
//Save to file
|
||||||
|
return m.savePluginTagMap()
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemovePluginFromTag removes a plugin from a tag
|
||||||
|
func (m *Manager) RemovePluginFromTag(tag string, pluginID string) error {
|
||||||
|
// Check if the plugin exists in Options.PluginGroups
|
||||||
|
m.Options.pluginGroupsMutex.RLock()
|
||||||
|
defer m.Options.pluginGroupsMutex.RUnlock()
|
||||||
|
pluginList, ok := m.Options.PluginGroups[tag]
|
||||||
|
if !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove the plugin from the list
|
||||||
|
for i, id := range pluginList {
|
||||||
|
if id == pluginID {
|
||||||
|
pluginList = append(pluginList[:i], pluginList[i+1:]...)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m.Options.PluginGroups[tag] = pluginList
|
||||||
|
|
||||||
|
// Update to runtime
|
||||||
|
m.UpdateTagsToPluginMaps()
|
||||||
|
|
||||||
|
// Save to file
|
||||||
|
return m.savePluginTagMap()
|
||||||
|
}
|
||||||
|
|
||||||
|
// savePluginTagMap saves the plugin tag map to the config file
|
||||||
|
func (m *Manager) savePluginTagMap() error {
|
||||||
|
m.Options.pluginGroupsMutex.RLock()
|
||||||
|
defer m.Options.pluginGroupsMutex.RUnlock()
|
||||||
|
|
||||||
|
js, _ := json.Marshal(m.Options.PluginGroups)
|
||||||
|
return os.WriteFile(m.Options.PluginGroupsConfig, js, 0644)
|
||||||
|
}
|
@ -45,6 +45,7 @@ func (m *Manager) HandleRoute(w http.ResponseWriter, r *http.Request, tags []str
|
|||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
go func(thisTag string) {
|
go func(thisTag string) {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
|
m.tagPluginListMutex.RLock()
|
||||||
for _, plugin := range m.tagPluginList[thisTag] {
|
for _, plugin := range m.tagPluginList[thisTag] {
|
||||||
if plugin.Enabled && plugin.Spec.DynamicCaptureSniff != "" && plugin.Spec.DynamicCaptureIngress != "" {
|
if plugin.Enabled && plugin.Spec.DynamicCaptureSniff != "" && plugin.Spec.DynamicCaptureIngress != "" {
|
||||||
mutex.Lock()
|
mutex.Lock()
|
||||||
@ -52,6 +53,7 @@ func (m *Manager) HandleRoute(w http.ResponseWriter, r *http.Request, tags []str
|
|||||||
mutex.Unlock()
|
mutex.Unlock()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
m.tagPluginListMutex.RUnlock()
|
||||||
}(tag)
|
}(tag)
|
||||||
}
|
}
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
|
@ -29,19 +29,24 @@ type Plugin struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type ManagerOptions struct {
|
type ManagerOptions struct {
|
||||||
PluginDir string //The directory where the plugins are stored
|
PluginDir string //The directory where the plugins are stored
|
||||||
PluginGroups map[string][]string //The plugin groups,key is the tag name and the value is an array of plugin IDs
|
PluginGroups map[string][]string //The plugin groups,key is the tag name and the value is an array of plugin IDs
|
||||||
|
PluginGroupsConfig string //The group / tag configuration file, if set the plugin groups will be loaded from this file
|
||||||
|
|
||||||
/* Runtime */
|
/* Runtime */
|
||||||
SystemConst *zoraxyPlugin.RuntimeConstantValue
|
SystemConst *zoraxyPlugin.RuntimeConstantValue
|
||||||
CSRFTokenGen func(*http.Request) string `json:"-"` //The CSRF token generator function
|
CSRFTokenGen func(*http.Request) string `json:"-"` //The CSRF token generator function
|
||||||
Database *database.Database `json:"-"`
|
Database *database.Database `json:"-"`
|
||||||
Logger *logger.Logger `json:"-"`
|
Logger *logger.Logger `json:"-"`
|
||||||
|
|
||||||
|
/* Internal */
|
||||||
|
pluginGroupsMutex sync.RWMutex //Mutex for the pluginGroups
|
||||||
}
|
}
|
||||||
|
|
||||||
type Manager struct {
|
type Manager struct {
|
||||||
LoadedPlugins sync.Map //Storing *Plugin
|
LoadedPlugins sync.Map //Storing *Plugin
|
||||||
tagPluginMap sync.Map //Storing *radix.Tree for each plugin tag
|
tagPluginMap sync.Map //Storing *radix.Tree for each plugin tag
|
||||||
tagPluginList map[string][]*Plugin //Storing the plugin list for each tag, only concurrent READ is allowed
|
tagPluginListMutex sync.RWMutex //Mutex for the tagPluginList
|
||||||
Options *ManagerOptions
|
tagPluginList map[string][]*Plugin //Storing the plugin list for each tag, only concurrent READ is allowed
|
||||||
|
Options *ManagerOptions
|
||||||
}
|
}
|
||||||
|
16
src/start.go
16
src/start.go
@ -312,14 +312,14 @@ func startupSequence() {
|
|||||||
ZoraxyVersion: SYSTEM_VERSION,
|
ZoraxyVersion: SYSTEM_VERSION,
|
||||||
ZoraxyUUID: nodeUUID,
|
ZoraxyUUID: nodeUUID,
|
||||||
},
|
},
|
||||||
Database: sysdb,
|
Database: sysdb,
|
||||||
Logger: SystemWideLogger,
|
Logger: SystemWideLogger,
|
||||||
//TODO: REMOVE AFTER DEBUG
|
PluginGroupsConfig: CONF_PLUGIN_GROUPS,
|
||||||
PluginGroups: map[string][]string{
|
/*PluginGroups: map[string][]string{
|
||||||
"debug": {
|
"debug": {
|
||||||
"org.aroz.zoraxy.debugger",
|
"org.aroz.zoraxy.debugger",
|
||||||
},
|
},
|
||||||
},
|
},*/
|
||||||
CSRFTokenGen: func(r *http.Request) string {
|
CSRFTokenGen: func(r *http.Request) string {
|
||||||
return csrf.Token(r)
|
return csrf.Token(r)
|
||||||
},
|
},
|
||||||
@ -377,6 +377,12 @@ func ShutdownSeq() {
|
|||||||
if acmeAutoRenewer != nil {
|
if acmeAutoRenewer != nil {
|
||||||
acmeAutoRenewer.Close()
|
acmeAutoRenewer.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if accessController != nil {
|
||||||
|
SystemWideLogger.Println("Closing Access Controller")
|
||||||
|
accessController.Close()
|
||||||
|
}
|
||||||
|
|
||||||
//Close the plugin manager
|
//Close the plugin manager
|
||||||
SystemWideLogger.Println("Shutting down plugin manager")
|
SystemWideLogger.Println("Shutting down plugin manager")
|
||||||
pluginManager.Close()
|
pluginManager.Close()
|
||||||
|
@ -375,6 +375,21 @@
|
|||||||
<div class="toggleSucc" style="float: right; display:none; color: #2abd4d;" >
|
<div class="toggleSucc" style="float: right; display:none; color: #2abd4d;" >
|
||||||
<i class="ui green checkmark icon"></i> Setting Saved
|
<i class="ui green checkmark icon"></i> Setting Saved
|
||||||
</div>
|
</div>
|
||||||
|
<div class="ui basic segment advanceoptions">
|
||||||
|
<div class="ui accordion advanceSettings">
|
||||||
|
<div class="title">
|
||||||
|
<i class="dropdown icon"></i>
|
||||||
|
Advance Settings
|
||||||
|
</div>
|
||||||
|
<div class="content">
|
||||||
|
<div class="ui toggle checkbox">
|
||||||
|
<input type="checkbox" id="enableWhitelistLoopback">
|
||||||
|
<label>Enable LAN and Loopback<br>
|
||||||
|
<small>Allowing loopback request from your public IP address and local area network devices</small></label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<h4>Country Whitelist</h4>
|
<h4>Country Whitelist</h4>
|
||||||
<p><i class="yellow exclamation triangle icon"></i>
|
<p><i class="yellow exclamation triangle icon"></i>
|
||||||
This will allow all requests from the selected country. The requester's location is estimated from their IP address and may not be 100% accurate.</p>
|
This will allow all requests from the selected country. The requester's location is estimated from their IP address and may not be 100% accurate.</p>
|
||||||
@ -1043,6 +1058,31 @@
|
|||||||
enableWhitelist();
|
enableWhitelist();
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$.get("/api/whitelist/allowLocal", function(data){
|
||||||
|
if (data == true){
|
||||||
|
$('#enableWhitelistLoopback').parent().checkbox("set checked");
|
||||||
|
}else{
|
||||||
|
$('#enableWhitelistLoopback').parent().checkbox("set unchecked");
|
||||||
|
}
|
||||||
|
|
||||||
|
//Register on change event
|
||||||
|
$("#enableWhitelistLoopback").off("change").on("change", function(){
|
||||||
|
enableWhitelistLoopback();
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function enableWhitelistLoopback(){
|
||||||
|
var isChecked = $('#enableWhitelistLoopback').is(':checked');
|
||||||
|
$.cjax({
|
||||||
|
type: 'POST',
|
||||||
|
url: '/api/whitelist/allowLocal',
|
||||||
|
data: { enable: isChecked, id: currentEditingAccessRule},
|
||||||
|
success: function(data){
|
||||||
|
msgbox("Loopback whitelist " + (isChecked ? "enabled" : "disabled"), true);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -1606,4 +1646,7 @@
|
|||||||
function handleUnban(targetIp){
|
function handleUnban(targetIp){
|
||||||
removeIpBlacklist(targetIp);
|
removeIpBlacklist(targetIp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Bind UI events
|
||||||
|
$(".advanceSettings").accordion();
|
||||||
</script>
|
</script>
|
Loading…
x
Reference in New Issue
Block a user