Browse Source

auto update script executed

Toby Chui 1 year ago
parent
commit
00d59ed7e3
5 changed files with 292 additions and 1 deletions
  1. 2 0
      api.go
  2. 2 0
      main.go
  3. 100 0
      mod/pathblock/handler.go
  4. 175 0
      mod/pathblock/pathblock.go
  5. 13 1
      start.go

+ 2 - 0
api.go

@@ -81,6 +81,8 @@ func initAPIs() {
 	authRouter.HandleFunc("/api/whitelist/ip/remove", handleIpWhitelistRemove)
 	authRouter.HandleFunc("/api/whitelist/enable", handleWhitelistEnable)
 
+	//Path Blocker APIs
+
 	//Statistic & uptime monitoring API
 	authRouter.HandleFunc("/api/stats/summary", statisticCollector.HandleTodayStatLoad)
 	authRouter.HandleFunc("/api/stats/countries", HandleCountryDistrSummary)

+ 2 - 0
main.go

@@ -21,6 +21,7 @@ import (
 	"imuslab.com/zoraxy/mod/geodb"
 	"imuslab.com/zoraxy/mod/mdns"
 	"imuslab.com/zoraxy/mod/netstat"
+	"imuslab.com/zoraxy/mod/pathblock"
 	"imuslab.com/zoraxy/mod/sshprox"
 	"imuslab.com/zoraxy/mod/statistic"
 	"imuslab.com/zoraxy/mod/statistic/analytic"
@@ -57,6 +58,7 @@ var (
 	authAgent          *auth.AuthAgent         //Authentication agent
 	tlsCertManager     *tlscert.Manager        //TLS / SSL management
 	redirectTable      *redirection.RuleTable  //Handle special redirection rule sets
+	pathBlockHandler   *pathblock.Handler      //Handle specific path blocking or custom headers
 	geodbStore         *geodb.Store            //GeoIP database, also handle black list and whitelist features
 	netstatBuffers     *netstat.NetStatBuffers //Realtime graph buffers
 	statisticCollector *statistic.Collector    //Collecting statistic from visitors

+ 100 - 0
mod/pathblock/handler.go

@@ -0,0 +1,100 @@
+package pathblock
+
+import (
+	"encoding/json"
+	"net/http"
+	"strconv"
+
+	uuid "github.com/satori/go.uuid"
+	"imuslab.com/zoraxy/mod/utils"
+)
+
+/*
+	handler.go
+
+	This script handles pathblock api
+*/
+
+func (h *Handler) HandleListBlockingPath(w http.ResponseWriter, r *http.Request) {
+	js, _ := json.Marshal(h.BlockingPaths)
+	utils.SendJSONResponse(w, string(js))
+}
+
+func (h *Handler) HandleAddBlockingPath(w http.ResponseWriter, r *http.Request) {
+	matchingPath, err := utils.PostPara(r, "matchingPath")
+	if err != nil {
+		utils.SendErrorResponse(w, "invalid matching path given")
+		return
+	}
+
+	exactMatch, err := utils.PostPara(r, "exactMatch")
+	if err != nil {
+		utils.SendErrorResponse(w, "invalid exact match value given")
+		return
+	}
+
+	statusCodeString, err := utils.PostPara(r, "statusCode")
+	if err != nil {
+		utils.SendErrorResponse(w, "invalid status code given")
+		return
+	}
+
+	statusCode, err := strconv.Atoi(statusCodeString)
+	if err != nil {
+		utils.SendErrorResponse(w, "invalid status code given")
+		return
+	}
+
+	enabled, err := utils.PostPara(r, "enabled")
+	if err != nil {
+		utils.SendErrorResponse(w, "invalid enabled value given")
+		return
+	}
+
+	caseSensitive, err := utils.PostPara(r, "caseSensitive")
+	if err != nil {
+		utils.SendErrorResponse(w, "invalid case sensitive value given")
+		return
+	}
+
+	targetBlockingPath := BlockingPath{
+		UUID:          uuid.NewV4().String(),
+		MatchingPath:  matchingPath,
+		ExactMatch:    exactMatch == "true",
+		StatusCode:    statusCode,
+		CustomHeaders: http.Header{},
+		CustomHTML:    []byte(""),
+		Enabled:       enabled == "true",
+		CaseSenitive:  caseSensitive == "true",
+	}
+
+	err = h.AddBlockingPath(&targetBlockingPath)
+	if err != nil {
+		utils.SendErrorResponse(w, err.Error())
+		return
+	}
+
+	utils.SendOK(w)
+}
+
+func (h *Handler) HandleRemoveBlockingPath(w http.ResponseWriter, r *http.Request) {
+	blockerUUID, err := utils.PostPara(r, "uuid")
+	if err != nil {
+		utils.SendErrorResponse(w, "invalid uuid given")
+		return
+	}
+
+	targetRule := h.GetPathBlockerFromUUID(blockerUUID)
+	if targetRule == nil {
+		//Not found
+		utils.SendErrorResponse(w, "target path blocker not found")
+		return
+	}
+
+	err = h.RemoveBlockingPathByUUID(blockerUUID)
+	if err != nil {
+		utils.SendErrorResponse(w, err.Error())
+		return
+	}
+	utils.SendOK(w)
+}

+ 175 - 0
mod/pathblock/pathblock.go

@@ -0,0 +1,175 @@
+package pathblock
+
+import (
+	"encoding/json"
+	"errors"
+	"net/http"
+	"os"
+	"path/filepath"
+	"strings"
+
+	"imuslab.com/zoraxy/mod/utils"
+)
+
+/*
+	Pathblock.go
+
+	This script block off some of the specific pathname in access
+	For example, this module can help you block request for a particular
+	apache directory or functional endpoints like /.well-known/ when you
+	are not using it
+*/
+
+type Options struct {
+	ConfigFolder string //The folder to store the path blocking config files
+}
+
+type BlockingPath struct {
+	UUID          string
+	MatchingPath  string
+	ExactMatch    bool
+	StatusCode    int
+	CustomHeaders http.Header
+	CustomHTML    []byte
+	Enabled       bool
+	CaseSenitive  bool
+}
+
+type Handler struct {
+	Options       *Options
+	BlockingPaths []*BlockingPath
+}
+
+// Create a new path blocker handler
+func NewPathBlocker(options *Options) *Handler {
+	//Create folder if not exists
+	if !utils.FileExists(options.ConfigFolder) {
+		os.Mkdir(options.ConfigFolder, 0775)
+	}
+
+	//Load the configs from file
+	//TODO
+
+	return &Handler{
+		Options:       options,
+		BlockingPaths: []*BlockingPath{},
+	}
+}
+
+func (h *Handler) ListBlockingPath() []*BlockingPath {
+	return h.BlockingPaths
+}
+
+// Get the blocker from matching path (path match, ignore tailing slash)
+func (h *Handler) GetPathBlockerFromMatchingPath(matchingPath string) *BlockingPath {
+	for _, blocker := range h.BlockingPaths {
+		if blocker.MatchingPath == matchingPath {
+			return blocker
+		} else if strings.TrimSuffix(blocker.MatchingPath, "/") == strings.TrimSuffix(matchingPath, "/") {
+			return blocker
+		}
+	}
+
+	return nil
+}
+
+func (h *Handler) GetPathBlockerFromUUID(UUID string) *BlockingPath {
+	for _, blocker := range h.BlockingPaths {
+		if blocker.UUID == UUID {
+			return blocker
+		}
+	}
+
+	return nil
+}
+
+func (h *Handler) AddBlockingPath(pathBlocker *BlockingPath) error {
+	//Check if the blocker exists
+	blockerPath := pathBlocker.MatchingPath
+	targetBlocker := h.GetPathBlockerFromMatchingPath(blockerPath)
+	if targetBlocker != nil {
+		//Blocker with the same matching path already exists
+		return errors.New("path blocker with the same path already exists")
+	}
+
+	h.BlockingPaths = append(h.BlockingPaths, pathBlocker)
+
+	//Write the new config to file
+	return h.SaveBlockerToFile(pathBlocker)
+}
+
+func (h *Handler) RemoveBlockingPathByUUID(uuid string) error {
+	newBlockingList := []*BlockingPath{}
+	for _, thisBlocker := range h.BlockingPaths {
+		if thisBlocker.UUID != uuid {
+			newBlockingList = append(newBlockingList, thisBlocker)
+		}
+	}
+
+	if len(h.BlockingPaths) == len(newBlockingList) {
+		//Nothing is removed
+		return errors.New("given matching path blocker not exists")
+	}
+
+	h.BlockingPaths = newBlockingList
+
+	return h.RemoveBlockerFromFile(uuid)
+}
+
+func (h *Handler) SaveBlockerToFile(pathBlocker *BlockingPath) error {
+	saveFilename := filepath.Join(h.Options.ConfigFolder, pathBlocker.UUID)
+	js, _ := json.MarshalIndent(pathBlocker, "", " ")
+	return os.WriteFile(saveFilename, js, 0775)
+}
+
+func (h *Handler) RemoveBlockerFromFile(uuid string) error {
+	expectedConfigFile := filepath.Join(h.Options.ConfigFolder, uuid)
+	if !utils.FileExists(expectedConfigFile) {
+		return errors.New("config file not found on disk")
+	}
+
+	return os.Remove(expectedConfigFile)
+}
+
+// Get all the matching blockers for the given URL path
+// return all the path blockers and the max length matching rule
+func (h *Handler) GetMatchingBlockers(urlPath string) ([]*BlockingPath, *BlockingPath) {
+	urlPath = strings.TrimSuffix(urlPath, "/")
+	matchingBlockers := []*BlockingPath{}
+	var longestMatchingPrefix *BlockingPath = nil
+	for _, thisBlocker := range h.BlockingPaths {
+		if thisBlocker.Enabled == false {
+			//This blocker is not enabled. Ignore this
+			continue
+		}
+
+		incomingURLPath := urlPath
+		matchingPath := strings.TrimSuffix(thisBlocker.MatchingPath, "/")
+
+		if !thisBlocker.CaseSenitive {
+			//This is not case sensitive
+			incomingURLPath = strings.ToLower(incomingURLPath)
+			matchingPath = strings.ToLower(matchingPath)
+		}
+
+		if matchingPath == incomingURLPath {
+			//This blocker have exact url path match
+			matchingBlockers = append(matchingBlockers, thisBlocker)
+			if longestMatchingPrefix == nil || len(thisBlocker.MatchingPath) > len(longestMatchingPrefix.MatchingPath) {
+				longestMatchingPrefix = thisBlocker
+			}
+			continue
+		}
+
+		if !thisBlocker.ExactMatch && strings.HasPrefix(incomingURLPath, matchingPath) {
+			//This blocker have prefix url match
+			matchingBlockers = append(matchingBlockers, thisBlocker)
+			if longestMatchingPrefix == nil || len(thisBlocker.MatchingPath) > len(longestMatchingPrefix.MatchingPath) {
+				longestMatchingPrefix = thisBlocker
+			}
+			continue
+		}
+	}
+
+	return matchingBlockers, longestMatchingPrefix
+}

+ 13 - 1
start.go

@@ -15,6 +15,7 @@ import (
 	"imuslab.com/zoraxy/mod/geodb"
 	"imuslab.com/zoraxy/mod/mdns"
 	"imuslab.com/zoraxy/mod/netstat"
+	"imuslab.com/zoraxy/mod/pathblock"
 	"imuslab.com/zoraxy/mod/sshprox"
 	"imuslab.com/zoraxy/mod/statistic"
 	"imuslab.com/zoraxy/mod/statistic/analytic"
@@ -67,7 +68,7 @@ func startupSequence() {
 	}
 
 	//Create a redirection rule table
-	redirectTable, err = redirection.NewRuleTable("./rules")
+	redirectTable, err = redirection.NewRuleTable("./rules/redirect")
 	if err != nil {
 		panic(err)
 	}
@@ -93,6 +94,17 @@ func startupSequence() {
 		panic(err)
 	}
 
+	/*
+		Path Blocker
+
+		This section of starutp script start the pathblocker
+		from file.
+	*/
+
+	pathBlockHandler = pathblock.NewPathBlocker(&pathblock.Options{
+		ConfigFolder: "./rules/pathblock",
+	})
+
 	/*
 		MDNS Discovery Service