mirror of
https://github.com/tobychui/zoraxy.git
synced 2025-06-03 06:07:20 +02:00
parent
dce58343db
commit
4f7f60188f
@ -55,6 +55,10 @@ func initAPIs() {
|
||||
authRouter.HandleFunc("/api/proxy/setIncoming", HandleIncomingPortSet)
|
||||
authRouter.HandleFunc("/api/proxy/useHttpsRedirect", HandleUpdateHttpsRedirect)
|
||||
authRouter.HandleFunc("/api/proxy/requestIsProxied", HandleManagementProxyCheck)
|
||||
//Reverse proxy auth related APIs
|
||||
authRouter.HandleFunc("/api/proxy/auth/exceptions/list", ListProxyBasicAuthExceptionPaths)
|
||||
authRouter.HandleFunc("/api/proxy/auth/exceptions/add", AddProxyBasicAuthExceptionPaths)
|
||||
authRouter.HandleFunc("/api/proxy/auth/exceptions/delete", RemoveProxyBasicAuthExceptionPaths)
|
||||
|
||||
//TLS / SSL config
|
||||
authRouter.HandleFunc("/api/cert/tls", handleToggleTLSProxy)
|
||||
|
@ -5,7 +5,6 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
@ -26,16 +25,18 @@ import (
|
||||
*/
|
||||
|
||||
type Record struct {
|
||||
ProxyType string
|
||||
Rootname string
|
||||
ProxyTarget string
|
||||
UseTLS bool
|
||||
SkipTlsValidation bool
|
||||
RequireBasicAuth bool
|
||||
BasicAuthCredentials []*dynamicproxy.BasicAuthCredentials
|
||||
ProxyType string
|
||||
Rootname string
|
||||
ProxyTarget string
|
||||
UseTLS bool
|
||||
SkipTlsValidation bool
|
||||
RequireBasicAuth bool
|
||||
BasicAuthCredentials []*dynamicproxy.BasicAuthCredentials
|
||||
BasicAuthExceptionRules []*dynamicproxy.BasicAuthExceptionRule
|
||||
}
|
||||
|
||||
func SaveReverseProxyConfig(proxyConfigRecord *Record) error {
|
||||
// Save a reverse proxy config record to file
|
||||
func SaveReverseProxyConfigToFile(proxyConfigRecord *Record) error {
|
||||
//TODO: Make this accept new def types
|
||||
os.MkdirAll("./conf/proxy/", 0775)
|
||||
filename := getFilenameFromRootName(proxyConfigRecord.Rootname)
|
||||
@ -45,10 +46,19 @@ func SaveReverseProxyConfig(proxyConfigRecord *Record) error {
|
||||
|
||||
//Write to file
|
||||
js, _ := json.MarshalIndent(thisRecord, "", " ")
|
||||
return ioutil.WriteFile(filepath.Join("./conf/proxy/", filename), js, 0775)
|
||||
return os.WriteFile(filepath.Join("./conf/proxy/", filename), js, 0775)
|
||||
}
|
||||
|
||||
func RemoveReverseProxyConfig(rootname string) error {
|
||||
// Save a running reverse proxy endpoint to file (with automatic endpoint to record conversion)
|
||||
func SaveReverseProxyEndpointToFile(proxyEndpoint *dynamicproxy.ProxyEndpoint) error {
|
||||
recordToSave, err := ConvertProxyEndpointToRecord(proxyEndpoint)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return SaveReverseProxyConfigToFile(recordToSave)
|
||||
}
|
||||
|
||||
func RemoveReverseProxyConfigFile(rootname string) error {
|
||||
filename := getFilenameFromRootName(rootname)
|
||||
removePendingFile := strings.ReplaceAll(filepath.Join("./conf/proxy/", filename), "\\", "/")
|
||||
log.Println("Config Removed: ", removePendingFile)
|
||||
@ -66,8 +76,18 @@ func RemoveReverseProxyConfig(rootname string) error {
|
||||
|
||||
// Return ptype, rootname and proxyTarget, error if any
|
||||
func LoadReverseProxyConfig(filename string) (*Record, error) {
|
||||
thisRecord := Record{}
|
||||
configContent, err := ioutil.ReadFile(filename)
|
||||
thisRecord := Record{
|
||||
ProxyType: "",
|
||||
Rootname: "",
|
||||
ProxyTarget: "",
|
||||
UseTLS: false,
|
||||
SkipTlsValidation: false,
|
||||
RequireBasicAuth: false,
|
||||
BasicAuthCredentials: []*dynamicproxy.BasicAuthCredentials{},
|
||||
BasicAuthExceptionRules: []*dynamicproxy.BasicAuthExceptionRule{},
|
||||
}
|
||||
|
||||
configContent, err := os.ReadFile(filename)
|
||||
if err != nil {
|
||||
return &thisRecord, err
|
||||
}
|
||||
@ -82,6 +102,22 @@ func LoadReverseProxyConfig(filename string) (*Record, error) {
|
||||
return &thisRecord, nil
|
||||
}
|
||||
|
||||
// Convert a running proxy endpoint object into a save-able record struct
|
||||
func ConvertProxyEndpointToRecord(targetProxyEndpoint *dynamicproxy.ProxyEndpoint) (*Record, error) {
|
||||
thisProxyConfigRecord := Record{
|
||||
ProxyType: targetProxyEndpoint.GetProxyTypeString(),
|
||||
Rootname: targetProxyEndpoint.RootOrMatchingDomain,
|
||||
ProxyTarget: targetProxyEndpoint.Domain,
|
||||
UseTLS: targetProxyEndpoint.RequireTLS,
|
||||
SkipTlsValidation: targetProxyEndpoint.SkipCertValidations,
|
||||
RequireBasicAuth: targetProxyEndpoint.RequireBasicAuth,
|
||||
BasicAuthCredentials: targetProxyEndpoint.BasicAuthCredentials,
|
||||
BasicAuthExceptionRules: targetProxyEndpoint.BasicAuthExceptionRules,
|
||||
}
|
||||
|
||||
return &thisProxyConfigRecord, nil
|
||||
}
|
||||
|
||||
func getFilenameFromRootName(rootname string) string {
|
||||
//Generate a filename for this rootname
|
||||
filename := strings.ReplaceAll(rootname, ".", "_")
|
||||
|
@ -98,7 +98,7 @@ func ShutdownSeq() {
|
||||
fmt.Println("- Closing Statistic Collector")
|
||||
statisticCollector.Close()
|
||||
if mdnsTickerStop != nil {
|
||||
fmt.Println("- Stopping mDNS Discoverer")
|
||||
fmt.Println("- Stopping mDNS Discoverer (might take a few minutes)")
|
||||
// Stop the mdns service
|
||||
mdnsTickerStop <- true
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ package dynamicproxy
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"imuslab.com/zoraxy/mod/auth"
|
||||
)
|
||||
@ -15,6 +16,16 @@ import (
|
||||
*/
|
||||
|
||||
func (h *ProxyHandler) handleBasicAuthRouting(w http.ResponseWriter, r *http.Request, pe *ProxyEndpoint) error {
|
||||
if len(pe.BasicAuthExceptionRules) > 0 {
|
||||
//Check if the current path matches the exception rules
|
||||
for _, exceptionRule := range pe.BasicAuthExceptionRules {
|
||||
if strings.HasPrefix(r.RequestURI, exceptionRule.PathPrefix) {
|
||||
//This path is excluded from basic auth
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
proxyType := "vdir-auth"
|
||||
if pe.ProxyType == ProxyType_Subdomain {
|
||||
proxyType = "subd-auth"
|
||||
|
@ -246,14 +246,15 @@ func (router *Router) AddVirtualDirectoryProxyService(options *VdirOptions) erro
|
||||
proxy := dpcore.NewDynamicProxyCore(path, options.RootName, options.SkipCertValidations)
|
||||
|
||||
endpointObject := ProxyEndpoint{
|
||||
ProxyType: ProxyType_Vdir,
|
||||
RootOrMatchingDomain: options.RootName,
|
||||
Domain: domain,
|
||||
RequireTLS: options.RequireTLS,
|
||||
SkipCertValidations: options.SkipCertValidations,
|
||||
RequireBasicAuth: options.RequireBasicAuth,
|
||||
BasicAuthCredentials: options.BasicAuthCredentials,
|
||||
Proxy: proxy,
|
||||
ProxyType: ProxyType_Vdir,
|
||||
RootOrMatchingDomain: options.RootName,
|
||||
Domain: domain,
|
||||
RequireTLS: options.RequireTLS,
|
||||
SkipCertValidations: options.SkipCertValidations,
|
||||
RequireBasicAuth: options.RequireBasicAuth,
|
||||
BasicAuthCredentials: options.BasicAuthCredentials,
|
||||
BasicAuthExceptionRules: options.BasicAuthExceptionRules,
|
||||
Proxy: proxy,
|
||||
}
|
||||
|
||||
router.ProxyEndpoints.Store(options.RootName, &endpointObject)
|
||||
@ -271,46 +272,24 @@ func (router *Router) LoadProxy(ptype string, key string) (*ProxyEndpoint, error
|
||||
if !ok {
|
||||
return nil, errors.New("target proxy not found")
|
||||
}
|
||||
return proxy.(*ProxyEndpoint), nil
|
||||
|
||||
targetProxy := proxy.(*ProxyEndpoint)
|
||||
targetProxy.parent = router
|
||||
return targetProxy, nil
|
||||
} else if ptype == "subd" {
|
||||
proxy, ok := router.SubdomainEndpoint.Load(key)
|
||||
if !ok {
|
||||
return nil, errors.New("target proxy not found")
|
||||
}
|
||||
return proxy.(*ProxyEndpoint), nil
|
||||
|
||||
targetProxy := proxy.(*ProxyEndpoint)
|
||||
targetProxy.parent = router
|
||||
return targetProxy, nil
|
||||
}
|
||||
|
||||
return nil, errors.New("unsupported ptype")
|
||||
}
|
||||
|
||||
/*
|
||||
Save routing from RP
|
||||
*/
|
||||
func (router *Router) SaveProxy(ptype string, key string, newConfig *ProxyEndpoint) {
|
||||
if ptype == "vdir" {
|
||||
router.ProxyEndpoints.Store(key, newConfig)
|
||||
|
||||
} else if ptype == "subd" {
|
||||
router.SubdomainEndpoint.Store(key, newConfig)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
Remove routing from RP
|
||||
*/
|
||||
func (router *Router) RemoveProxy(ptype string, key string) error {
|
||||
//fmt.Println(ptype, key)
|
||||
if ptype == "vdir" {
|
||||
router.ProxyEndpoints.Delete(key)
|
||||
return nil
|
||||
} else if ptype == "subd" {
|
||||
router.SubdomainEndpoint.Delete(key)
|
||||
return nil
|
||||
}
|
||||
return errors.New("invalid ptype")
|
||||
}
|
||||
|
||||
/*
|
||||
Add an default router for the proxy server
|
||||
*/
|
||||
|
68
src/mod/dynamicproxy/proxyEndpoint.go
Normal file
68
src/mod/dynamicproxy/proxyEndpoint.go
Normal file
@ -0,0 +1,68 @@
|
||||
package dynamicproxy
|
||||
|
||||
import "errors"
|
||||
|
||||
/*
|
||||
ProxyEndpoint.go
|
||||
author: tobychui
|
||||
|
||||
This script handle the proxy endpoint object actions
|
||||
so proxyEndpoint can be handled like a proper oop object
|
||||
|
||||
Most of the functions are implemented in dynamicproxy.go
|
||||
*/
|
||||
|
||||
//Get the string version of proxy type
|
||||
func (ep *ProxyEndpoint) GetProxyTypeString() string {
|
||||
if ep.ProxyType == ProxyType_Subdomain {
|
||||
return "subd"
|
||||
} else if ep.ProxyType == ProxyType_Vdir {
|
||||
return "vdir"
|
||||
}
|
||||
|
||||
return "unknown"
|
||||
}
|
||||
|
||||
//Update change in the current running proxy endpoint config
|
||||
func (ep *ProxyEndpoint) UpdateToRuntime() {
|
||||
if ep.IsVdir() {
|
||||
ep.parent.ProxyEndpoints.Store(ep.RootOrMatchingDomain, ep)
|
||||
|
||||
} else if ep.IsSubDomain() {
|
||||
ep.parent.SubdomainEndpoint.Store(ep.RootOrMatchingDomain, ep)
|
||||
}
|
||||
}
|
||||
|
||||
//Return true if the endpoint type is virtual directory
|
||||
func (ep *ProxyEndpoint) IsVdir() bool {
|
||||
return ep.ProxyType == ProxyType_Vdir
|
||||
}
|
||||
|
||||
//Return true if the endpoint type is subdomain
|
||||
func (ep *ProxyEndpoint) IsSubDomain() bool {
|
||||
return ep.ProxyType == ProxyType_Subdomain
|
||||
}
|
||||
|
||||
//Remove this proxy endpoint from running proxy endpoint list
|
||||
func (ep *ProxyEndpoint) Remove() error {
|
||||
//fmt.Println(ptype, key)
|
||||
if ep.IsVdir() {
|
||||
ep.parent.ProxyEndpoints.Delete(ep.RootOrMatchingDomain)
|
||||
return nil
|
||||
} else if ep.IsSubDomain() {
|
||||
ep.parent.SubdomainEndpoint.Delete(ep.RootOrMatchingDomain)
|
||||
return nil
|
||||
}
|
||||
return errors.New("invalid or unsupported type")
|
||||
|
||||
}
|
||||
|
||||
//ProxyEndpoint remove provide global access by key
|
||||
func (router *Router) RemoveProxyEndpointByRootname(proxyType string, rootnameOrMatchingDomain string) error {
|
||||
targetEpt, err := router.LoadProxy(proxyType, rootnameOrMatchingDomain)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return targetEpt.Remove()
|
||||
}
|
@ -34,13 +34,14 @@ func (router *Router) AddSubdomainRoutingService(options *SubdOptions) error {
|
||||
proxy := dpcore.NewDynamicProxyCore(path, "", options.SkipCertValidations)
|
||||
|
||||
router.SubdomainEndpoint.Store(options.MatchingDomain, &ProxyEndpoint{
|
||||
RootOrMatchingDomain: options.MatchingDomain,
|
||||
Domain: domain,
|
||||
RequireTLS: options.RequireTLS,
|
||||
Proxy: proxy,
|
||||
SkipCertValidations: options.SkipCertValidations,
|
||||
RequireBasicAuth: options.RequireBasicAuth,
|
||||
BasicAuthCredentials: options.BasicAuthCredentials,
|
||||
RootOrMatchingDomain: options.MatchingDomain,
|
||||
Domain: domain,
|
||||
RequireTLS: options.RequireTLS,
|
||||
Proxy: proxy,
|
||||
SkipCertValidations: options.SkipCertValidations,
|
||||
RequireBasicAuth: options.RequireBasicAuth,
|
||||
BasicAuthCredentials: options.BasicAuthCredentials,
|
||||
BasicAuthExceptionRules: options.BasicAuthExceptionRules,
|
||||
})
|
||||
|
||||
log.Println("Adding Subdomain Rule: ", options.MatchingDomain+" to "+domain)
|
||||
|
@ -59,56 +59,51 @@ type BasicAuthUnhashedCredentials struct {
|
||||
Password string
|
||||
}
|
||||
|
||||
// Paths to exclude in basic auth enabled proxy handler
|
||||
type BasicAuthExceptionRule struct {
|
||||
PathPrefix string
|
||||
}
|
||||
|
||||
// A proxy endpoint record
|
||||
type ProxyEndpoint struct {
|
||||
ProxyType int //The type of this proxy, see const def
|
||||
RootOrMatchingDomain string //Root for vdir or Matching domain for subd
|
||||
Domain string //Domain or IP to proxy to
|
||||
RequireTLS bool //Target domain require TLS
|
||||
SkipCertValidations bool //Set to true to accept self signed certs
|
||||
RequireBasicAuth bool //Set to true to request basic auth before proxy
|
||||
BasicAuthCredentials []*BasicAuthCredentials `json:"-"`
|
||||
Proxy *dpcore.ReverseProxy `json:"-"`
|
||||
ProxyType int //The type of this proxy, see const def
|
||||
RootOrMatchingDomain string //Root for vdir or Matching domain for subd, also act as key
|
||||
Domain string //Domain or IP to proxy to
|
||||
RequireTLS bool //Target domain require TLS
|
||||
SkipCertValidations bool //Set to true to accept self signed certs
|
||||
RequireBasicAuth bool //Set to true to request basic auth before proxy
|
||||
BasicAuthCredentials []*BasicAuthCredentials `json:"-"` //Basic auth credentials
|
||||
BasicAuthExceptionRules []*BasicAuthExceptionRule //Path to exclude in a basic auth enabled proxy target
|
||||
Proxy *dpcore.ReverseProxy `json:"-"`
|
||||
|
||||
parent *Router
|
||||
}
|
||||
|
||||
type RootOptions struct {
|
||||
ProxyLocation string
|
||||
RequireTLS bool
|
||||
SkipCertValidations bool
|
||||
RequireBasicAuth bool
|
||||
BasicAuthCredentials []*BasicAuthCredentials
|
||||
ProxyLocation string
|
||||
RequireTLS bool
|
||||
SkipCertValidations bool
|
||||
RequireBasicAuth bool
|
||||
BasicAuthCredentials []*BasicAuthCredentials
|
||||
BasicAuthExceptionRules []*BasicAuthExceptionRule
|
||||
}
|
||||
|
||||
type VdirOptions struct {
|
||||
RootName string
|
||||
Domain string
|
||||
RequireTLS bool
|
||||
SkipCertValidations bool
|
||||
RequireBasicAuth bool
|
||||
BasicAuthCredentials []*BasicAuthCredentials
|
||||
RootName string
|
||||
Domain string
|
||||
RequireTLS bool
|
||||
SkipCertValidations bool
|
||||
RequireBasicAuth bool
|
||||
BasicAuthCredentials []*BasicAuthCredentials
|
||||
BasicAuthExceptionRules []*BasicAuthExceptionRule
|
||||
}
|
||||
|
||||
type SubdOptions struct {
|
||||
MatchingDomain string
|
||||
Domain string
|
||||
RequireTLS bool
|
||||
SkipCertValidations bool
|
||||
RequireBasicAuth bool
|
||||
BasicAuthCredentials []*BasicAuthCredentials
|
||||
MatchingDomain string
|
||||
Domain string
|
||||
RequireTLS bool
|
||||
SkipCertValidations bool
|
||||
RequireBasicAuth bool
|
||||
BasicAuthCredentials []*BasicAuthCredentials
|
||||
BasicAuthExceptionRules []*BasicAuthExceptionRule
|
||||
}
|
||||
|
||||
/*
|
||||
type ProxyEndpoint struct {
|
||||
Root string
|
||||
Domain string
|
||||
RequireTLS bool
|
||||
Proxy *reverseproxy.ReverseProxy `json:"-"`
|
||||
}
|
||||
|
||||
type SubdomainEndpoint struct {
|
||||
MatchingDomain string
|
||||
Domain string
|
||||
RequireTLS bool
|
||||
Proxy *reverseproxy.ReverseProxy `json:"-"`
|
||||
}
|
||||
*/
|
||||
|
@ -88,21 +88,23 @@ func ReverseProxtInit() {
|
||||
})
|
||||
} else if record.ProxyType == "subd" {
|
||||
dynamicProxyRouter.AddSubdomainRoutingService(&dynamicproxy.SubdOptions{
|
||||
MatchingDomain: record.Rootname,
|
||||
Domain: record.ProxyTarget,
|
||||
RequireTLS: record.UseTLS,
|
||||
SkipCertValidations: record.SkipTlsValidation,
|
||||
RequireBasicAuth: record.RequireBasicAuth,
|
||||
BasicAuthCredentials: record.BasicAuthCredentials,
|
||||
MatchingDomain: record.Rootname,
|
||||
Domain: record.ProxyTarget,
|
||||
RequireTLS: record.UseTLS,
|
||||
SkipCertValidations: record.SkipTlsValidation,
|
||||
RequireBasicAuth: record.RequireBasicAuth,
|
||||
BasicAuthCredentials: record.BasicAuthCredentials,
|
||||
BasicAuthExceptionRules: record.BasicAuthExceptionRules,
|
||||
})
|
||||
} else if record.ProxyType == "vdir" {
|
||||
dynamicProxyRouter.AddVirtualDirectoryProxyService(&dynamicproxy.VdirOptions{
|
||||
RootName: record.Rootname,
|
||||
Domain: record.ProxyTarget,
|
||||
RequireTLS: record.UseTLS,
|
||||
SkipCertValidations: record.SkipTlsValidation,
|
||||
RequireBasicAuth: record.RequireBasicAuth,
|
||||
BasicAuthCredentials: record.BasicAuthCredentials,
|
||||
RootName: record.Rootname,
|
||||
Domain: record.ProxyTarget,
|
||||
RequireTLS: record.UseTLS,
|
||||
SkipCertValidations: record.SkipTlsValidation,
|
||||
RequireBasicAuth: record.RequireBasicAuth,
|
||||
BasicAuthCredentials: record.BasicAuthCredentials,
|
||||
BasicAuthExceptionRules: record.BasicAuthExceptionRules,
|
||||
})
|
||||
} else {
|
||||
log.Println("Unsupported endpoint type: " + record.ProxyType + ". Skipping " + filepath.Base(conf))
|
||||
@ -282,7 +284,7 @@ func ReverseProxyHandleAddEndpoint(w http.ResponseWriter, r *http.Request) {
|
||||
RequireBasicAuth: requireBasicAuth,
|
||||
BasicAuthCredentials: basicAuthCredentials,
|
||||
}
|
||||
SaveReverseProxyConfig(&thisProxyConfigRecord)
|
||||
SaveReverseProxyConfigToFile(&thisProxyConfigRecord)
|
||||
|
||||
//Update utm if exists
|
||||
if uptimeMonitor != nil {
|
||||
@ -355,7 +357,7 @@ func ReverseProxyHandleEditEndpoint(w http.ResponseWriter, r *http.Request) {
|
||||
RequireBasicAuth: requireBasicAuth,
|
||||
BasicAuthCredentials: targetProxyEntry.BasicAuthCredentials,
|
||||
}
|
||||
dynamicProxyRouter.RemoveProxy("vdir", thisOption.RootName)
|
||||
targetProxyEntry.Remove()
|
||||
dynamicProxyRouter.AddVirtualDirectoryProxyService(&thisOption)
|
||||
|
||||
} else if eptype == "subd" {
|
||||
@ -367,7 +369,7 @@ func ReverseProxyHandleEditEndpoint(w http.ResponseWriter, r *http.Request) {
|
||||
RequireBasicAuth: requireBasicAuth,
|
||||
BasicAuthCredentials: targetProxyEntry.BasicAuthCredentials,
|
||||
}
|
||||
dynamicProxyRouter.RemoveProxy("subd", thisOption.MatchingDomain)
|
||||
targetProxyEntry.Remove()
|
||||
dynamicProxyRouter.AddSubdomainRoutingService(&thisOption)
|
||||
}
|
||||
|
||||
@ -381,7 +383,7 @@ func ReverseProxyHandleEditEndpoint(w http.ResponseWriter, r *http.Request) {
|
||||
RequireBasicAuth: requireBasicAuth,
|
||||
BasicAuthCredentials: targetProxyEntry.BasicAuthCredentials,
|
||||
}
|
||||
SaveReverseProxyConfig(&thisProxyConfigRecord)
|
||||
SaveReverseProxyConfigToFile(&thisProxyConfigRecord)
|
||||
utils.SendOK(w)
|
||||
}
|
||||
|
||||
@ -398,13 +400,15 @@ func DeleteProxyEndpoint(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
err = dynamicProxyRouter.RemoveProxy(ptype, ep)
|
||||
//Remove the config from runtime
|
||||
err = dynamicProxyRouter.RemoveProxyEndpointByRootname(ptype, ep)
|
||||
if err != nil {
|
||||
utils.SendErrorResponse(w, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
RemoveReverseProxyConfig(ep)
|
||||
//Remove the config from file
|
||||
RemoveReverseProxyConfigFile(ep)
|
||||
|
||||
//Update utm if exists
|
||||
if uptimeMonitor != nil {
|
||||
@ -528,19 +532,10 @@ func UpdateProxyBasicAuthCredentials(w http.ResponseWriter, r *http.Request) {
|
||||
targetProxy.BasicAuthCredentials = mergedCredentials
|
||||
|
||||
//Save it to file
|
||||
thisProxyConfigRecord := Record{
|
||||
ProxyType: ptype,
|
||||
Rootname: targetProxy.RootOrMatchingDomain,
|
||||
ProxyTarget: targetProxy.Domain,
|
||||
UseTLS: targetProxy.RequireTLS,
|
||||
SkipTlsValidation: targetProxy.SkipCertValidations,
|
||||
RequireBasicAuth: targetProxy.RequireBasicAuth,
|
||||
BasicAuthCredentials: targetProxy.BasicAuthCredentials,
|
||||
}
|
||||
SaveReverseProxyConfig(&thisProxyConfigRecord)
|
||||
SaveReverseProxyEndpointToFile(targetProxy)
|
||||
|
||||
//Replace runtime configuration
|
||||
dynamicProxyRouter.SaveProxy(ptype, ep, targetProxy)
|
||||
targetProxy.UpdateToRuntime()
|
||||
utils.SendOK(w)
|
||||
} else {
|
||||
http.Error(w, "invalid usage", http.StatusMethodNotAllowed)
|
||||
@ -548,6 +543,147 @@ func UpdateProxyBasicAuthCredentials(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
}
|
||||
|
||||
// List, Update or Remove the exception paths for basic auth.
|
||||
func ListProxyBasicAuthExceptionPaths(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
if r.Method != http.MethodGet {
|
||||
http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed)
|
||||
}
|
||||
ep, err := utils.GetPara(r, "ep")
|
||||
if err != nil {
|
||||
utils.SendErrorResponse(w, "Invalid ep given")
|
||||
return
|
||||
}
|
||||
|
||||
ptype, err := utils.GetPara(r, "ptype")
|
||||
if err != nil {
|
||||
utils.SendErrorResponse(w, "Invalid ptype given")
|
||||
return
|
||||
}
|
||||
|
||||
//Load the target proxy object from router
|
||||
targetProxy, err := dynamicProxyRouter.LoadProxy(ptype, ep)
|
||||
if err != nil {
|
||||
utils.SendErrorResponse(w, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
//List all the exception paths for this proxy
|
||||
results := targetProxy.BasicAuthExceptionRules
|
||||
if results == nil {
|
||||
//It is a config from a really old version of zoraxy. Overwrite it with empty array
|
||||
results = []*dynamicproxy.BasicAuthExceptionRule{}
|
||||
}
|
||||
js, _ := json.Marshal(results)
|
||||
utils.SendJSONResponse(w, string(js))
|
||||
return
|
||||
}
|
||||
|
||||
func AddProxyBasicAuthExceptionPaths(w http.ResponseWriter, r *http.Request) {
|
||||
ep, err := utils.PostPara(r, "ep")
|
||||
if err != nil {
|
||||
utils.SendErrorResponse(w, "Invalid ep given")
|
||||
return
|
||||
}
|
||||
|
||||
ptype, err := utils.PostPara(r, "ptype")
|
||||
if err != nil {
|
||||
utils.SendErrorResponse(w, "Invalid ptype given")
|
||||
return
|
||||
}
|
||||
|
||||
matchingPrefix, err := utils.PostPara(r, "prefix")
|
||||
if err != nil {
|
||||
utils.SendErrorResponse(w, "Invalid matching prefix given")
|
||||
return
|
||||
}
|
||||
|
||||
//Load the target proxy object from router
|
||||
targetProxy, err := dynamicProxyRouter.LoadProxy(ptype, ep)
|
||||
if err != nil {
|
||||
utils.SendErrorResponse(w, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
//Check if the prefix starts with /. If not, prepend it
|
||||
if !strings.HasPrefix(matchingPrefix, "/") {
|
||||
matchingPrefix = "/" + matchingPrefix
|
||||
}
|
||||
|
||||
//Add a new exception rule if it is not already exists
|
||||
alreadyExists := false
|
||||
for _, thisExceptionRule := range targetProxy.BasicAuthExceptionRules {
|
||||
if thisExceptionRule.PathPrefix == matchingPrefix {
|
||||
alreadyExists = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if alreadyExists {
|
||||
utils.SendErrorResponse(w, "This matching path already exists")
|
||||
return
|
||||
}
|
||||
targetProxy.BasicAuthExceptionRules = append(targetProxy.BasicAuthExceptionRules, &dynamicproxy.BasicAuthExceptionRule{
|
||||
PathPrefix: strings.TrimSpace(matchingPrefix),
|
||||
})
|
||||
|
||||
//Save configs to runtime and file
|
||||
targetProxy.UpdateToRuntime()
|
||||
SaveReverseProxyEndpointToFile(targetProxy)
|
||||
|
||||
utils.SendOK(w)
|
||||
}
|
||||
|
||||
func RemoveProxyBasicAuthExceptionPaths(w http.ResponseWriter, r *http.Request) {
|
||||
// Delete a rule
|
||||
ep, err := utils.PostPara(r, "ep")
|
||||
if err != nil {
|
||||
utils.SendErrorResponse(w, "Invalid ep given")
|
||||
return
|
||||
}
|
||||
|
||||
ptype, err := utils.PostPara(r, "ptype")
|
||||
if err != nil {
|
||||
utils.SendErrorResponse(w, "Invalid ptype given")
|
||||
return
|
||||
}
|
||||
|
||||
matchingPrefix, err := utils.PostPara(r, "prefix")
|
||||
if err != nil {
|
||||
utils.SendErrorResponse(w, "Invalid matching prefix given")
|
||||
return
|
||||
}
|
||||
|
||||
// Load the target proxy object from router
|
||||
targetProxy, err := dynamicProxyRouter.LoadProxy(ptype, ep)
|
||||
if err != nil {
|
||||
utils.SendErrorResponse(w, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
newExceptionRuleList := []*dynamicproxy.BasicAuthExceptionRule{}
|
||||
matchingExists := false
|
||||
for _, thisExceptionalRule := range targetProxy.BasicAuthExceptionRules {
|
||||
if thisExceptionalRule.PathPrefix != matchingPrefix {
|
||||
newExceptionRuleList = append(newExceptionRuleList, thisExceptionalRule)
|
||||
} else {
|
||||
matchingExists = true
|
||||
}
|
||||
}
|
||||
|
||||
if !matchingExists {
|
||||
utils.SendErrorResponse(w, "target matching rule not exists")
|
||||
return
|
||||
}
|
||||
|
||||
targetProxy.BasicAuthExceptionRules = newExceptionRuleList
|
||||
|
||||
// Save configs to runtime and file
|
||||
targetProxy.UpdateToRuntime()
|
||||
SaveReverseProxyEndpointToFile(targetProxy)
|
||||
|
||||
utils.SendOK(w)
|
||||
}
|
||||
|
||||
func ReverseProxyStatus(w http.ResponseWriter, r *http.Request) {
|
||||
js, _ := json.Marshal(dynamicProxyRouter)
|
||||
utils.SendJSONResponse(w, string(js))
|
||||
|
@ -1,3 +1,13 @@
|
||||
<style>
|
||||
.expired.certdate{
|
||||
font-weight: bolder;
|
||||
color: #bd001c;
|
||||
}
|
||||
|
||||
.valid.certdate{
|
||||
color: #31c071;
|
||||
}
|
||||
</style>
|
||||
<div class="standardContainer">
|
||||
<div class="ui basic segment">
|
||||
<h2>TLS / SSL Certificates</h2>
|
||||
@ -111,10 +121,12 @@
|
||||
return a.Domain > b.Domain
|
||||
});
|
||||
data.forEach(entry => {
|
||||
let isExpired = entry.RemainingDays <= 0;
|
||||
|
||||
$("#certifiedDomainList").append(`<tr>
|
||||
<td>${entry.Domain}</td>
|
||||
<td>${entry.LastModifiedDate}</td>
|
||||
<td>${entry.ExpireDate} (${entry.RemainingDays} days left)</td>
|
||||
<td class="${isExpired?"expired":"valid"} certdate">${entry.ExpireDate} (${!isExpired?entry.RemainingDays+" days left":"Expired"})</td>
|
||||
<td><button title="Delete key-pair" class="ui mini basic red icon button" onclick="deleteCertificate('${entry.Domain}');"><i class="ui red trash icon"></i></button></td>
|
||||
</tr>`);
|
||||
});
|
||||
|
@ -42,39 +42,53 @@
|
||||
</div>
|
||||
<div class="field" >
|
||||
<button class="ui basic button" onclick="addCredentialsToEditingList();"><i class="blue add icon"></i> Add Credential</button>
|
||||
<button class="ui basic button" style="float: right;" onclick="saveCredentials();"><i class="green save icon"></i> Save Credential</button>
|
||||
</div>
|
||||
<div class="ui divider"></div>
|
||||
<div class="field" >
|
||||
<button class="ui basic button" style="float: right;" onclick="saveCredentials();"><i class="green save icon"></i> Save</button>
|
||||
<button class="ui basic button" style="float: right;" onclick="cancelCredentialEdit();"><i class="remove icon"></i> Cancel</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ui divider"></div>
|
||||
<h3 class="ui header">No-Auth Paths</h3>
|
||||
<h3 class="ui header">Authentication Exclusion Paths</h3>
|
||||
<div class="scrolling content ui form">
|
||||
<p>Exclude specific paths from the basic auth interface. Useful if you are hosting services require remote API access.</p>
|
||||
<table class="ui very basic compacted unstackable celled table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Username</th>
|
||||
<th>Password</th>
|
||||
<th>Remove</th>
|
||||
</tr></thead>
|
||||
<tbody id="inlineEditExclusionPaths">
|
||||
<tr>
|
||||
<td colspan="3"><i class="ui green circle check icon"></i> No Path Excluded</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="field">
|
||||
<input id="inlineEditExclusionPath" type="text" placeholder="/api" autocomplete="off">
|
||||
</div>
|
||||
<div class="field" >
|
||||
<button class="ui basic button" onclick="addCredentialsToEditingList();"><i class="blue add icon"></i> Add Credential</button>
|
||||
<p>Exclude specific directories / paths which contains the following subpath prefix from authentication. Useful if you are hosting services require remote API access.</p>
|
||||
<table class="ui very basic compacted unstackable celled table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Path Prefix</th>
|
||||
<th>Remove</th>
|
||||
</tr></thead>
|
||||
<tbody id="exclusionPaths">
|
||||
<tr>
|
||||
<td colspan="2"><i class="ui green circle check icon"></i> No Path Excluded</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="field">
|
||||
<input id="newExclusionPath" type="text" placeholder="/public/api/" autocomplete="off">
|
||||
<small>Make sure you add the tailing slash for only selecting the files / folder inside that path.</small>
|
||||
</div>
|
||||
<div class="field" >
|
||||
<button class="ui basic button" onclick="addExceptionPath();"><i class="blue add icon"></i> Add Exception</button>
|
||||
</div>
|
||||
<div class="field">
|
||||
<div class="ui basic message">
|
||||
<h4>How to use set excluded paths?</h4>
|
||||
<p>All request URI that contains the given prefix will be allowed to bypass authentication and <b>the prefix must start with a slash.</b> For example, given the following prefix.<br>
|
||||
<code>/public/res/</code><br>
|
||||
<br>
|
||||
Zoraxy will allow authentication bypass of any subdirectories or resources under the /public/res/ directory. For example, the following paths access will be able to bypass basic auth mechanism under this setting.<br>
|
||||
<code>/public/res/photo.png</code><br>
|
||||
<code>/public/res/far/boo/</code></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ui divider"></div>
|
||||
<div class="field" >
|
||||
<button class="ui basic button" style="float: right;" onclick="closeThisWrapper();">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<br><br><br><br>
|
||||
|
||||
</div>
|
||||
<script>
|
||||
@ -151,6 +165,80 @@
|
||||
updateEditingCredentialList();
|
||||
}
|
||||
|
||||
function addExceptionPath(){
|
||||
// Retrieve the username and password input values
|
||||
var newExclusionPathMatchingPrefix = $('#newExclusionPath').val().trim();
|
||||
if (newExclusionPathMatchingPrefix == ""){
|
||||
parent.msgbox("Matching prefix cannot be empty!", false, 5000);
|
||||
return;
|
||||
}
|
||||
$.ajax({
|
||||
url: "/api/proxy/auth/exceptions/add",
|
||||
data:{
|
||||
ptype: editingEndpoint.ept,
|
||||
ep: editingEndpoint.ep,
|
||||
prefix: newExclusionPathMatchingPrefix
|
||||
},
|
||||
method: "POST",
|
||||
success: function(data){
|
||||
if (data.error != undefined){
|
||||
parent.msgbox(data.error, false, 5000);
|
||||
}else{
|
||||
initExceptionPaths();
|
||||
parent.msgbox("New exception path added", true);
|
||||
$('#newExclusionPath').val("");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function removeExceptionPath(object){
|
||||
let matchingPrefix = $(object).attr("prefix");
|
||||
$.ajax({
|
||||
url: "/api/proxy/auth/exceptions/delete",
|
||||
data:{
|
||||
ptype: editingEndpoint.ept,
|
||||
ep: editingEndpoint.ep,
|
||||
prefix: matchingPrefix
|
||||
},
|
||||
method: "POST",
|
||||
success: function(data){
|
||||
if (data.error != undefined){
|
||||
parent.msgbox(data.error, false, 5000);
|
||||
}else{
|
||||
initExceptionPaths();
|
||||
parent.msgbox("Exception path removed", true);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
//Load exception paths from server
|
||||
function initExceptionPaths(){
|
||||
$.get(`/api/proxy/auth/exceptions/list?ptype=${editingEndpoint.ept}&ep=${editingEndpoint.ep}`, function(data){
|
||||
if (data.error != undefined){
|
||||
parent.msgbox(data.error, false, 5000);
|
||||
}else{
|
||||
if (data.length == 0){
|
||||
$("#exclusionPaths").html(` <tr>
|
||||
<td colspan="2"><i class="ui green circle check icon"></i> No Path Excluded</td>
|
||||
</tr>`);
|
||||
}else{
|
||||
$("#exclusionPaths").html("");
|
||||
data.forEach(function(rule){
|
||||
$("#exclusionPaths").append(` <tr>
|
||||
<td>${rule.PathPrefix}</td>
|
||||
<td><button class="ui red basic mini icon button" onclick="removeExceptionPath(this);" prefix="${rule.PathPrefix}"><i class="ui red times icon"></i></button></td>
|
||||
</tr>`);
|
||||
})
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
initExceptionPaths();
|
||||
|
||||
function updateEditingCredentialList() {
|
||||
var tableBody = $('#inlineEditBasicAuthCredentialTable');
|
||||
tableBody.empty();
|
||||
@ -195,7 +283,7 @@
|
||||
return isExists;
|
||||
}
|
||||
|
||||
function cancelCredentialEdit(){
|
||||
function closeThisWrapper(){
|
||||
parent.hideSideWrapper(true);
|
||||
}
|
||||
|
||||
@ -213,7 +301,7 @@
|
||||
parent.msgbox(data.error, false, 6000);
|
||||
}else{
|
||||
parent.msgbox("Credentials Updated");
|
||||
parent.hideSideWrapper(true);
|
||||
//parent.hideSideWrapper(true);
|
||||
}
|
||||
}
|
||||
})
|
||||
|
Loading…
x
Reference in New Issue
Block a user