mirror of
https://github.com/tobychui/zoraxy.git
synced 2025-06-03 06:07:20 +02:00
617 lines
14 KiB
Go
617 lines
14 KiB
Go
package main
|
|
|
|
import (
|
|
"encoding/json"
|
|
"net/http"
|
|
"sort"
|
|
"strings"
|
|
|
|
"github.com/google/uuid"
|
|
"github.com/microcosm-cc/bluemonday"
|
|
|
|
"imuslab.com/zoraxy/mod/access"
|
|
"imuslab.com/zoraxy/mod/utils"
|
|
)
|
|
|
|
/*
|
|
accesslist.go
|
|
|
|
This script file is added to extend the
|
|
reverse proxy function to include
|
|
banning / whitelist a specific IP address or country code
|
|
*/
|
|
|
|
/*
|
|
General Function
|
|
*/
|
|
|
|
func handleListAccessRules(w http.ResponseWriter, r *http.Request) {
|
|
allAccessRules := accessController.ListAllAccessRules()
|
|
js, _ := json.Marshal(allAccessRules)
|
|
utils.SendJSONResponse(w, string(js))
|
|
}
|
|
|
|
func handleAttachRuleToHost(w http.ResponseWriter, r *http.Request) {
|
|
ruleid, err := utils.PostPara(r, "id")
|
|
if err != nil {
|
|
utils.SendErrorResponse(w, "invalid rule name")
|
|
return
|
|
}
|
|
|
|
host, err := utils.PostPara(r, "host")
|
|
if err != nil {
|
|
utils.SendErrorResponse(w, "invalid rule name")
|
|
return
|
|
}
|
|
|
|
//Check if access rule and proxy rule exists
|
|
targetProxyEndpoint, err := dynamicProxyRouter.LoadProxy(host)
|
|
if err != nil {
|
|
utils.SendErrorResponse(w, "invalid host given")
|
|
return
|
|
}
|
|
if !accessController.AccessRuleExists(ruleid) {
|
|
utils.SendErrorResponse(w, "access rule not exists")
|
|
return
|
|
}
|
|
|
|
//Update the proxy host acess rule id
|
|
targetProxyEndpoint.AccessFilterUUID = ruleid
|
|
targetProxyEndpoint.UpdateToRuntime()
|
|
err = SaveReverseProxyConfig(targetProxyEndpoint)
|
|
if err != nil {
|
|
utils.SendErrorResponse(w, err.Error())
|
|
return
|
|
}
|
|
|
|
utils.SendOK(w)
|
|
}
|
|
|
|
// Create a new access rule, require name and desc only
|
|
func handleCreateAccessRule(w http.ResponseWriter, r *http.Request) {
|
|
ruleName, err := utils.PostPara(r, "name")
|
|
if err != nil {
|
|
utils.SendErrorResponse(w, "invalid rule name")
|
|
return
|
|
}
|
|
ruleDesc, _ := utils.PostPara(r, "desc")
|
|
|
|
//Filter out injection if any
|
|
p := bluemonday.StripTagsPolicy()
|
|
ruleName = p.Sanitize(ruleName)
|
|
ruleDesc = p.Sanitize(ruleDesc)
|
|
|
|
ruleUUID := uuid.New().String()
|
|
newAccessRule := access.AccessRule{
|
|
ID: ruleUUID,
|
|
Name: ruleName,
|
|
Desc: ruleDesc,
|
|
BlacklistEnabled: false,
|
|
WhitelistEnabled: false,
|
|
}
|
|
|
|
//Add it to runtime
|
|
err = accessController.AddNewAccessRule(&newAccessRule)
|
|
if err != nil {
|
|
utils.SendErrorResponse(w, err.Error())
|
|
return
|
|
}
|
|
|
|
utils.SendOK(w)
|
|
}
|
|
|
|
// Handle removing an access rule. All proxy endpoint using this rule will be
|
|
// set to use the default rule
|
|
func handleRemoveAccessRule(w http.ResponseWriter, r *http.Request) {
|
|
ruleID, err := utils.PostPara(r, "id")
|
|
if err != nil {
|
|
utils.SendErrorResponse(w, "invalid rule id given")
|
|
return
|
|
}
|
|
|
|
if ruleID == "default" {
|
|
utils.SendErrorResponse(w, "default access rule cannot be removed")
|
|
return
|
|
}
|
|
|
|
ruleID = strings.TrimSpace(ruleID)
|
|
|
|
//Set all proxy hosts that use this access rule back to using "default"
|
|
allProxyEndpoints := dynamicProxyRouter.GetProxyEndpointsAsMap()
|
|
for _, proxyEndpoint := range allProxyEndpoints {
|
|
if strings.EqualFold(proxyEndpoint.AccessFilterUUID, ruleID) {
|
|
//This proxy endpoint is using the current access filter.
|
|
//set it to default
|
|
proxyEndpoint.AccessFilterUUID = "default"
|
|
proxyEndpoint.UpdateToRuntime()
|
|
err = SaveReverseProxyConfig(proxyEndpoint)
|
|
if err != nil {
|
|
SystemWideLogger.PrintAndLog("Access", "Unable to save updated proxy endpoint "+proxyEndpoint.RootOrMatchingDomain, err)
|
|
} else {
|
|
SystemWideLogger.PrintAndLog("Access", "Updated "+proxyEndpoint.RootOrMatchingDomain+" access filter to \"default\"", nil)
|
|
}
|
|
}
|
|
}
|
|
|
|
//Remove the access rule by ID
|
|
err = accessController.RemoveAccessRuleByID(ruleID)
|
|
if err != nil {
|
|
utils.SendErrorResponse(w, err.Error())
|
|
return
|
|
}
|
|
|
|
SystemWideLogger.PrintAndLog("Access", "Access Rule "+ruleID+" removed", nil)
|
|
utils.SendOK(w)
|
|
}
|
|
|
|
// Only the name and desc, for other properties use blacklist / whitelist api
|
|
func handleUpadateAccessRule(w http.ResponseWriter, r *http.Request) {
|
|
ruleID, err := utils.PostPara(r, "id")
|
|
if err != nil {
|
|
utils.SendErrorResponse(w, "invalid rule id")
|
|
return
|
|
}
|
|
ruleName, err := utils.PostPara(r, "name")
|
|
if err != nil {
|
|
utils.SendErrorResponse(w, "invalid rule name")
|
|
return
|
|
}
|
|
ruleDesc, _ := utils.PostPara(r, "desc")
|
|
|
|
//Filter anything weird
|
|
p := bluemonday.StrictPolicy()
|
|
ruleName = p.Sanitize(ruleName)
|
|
ruleDesc = p.Sanitize(ruleDesc)
|
|
|
|
err = accessController.UpdateAccessRule(ruleID, ruleName, ruleDesc)
|
|
if err != nil {
|
|
utils.SendErrorResponse(w, err.Error())
|
|
return
|
|
}
|
|
|
|
utils.SendOK(w)
|
|
}
|
|
|
|
/*
|
|
Blacklist Related
|
|
*/
|
|
|
|
// List a of blacklisted ip address or country code
|
|
func handleListBlacklisted(w http.ResponseWriter, r *http.Request) {
|
|
bltype, err := utils.GetPara(r, "type")
|
|
if err != nil {
|
|
bltype = "country"
|
|
}
|
|
|
|
ruleID, err := utils.GetPara(r, "id")
|
|
if err != nil {
|
|
//Use default if not set
|
|
ruleID = "default"
|
|
}
|
|
|
|
//Load the target rule from access controller
|
|
rule, err := accessController.GetAccessRuleByID(ruleID)
|
|
if err != nil {
|
|
utils.SendErrorResponse(w, err.Error())
|
|
return
|
|
}
|
|
|
|
resulst := []string{}
|
|
if bltype == "country" {
|
|
resulst = rule.GetAllBlacklistedCountryCode()
|
|
} else if bltype == "ip" {
|
|
resulst = rule.GetAllBlacklistedIp()
|
|
}
|
|
|
|
js, _ := json.Marshal(resulst)
|
|
utils.SendJSONResponse(w, string(js))
|
|
|
|
}
|
|
|
|
func handleCountryBlacklistAdd(w http.ResponseWriter, r *http.Request) {
|
|
countryCode, err := utils.PostPara(r, "cc")
|
|
if err != nil {
|
|
utils.SendErrorResponse(w, "invalid or empty country code")
|
|
return
|
|
}
|
|
|
|
ruleID, err := utils.PostPara(r, "id")
|
|
if err != nil {
|
|
ruleID = "default"
|
|
}
|
|
|
|
comment, _ := utils.PostPara(r, "comment")
|
|
p := bluemonday.StripTagsPolicy()
|
|
comment = p.Sanitize(comment)
|
|
|
|
//Load the target rule from access controller
|
|
rule, err := accessController.GetAccessRuleByID(ruleID)
|
|
if err != nil {
|
|
utils.SendErrorResponse(w, err.Error())
|
|
return
|
|
}
|
|
|
|
//Check if the country code contains comma, if yes, split it
|
|
if strings.Contains(countryCode, ",") {
|
|
codes := strings.Split(countryCode, ",")
|
|
for _, code := range codes {
|
|
code = strings.TrimSpace(code)
|
|
rule.AddCountryCodeToBlackList(code, comment)
|
|
}
|
|
} else {
|
|
countryCode = strings.TrimSpace(countryCode)
|
|
rule.AddCountryCodeToBlackList(countryCode, comment)
|
|
}
|
|
|
|
utils.SendOK(w)
|
|
}
|
|
|
|
func handleCountryBlacklistRemove(w http.ResponseWriter, r *http.Request) {
|
|
countryCode, err := utils.PostPara(r, "cc")
|
|
if err != nil {
|
|
utils.SendErrorResponse(w, "invalid or empty country code")
|
|
return
|
|
}
|
|
|
|
ruleID, err := utils.PostPara(r, "id")
|
|
if err != nil {
|
|
ruleID = "default"
|
|
}
|
|
|
|
//Load the target rule from access controller
|
|
rule, err := accessController.GetAccessRuleByID(ruleID)
|
|
if err != nil {
|
|
utils.SendErrorResponse(w, err.Error())
|
|
return
|
|
}
|
|
|
|
//Check if the country code contains comma, if yes, split it
|
|
if strings.Contains(countryCode, ",") {
|
|
codes := strings.Split(countryCode, ",")
|
|
for _, code := range codes {
|
|
code = strings.TrimSpace(code)
|
|
rule.RemoveCountryCodeFromBlackList(code)
|
|
}
|
|
} else {
|
|
countryCode = strings.TrimSpace(countryCode)
|
|
rule.RemoveCountryCodeFromBlackList(countryCode)
|
|
}
|
|
|
|
utils.SendOK(w)
|
|
}
|
|
|
|
func handleIpBlacklistAdd(w http.ResponseWriter, r *http.Request) {
|
|
ipAddr, err := utils.PostPara(r, "ip")
|
|
if err != nil {
|
|
utils.SendErrorResponse(w, "invalid or empty ip address")
|
|
return
|
|
}
|
|
|
|
ruleID, err := utils.PostPara(r, "id")
|
|
if err != nil {
|
|
ruleID = "default"
|
|
}
|
|
|
|
//Load the target rule from access controller
|
|
rule, err := accessController.GetAccessRuleByID(ruleID)
|
|
if err != nil {
|
|
utils.SendErrorResponse(w, err.Error())
|
|
return
|
|
}
|
|
|
|
comment, _ := utils.GetPara(r, "comment")
|
|
p := bluemonday.StripTagsPolicy()
|
|
comment = p.Sanitize(comment)
|
|
|
|
rule.AddIPToBlackList(ipAddr, comment)
|
|
utils.SendOK(w)
|
|
}
|
|
|
|
func handleIpBlacklistRemove(w http.ResponseWriter, r *http.Request) {
|
|
ipAddr, err := utils.PostPara(r, "ip")
|
|
if err != nil {
|
|
utils.SendErrorResponse(w, "invalid or empty ip address")
|
|
return
|
|
}
|
|
|
|
ruleID, err := utils.PostPara(r, "id")
|
|
if err != nil {
|
|
ruleID = "default"
|
|
}
|
|
|
|
//Load the target rule from access controller
|
|
rule, err := accessController.GetAccessRuleByID(ruleID)
|
|
if err != nil {
|
|
utils.SendErrorResponse(w, err.Error())
|
|
return
|
|
}
|
|
|
|
rule.RemoveIPFromBlackList(ipAddr)
|
|
|
|
utils.SendOK(w)
|
|
}
|
|
|
|
func handleBlacklistEnable(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 == "" {
|
|
//enable paramter not set
|
|
currentEnabled := rule.BlacklistEnabled
|
|
js, _ := json.Marshal(currentEnabled)
|
|
utils.SendJSONResponse(w, string(js))
|
|
} else {
|
|
if enable == "true" {
|
|
rule.ToggleBlacklist(true)
|
|
} else if enable == "false" {
|
|
rule.ToggleBlacklist(false)
|
|
} else {
|
|
utils.SendErrorResponse(w, "invalid enable state: only true and false is accepted")
|
|
return
|
|
}
|
|
|
|
utils.SendOK(w)
|
|
}
|
|
}
|
|
|
|
/*
|
|
Whitelist Related
|
|
*/
|
|
|
|
func handleListWhitelisted(w http.ResponseWriter, r *http.Request) {
|
|
bltype, err := utils.GetPara(r, "type")
|
|
if err != nil {
|
|
bltype = "country"
|
|
}
|
|
|
|
ruleID, err := utils.GetPara(r, "id")
|
|
if err != nil {
|
|
ruleID = "default"
|
|
}
|
|
|
|
rule, err := accessController.GetAccessRuleByID(ruleID)
|
|
if err != nil {
|
|
utils.SendErrorResponse(w, err.Error())
|
|
return
|
|
}
|
|
|
|
resulst := []*access.WhitelistEntry{}
|
|
if bltype == "country" {
|
|
resulst = rule.GetAllWhitelistedCountryCode()
|
|
} else if bltype == "ip" {
|
|
resulst = rule.GetAllWhitelistedIp()
|
|
}
|
|
|
|
js, _ := json.Marshal(resulst)
|
|
utils.SendJSONResponse(w, string(js))
|
|
|
|
}
|
|
|
|
func handleCountryWhitelistAdd(w http.ResponseWriter, r *http.Request) {
|
|
countryCode, err := utils.PostPara(r, "cc")
|
|
if err != nil {
|
|
utils.SendErrorResponse(w, "invalid or empty country code")
|
|
return
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
comment, _ := utils.PostPara(r, "comment")
|
|
p := bluemonday.StrictPolicy()
|
|
comment = p.Sanitize(comment)
|
|
|
|
//Check if the country code contains comma, if yes, split it
|
|
if strings.Contains(countryCode, ",") {
|
|
codes := strings.Split(countryCode, ",")
|
|
for _, code := range codes {
|
|
code = strings.TrimSpace(code)
|
|
rule.AddCountryCodeToWhitelist(code, comment)
|
|
}
|
|
} else {
|
|
countryCode = strings.TrimSpace(countryCode)
|
|
rule.AddCountryCodeToWhitelist(countryCode, comment)
|
|
}
|
|
|
|
utils.SendOK(w)
|
|
}
|
|
|
|
func handleCountryWhitelistRemove(w http.ResponseWriter, r *http.Request) {
|
|
countryCode, err := utils.PostPara(r, "cc")
|
|
if err != nil {
|
|
utils.SendErrorResponse(w, "invalid or empty country code")
|
|
return
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
//Check if the country code contains comma, if yes, split it
|
|
if strings.Contains(countryCode, ",") {
|
|
codes := strings.Split(countryCode, ",")
|
|
for _, code := range codes {
|
|
code = strings.TrimSpace(code)
|
|
rule.RemoveCountryCodeFromWhitelist(code)
|
|
}
|
|
} else {
|
|
countryCode = strings.TrimSpace(countryCode)
|
|
rule.RemoveCountryCodeFromWhitelist(countryCode)
|
|
}
|
|
|
|
utils.SendOK(w)
|
|
}
|
|
|
|
func handleIpWhitelistAdd(w http.ResponseWriter, r *http.Request) {
|
|
ipAddr, err := utils.PostPara(r, "ip")
|
|
if err != nil {
|
|
utils.SendErrorResponse(w, "invalid or empty ip address")
|
|
return
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
comment, _ := utils.PostPara(r, "comment")
|
|
p := bluemonday.StrictPolicy()
|
|
comment = p.Sanitize(comment)
|
|
|
|
rule.AddIPToWhiteList(ipAddr, comment)
|
|
utils.SendOK(w)
|
|
}
|
|
|
|
func handleIpWhitelistRemove(w http.ResponseWriter, r *http.Request) {
|
|
ipAddr, err := utils.PostPara(r, "ip")
|
|
if err != nil {
|
|
utils.SendErrorResponse(w, "invalid or empty ip address")
|
|
return
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
rule.RemoveIPFromWhiteList(ipAddr)
|
|
|
|
utils.SendOK(w)
|
|
}
|
|
|
|
func handleWhitelistEnable(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.WhitelistEnabled
|
|
js, _ := json.Marshal(currentEnabled)
|
|
utils.SendJSONResponse(w, string(js))
|
|
} else {
|
|
if enable == "true" {
|
|
rule.ToggleWhitelist(true)
|
|
} else if enable == "false" {
|
|
rule.ToggleWhitelist(false)
|
|
} else {
|
|
utils.SendErrorResponse(w, "invalid enable state: only true and false is accepted")
|
|
return
|
|
}
|
|
|
|
utils.SendOK(w)
|
|
}
|
|
}
|
|
|
|
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
|
|
func handleListQuickBan(w http.ResponseWriter, r *http.Request) {
|
|
currentSummary := statisticCollector.GetCurrentDailySummary()
|
|
type quickBanEntry struct {
|
|
IpAddr string
|
|
Count int
|
|
CountryCode string
|
|
}
|
|
result := []quickBanEntry{}
|
|
currentSummary.RequestClientIp.Range(func(key, value interface{}) bool {
|
|
ip := key.(string)
|
|
count := value.(int)
|
|
thisEntry := quickBanEntry{
|
|
IpAddr: ip,
|
|
Count: count,
|
|
}
|
|
|
|
//Get the country code
|
|
geoinfo, err := geodbStore.ResolveCountryCodeFromIP(ip)
|
|
if err == nil {
|
|
thisEntry.CountryCode = geoinfo.CountryIsoCode
|
|
}
|
|
|
|
result = append(result, thisEntry)
|
|
return true
|
|
})
|
|
|
|
//Sort result based on count
|
|
sort.Slice(result, func(i, j int) bool {
|
|
return result[i].Count > result[j].Count
|
|
})
|
|
|
|
js, _ := json.Marshal(result)
|
|
utils.SendJSONResponse(w, string(js))
|
|
}
|