From 9369237229fa42e8424fc96d9a73bac160edc3c2 Mon Sep 17 00:00:00 2001 From: Alan Yeung Date: Sun, 20 Aug 2023 22:29:15 -0700 Subject: [PATCH 01/18] updated EAB --- .gitignore | 4 +++- src/acme.go | 2 +- src/mod/acme/acme.go | 41 +++++++++++++++++++++++++++++++++------ src/mod/acme/autorenew.go | 2 +- 4 files changed, 40 insertions(+), 9 deletions(-) diff --git a/.gitignore b/.gitignore index e164ceb..42aede7 100644 --- a/.gitignore +++ b/.gitignore @@ -31,4 +31,6 @@ src/rules/* src/README.md docker/ContainerTester.sh docker/ImagePublisher.sh -src/mod/acme/test/stackoverflow.pem \ No newline at end of file +src/mod/acme/test/stackoverflow.pem +src/sys.uuid +src/sys.db.lock diff --git a/src/acme.go b/src/acme.go index 72e5883..1bacb51 100644 --- a/src/acme.go +++ b/src/acme.go @@ -38,7 +38,7 @@ func initACME() *acme.ACMEHandler { port = getRandomPort(30000) } - return acme.NewACME("https://acme-staging-v02.api.letsencrypt.org/directory", strconv.Itoa(port)) + return acme.NewACME("https://acme-staging-v02.api.letsencrypt.org/directory", strconv.Itoa(port), "", "") } // create the special routing rule for ACME diff --git a/src/mod/acme/acme.go b/src/mod/acme/acme.go index ec8bc1c..12f8581 100644 --- a/src/mod/acme/acme.go +++ b/src/mod/acme/acme.go @@ -54,18 +54,22 @@ func (u *ACMEUser) GetPrivateKey() crypto.PrivateKey { type ACMEHandler struct { DefaultAcmeServer string Port string + Kid string + HmacEncoded string } // NewACME creates a new ACMEHandler instance. -func NewACME(acmeServer string, port string) *ACMEHandler { +func NewACME(acmeServer string, port string, kid string, hmacEncoded string) *ACMEHandler { return &ACMEHandler{ DefaultAcmeServer: acmeServer, Port: port, + Kid: kid, + HmacEncoded: hmacEncoded, } } // ObtainCert obtains a certificate for the specified domains. -func (a *ACMEHandler) ObtainCert(domains []string, certificateName string, email string, ca string) (bool, error) { +func (a *ACMEHandler) ObtainCert(domains []string, certificateName string, email string, ca string, kid string, hmacEncoded string) (bool, error) { log.Println("[ACME] Obtaining certificate...") // generate private key @@ -113,12 +117,37 @@ func (a *ACMEHandler) ObtainCert(domains []string, certificateName string, email return false, err } + var reg *registration.Resource // New users will need to register - reg, err := client.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: true}) - if err != nil { - log.Println(err) - return false, err + if client.GetExternalAccountRequired() { + log.Println("External Account Required for this ACME Provider.") + // IF KID and HmacEncoded is overidden + if kid != "" && hmacEncoded != "" { + reg, err = client.Registration.RegisterWithExternalAccountBinding(registration.RegisterEABOptions{ + TermsOfServiceAgreed: true, + Kid: kid, + HmacEncoded: hmacEncoded, + }) + } else { + reg, err = client.Registration.RegisterWithExternalAccountBinding(registration.RegisterEABOptions{ + TermsOfServiceAgreed: true, + Kid: a.Kid, + HmacEncoded: a.HmacEncoded, + }) + } + if err != nil { + log.Println(err) + return false, err + } + //return false, errors.New("External Account Required for this ACME Provider.") + } else { + reg, err = client.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: true}) + if err != nil { + log.Println(err) + return false, err + } } + adminUser.Registration = reg // obtain the certificate diff --git a/src/mod/acme/autorenew.go b/src/mod/acme/autorenew.go index 211e168..28bee75 100644 --- a/src/mod/acme/autorenew.go +++ b/src/mod/acme/autorenew.go @@ -355,7 +355,7 @@ func (a *AutoRenewer) renewExpiredDomains(certs []*ExpiredCerts) ([]string, erro log.Println("Renewing " + expiredCert.Filepath + " (Might take a few minutes)") fileName := filepath.Base(expiredCert.Filepath) certName := fileName[:len(fileName)-len(filepath.Ext(fileName))] - _, err := a.AcmeHandler.ObtainCert(expiredCert.Domains, certName, a.RenewerConfig.Email, expiredCert.CA) + _, err := a.AcmeHandler.ObtainCert(expiredCert.Domains, certName, a.RenewerConfig.Email, expiredCert.CA, "", "") if err != nil { log.Println("Renew " + fileName + "(" + strings.Join(expiredCert.Domains, ",") + ") failed: " + err.Error()) } else { From b1a14872c3fe7ce41e15606d4a2012422131b57e Mon Sep 17 00:00:00 2001 From: Alan Yeung Date: Sat, 2 Sep 2023 18:56:04 -0700 Subject: [PATCH 02/18] EAB implementation done --- src/acme.go | 2 +- src/mod/acme/acme.go | 72 ++++++++++++++++++++++++++++++++++---------- 2 files changed, 57 insertions(+), 17 deletions(-) diff --git a/src/acme.go b/src/acme.go index 1bacb51..3b13a8c 100644 --- a/src/acme.go +++ b/src/acme.go @@ -38,7 +38,7 @@ func initACME() *acme.ACMEHandler { port = getRandomPort(30000) } - return acme.NewACME("https://acme-staging-v02.api.letsencrypt.org/directory", strconv.Itoa(port), "", "") + return acme.NewACME("https://acme-staging-v02.api.letsencrypt.org/directory", strconv.Itoa(port), "./conf/acme_auth.json") } // create the special routing rule for ACME diff --git a/src/mod/acme/acme.go b/src/mod/acme/acme.go index b25c4ac..049f941 100644 --- a/src/mod/acme/acme.go +++ b/src/mod/acme/acme.go @@ -9,6 +9,7 @@ import ( "crypto/x509" "encoding/json" "encoding/pem" + "errors" "fmt" "io/ioutil" "log" @@ -41,6 +42,11 @@ type ACMEUser struct { key crypto.PrivateKey } +type EABConfig struct { + Kid string `json:"kid"` + HmacKey string `json:"HmacKey"` +} + // GetEmail returns the email of the ACMEUser. func (u *ACMEUser) GetEmail() string { return u.Email @@ -58,24 +64,35 @@ func (u *ACMEUser) GetPrivateKey() crypto.PrivateKey { // ACMEHandler handles ACME-related operations. type ACMEHandler struct { - DefaultAcmeServer string - Port string - Kid string - HmacEncoded string + DefaultAcmeServer string + Port string + AuthConfigLocation string } // NewACME creates a new ACMEHandler instance. -func NewACME(acmeServer string, port string, kid string, hmacEncoded string) *ACMEHandler { +func NewACME(acmeServer string, port string, authConfigLocation string) *ACMEHandler { + + if !utils.FileExists(authConfigLocation) { + //Create one + os.MkdirAll(filepath.Dir(authConfigLocation), 0775) + js := []byte("{ \"EXAMPLE_CONFIG_PUT_YOUR_SERVICE_DIRECTORY_URL_HERE\": { \"kid\": \"PUT_YOUR_KID_HERE\", \"HmacKey\": \"PUT_YOUR_HMAC_HERE\" } }") + err := os.WriteFile(authConfigLocation, js, 0775) + if err != nil { + log.Fatal("[ACME] Failed to write ACME EAB config") + return nil + } + } + return &ACMEHandler{ - DefaultAcmeServer: acmeServer, - Port: port, - Kid: kid, - HmacEncoded: hmacEncoded, + DefaultAcmeServer: acmeServer, + Port: port, + AuthConfigLocation: authConfigLocation, } } // ObtainCert obtains a certificate for the specified domains. -func (a *ACMEHandler) ObtainCert(domains []string, certificateName string, email string, caName string, caUrl string, skipTLS bool, kid string, hmacEncoded string) (bool, error) { +// , kid string, hmacEncoded string +func (a *ACMEHandler) ObtainCert(domains []string, certificateName string, email string, caName string, caUrl string, skipTLS bool) (bool, error) { log.Println("[ACME] Obtaining certificate...") // generate private key @@ -152,18 +169,18 @@ func (a *ACMEHandler) ObtainCert(domains []string, certificateName string, email if client.GetExternalAccountRequired() { log.Println("External Account Required for this ACME Provider.") // IF KID and HmacEncoded is overidden + kid, hmacEncoded, err := a.getKidHmac(config.CADirURL) + if err != nil { + log.Println(err) + return false, err + } + log.Println("EAB Credential retrieved.") if kid != "" && hmacEncoded != "" { reg, err = client.Registration.RegisterWithExternalAccountBinding(registration.RegisterEABOptions{ TermsOfServiceAgreed: true, Kid: kid, HmacEncoded: hmacEncoded, }) - } else { - reg, err = client.Registration.RegisterWithExternalAccountBinding(registration.RegisterEABOptions{ - TermsOfServiceAgreed: true, - Kid: a.Kid, - HmacEncoded: a.HmacEncoded, - }) } if err != nil { log.Println(err) @@ -400,3 +417,26 @@ func loadCertInfoJSON(filename string) (*CertificateInfoJSON, error) { return certInfo, nil } + +func (a *ACMEHandler) getKidHmac(caName string) (string, string, error) { + var config map[string]EABConfig + + jsonData, err := ioutil.ReadFile(a.AuthConfigLocation) + if err != nil { + return "", "", err + } + + err = json.Unmarshal([]byte(jsonData), &config) + if err != nil { + return "", "", err + } + + // Access the values for dynamic keys + for key, value := range config { + if key == caName { + return value.Kid, value.HmacKey, nil + } + } + + return "", "", errors.New("Unable to find appropiate EAB information") +} From e3f8c99ed3c7ad69574e1176b6219edf6c5de03a Mon Sep 17 00:00:00 2001 From: kirari04 Date: Mon, 10 Jun 2024 17:52:16 +0200 Subject: [PATCH 03/18] poc of an ratelimit implementation --- src/mod/dynamicproxy/Server.go | 7 ++ src/mod/dynamicproxy/dynamicproxy.go | 7 ++ src/mod/dynamicproxy/ratelimit.go | 133 +++++++++++++++++++++++++++ src/reverseproxy.go | 4 + 4 files changed, 151 insertions(+) create mode 100644 src/mod/dynamicproxy/ratelimit.go diff --git a/src/mod/dynamicproxy/Server.go b/src/mod/dynamicproxy/Server.go index 2771059..8756964 100644 --- a/src/mod/dynamicproxy/Server.go +++ b/src/mod/dynamicproxy/Server.go @@ -72,6 +72,13 @@ func (h *ProxyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { return } + // Rate Limit Check + // if sep.RequireBasicAuth { + if err := handleRateLimit(w, r, sep); err != nil { + return + } + // } + //Validate basic auth if sep.RequireBasicAuth { err := h.handleBasicAuthRouting(w, r, sep) diff --git a/src/mod/dynamicproxy/dynamicproxy.go b/src/mod/dynamicproxy/dynamicproxy.go index 7532d09..1395734 100644 --- a/src/mod/dynamicproxy/dynamicproxy.go +++ b/src/mod/dynamicproxy/dynamicproxy.go @@ -129,6 +129,13 @@ func (router *Router) StartProxyService() error { } } + // Rate Limit Check + // if sep.RequireBasicAuth { + if err := handleRateLimit(w, r, sep); err != nil { + return + } + // } + //Validate basic auth if sep.RequireBasicAuth { err := handleBasicAuth(w, r, sep) diff --git a/src/mod/dynamicproxy/ratelimit.go b/src/mod/dynamicproxy/ratelimit.go new file mode 100644 index 0000000..e9884d7 --- /dev/null +++ b/src/mod/dynamicproxy/ratelimit.go @@ -0,0 +1,133 @@ +package dynamicproxy + +import ( + "errors" + "net" + "net/http" + "sync" + "time" + + "log" +) + +/* + ratelimit.go + + This file handles the ratelimit on proxy endpoints + if RateLimit is set to true +*/ + +// idk what this was for +// func (h *ProxyHandler) handleRateLimitRouting(w http.ResponseWriter, r *http.Request, pe *ProxyEndpoint) error { +// err := handleRateLimit(w, r, pe) +// if err != nil { +// h.logRequest(r, false, 429, "host", pe.Domain) +// } +// return err +// } + +type IpTable struct { + sync.RWMutex + table map[string]*IpTableValue +} + +// Get the ip from the table +func (t *IpTable) Get(ip string) (*IpTableValue, bool) { + t.RLock() + defer t.RUnlock() + v, ok := t.table[ip] + return v, ok +} + +// Clear the ip from the table +func (t *IpTable) Clear() { + t.Lock() + defer t.Unlock() + t.table = make(map[string]*IpTableValue) +} + +// Increment the count of requests for a given ip +// init ip in ipTable if not exists +func (t *IpTable) Increment(ip string) { + t.Lock() + defer t.Unlock() + v, ok := t.table[ip] + if !ok { + v = &IpTableValue{Count: 0, LastHit: time.Now()} + } + v.Count++ + t.table[ip] = v +} + +// Check if the ip is in the table and if it is, check if the count is less than the limit +func (t *IpTable) Exceeded(ip string, limit int64) bool { + t.RLock() + defer t.RUnlock() + v, ok := t.table[ip] + if !ok { + return false + } + if v.Count < limit { + return false + } + return true +} + +// Get the count of requests for a given ip +// returns 0 if ip is not in the table +func (t *IpTable) GetCount(ip string) int64 { + t.RLock() + defer t.RUnlock() + v, ok := t.table[ip] + if !ok { + return 0 + } + return v.Count +} + +type IpTableValue struct { + Count int64 + LastHit time.Time +} + +var ipTable IpTable = IpTable{table: make(map[string]*IpTableValue)} + +// Handle rate limit logic +// do not write to http.ResponseWriter if err return is not nil (already handled by this function) +func handleRateLimit(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 + // } + // } + // } + + ip, _, err := net.SplitHostPort(r.RemoteAddr) + if err != nil { + w.WriteHeader(500) + log.Println("Error resolving remote address", r.RemoteAddr, err) + return errors.New("internal server error") + } + + ipTable.Increment(ip) + + // if ipTable.Exceeded(ip, pe.RateLimit) { + if ipTable.Exceeded(ip, 10) { + w.WriteHeader(429) + return errors.New("rate limit exceeded") + } + + log.Println("Rate limit check", ip, ipTable.GetCount(ip)) + + return nil +} + +func InitRateLimit() { + for { + ipTable.Clear() + time.Sleep(time.Second) + } +} diff --git a/src/reverseproxy.go b/src/reverseproxy.go index e2113da..81ffa35 100644 --- a/src/reverseproxy.go +++ b/src/reverseproxy.go @@ -146,6 +146,10 @@ func ReverseProxtInit() { SystemWideLogger.Println("Uptime Monitor background service started") }() + // Init Rate Limit + go func() { + dynamicproxy.InitRateLimit() + }() } func ReverseProxyHandleOnOff(w http.ResponseWriter, r *http.Request) { From 6026c4fd5333b79981f7f1bb2904b40f9738dff9 Mon Sep 17 00:00:00 2001 From: kirari04 Date: Tue, 11 Jun 2024 16:40:04 +0200 Subject: [PATCH 04/18] implement sync.Map and atomic values with benchmark --- src/mod/dynamicproxy/ratelimit.go | 2 +- .../dynamicproxy/ratelimit_benchmark_test.go | 92 +++++++++++++++++++ src/mod/dynamicproxy/ratelimitb.go | 79 ++++++++++++++++ src/mod/dynamicproxy/ratelimitc.go | 78 ++++++++++++++++ src/mod/dynamicproxy/typedef.go | 4 + 5 files changed, 254 insertions(+), 1 deletion(-) create mode 100644 src/mod/dynamicproxy/ratelimit_benchmark_test.go create mode 100644 src/mod/dynamicproxy/ratelimitb.go create mode 100644 src/mod/dynamicproxy/ratelimitc.go diff --git a/src/mod/dynamicproxy/ratelimit.go b/src/mod/dynamicproxy/ratelimit.go index e9884d7..e173fb7 100644 --- a/src/mod/dynamicproxy/ratelimit.go +++ b/src/mod/dynamicproxy/ratelimit.go @@ -120,7 +120,7 @@ func handleRateLimit(w http.ResponseWriter, r *http.Request, pe *ProxyEndpoint) return errors.New("rate limit exceeded") } - log.Println("Rate limit check", ip, ipTable.GetCount(ip)) + // log.Println("Rate limit check", ip, ipTable.GetCount(ip)) return nil } diff --git a/src/mod/dynamicproxy/ratelimit_benchmark_test.go b/src/mod/dynamicproxy/ratelimit_benchmark_test.go new file mode 100644 index 0000000..ed5b846 --- /dev/null +++ b/src/mod/dynamicproxy/ratelimit_benchmark_test.go @@ -0,0 +1,92 @@ +package dynamicproxy + +import ( + "net/http" + "net/http/httptest" + "sync" + "testing" +) + +func BenchmarkRateLimitSyncMapInt64(b *testing.B) { + ipTableSyncMapInt64 = IpTableSyncMapInt64{} // Reset the table + + handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + handleRateLimitSyncMapInt64(w, r, &ProxyEndpoint{RateLimiting: 10}) + }) + + request := httptest.NewRequest("GET", "/", nil) + request.RemoteAddr = "192.168.1.1:1234" + + b.ResetTimer() + + for i := 0; i < b.N; i++ { + recorder := httptest.NewRecorder() + handler.ServeHTTP(recorder, request) + } +} + +func BenchmarkRateLimitSyncMapAtomicInt64(b *testing.B) { + ipTableSyncMapAtomicInt64 = IpTableSyncMapAtomicInt64{} // Reset the table + + handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + handleRateLimitSyncMapAtomicInt64(w, r, &ProxyEndpoint{RateLimiting: 10}) + }) + + request := httptest.NewRequest("GET", "/", nil) + request.RemoteAddr = "192.168.1.1:1234" + + b.ResetTimer() + + for i := 0; i < b.N; i++ { + recorder := httptest.NewRecorder() + handler.ServeHTTP(recorder, request) + } +} + +func BenchmarkRateLimitSyncMapInt64Concurrent(b *testing.B) { + ipTableSyncMapInt64 = IpTableSyncMapInt64{} // Reset the table + + handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + handleRateLimitSyncMapInt64(w, r, &ProxyEndpoint{RateLimiting: 10}) + }) + + request := httptest.NewRequest("GET", "/", nil) + request.RemoteAddr = "192.168.1.1:1234" + + b.ResetTimer() + + var wg sync.WaitGroup + for i := 0; i < b.N; i++ { + wg.Add(1) + go func() { + defer wg.Done() + recorder := httptest.NewRecorder() + handler.ServeHTTP(recorder, request) + }() + } + wg.Wait() +} + +func BenchmarkRateLimitSyncMapAtomicInt64Concurrent(b *testing.B) { + ipTableSyncMapAtomicInt64 = IpTableSyncMapAtomicInt64{} // Reset the table + + handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + handleRateLimitSyncMapAtomicInt64(w, r, &ProxyEndpoint{RateLimiting: 10}) + }) + + request := httptest.NewRequest("GET", "/", nil) + request.RemoteAddr = "192.168.1.1:1234" + + b.ResetTimer() + + var wg sync.WaitGroup + for i := 0; i < b.N; i++ { + wg.Add(1) + go func() { + defer wg.Done() + recorder := httptest.NewRecorder() + handler.ServeHTTP(recorder, request) + }() + } + wg.Wait() +} diff --git a/src/mod/dynamicproxy/ratelimitb.go b/src/mod/dynamicproxy/ratelimitb.go new file mode 100644 index 0000000..48c749b --- /dev/null +++ b/src/mod/dynamicproxy/ratelimitb.go @@ -0,0 +1,79 @@ +package dynamicproxy + +import ( + "errors" + "log" + "net" + "net/http" + "sync" + "time" +) + +// IpTableSyncMapInt64 is a rate limiter implementation using sync.Map with int64 +type IpTableSyncMapInt64 struct { + table sync.Map +} + +// Increment the count of requests for a given IP +func (t *IpTableSyncMapInt64) Increment(ip string) { + v, _ := t.table.LoadOrStore(ip, new(int64)) + count := v.(*int64) + *count++ +} + +// Check if the IP is in the table and if it is, check if the count is less than the limit +func (t *IpTableSyncMapInt64) Exceeded(ip string, limit int64) bool { + v, ok := t.table.Load(ip) + if !ok { + return false + } + count := v.(*int64) + return *count >= limit +} + +// Get the count of requests for a given IP +func (t *IpTableSyncMapInt64) GetCount(ip string) int64 { + v, ok := t.table.Load(ip) + if !ok { + return 0 + } + count := v.(*int64) + return *count +} + +// Clear the IP table +func (t *IpTableSyncMapInt64) Clear() { + t.table.Range(func(key, value interface{}) bool { + t.table.Delete(key) + return true + }) +} + +var ipTableSyncMapInt64 = IpTableSyncMapInt64{} + +func handleRateLimitSyncMapInt64(w http.ResponseWriter, r *http.Request, pe *ProxyEndpoint) error { + ip, _, err := net.SplitHostPort(r.RemoteAddr) + if err != nil { + w.WriteHeader(500) + log.Println("Error resolving remote address", r.RemoteAddr, err) + return errors.New("internal server error") + } + + ipTableSyncMapInt64.Increment(ip) + + if ipTableSyncMapInt64.Exceeded(ip, 10) { + w.WriteHeader(429) + return errors.New("rate limit exceeded") + } + + // log.Println("Rate limit check", ip, ipTableSyncMapInt64.GetCount(ip)) + + return nil +} + +func InitRateLimitSyncMapInt64() { + for { + ipTableSyncMapInt64.Clear() + time.Sleep(time.Second) + } +} diff --git a/src/mod/dynamicproxy/ratelimitc.go b/src/mod/dynamicproxy/ratelimitc.go new file mode 100644 index 0000000..88d67ea --- /dev/null +++ b/src/mod/dynamicproxy/ratelimitc.go @@ -0,0 +1,78 @@ +package dynamicproxy + +import ( + "errors" + "log" + "net" + "net/http" + "sync" + "sync/atomic" + "time" +) + +// IpTableSyncMapAtomicInt64 is a rate limiter implementation using sync.Map with atomic int64 +type IpTableSyncMapAtomicInt64 struct { + table sync.Map +} + +// Increment the count of requests for a given IP +func (t *IpTableSyncMapAtomicInt64) Increment(ip string) { + v, _ := t.table.LoadOrStore(ip, new(int64)) + atomic.AddInt64(v.(*int64), 1) +} + +// Check if the IP is in the table and if it is, check if the count is less than the limit +func (t *IpTableSyncMapAtomicInt64) Exceeded(ip string, limit int64) bool { + v, ok := t.table.Load(ip) + if !ok { + return false + } + count := atomic.LoadInt64(v.(*int64)) + return count >= limit +} + +// Get the count of requests for a given IP +func (t *IpTableSyncMapAtomicInt64) GetCount(ip string) int64 { + v, ok := t.table.Load(ip) + if !ok { + return 0 + } + return atomic.LoadInt64(v.(*int64)) +} + +// Clear the IP table +func (t *IpTableSyncMapAtomicInt64) Clear() { + t.table.Range(func(key, value interface{}) bool { + t.table.Delete(key) + return true + }) +} + +var ipTableSyncMapAtomicInt64 = IpTableSyncMapAtomicInt64{} + +func handleRateLimitSyncMapAtomicInt64(w http.ResponseWriter, r *http.Request, pe *ProxyEndpoint) error { + ip, _, err := net.SplitHostPort(r.RemoteAddr) + if err != nil { + w.WriteHeader(500) + log.Println("Error resolving remote address", r.RemoteAddr, err) + return errors.New("internal server error") + } + + ipTableSyncMapAtomicInt64.Increment(ip) + + if ipTableSyncMapAtomicInt64.Exceeded(ip, 10) { + w.WriteHeader(429) + return errors.New("rate limit exceeded") + } + + // log.Println("Rate limit check", ip, ipTableSyncMapAtomicInt64.GetCount(ip)) + + return nil +} + +func InitRateLimitSyncMapAtomicInt64() { + for { + ipTableSyncMapAtomicInt64.Clear() + time.Sleep(time.Second) + } +} diff --git a/src/mod/dynamicproxy/typedef.go b/src/mod/dynamicproxy/typedef.go index 3386aa6..660e663 100644 --- a/src/mod/dynamicproxy/typedef.go +++ b/src/mod/dynamicproxy/typedef.go @@ -124,6 +124,10 @@ type ProxyEndpoint struct { BasicAuthCredentials []*BasicAuthCredentials //Basic auth credentials BasicAuthExceptionRules []*BasicAuthExceptionRule //Path to exclude in a basic auth enabled proxy target + // Rate Limiting + EnableRateLimiting bool + RateLimiting int // Rate limit in requests per second + //Access Control AccessFilterUUID string //Access filter ID From 61e4d45430685fecd51f8e85c9cfefc0cab359f3 Mon Sep 17 00:00:00 2001 From: kirari04 Date: Tue, 11 Jun 2024 16:53:29 +0200 Subject: [PATCH 05/18] improoved benchmark --- .../dynamicproxy/ratelimit_benchmark_test.go | 52 ++++++++++++++++--- 1 file changed, 44 insertions(+), 8 deletions(-) diff --git a/src/mod/dynamicproxy/ratelimit_benchmark_test.go b/src/mod/dynamicproxy/ratelimit_benchmark_test.go index ed5b846..f19a40b 100644 --- a/src/mod/dynamicproxy/ratelimit_benchmark_test.go +++ b/src/mod/dynamicproxy/ratelimit_benchmark_test.go @@ -7,9 +7,25 @@ import ( "testing" ) -func BenchmarkRateLimitSyncMapInt64(b *testing.B) { - ipTableSyncMapInt64 = IpTableSyncMapInt64{} // Reset the table +func BenchmarkRateLimitLockMapInt64(b *testing.B) { + go InitRateLimit() + handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + handleRateLimit(w, r, &ProxyEndpoint{RateLimiting: 10}) + }) + + request := httptest.NewRequest("GET", "/", nil) + request.RemoteAddr = "192.168.1.1:1234" + + b.ResetTimer() + for i := 0; i < b.N; i++ { + recorder := httptest.NewRecorder() + handler.ServeHTTP(recorder, request) + } +} + +func BenchmarkRateLimitSyncMapInt64(b *testing.B) { + go InitRateLimitSyncMapInt64() handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { handleRateLimitSyncMapInt64(w, r, &ProxyEndpoint{RateLimiting: 10}) }) @@ -26,8 +42,7 @@ func BenchmarkRateLimitSyncMapInt64(b *testing.B) { } func BenchmarkRateLimitSyncMapAtomicInt64(b *testing.B) { - ipTableSyncMapAtomicInt64 = IpTableSyncMapAtomicInt64{} // Reset the table - + go InitRateLimitSyncMapAtomicInt64() handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { handleRateLimitSyncMapAtomicInt64(w, r, &ProxyEndpoint{RateLimiting: 10}) }) @@ -43,9 +58,31 @@ func BenchmarkRateLimitSyncMapAtomicInt64(b *testing.B) { } } -func BenchmarkRateLimitSyncMapInt64Concurrent(b *testing.B) { - ipTableSyncMapInt64 = IpTableSyncMapInt64{} // Reset the table +func BenchmarkRateLimitLockMapInt64Concurrent(b *testing.B) { + go InitRateLimit() + handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + handleRateLimit(w, r, &ProxyEndpoint{RateLimiting: 10}) + }) + request := httptest.NewRequest("GET", "/", nil) + request.RemoteAddr = "192.168.1.1:1234" + + b.ResetTimer() + + var wg sync.WaitGroup + for i := 0; i < b.N; i++ { + wg.Add(1) + go func() { + defer wg.Done() + recorder := httptest.NewRecorder() + handler.ServeHTTP(recorder, request) + }() + } + wg.Wait() +} + +func BenchmarkRateLimitSyncMapInt64Concurrent(b *testing.B) { + go InitRateLimitSyncMapInt64() handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { handleRateLimitSyncMapInt64(w, r, &ProxyEndpoint{RateLimiting: 10}) }) @@ -68,8 +105,7 @@ func BenchmarkRateLimitSyncMapInt64Concurrent(b *testing.B) { } func BenchmarkRateLimitSyncMapAtomicInt64Concurrent(b *testing.B) { - ipTableSyncMapAtomicInt64 = IpTableSyncMapAtomicInt64{} // Reset the table - + go InitRateLimitSyncMapAtomicInt64() handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { handleRateLimitSyncMapAtomicInt64(w, r, &ProxyEndpoint{RateLimiting: 10}) }) From 95453431517ca3b4b52404a70d14016e9c4e266e Mon Sep 17 00:00:00 2001 From: kirari04 Date: Tue, 11 Jun 2024 16:56:59 +0200 Subject: [PATCH 06/18] Removing Benchmark & Updated implementation --- src/mod/dynamicproxy/ratelimit.go | 97 +++---------- .../dynamicproxy/ratelimit_benchmark_test.go | 128 ------------------ src/mod/dynamicproxy/ratelimitb.go | 79 ----------- src/mod/dynamicproxy/ratelimitc.go | 78 ----------- 4 files changed, 21 insertions(+), 361 deletions(-) delete mode 100644 src/mod/dynamicproxy/ratelimit_benchmark_test.go delete mode 100644 src/mod/dynamicproxy/ratelimitb.go delete mode 100644 src/mod/dynamicproxy/ratelimitc.go diff --git a/src/mod/dynamicproxy/ratelimit.go b/src/mod/dynamicproxy/ratelimit.go index e173fb7..cdcb96c 100644 --- a/src/mod/dynamicproxy/ratelimit.go +++ b/src/mod/dynamicproxy/ratelimit.go @@ -2,109 +2,55 @@ package dynamicproxy import ( "errors" + "log" "net" "net/http" "sync" + "sync/atomic" "time" - - "log" ) -/* - ratelimit.go - - This file handles the ratelimit on proxy endpoints - if RateLimit is set to true -*/ - -// idk what this was for -// func (h *ProxyHandler) handleRateLimitRouting(w http.ResponseWriter, r *http.Request, pe *ProxyEndpoint) error { -// err := handleRateLimit(w, r, pe) -// if err != nil { -// h.logRequest(r, false, 429, "host", pe.Domain) -// } -// return err -// } - +// IpTable is a rate limiter implementation using sync.Map with atomic int64 type IpTable struct { - sync.RWMutex - table map[string]*IpTableValue -} - -// Get the ip from the table -func (t *IpTable) Get(ip string) (*IpTableValue, bool) { - t.RLock() - defer t.RUnlock() - v, ok := t.table[ip] - return v, ok + table sync.Map } -// Clear the ip from the table -func (t *IpTable) Clear() { - t.Lock() - defer t.Unlock() - t.table = make(map[string]*IpTableValue) -} - -// Increment the count of requests for a given ip -// init ip in ipTable if not exists +// Increment the count of requests for a given IP func (t *IpTable) Increment(ip string) { - t.Lock() - defer t.Unlock() - v, ok := t.table[ip] - if !ok { - v = &IpTableValue{Count: 0, LastHit: time.Now()} - } - v.Count++ - t.table[ip] = v + v, _ := t.table.LoadOrStore(ip, new(int64)) + atomic.AddInt64(v.(*int64), 1) } -// Check if the ip is in the table and if it is, check if the count is less than the limit +// Check if the IP is in the table and if it is, check if the count is less than the limit func (t *IpTable) Exceeded(ip string, limit int64) bool { - t.RLock() - defer t.RUnlock() - v, ok := t.table[ip] + v, ok := t.table.Load(ip) if !ok { return false } - if v.Count < limit { - return false - } - return true + count := atomic.LoadInt64(v.(*int64)) + return count >= limit } -// Get the count of requests for a given ip -// returns 0 if ip is not in the table +// Get the count of requests for a given IP func (t *IpTable) GetCount(ip string) int64 { - t.RLock() - defer t.RUnlock() - v, ok := t.table[ip] + v, ok := t.table.Load(ip) if !ok { return 0 } - return v.Count + return atomic.LoadInt64(v.(*int64)) } -type IpTableValue struct { - Count int64 - LastHit time.Time +// Clear the IP table +func (t *IpTable) Clear() { + t.table.Range(func(key, value interface{}) bool { + t.table.Delete(key) + return true + }) } -var ipTable IpTable = IpTable{table: make(map[string]*IpTableValue)} +var ipTable = IpTable{} -// Handle rate limit logic -// do not write to http.ResponseWriter if err return is not nil (already handled by this function) func handleRateLimit(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 - // } - // } - // } - ip, _, err := net.SplitHostPort(r.RemoteAddr) if err != nil { w.WriteHeader(500) @@ -114,7 +60,6 @@ func handleRateLimit(w http.ResponseWriter, r *http.Request, pe *ProxyEndpoint) ipTable.Increment(ip) - // if ipTable.Exceeded(ip, pe.RateLimit) { if ipTable.Exceeded(ip, 10) { w.WriteHeader(429) return errors.New("rate limit exceeded") diff --git a/src/mod/dynamicproxy/ratelimit_benchmark_test.go b/src/mod/dynamicproxy/ratelimit_benchmark_test.go deleted file mode 100644 index f19a40b..0000000 --- a/src/mod/dynamicproxy/ratelimit_benchmark_test.go +++ /dev/null @@ -1,128 +0,0 @@ -package dynamicproxy - -import ( - "net/http" - "net/http/httptest" - "sync" - "testing" -) - -func BenchmarkRateLimitLockMapInt64(b *testing.B) { - go InitRateLimit() - handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - handleRateLimit(w, r, &ProxyEndpoint{RateLimiting: 10}) - }) - - request := httptest.NewRequest("GET", "/", nil) - request.RemoteAddr = "192.168.1.1:1234" - - b.ResetTimer() - - for i := 0; i < b.N; i++ { - recorder := httptest.NewRecorder() - handler.ServeHTTP(recorder, request) - } -} - -func BenchmarkRateLimitSyncMapInt64(b *testing.B) { - go InitRateLimitSyncMapInt64() - handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - handleRateLimitSyncMapInt64(w, r, &ProxyEndpoint{RateLimiting: 10}) - }) - - request := httptest.NewRequest("GET", "/", nil) - request.RemoteAddr = "192.168.1.1:1234" - - b.ResetTimer() - - for i := 0; i < b.N; i++ { - recorder := httptest.NewRecorder() - handler.ServeHTTP(recorder, request) - } -} - -func BenchmarkRateLimitSyncMapAtomicInt64(b *testing.B) { - go InitRateLimitSyncMapAtomicInt64() - handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - handleRateLimitSyncMapAtomicInt64(w, r, &ProxyEndpoint{RateLimiting: 10}) - }) - - request := httptest.NewRequest("GET", "/", nil) - request.RemoteAddr = "192.168.1.1:1234" - - b.ResetTimer() - - for i := 0; i < b.N; i++ { - recorder := httptest.NewRecorder() - handler.ServeHTTP(recorder, request) - } -} - -func BenchmarkRateLimitLockMapInt64Concurrent(b *testing.B) { - go InitRateLimit() - handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - handleRateLimit(w, r, &ProxyEndpoint{RateLimiting: 10}) - }) - - request := httptest.NewRequest("GET", "/", nil) - request.RemoteAddr = "192.168.1.1:1234" - - b.ResetTimer() - - var wg sync.WaitGroup - for i := 0; i < b.N; i++ { - wg.Add(1) - go func() { - defer wg.Done() - recorder := httptest.NewRecorder() - handler.ServeHTTP(recorder, request) - }() - } - wg.Wait() -} - -func BenchmarkRateLimitSyncMapInt64Concurrent(b *testing.B) { - go InitRateLimitSyncMapInt64() - handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - handleRateLimitSyncMapInt64(w, r, &ProxyEndpoint{RateLimiting: 10}) - }) - - request := httptest.NewRequest("GET", "/", nil) - request.RemoteAddr = "192.168.1.1:1234" - - b.ResetTimer() - - var wg sync.WaitGroup - for i := 0; i < b.N; i++ { - wg.Add(1) - go func() { - defer wg.Done() - recorder := httptest.NewRecorder() - handler.ServeHTTP(recorder, request) - }() - } - wg.Wait() -} - -func BenchmarkRateLimitSyncMapAtomicInt64Concurrent(b *testing.B) { - go InitRateLimitSyncMapAtomicInt64() - handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - handleRateLimitSyncMapAtomicInt64(w, r, &ProxyEndpoint{RateLimiting: 10}) - }) - - request := httptest.NewRequest("GET", "/", nil) - request.RemoteAddr = "192.168.1.1:1234" - - b.ResetTimer() - - var wg sync.WaitGroup - for i := 0; i < b.N; i++ { - wg.Add(1) - go func() { - defer wg.Done() - recorder := httptest.NewRecorder() - handler.ServeHTTP(recorder, request) - }() - } - wg.Wait() -} diff --git a/src/mod/dynamicproxy/ratelimitb.go b/src/mod/dynamicproxy/ratelimitb.go deleted file mode 100644 index 48c749b..0000000 --- a/src/mod/dynamicproxy/ratelimitb.go +++ /dev/null @@ -1,79 +0,0 @@ -package dynamicproxy - -import ( - "errors" - "log" - "net" - "net/http" - "sync" - "time" -) - -// IpTableSyncMapInt64 is a rate limiter implementation using sync.Map with int64 -type IpTableSyncMapInt64 struct { - table sync.Map -} - -// Increment the count of requests for a given IP -func (t *IpTableSyncMapInt64) Increment(ip string) { - v, _ := t.table.LoadOrStore(ip, new(int64)) - count := v.(*int64) - *count++ -} - -// Check if the IP is in the table and if it is, check if the count is less than the limit -func (t *IpTableSyncMapInt64) Exceeded(ip string, limit int64) bool { - v, ok := t.table.Load(ip) - if !ok { - return false - } - count := v.(*int64) - return *count >= limit -} - -// Get the count of requests for a given IP -func (t *IpTableSyncMapInt64) GetCount(ip string) int64 { - v, ok := t.table.Load(ip) - if !ok { - return 0 - } - count := v.(*int64) - return *count -} - -// Clear the IP table -func (t *IpTableSyncMapInt64) Clear() { - t.table.Range(func(key, value interface{}) bool { - t.table.Delete(key) - return true - }) -} - -var ipTableSyncMapInt64 = IpTableSyncMapInt64{} - -func handleRateLimitSyncMapInt64(w http.ResponseWriter, r *http.Request, pe *ProxyEndpoint) error { - ip, _, err := net.SplitHostPort(r.RemoteAddr) - if err != nil { - w.WriteHeader(500) - log.Println("Error resolving remote address", r.RemoteAddr, err) - return errors.New("internal server error") - } - - ipTableSyncMapInt64.Increment(ip) - - if ipTableSyncMapInt64.Exceeded(ip, 10) { - w.WriteHeader(429) - return errors.New("rate limit exceeded") - } - - // log.Println("Rate limit check", ip, ipTableSyncMapInt64.GetCount(ip)) - - return nil -} - -func InitRateLimitSyncMapInt64() { - for { - ipTableSyncMapInt64.Clear() - time.Sleep(time.Second) - } -} diff --git a/src/mod/dynamicproxy/ratelimitc.go b/src/mod/dynamicproxy/ratelimitc.go deleted file mode 100644 index 88d67ea..0000000 --- a/src/mod/dynamicproxy/ratelimitc.go +++ /dev/null @@ -1,78 +0,0 @@ -package dynamicproxy - -import ( - "errors" - "log" - "net" - "net/http" - "sync" - "sync/atomic" - "time" -) - -// IpTableSyncMapAtomicInt64 is a rate limiter implementation using sync.Map with atomic int64 -type IpTableSyncMapAtomicInt64 struct { - table sync.Map -} - -// Increment the count of requests for a given IP -func (t *IpTableSyncMapAtomicInt64) Increment(ip string) { - v, _ := t.table.LoadOrStore(ip, new(int64)) - atomic.AddInt64(v.(*int64), 1) -} - -// Check if the IP is in the table and if it is, check if the count is less than the limit -func (t *IpTableSyncMapAtomicInt64) Exceeded(ip string, limit int64) bool { - v, ok := t.table.Load(ip) - if !ok { - return false - } - count := atomic.LoadInt64(v.(*int64)) - return count >= limit -} - -// Get the count of requests for a given IP -func (t *IpTableSyncMapAtomicInt64) GetCount(ip string) int64 { - v, ok := t.table.Load(ip) - if !ok { - return 0 - } - return atomic.LoadInt64(v.(*int64)) -} - -// Clear the IP table -func (t *IpTableSyncMapAtomicInt64) Clear() { - t.table.Range(func(key, value interface{}) bool { - t.table.Delete(key) - return true - }) -} - -var ipTableSyncMapAtomicInt64 = IpTableSyncMapAtomicInt64{} - -func handleRateLimitSyncMapAtomicInt64(w http.ResponseWriter, r *http.Request, pe *ProxyEndpoint) error { - ip, _, err := net.SplitHostPort(r.RemoteAddr) - if err != nil { - w.WriteHeader(500) - log.Println("Error resolving remote address", r.RemoteAddr, err) - return errors.New("internal server error") - } - - ipTableSyncMapAtomicInt64.Increment(ip) - - if ipTableSyncMapAtomicInt64.Exceeded(ip, 10) { - w.WriteHeader(429) - return errors.New("rate limit exceeded") - } - - // log.Println("Rate limit check", ip, ipTableSyncMapAtomicInt64.GetCount(ip)) - - return nil -} - -func InitRateLimitSyncMapAtomicInt64() { - for { - ipTableSyncMapAtomicInt64.Clear() - time.Sleep(time.Second) - } -} From bb1b161ae2feac235ecef9e63da3f662f5cc9f44 Mon Sep 17 00:00:00 2001 From: kirari04 Date: Tue, 11 Jun 2024 22:04:30 +0200 Subject: [PATCH 07/18] clean up implementation --- src/mod/dynamicproxy/Server.go | 9 +++++---- src/mod/dynamicproxy/ratelimit.go | 10 +++++++++- src/mod/dynamicproxy/typedef.go | 4 ++-- 3 files changed, 16 insertions(+), 7 deletions(-) diff --git a/src/mod/dynamicproxy/Server.go b/src/mod/dynamicproxy/Server.go index 8756964..f89fa26 100644 --- a/src/mod/dynamicproxy/Server.go +++ b/src/mod/dynamicproxy/Server.go @@ -73,11 +73,12 @@ func (h *ProxyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { } // Rate Limit Check - // if sep.RequireBasicAuth { - if err := handleRateLimit(w, r, sep); err != nil { - return + if sep.RequireRateLimit { + err := h.handleRateLimitRouting(w, r, sep) + if err != nil { + return + } } - // } //Validate basic auth if sep.RequireBasicAuth { diff --git a/src/mod/dynamicproxy/ratelimit.go b/src/mod/dynamicproxy/ratelimit.go index cdcb96c..0ac4e9f 100644 --- a/src/mod/dynamicproxy/ratelimit.go +++ b/src/mod/dynamicproxy/ratelimit.go @@ -50,6 +50,14 @@ func (t *IpTable) Clear() { var ipTable = IpTable{} +func (h *ProxyHandler) handleRateLimitRouting(w http.ResponseWriter, r *http.Request, pe *ProxyEndpoint) error { + err := handleRateLimit(w, r, pe) + if err != nil { + h.logRequest(r, false, 429, "ratelimit", pe.Domain) + } + return err +} + func handleRateLimit(w http.ResponseWriter, r *http.Request, pe *ProxyEndpoint) error { ip, _, err := net.SplitHostPort(r.RemoteAddr) if err != nil { @@ -60,7 +68,7 @@ func handleRateLimit(w http.ResponseWriter, r *http.Request, pe *ProxyEndpoint) ipTable.Increment(ip) - if ipTable.Exceeded(ip, 10) { + if ipTable.Exceeded(ip, int64(pe.RateLimit)) { w.WriteHeader(429) return errors.New("rate limit exceeded") } diff --git a/src/mod/dynamicproxy/typedef.go b/src/mod/dynamicproxy/typedef.go index 660e663..d4f9a8a 100644 --- a/src/mod/dynamicproxy/typedef.go +++ b/src/mod/dynamicproxy/typedef.go @@ -125,8 +125,8 @@ type ProxyEndpoint struct { BasicAuthExceptionRules []*BasicAuthExceptionRule //Path to exclude in a basic auth enabled proxy target // Rate Limiting - EnableRateLimiting bool - RateLimiting int // Rate limit in requests per second + RequireRateLimit bool + RateLimit int64 // Rate limit in requests per second //Access Control AccessFilterUUID string //Access filter ID From fa11422748065c95924bd2a44b6b3bb908c06c47 Mon Sep 17 00:00:00 2001 From: kirari04 Date: Tue, 11 Jun 2024 22:36:03 +0200 Subject: [PATCH 08/18] Implemented ui part for rate limit --- .gitignore | 6 ++++++ src/reverseproxy.go | 23 +++++++++++++++++++++++ src/web/components/httprp.html | 4 ++++ src/web/components/rules.html | 25 +++++++++++++++++++++++++ 4 files changed, 58 insertions(+) diff --git a/.gitignore b/.gitignore index 26006a7..532fdbe 100644 --- a/.gitignore +++ b/.gitignore @@ -34,3 +34,9 @@ docker/ImagePublisher.sh src/mod/acme/test/stackoverflow.pem /tools/dns_challenge_update/code-gen/acmedns /tools/dns_challenge_update/code-gen/lego +src/tmp/localhost.key +src/tmp/localhost.pem +src/www/html/index.html +src/sys.uuid +src/zoraxy +src/log/zr_2024-6.log diff --git a/src/reverseproxy.go b/src/reverseproxy.go index 81ffa35..fa980a9 100644 --- a/src/reverseproxy.go +++ b/src/reverseproxy.go @@ -233,6 +233,26 @@ func ReverseProxyHandleAddEndpoint(w http.ResponseWriter, r *http.Request) { requireBasicAuth := (rba == "true") + // Require Rate Limiting? + rl, _ := utils.PostPara(r, "rate") + if rl == "" { + rl = "false" + } + requireRateLimit := (rl == "true") + rlnum, _ := utils.PostPara(r, "ratenum") + if rlnum == "" { + rlnum = "0" + } + proxyRateLimit, err := strconv.ParseInt(rlnum, 10, 64) + if err != nil { + utils.SendErrorResponse(w, "invalid rate limit number") + return + } + if proxyRateLimit <= 0 { + utils.SendErrorResponse(w, "rate limit number must be greater than 0") + return + } + // Bypass WebSocket Origin Check strbpwsorg, _ := utils.PostPara(r, "bpwsorg") if strbpwsorg == "" { @@ -313,6 +333,9 @@ func ReverseProxyHandleAddEndpoint(w http.ResponseWriter, r *http.Request) { BasicAuthExceptionRules: []*dynamicproxy.BasicAuthExceptionRule{}, DefaultSiteOption: 0, DefaultSiteValue: "", + // Rate Limit + RequireRateLimit: requireRateLimit, + RateLimit: proxyRateLimit, } preparedEndpoint, err := dynamicProxyRouter.PrepareProxyRoute(&thisProxyEndpoint) diff --git a/src/web/components/httprp.html b/src/web/components/httprp.html index b96dedc..e9e4f49 100644 --- a/src/web/components/httprp.html +++ b/src/web/components/httprp.html @@ -20,6 +20,7 @@

HTTP Proxy

Destination Virtual Directory Basic Auth + Rate Limit Actions @@ -107,6 +108,9 @@

HTTP Proxy

${subd.RequireBasicAuth?``:``} + + ${subd.RequireRateLimit?` ${subd.RateLimit}req/s`:``} +
diff --git a/src/web/components/rules.html b/src/web/components/rules.html index 6a4faad..9a8474e 100644 --- a/src/web/components/rules.html +++ b/src/web/components/rules.html @@ -73,6 +73,17 @@

New Proxy Rule

+
+
+ + +
+
+
+ + + The Rate Limit is applied to the whole proxy endpoint. If the number of requests exceeds the limit, the proxy will return a 429 error code. +
@@ -147,6 +158,8 @@

New Proxy Rule

var skipTLSValidation = $("#skipTLSValidation")[0].checked; var bypassGlobalTLS = $("#bypassGlobalTLS")[0].checked; var requireBasicAuth = $("#requireBasicAuth")[0].checked; + var proxyRateLimit = $("#proxyRateLimit").val(); + var requireRateLimit = $("#requireRateLimit")[0].checked; var skipWebSocketOriginCheck = $("#skipWebsocketOriginCheck")[0].checked; var accessRuleToUse = $("#newProxyRuleAccessFilter").val(); @@ -176,6 +189,8 @@

New Proxy Rule

bpwsorg: skipWebSocketOriginCheck, bypassGlobalTLS: bypassGlobalTLS, bauth: requireBasicAuth, + rate: requireRateLimit, + ratenum: proxyRateLimit, cred: JSON.stringify(credentials), access: accessRuleToUse, }, @@ -264,6 +279,16 @@

New Proxy Rule

} $("#requireBasicAuth").on('change', toggleBasicAuth); toggleBasicAuth(); + + function toggleRateLimit() { + if ($("#requireRateLimit").parent().checkbox("is checked")) { + $("#proxyRateLimit").parent().removeClass("disabled"); + } else { + $("#proxyRateLimit").parent().addClass("disabled"); + } + } + $("#requireRateLimit").on('change', toggleRateLimit); + toggleRateLimit(); /* From 49babbd60f4315459de42594fb1700576b5d4877 Mon Sep 17 00:00:00 2001 From: kirari04 Date: Tue, 11 Jun 2024 22:45:46 +0200 Subject: [PATCH 09/18] implemented update ratelimit --- src/reverseproxy.go | 22 ++++++++++++++++++++++ src/web/components/httprp.html | 21 +++++++++++++++++++++ 2 files changed, 43 insertions(+) diff --git a/src/reverseproxy.go b/src/reverseproxy.go index fa980a9..9e7664f 100644 --- a/src/reverseproxy.go +++ b/src/reverseproxy.go @@ -457,6 +457,26 @@ func ReverseProxyHandleEditEndpoint(w http.ResponseWriter, r *http.Request) { requireBasicAuth := (rba == "true") + // Rate Limiting? + rl, _ := utils.PostPara(r, "rate") + if rl == "" { + rl = "false" + } + requireRateLimit := (rl == "true") + rlnum, _ := utils.PostPara(r, "ratenum") + if rlnum == "" { + rlnum = "0" + } + proxyRateLimit, err := strconv.ParseInt(rlnum, 10, 64) + if err != nil { + utils.SendErrorResponse(w, "invalid rate limit number") + return + } + if proxyRateLimit <= 0 { + utils.SendErrorResponse(w, "rate limit number must be greater than 0") + return + } + // Bypass WebSocket Origin Check strbpwsorg, _ := utils.PostPara(r, "bpwsorg") if strbpwsorg == "" { @@ -478,6 +498,8 @@ func ReverseProxyHandleEditEndpoint(w http.ResponseWriter, r *http.Request) { newProxyEndpoint.BypassGlobalTLS = bypassGlobalTLS newProxyEndpoint.SkipCertValidations = skipTlsValidation newProxyEndpoint.RequireBasicAuth = requireBasicAuth + newProxyEndpoint.RequireRateLimit = requireRateLimit + newProxyEndpoint.RateLimit = proxyRateLimit newProxyEndpoint.SkipWebSocketOriginCheck = bypassWebsocketOriginCheck //Prepare to replace the current routing rule diff --git a/src/web/components/httprp.html b/src/web/components/httprp.html index e9e4f49..c660eb3 100644 --- a/src/web/components/httprp.html +++ b/src/web/components/httprp.html @@ -305,6 +305,23 @@

HTTP Proxy

`); + } else if (datatype == "ratelimit"){ + let requireRateLimit = payload.RequireRateLimit; + let checkstate = ""; + if (requireRateLimit){ + checkstate = "checked"; + } + let rateLimit = payload.RateLimit; + + column.empty().append(`
+ + +
+
+ +
+ `); + }else if (datatype == 'action'){ column.empty().append(` @@ -352,6 +369,8 @@

HTTP Proxy

let requireTLS = $(row).find(".RequireTLS")[0].checked; let skipCertValidations = $(row).find(".SkipCertValidations")[0].checked; let requireBasicAuth = $(row).find(".RequireBasicAuth")[0].checked; + let requireRateLimit = $(row).find(".RequireRateLimit")[0].checked; + let rateLimit = $(row).find(".RateLimit").val(); let bypassGlobalTLS = $(row).find(".BypassGlobalTLS")[0].checked; let bypassWebsocketOrigin = $(row).find(".SkipWebSocketOriginCheck")[0].checked; console.log(newDomain, requireTLS, skipCertValidations, requireBasicAuth) @@ -368,6 +387,8 @@

HTTP Proxy

"tlsval": skipCertValidations, "bpwsorg" : bypassWebsocketOrigin, "bauth" :requireBasicAuth, + "rate" :requireRateLimit, + "ratenum" :rateLimit, }, success: function(data){ if (data.error !== undefined){ From 25c7e8ac1a68e2d889438a8ca8eedd35050a8046 Mon Sep 17 00:00:00 2001 From: kirari04 Date: Wed, 12 Jun 2024 18:00:08 +0200 Subject: [PATCH 10/18] update git ignore --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 532fdbe..6e386d5 100644 --- a/.gitignore +++ b/.gitignore @@ -39,4 +39,4 @@ src/tmp/localhost.pem src/www/html/index.html src/sys.uuid src/zoraxy -src/log/zr_2024-6.log +src/log/ \ No newline at end of file From 10048150bbe732f913cdc15aed42f76a40e6ae88 Mon Sep 17 00:00:00 2001 From: Toby Chui Date: Fri, 14 Jun 2024 23:42:52 +0800 Subject: [PATCH 11/18] Optimized rate limiter implementation - Moved rate limiter scope into proxy router - Give IpTable a better name following clean code guideline - Optimized client IP retrieval method - Added stop channel for request counter ticker - Fixed #199 - Optimized UI for rate limit --- src/main.go | 4 +- src/mod/dynamicproxy/Server.go | 2 +- src/mod/dynamicproxy/dpcore/header.go | 1 - src/mod/dynamicproxy/dynamicproxy.go | 41 +++++++++---- src/mod/dynamicproxy/ratelimit.go | 73 ++++++++++++++++------- src/mod/dynamicproxy/typedef.go | 5 +- src/redirect.go | 2 +- src/reverseproxy.go | 5 -- src/web/components/httprp.html | 83 ++++++++++++++++++--------- src/web/components/rules.html | 21 ++++--- src/web/components/status.html | 2 +- 11 files changed, 159 insertions(+), 80 deletions(-) diff --git a/src/main.go b/src/main.go index d813665..44cf82b 100644 --- a/src/main.go +++ b/src/main.go @@ -52,9 +52,9 @@ var logOutputToFile = flag.Bool("log", true, "Log terminal output to file") var ( name = "Zoraxy" - version = "3.0.6" + version = "3.0.7" nodeUUID = "generic" - development = false //Set this to false to use embedded web fs + development = true //Set this to false to use embedded web fs bootTime = time.Now().Unix() /* diff --git a/src/mod/dynamicproxy/Server.go b/src/mod/dynamicproxy/Server.go index f89fa26..62a0e89 100644 --- a/src/mod/dynamicproxy/Server.go +++ b/src/mod/dynamicproxy/Server.go @@ -72,7 +72,7 @@ func (h *ProxyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { return } - // Rate Limit Check + // Rate Limit if sep.RequireRateLimit { err := h.handleRateLimitRouting(w, r, sep) if err != nil { diff --git a/src/mod/dynamicproxy/dpcore/header.go b/src/mod/dynamicproxy/dpcore/header.go index 40c029e..4c38ada 100644 --- a/src/mod/dynamicproxy/dpcore/header.go +++ b/src/mod/dynamicproxy/dpcore/header.go @@ -91,7 +91,6 @@ func addXForwardedForHeader(req *http.Request) { req.Header.Set("X-Real-Ip", strings.TrimSpace(ips[0])) } } - } } diff --git a/src/mod/dynamicproxy/dynamicproxy.go b/src/mod/dynamicproxy/dynamicproxy.go index 1395734..b0e9b5e 100644 --- a/src/mod/dynamicproxy/dynamicproxy.go +++ b/src/mod/dynamicproxy/dynamicproxy.go @@ -23,12 +23,12 @@ import ( func NewDynamicProxy(option RouterOption) (*Router, error) { proxyMap := sync.Map{} thisRouter := Router{ - Option: &option, - ProxyEndpoints: &proxyMap, - Running: false, - server: nil, - routingRules: []*RoutingRule{}, - tldMap: map[string]int{}, + Option: &option, + ProxyEndpoints: &proxyMap, + Running: false, + server: nil, + routingRules: []*RoutingRule{}, + rateLimitCounter: RequestCountPerIpTable{}, } thisRouter.mux = &ProxyHandler{ @@ -85,6 +85,12 @@ func (router *Router) StartProxyService() error { MinVersion: uint16(minVersion), } + //Start rate limitor + err := router.startRateLimterCounterResetTicker() + if err != nil { + return err + } + if router.Option.UseTls { router.server = &http.Server{ Addr: ":" + strconv.Itoa(router.Option.Port), @@ -129,12 +135,12 @@ func (router *Router) StartProxyService() error { } } - // Rate Limit Check - // if sep.RequireBasicAuth { - if err := handleRateLimit(w, r, sep); err != nil { - return + // Rate Limit + if sep.RequireRateLimit { + if err := router.handleRateLimit(w, r, sep); err != nil { + return + } } - // } //Validate basic auth if sep.RequireBasicAuth { @@ -239,10 +245,23 @@ func (router *Router) StopProxyService() error { return err } + //Stop TLS listener if router.tlsListener != nil { router.tlsListener.Close() } + //Stop rate limiter + if router.rateLimterStop != nil { + go func() { + // As the rate timer loop has a 1 sec ticker + // stop the rate limiter in go routine can prevent + // front end from freezing for 1 sec + router.rateLimterStop <- true + }() + + } + + //Stop TLS redirection (from port 80) if router.tlsRedirectStop != nil { router.tlsRedirectStop <- true } diff --git a/src/mod/dynamicproxy/ratelimit.go b/src/mod/dynamicproxy/ratelimit.go index 0ac4e9f..17969e7 100644 --- a/src/mod/dynamicproxy/ratelimit.go +++ b/src/mod/dynamicproxy/ratelimit.go @@ -2,27 +2,27 @@ package dynamicproxy import ( "errors" - "log" "net" "net/http" + "strings" "sync" "sync/atomic" "time" ) // IpTable is a rate limiter implementation using sync.Map with atomic int64 -type IpTable struct { +type RequestCountPerIpTable struct { table sync.Map } // Increment the count of requests for a given IP -func (t *IpTable) Increment(ip string) { +func (t *RequestCountPerIpTable) Increment(ip string) { v, _ := t.table.LoadOrStore(ip, new(int64)) atomic.AddInt64(v.(*int64), 1) } // Check if the IP is in the table and if it is, check if the count is less than the limit -func (t *IpTable) Exceeded(ip string, limit int64) bool { +func (t *RequestCountPerIpTable) Exceeded(ip string, limit int64) bool { v, ok := t.table.Load(ip) if !ok { return false @@ -32,7 +32,7 @@ func (t *IpTable) Exceeded(ip string, limit int64) bool { } // Get the count of requests for a given IP -func (t *IpTable) GetCount(ip string) int64 { +func (t *RequestCountPerIpTable) GetCount(ip string) int64 { v, ok := t.table.Load(ip) if !ok { return 0 @@ -41,34 +41,50 @@ func (t *IpTable) GetCount(ip string) int64 { } // Clear the IP table -func (t *IpTable) Clear() { +func (t *RequestCountPerIpTable) Clear() { t.table.Range(func(key, value interface{}) bool { t.table.Delete(key) return true }) } -var ipTable = IpTable{} - func (h *ProxyHandler) handleRateLimitRouting(w http.ResponseWriter, r *http.Request, pe *ProxyEndpoint) error { - err := handleRateLimit(w, r, pe) + err := h.Parent.handleRateLimit(w, r, pe) if err != nil { h.logRequest(r, false, 429, "ratelimit", pe.Domain) } return err } -func handleRateLimit(w http.ResponseWriter, r *http.Request, pe *ProxyEndpoint) error { - ip, _, err := net.SplitHostPort(r.RemoteAddr) +func (router *Router) handleRateLimit(w http.ResponseWriter, r *http.Request, pe *ProxyEndpoint) error { + //Get the real client-ip from request header + clientIP := r.RemoteAddr + if r.Header.Get("X-Real-Ip") == "" { + CF_Connecting_IP := r.Header.Get("CF-Connecting-IP") + Fastly_Client_IP := r.Header.Get("Fastly-Client-IP") + if CF_Connecting_IP != "" { + //Use CF Connecting IP + clientIP = CF_Connecting_IP + } else if Fastly_Client_IP != "" { + //Use Fastly Client IP + clientIP = Fastly_Client_IP + } else { + ips := strings.Split(clientIP, ",") + if len(ips) > 0 { + clientIP = strings.TrimSpace(ips[0]) + } + } + } + + ip, _, err := net.SplitHostPort(clientIP) if err != nil { - w.WriteHeader(500) - log.Println("Error resolving remote address", r.RemoteAddr, err) - return errors.New("internal server error") + //Default allow passthrough on error + return nil } - ipTable.Increment(ip) + router.rateLimitCounter.Increment(ip) - if ipTable.Exceeded(ip, int64(pe.RateLimit)) { + if router.rateLimitCounter.Exceeded(ip, int64(pe.RateLimit)) { w.WriteHeader(429) return errors.New("rate limit exceeded") } @@ -78,9 +94,26 @@ func handleRateLimit(w http.ResponseWriter, r *http.Request, pe *ProxyEndpoint) return nil } -func InitRateLimit() { - for { - ipTable.Clear() - time.Sleep(time.Second) +// Start the ticker routine for reseting the rate limit counter every seconds +func (r *Router) startRateLimterCounterResetTicker() error { + if r.rateLimterStop != nil { + return errors.New("another rate limiter ticker already running") } + tickerStopChan := make(chan bool) + r.rateLimterStop = tickerStopChan + + counterResetTicker := time.NewTicker(1 * time.Second) + go func() { + for { + select { + case <-tickerStopChan: + r.rateLimterStop = nil + return + case <-counterResetTicker.C: + r.rateLimitCounter.Clear() + } + } + }() + + return nil } diff --git a/src/mod/dynamicproxy/typedef.go b/src/mod/dynamicproxy/typedef.go index d4f9a8a..49c3253 100644 --- a/src/mod/dynamicproxy/typedef.go +++ b/src/mod/dynamicproxy/typedef.go @@ -51,8 +51,9 @@ type Router struct { tlsListener net.Listener routingRules []*RoutingRule - tlsRedirectStop chan bool //Stop channel for tls redirection server - tldMap map[string]int //Top level domain map, see tld.json + tlsRedirectStop chan bool //Stop channel for tls redirection server + rateLimterStop chan bool //Stop channel for rate limiter + rateLimitCounter RequestCountPerIpTable //Request counter for rate limter } // Auth credential for basic auth on certain endpoints diff --git a/src/redirect.go b/src/redirect.go index 824cbe9..fa0a5c2 100644 --- a/src/redirect.go +++ b/src/redirect.go @@ -91,7 +91,7 @@ func handleToggleRedirectRegexpSupport(w http.ResponseWriter, r *http.Request) { //Update the current regex support rule enable state enableRegexSupport := strings.EqualFold(strings.TrimSpace(enabled), "true") redirectTable.AllowRegex = enableRegexSupport - err = sysdb.Write("Redirect", "regex", enableRegexSupport) + err = sysdb.Write("redirect", "regex", enableRegexSupport) if enableRegexSupport { SystemWideLogger.PrintAndLog("redirect", "Regex redirect rule enabled", nil) diff --git a/src/reverseproxy.go b/src/reverseproxy.go index 9e7664f..b2c164f 100644 --- a/src/reverseproxy.go +++ b/src/reverseproxy.go @@ -145,11 +145,6 @@ func ReverseProxtInit() { }) SystemWideLogger.Println("Uptime Monitor background service started") }() - - // Init Rate Limit - go func() { - dynamicproxy.InitRateLimit() - }() } func ReverseProxyHandleOnOff(w http.ResponseWriter, r *http.Request) { diff --git a/src/web/components/httprp.html b/src/web/components/httprp.html index c660eb3..36bc297 100644 --- a/src/web/components/httprp.html +++ b/src/web/components/httprp.html @@ -19,8 +19,7 @@

HTTP Proxy

Host Destination Virtual Directory - Basic Auth - Rate Limit + Advanced Settings Actions @@ -105,11 +104,9 @@

HTTP Proxy

${subd.Domain} ${tlsIcon} ${vdList} - - ${subd.RequireBasicAuth?``:``} - - - ${subd.RequireRateLimit?` ${subd.RateLimit}req/s`:``} + + ${subd.RequireBasicAuth?` Basic Auth`:` Basic Auth`}
+ ${subd.RequireRateLimit?` Rate Limit @ ${subd.RateLimit} req/s`:` Rate Limit`}
@@ -267,11 +264,11 @@

HTTP Proxy

Edit Virtual Directories `); - }else if (datatype == "basicauth"){ + }else if (datatype == "advanced"){ let requireBasicAuth = payload.RequireBasicAuth; - let checkstate = ""; + let basicAuthCheckstate = ""; if (requireBasicAuth){ - checkstate = "checked"; + basicAuthCheckstate = "checked"; } let skipWebSocketOriginCheck = payload.SkipWebSocketOriginCheck; @@ -280,16 +277,36 @@

HTTP Proxy

wsCheckstate = "checked"; } + let requireRateLimit = payload.RequireRateLimit; + let rateLimitCheckState = ""; + if (requireRateLimit){ + rateLimitCheckState = "checked"; + } + let rateLimit = payload.RateLimit; + if (rateLimit == 0){ + //This value is not set. Make it default to 100 + rateLimit = 100; + } + let rateLimitDisableState = ""; + if (!payload.RequireRateLimit){ + rateLimitDisableState = "disabled"; + } + column.empty().append(`
- +
- +
+ +
+ + +
- Advance Configs + Security Options
@@ -298,23 +315,26 @@

HTTP Proxy

Check this to allow cross-origin websocket requests

- - +
+ + +

+
+ + +
`); } else if (datatype == "ratelimit"){ - let requireRateLimit = payload.RequireRateLimit; - let checkstate = ""; - if (requireRateLimit){ - checkstate = "checked"; - } - let rateLimit = payload.RateLimit; - - column.empty().append(`
- + + column.empty().append(` +
+
@@ -352,6 +372,17 @@

HTTP Proxy

$("#httpProxyList").find(".editBtn").addClass("disabled"); } + //handleToggleRateLimitInput will get trigger if the "require rate limit" checkbox + // is changed and toggle the disable state of the rate limit input field + function handleToggleRateLimitInput(){ + let isRateLimitEnabled = $("#httpProxyList input.RequireRateLimit")[0].checked; + if (isRateLimitEnabled){ + $("#httpProxyList input.RateLimit").parent().removeClass("disabled"); + }else{ + $("#httpProxyList input.RateLimit").parent().addClass("disabled"); + } + } + function exitProxyInlineEdit(){ listProxyEndpoints(); $("#httpProxyList").find(".editBtn").removeClass("disabled"); @@ -463,10 +494,6 @@

HTTP Proxy

}) } - /* Access List handling */ - - - //Bind on tab switch events tabSwitchEventBind["httprp"] = function(){ listProxyEndpoints(); diff --git a/src/web/components/rules.html b/src/web/components/rules.html index 9a8474e..e5b7913 100644 --- a/src/web/components/rules.html +++ b/src/web/components/rules.html @@ -81,8 +81,13 @@

New Proxy Rule

- - The Rate Limit is applied to the whole proxy endpoint. If the number of requests exceeds the limit, the proxy will return a 429 error code. +
+ +
+ req / sec / IP +
+
+ Return a 429 error code if request rate exceed the rate limit.
@@ -282,9 +287,9 @@

New Proxy Rule

function toggleRateLimit() { if ($("#requireRateLimit").parent().checkbox("is checked")) { - $("#proxyRateLimit").parent().removeClass("disabled"); + $("#proxyRateLimit").parent().parent().removeClass("disabled"); } else { - $("#proxyRateLimit").parent().addClass("disabled"); + $("#proxyRateLimit").parent().parent().addClass("disabled"); } } $("#requireRateLimit").on('change', toggleRateLimit); @@ -422,9 +427,9 @@

New Proxy Rule

initNewProxyRuleAccessDropdownList(); } - $(document).ready(function(){ - $("#advanceProxyRules").accordion(); - $("#newProxyRuleAccessFilter").parent().dropdown(); - }); + + $("#advanceProxyRules").accordion(); + $("#newProxyRuleAccessFilter").parent().dropdown(); + \ No newline at end of file diff --git a/src/web/components/status.html b/src/web/components/status.html index 9bfd908..3d187bb 100644 --- a/src/web/components/status.html +++ b/src/web/components/status.html @@ -74,7 +74,7 @@

Global Settings

-

+
From 443cd961d2198db9de5cf9022b6c31bce4c57fd9 Mon Sep 17 00:00:00 2001 From: Borys Anikiyenko <7brend7@gmail.com> Date: Sat, 15 Jun 2024 17:19:19 +0300 Subject: [PATCH 12/18] add docker containers list to set rules --- src/api.go | 2 + src/docker.go | 46 ++++++++ src/web/components/rules.html | 15 ++- src/web/snippet/dockerContainersList.html | 137 ++++++++++++++++++++++ 4 files changed, 199 insertions(+), 1 deletion(-) create mode 100644 src/docker.go create mode 100644 src/web/snippet/dockerContainersList.html diff --git a/src/api.go b/src/api.go index f367de2..aa47411 100644 --- a/src/api.go +++ b/src/api.go @@ -222,6 +222,8 @@ func initAPIs() { authRouter.HandleFunc("/api/info/pprof", pprof.Index) //If you got APIs to add, append them here + // get available docker containers + authRouter.HandleFunc("/api/docker/containers", handleDockerContainersList) } // Function to renders Auth related APIs diff --git a/src/docker.go b/src/docker.go new file mode 100644 index 0000000..71f67c9 --- /dev/null +++ b/src/docker.go @@ -0,0 +1,46 @@ +package main + +import ( + "context" + "encoding/json" + "net/http" + + "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/container" + "github.com/docker/docker/client" + "imuslab.com/zoraxy/mod/utils" +) + +func handleDockerContainersList(w http.ResponseWriter, r *http.Request) { + apiClient, err := client.NewClientWithOpts(client.WithVersion("1.43")) + if err != nil { + utils.SendErrorResponse(w, err.Error()) + return + } + defer apiClient.Close() + + containers, err := apiClient.ContainerList(context.Background(), container.ListOptions{All: true}) + if err != nil { + utils.SendErrorResponse(w, err.Error()) + return + } + + networks, err := apiClient.NetworkList(context.Background(), types.NetworkListOptions{}) + if err != nil { + utils.SendErrorResponse(w, err.Error()) + return + } + + result := map[string]interface{}{ + "network": networks, + "containers": containers, + } + + js, err := json.Marshal(result) + if err != nil { + utils.SendErrorResponse(w, err.Error()) + return + } + + utils.SendJSONResponse(w, string(js)) +} diff --git a/src/web/components/rules.html b/src/web/components/rules.html index 6a4faad..dd58a64 100644 --- a/src/web/components/rules.html +++ b/src/web/components/rules.html @@ -26,7 +26,10 @@

New Proxy Rule

- +
+ + +
E.g. 192.168.0.101:8000 or example.com
@@ -401,5 +404,15 @@

New Proxy Rule

$("#advanceProxyRules").accordion(); $("#newProxyRuleAccessFilter").parent().dropdown(); }); + + function openDockerContainersList(){ + showSideWrapper('snippet/dockerContainersList.html'); + } + + function addContainerItem(item) { + $('#rootname').val(item.name); + $('#proxyDomain').val(`${item.ip}:${item.port}`) + hideSideWrapper(true); + } \ No newline at end of file diff --git a/src/web/snippet/dockerContainersList.html b/src/web/snippet/dockerContainersList.html new file mode 100644 index 0000000..ee9f7fd --- /dev/null +++ b/src/web/snippet/dockerContainersList.html @@ -0,0 +1,137 @@ + + + + + + + + + +
+
+
+
+ List of Docker Containers +
+ Below is a list of all detected Docker containers currently running + on the system. +
+
+
+
+
+
+
+ +
+
+ + + + From dfb81513b1b64b14344d053bd22deba376f35568 Mon Sep 17 00:00:00 2001 From: Toby Chui Date: Sun, 16 Jun 2024 12:46:29 +0800 Subject: [PATCH 13/18] Optimized docker detection structure - Merged #202 and optimized UI elements - Added HSTS headers toggle - Added permission policy injector in dynamicproxy - Fixed slow search LAN ip detection - Optimized UI for HTTP reverse proxy rules - Added wip permission policy and load balancer --- src/api.go | 8 +- src/docker.go | 40 ++- src/go.mod | 65 +++-- src/go.sum | 171 ++++++++----- src/mod/dynamicproxy/customHeader.go | 17 +- .../permissionpolicy/permissionpolicy.go | 20 +- src/mod/dynamicproxy/typedef.go | 6 +- src/mod/geodb/slowSearch.go | 6 + src/reverseproxy.go | 88 +++++++ src/web/components/httprp.html | 31 ++- src/web/components/rules.html | 21 +- src/web/snippet/customHeaders.html | 236 ++++++++++++++---- src/web/snippet/loadBalancer.html | 49 ++++ 13 files changed, 596 insertions(+), 162 deletions(-) create mode 100644 src/web/snippet/loadBalancer.html diff --git a/src/api.go b/src/api.go index aa47411..519b532 100644 --- a/src/api.go +++ b/src/api.go @@ -70,6 +70,8 @@ func initAPIs() { authRouter.HandleFunc("/api/proxy/header/list", HandleCustomHeaderList) authRouter.HandleFunc("/api/proxy/header/add", HandleCustomHeaderAdd) authRouter.HandleFunc("/api/proxy/header/remove", HandleCustomHeaderRemove) + authRouter.HandleFunc("/api/proxy/header/handleHSTS", HandleHSTSState) + authRouter.HandleFunc("/api/proxy/header/handlePermissionPolicy", HandlePermissionPolicy) //Reverse proxy auth related APIs authRouter.HandleFunc("/api/proxy/auth/exceptions/list", ListProxyBasicAuthExceptionPaths) authRouter.HandleFunc("/api/proxy/auth/exceptions/add", AddProxyBasicAuthExceptionPaths) @@ -212,6 +214,10 @@ func initAPIs() { authRouter.HandleFunc("/api/fs/del", staticWebServer.FileManager.HandleFileDelete) } + //Docker UX Optimizations + authRouter.HandleFunc("/api/docker/available", HandleDockerAvailable) + authRouter.HandleFunc("/api/docker/containers", HandleDockerContainersList) + //Others http.HandleFunc("/api/info/x", HandleZoraxyInfo) authRouter.HandleFunc("/api/info/geoip", HandleGeoIpLookup) @@ -223,7 +229,7 @@ func initAPIs() { //If you got APIs to add, append them here // get available docker containers - authRouter.HandleFunc("/api/docker/containers", handleDockerContainersList) + } // Function to renders Auth related APIs diff --git a/src/docker.go b/src/docker.go index 71f67c9..da68c3a 100644 --- a/src/docker.go +++ b/src/docker.go @@ -1,9 +1,13 @@ package main import ( + "bufio" "context" "encoding/json" "net/http" + "os" + "os/exec" + "strings" "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/container" @@ -11,7 +15,41 @@ import ( "imuslab.com/zoraxy/mod/utils" ) -func handleDockerContainersList(w http.ResponseWriter, r *http.Request) { +// IsDockerized checks if the program is running in a Docker container. +func IsDockerized() bool { + // Check for the /proc/1/cgroup file + file, err := os.Open("/proc/1/cgroup") + if err != nil { + return false + } + defer file.Close() + + scanner := bufio.NewScanner(file) + for scanner.Scan() { + if strings.Contains(scanner.Text(), "docker") { + return true + } + } + + return false +} + +// IsDockerInstalled checks if Docker is installed on the host. +func IsDockerInstalled() bool { + _, err := exec.LookPath("docker") + return err == nil +} + +// HandleDockerAvaible check if teh docker related functions should be avaible in front-end +func HandleDockerAvailable(w http.ResponseWriter, r *http.Request) { + dockerAvailable := IsDockerized() + js, _ := json.Marshal(dockerAvailable) + utils.SendJSONResponse(w, string(js)) +} + +// handleDockerContainersList return the current list of docker containers +// currently listening to the same bridge network interface. See PR #202 for details. +func HandleDockerContainersList(w http.ResponseWriter, r *http.Request) { apiClient, err := client.NewClientWithOpts(client.WithVersion("1.43")) if err != nil { utils.SendErrorResponse(w, err.Error()) diff --git a/src/go.mod b/src/go.mod index 01565c9..d665e55 100644 --- a/src/go.mod +++ b/src/go.mod @@ -6,6 +6,7 @@ toolchain go1.22.2 require ( github.com/boltdb/bolt v1.3.1 + github.com/docker/docker v27.0.0+incompatible github.com/go-acme/lego/v4 v4.16.1 github.com/go-ping/ping v1.1.0 github.com/google/uuid v1.6.0 @@ -14,13 +15,13 @@ require ( github.com/grandcat/zeroconf v1.0.0 github.com/likexian/whois v1.15.1 github.com/microcosm-cc/bluemonday v1.0.26 - golang.org/x/net v0.23.0 - golang.org/x/sys v0.18.0 - golang.org/x/text v0.14.0 + golang.org/x/net v0.25.0 + golang.org/x/sys v0.20.0 + golang.org/x/text v0.15.0 ) require ( - cloud.google.com/go/compute v1.20.1 // indirect + cloud.google.com/go/compute v1.25.1 // indirect cloud.google.com/go/compute/metadata v0.2.3 // indirect github.com/AdamSLevy/jsonrpc2/v14 v14.1.0 // indirect github.com/Azure/azure-sdk-for-go v68.0.0+incompatible // indirect @@ -39,8 +40,8 @@ require ( github.com/Azure/go-autorest/logger v0.2.1 // indirect github.com/Azure/go-autorest/tracing v0.6.0 // indirect github.com/AzureAD/microsoft-authentication-library-for-go v1.0.0 // indirect + github.com/Microsoft/go-winio v0.4.14 // indirect github.com/OpenDNS/vegadns2client v0.0.0-20180418235048-a3fa4a771d87 // indirect - github.com/akamai/AkamaiOPEN-edgegrid-golang v1.2.2 // indirect github.com/aliyun/alibaba-cloud-sdk-go v1.61.1755 // indirect github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129 // indirect github.com/aws/aws-sdk-go-v2 v1.24.1 // indirect @@ -64,30 +65,36 @@ require ( github.com/cenkalti/backoff/v4 v4.3.0 // indirect github.com/civo/civogo v0.3.11 // indirect github.com/cloudflare/cloudflare-go v0.86.0 // indirect - github.com/cpu/goacmedns v0.1.1 // indirect + github.com/containerd/log v0.1.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/deepmap/oapi-codegen v1.9.1 // indirect github.com/dimchansky/utfbom v1.1.1 // indirect + github.com/distribution/reference v0.6.0 // indirect github.com/dnsimple/dnsimple-go v1.2.0 // indirect + github.com/docker/go-connections v0.5.0 // indirect + github.com/docker/go-units v0.5.0 // indirect github.com/exoscale/egoscale v0.102.3 // indirect github.com/fatih/structs v1.1.0 // indirect + github.com/felixge/httpsnoop v1.0.4 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/ghodss/yaml v1.0.0 // indirect github.com/go-errors/errors v1.0.1 // indirect github.com/go-jose/go-jose/v4 v4.0.1 // indirect + github.com/go-logr/logr v1.4.1 // indirect + github.com/go-logr/stdr v1.2.2 // indirect github.com/go-resty/resty/v2 v2.11.0 // indirect github.com/go-viper/mapstructure/v2 v2.0.0-alpha.1 // indirect github.com/goccy/go-json v0.10.2 // indirect github.com/gofrs/uuid v4.4.0+incompatible // indirect + github.com/gogo/protobuf v1.3.2 // indirect github.com/golang-jwt/jwt/v4 v4.5.0 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect - github.com/golang/protobuf v1.5.3 // indirect + github.com/golang/protobuf v1.5.4 // indirect github.com/google/go-querystring v1.1.0 // indirect - github.com/google/s2a-go v0.1.4 // indirect - github.com/googleapis/enterprise-certificate-proxy v0.2.3 // indirect - github.com/googleapis/gax-go/v2 v2.11.0 // indirect + github.com/google/s2a-go v0.1.7 // indirect + github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect + github.com/googleapis/gax-go/v2 v2.12.2 // indirect github.com/gophercloud/gophercloud v1.0.0 // indirect - github.com/gophercloud/utils v0.0.0-20210216074907-f6de111f2eae // indirect github.com/gorilla/css v1.0.1 // indirect github.com/gorilla/securecookie v1.1.2 // indirect github.com/hashicorp/errwrap v1.0.0 // indirect @@ -111,8 +118,11 @@ require ( github.com/mimuret/golang-iij-dpf v0.9.1 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect + github.com/moby/docker-image-spec v1.3.1 // indirect + github.com/moby/term v0.5.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/morikuni/aec v1.0.0 // indirect github.com/namedotcom/go v0.0.0-20180403034216-08470befbe04 // indirect github.com/nrdcg/auroradns v1.1.0 // indirect github.com/nrdcg/bunny-go v0.0.0-20230728143221-c9dda82568d9 // indirect @@ -125,9 +135,9 @@ require ( github.com/nrdcg/nodion v0.1.0 // indirect github.com/nrdcg/porkbun v0.3.0 // indirect github.com/nzdjb/go-metaname v1.0.0 // indirect - github.com/oracle/oci-go-sdk v24.3.0+incompatible // indirect + github.com/opencontainers/go-digest v1.0.0 // indirect + github.com/opencontainers/image-spec v1.1.0 // indirect github.com/ovh/go-ovh v1.4.3 // indirect - github.com/patrickmn/go-cache v2.1.0+incompatible // indirect github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect @@ -142,8 +152,8 @@ require ( github.com/softlayer/softlayer-go v1.1.3 // indirect github.com/softlayer/xmlrpc v0.0.0-20200409220501-5f089df7cb7e // indirect github.com/spf13/cast v1.3.1 // indirect - github.com/stretchr/objx v0.5.1 // indirect - github.com/stretchr/testify v1.8.4 // indirect + github.com/stretchr/objx v0.5.2 // indirect + github.com/stretchr/testify v1.9.0 // indirect github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.490 // indirect github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.490 // indirect github.com/transip/gotransip/v6 v6.23.0 // indirect @@ -153,22 +163,29 @@ require ( github.com/yandex-cloud/go-genproto v0.0.0-20220805142335-27b56ddae16f // indirect github.com/yandex-cloud/go-sdk v0.0.0-20220805164847-cf028e604997 // indirect go.opencensus.io v0.24.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.52.0 // indirect + go.opentelemetry.io/otel v1.27.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.27.0 // indirect + go.opentelemetry.io/otel/metric v1.27.0 // indirect + go.opentelemetry.io/otel/sdk v1.27.0 // indirect + go.opentelemetry.io/otel/trace v1.27.0 // indirect go.uber.org/ratelimit v0.2.0 // indirect - golang.org/x/crypto v0.21.0 // indirect + golang.org/x/crypto v0.23.0 // indirect golang.org/x/mod v0.16.0 // indirect - golang.org/x/oauth2 v0.16.0 // indirect + golang.org/x/oauth2 v0.18.0 // indirect golang.org/x/sync v0.6.0 // indirect golang.org/x/time v0.5.0 // indirect golang.org/x/tools v0.19.0 // indirect - google.golang.org/api v0.126.0 // indirect - google.golang.org/appengine v1.6.7 // indirect - google.golang.org/genproto v0.0.0-20230530153820-e85fd2cbaebc // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20230530153820-e85fd2cbaebc // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc // indirect - google.golang.org/grpc v1.55.0 // indirect - google.golang.org/protobuf v1.31.0 // indirect + google.golang.org/api v0.169.0 // indirect + google.golang.org/appengine v1.6.8 // indirect + google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20240520151616-dc85e6b867a5 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240515191416-fc5f0ca64291 // indirect + google.golang.org/grpc v1.64.0 // indirect + google.golang.org/protobuf v1.34.1 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/ns1/ns1-go.v2 v2.7.13 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect + gotest.tools/v3 v3.5.1 // indirect ) diff --git a/src/go.sum b/src/go.sum index d25b3d7..5aaab55 100644 --- a/src/go.sum +++ b/src/go.sum @@ -6,8 +6,8 @@ cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxK cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= -cloud.google.com/go/compute v1.20.1 h1:6aKEtlUiwEpJzM001l0yFkpXmUVXaN8W+fbkb2AZNbg= -cloud.google.com/go/compute v1.20.1/go.mod h1:4tCnrn48xsqlwSAiLf1HXMQk8CONslYbdiEZc9FEIbM= +cloud.google.com/go/compute v1.25.1 h1:ZRpHJedLtTpKgr3RV1Fx23NuaAEN1Zfx9hw1u4aJdjU= +cloud.google.com/go/compute v1.25.1/go.mod h1:oopOIR53ly6viBYxaDhBfJwzUAxf1zE//uf3IB011ls= cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= @@ -33,6 +33,8 @@ github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/privatedns/armprivatedns v github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/privatedns/armprivatedns v1.1.0/go.mod h1:y2zXtLSMM/X5Mfawq0lOftpWn3f4V6OCsRdINsvWBPI= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.0.0 h1:ECsQtyERDVz3NP3kvDOTLvbQhqWp/x9EsGKtb4ogUr8= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.0.0/go.mod h1:s1tW/At+xHqjNFvWU4G0c0Qv33KOhvbGNj0RCTQDV8s= +github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= +github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs= github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= github.com/Azure/go-autorest/autorest v0.11.24/go.mod h1:G6kyRlFnTuSbEYkQGawPfsCswgme4iYf6rfSKUDzbCc= @@ -60,11 +62,11 @@ github.com/AzureAD/microsoft-authentication-library-for-go v1.0.0 h1:OBhqkivkhkM github.com/AzureAD/microsoft-authentication-library-for-go v1.0.0/go.mod h1:kgDmCTgBzIEPFElEF+FK0SdjAor06dRq2Go927dnQ6o= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/Microsoft/go-winio v0.4.14 h1:+hMXMk01us9KgxGb7ftKQt2Xpf5hH/yky+TDA+qxleU= +github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/OpenDNS/vegadns2client v0.0.0-20180418235048-a3fa4a771d87 h1:xPMsUicZ3iosVPSIP7bW5EcGUzjiiMl1OYTe14y/R24= github.com/OpenDNS/vegadns2client v0.0.0-20180418235048-a3fa4a771d87/go.mod h1:iGLljf5n9GjT6kc0HBvyI1nOKnGQbNB66VzSNbK5iks= -github.com/akamai/AkamaiOPEN-edgegrid-golang v1.2.2 h1:F1j7z+/DKEsYqZNoxC6wvfmaiDneLsQOFQmuq9NADSY= -github.com/akamai/AkamaiOPEN-edgegrid-golang v1.2.2/go.mod h1:QlXr/TrICfQ/ANa76sLeQyhAJyNR9sEcfNuZBkY9jgY= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/aliyun/alibaba-cloud-sdk-go v1.61.1755 h1:J45/QHgrzUdqe/Vco/Vxk0wRvdS2nKUxmf/zLgvfass= @@ -135,18 +137,15 @@ github.com/cloudflare/cloudflare-go v0.86.0 h1:jEKN5VHNYNYtfDL2lUFLTRo+nOVNPFxpX github.com/cloudflare/cloudflare-go v0.86.0/go.mod h1:wYW/5UP02TUfBToa/yKbQHV+r6h1NnJ1Je7XjuGM4Jw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= +github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= -github.com/cpu/goacmedns v0.1.1 h1:DM3H2NiN2oam7QljgGY5ygy4yDXhK5Z4JUnqaugs2C4= -github.com/cpu/goacmedns v0.1.1/go.mod h1:MuaouqEhPAHxsbqjgnck5zeghuwBP1dLnPoobeGqugQ= github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/cyberdelia/templates v0.0.0-20141128023046-ca7fffd4298c/go.mod h1:GyV+0YP4qX0UQ7r2MoYZ+AvYDp12OF5yg4q8rGnyNh4= @@ -161,10 +160,18 @@ github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZm github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/dimchansky/utfbom v1.1.1 h1:vV6w1AhK4VMnhBno/TPVCoK9U/LP0PkLCS9tbxHdi/U= github.com/dimchansky/utfbom v1.1.1/go.mod h1:SxdoEBH5qIqFocHMyGOXVAybYJdr71b1Q/j0mACtrfE= +github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= +github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= github.com/dnaeon/go-vcr v1.2.0 h1:zHCHvJYTMh1N7xnV7zf1m1GPBF9Ad0Jk/whtQ1663qI= github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ= github.com/dnsimple/dnsimple-go v1.2.0 h1:ddTGyLVKly5HKb5L65AkLqFqwZlWo3WnR0BlFZlIddM= github.com/dnsimple/dnsimple-go v1.2.0/go.mod h1:z/cs26v/eiRvUyXsHQBLd8lWF8+cD6GbmkPH84plM4U= +github.com/docker/docker v27.0.0+incompatible h1:JRugTYuelmWlW0M3jakcIadDx2HUoUO6+Tf2C5jVfwA= +github.com/docker/docker v27.0.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c= +github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc= +github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= +github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= @@ -179,6 +186,8 @@ github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo= github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= +github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= +github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU= @@ -201,6 +210,11 @@ github.com/go-jose/go-jose/v4 v4.0.1/go.mod h1:WVf9LFMHh/QVrmqrOfqun0C45tMe3RoiK github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= +github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-ping/ping v1.1.0 h1:3MCGhVX4fyEUuhsfwPrsEdQw6xspHkv5zHsiSoDFZYw= @@ -226,7 +240,10 @@ github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MG github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1YrTJupqA= github.com/gofrs/uuid v4.4.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.1 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/goji/httpauth v0.0.0-20160601135302-2da839ab0f4d/go.mod h1:nnjvkQ9ptGaCkuDUx6wNykzzlUixGxvkme+H/lnzb+A= github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= @@ -256,8 +273,8 @@ github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= -github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/golangci/lint-1 v0.0.0-20181222135242-d2cdd8c08219/go.mod h1:/X8TswGSh1pIozq4ZwCfxS0WA5JGXguxk94ar/4c87Y= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= @@ -284,25 +301,21 @@ github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OI github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/s2a-go v0.1.4 h1:1kZ/sQM3srePvKs3tXAvQzo66XfcReoqFpIpIccE7Oc= -github.com/google/s2a-go v0.1.4/go.mod h1:Ej+mSEMGRnqRzjc7VtF+jdBwYG5fuJfiZ8ELkjEwM0A= -github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o= +github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/enterprise-certificate-proxy v0.2.3 h1:yk9/cqRKtT9wXZSsRH9aurXEpJX+U6FLtpYTdC3R06k= -github.com/googleapis/enterprise-certificate-proxy v0.2.3/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= +github.com/googleapis/enterprise-certificate-proxy v0.3.2 h1:Vie5ybvEvT75RniqhfFxPRy3Bf7vr3h0cechB90XaQs= +github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/googleapis/gax-go/v2 v2.11.0 h1:9V9PWXEsWnPpQhu/PeQIkS4eGzMlTLGgt80cUUI8Ki4= -github.com/googleapis/gax-go/v2 v2.11.0/go.mod h1:DxmR61SGKkGLa2xigwuZIQpkCI2S5iydzRfb3peWZJI= -github.com/gophercloud/gophercloud v0.15.1-0.20210202035223-633d73521055/go.mod h1:wRtmUelyIIv3CSSDI47aUwbs075O6i+LY+pXsKCBsb4= +github.com/googleapis/gax-go/v2 v2.12.2 h1:mhN09QQW1jEWeMF74zGR81R30z4VJzjZsfkUhuHF+DA= +github.com/googleapis/gax-go/v2 v2.12.2/go.mod h1:61M8vcyyXR2kqKFxKrfA22jaA8JGF7Dc8App1U3H6jc= github.com/gophercloud/gophercloud v1.0.0 h1:9nTGx0jizmHxDobe4mck89FyQHVyA3CaXLIUSGJjP9k= github.com/gophercloud/gophercloud v1.0.0/go.mod h1:Q8fZtyi5zZxPS/j9aj3sSxtvj41AdQMDwyo1myduD5c= -github.com/gophercloud/utils v0.0.0-20210216074907-f6de111f2eae h1:Hi3IgB9RQDE15Kfovd8MTZrcana+UlQqNbOif8dLpA0= -github.com/gophercloud/utils v0.0.0-20210216074907-f6de111f2eae/go.mod h1:wx8HMD8oQD0Ryhz6+6ykq75PJ79iPyEqYHfwZ4l7OsA= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/css v1.0.1 h1:ntNaBIghp6JmvWnxbZKANoLyuXTPZ4cAMlo6RyhlbO8= github.com/gorilla/css v1.0.1/go.mod h1:BvnYkspnSzMmwRK+b8/xgNPLiIuNZr6vbZBTPQ2A3b0= @@ -320,9 +333,10 @@ github.com/grandcat/zeroconf v1.0.0/go.mod h1:lTKmG1zh86XyCoUeIHSA4FJMBwCJiQmGfc github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= -github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw= -github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 h1:bkypFPDjIYGfCYD5mRBvpqxfYX1YCS1PXdKYWi8FsN0= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0/go.mod h1:P+Lt/0by1T8bfcF3z737NnSbmxQAppXMRziHUxPOC8k= github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= @@ -345,7 +359,6 @@ github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerX github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= @@ -382,6 +395,7 @@ github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7V github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213 h1:qGQQKEcAR99REcMpsXCp3lJ03zYT1PkRd3kQGPn9GVg= github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213/go.mod h1:vNUNkEQ1e29fT/6vq2aBdFsgNPmy8qMdSay1npru+Sw= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kolo/xmlrpc v0.0.0-20220921171641-a4b6fa1dd06b h1:udzkj9S/zlT5X367kqJis0QP7YMxobob6zhzq6Yre00= github.com/kolo/xmlrpc v0.0.0-20220921171641-a4b6fa1dd06b/go.mod h1:pcaDhQK0/NJZEvtCO0qQPPropqV0sJOJ6YW7X+9kRwM= @@ -471,6 +485,10 @@ github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh github.com/mitchellh/mapstructure v1.3.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= +github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= +github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= +github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -478,10 +496,11 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= +github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/namedotcom/go v0.0.0-20180403034216-08470befbe04 h1:o6uBwrhM5C8Ll3MAAxrQxRHEu7FkapwTuI2WmL1rw4g= github.com/namedotcom/go v0.0.0-20180403034216-08470befbe04/go.mod h1:5sN+Lt1CaY4wsPvgQH/jsuJi4XO2ssZbdsIizr4CVC8= -github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/nrdcg/auroradns v1.1.0 h1:KekGh8kmf2MNwqZVVYo/fw/ZONt8QMEmbMFOeljteWo= github.com/nrdcg/auroradns v1.1.0/go.mod h1:O7tViUZbAcnykVnrGkXzIJTHoQCHcgalgAe6X1mzHfk= @@ -521,13 +540,13 @@ github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAl github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs= github.com/onsi/gomega v1.19.0 h1:4ieX6qQjPP/BfC3mpsAtIGGlxTWPeA3Inl/7DtXw1tw= github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro= -github.com/oracle/oci-go-sdk v24.3.0+incompatible h1:x4mcfb4agelf1O4/1/auGlZ1lr97jXRSSN5MxTgG/zU= -github.com/oracle/oci-go-sdk v24.3.0+incompatible/go.mod h1:VQb79nF8Z2cwLkLS35ukwStZIg5F66tcBccjip/j888= +github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= +github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= +github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug= +github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM= github.com/ovh/go-ovh v1.4.3 h1:Gs3V823zwTFpzgGLZNI6ILS4rmxZgJwJCz54Er9LwD0= github.com/ovh/go-ovh v1.4.3/go.mod h1:AkPXVtgwB6xlKblMjRKJJmjRp+ogrE7fz2lVgcQY8SY= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= -github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= -github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc= github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 h1:KoWmjvw+nsYOo29YJK9vDA65RGE3NrOnUtO7a+RF9HU= @@ -582,6 +601,7 @@ github.com/scaleway/scaleway-sdk-go v1.0.0-beta.22/go.mod h1:fCa7OJZ/9DRTnOKmxvT github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= @@ -615,8 +635,8 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/objx v0.5.1 h1:4VhoImhV/Bm0ToFkXFi8hXNXwpDRZ/ynw3amt82mzq0= -github.com/stretchr/objx v0.5.1/go.mod h1:/iHQpkQwBD6DLUmQ4pE+s1TXdob1mORJ4/UFdrifcy0= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= @@ -626,9 +646,8 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.490 h1:mmz27tVi2r70JYnm5y0Zk8w0Qzsx+vfUw3oqSyrEfP8= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.490/go.mod h1:7sCQWVkxcsR38nffDW057DRGk8mUjK1Ing/EFOK8s8Y= @@ -649,14 +668,12 @@ github.com/vinyldns/go-vinyldns v0.9.16 h1:GZJStDkcCk1F1AcRc64LuuMh+ENL8pHA0CVd4 github.com/vinyldns/go-vinyldns v0.9.16/go.mod h1:5qIJOdmzAnatKjurI+Tl4uTus7GJKJxb+zitufjHs3Q= github.com/vultr/govultr/v2 v2.17.2 h1:gej/rwr91Puc/tgh+j33p/BLR16UrIPnSr+AIwYWZQs= github.com/vultr/govultr/v2 v2.17.2/go.mod h1:ZFOKGWmgjytfyjeyAdhQlSWwTjh2ig+X49cAp50dzXI= -github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= -github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= -github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/yandex-cloud/go-genproto v0.0.0-20220805142335-27b56ddae16f h1:cG+ehPRJSlqljSufLf1KXeXpUd1dLNjnzA18mZcB/O0= github.com/yandex-cloud/go-genproto v0.0.0-20220805142335-27b56ddae16f/go.mod h1:HEUYX/p8966tMUHHT+TsS0hF/Ca/NYwqprC5WXSDMfE= github.com/yandex-cloud/go-sdk v0.0.0-20220805164847-cf028e604997 h1:2wzke3JH7OtN20WsNDZx2VH/TCmsbqtDEbXzjF+i05E= github.com/yandex-cloud/go-sdk v0.0.0-20220805164847-cf028e604997/go.mod h1:2CHKs/YGbCcNn/BPaCkEBwKz/FNCELi+MLILjR9RaTA= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= @@ -665,7 +682,23 @@ go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.52.0 h1:9l89oX4ba9kHbBol3Xin3leYJ+252h0zszDtBwyKe2A= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.52.0/go.mod h1:XLZfZboOJWHNKUv7eH0inh0E9VV6eWDFB/9yJyTLPp0= +go.opentelemetry.io/otel v1.27.0 h1:9BZoF3yMK/O1AafMiQTVu0YDj5Ea4hPhxCs7sGva+cg= +go.opentelemetry.io/otel v1.27.0/go.mod h1:DMpAK8fzYRzs+bi3rS5REupisuqTheUlSZJ1WnZaPAQ= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.27.0 h1:R9DE4kQ4k+YtfLI2ULwX82VtNQ2J8yZmA7ZIF/D+7Mc= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.27.0/go.mod h1:OQFyQVrDlbe+R7xrEyDr/2Wr67Ol0hRUgsfA+V5A95s= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.27.0 h1:QY7/0NeRPKlzusf40ZE4t1VlMKbqSNT7cJRYzWuja0s= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.27.0/go.mod h1:HVkSiDhTM9BoUJU8qE6j2eSWLLXvi1USXjyd2BXT8PY= +go.opentelemetry.io/otel/metric v1.27.0 h1:hvj3vdEKyeCi4YaYfNjv2NUje8FqKqUY8IlF0FxV/ik= +go.opentelemetry.io/otel/metric v1.27.0/go.mod h1:mVFgmRlhljgBiuk/MP/oKylr4hs85GZAylncepAX/ak= +go.opentelemetry.io/otel/sdk v1.27.0 h1:mlk+/Y1gLPLn84U4tI8d3GNJmGT/eXe3ZuOXN9kTWmI= +go.opentelemetry.io/otel/sdk v1.27.0/go.mod h1:Ha9vbLwJE6W86YstIywK2xFfPjbWlCuwPtMkKdz/Y4A= +go.opentelemetry.io/otel/trace v1.27.0 h1:IqYb813p7cmbHk0a5y6pD5JPakbVfftRXABGt5/Rscw= +go.opentelemetry.io/otel/trace v1.27.0/go.mod h1:6RiD1hkAprV4/q+yd2ln1HG9GoPx39SuvvstaLBl+l4= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= +go.opentelemetry.io/proto/otlp v1.2.0 h1:pVeZGk7nXDC9O2hncA6nHldxEjm6LByfA2aN8IOkz94= +go.opentelemetry.io/proto/otlp v1.2.0/go.mod h1:gGpR8txAl5M03pDhMC79G6SdqNV26naRm/KDsgaHD8A= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= @@ -683,18 +716,16 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201217014255-9d1352758620/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= -golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211202192323-5770296d904e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220314234659-1baeb1ce4c0b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= -golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= -golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= +golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= +golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -714,6 +745,7 @@ golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCc golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= @@ -739,6 +771,7 @@ golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= @@ -755,14 +788,14 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= -golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs= -golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= +golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= +golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.16.0 h1:aDkGMBSYxElaoP81NpoUoz2oo2R2wHdZpGToUxfyQrQ= -golang.org/x/oauth2 v0.16.0/go.mod h1:hqZ+0LWXsiVoZpeld6jVt06P3adbS2Uu911W1SsJv2o= +golang.org/x/oauth2 v0.18.0 h1:09qnuIAgzdx1XplqJvW6CQqMCtGZykZWcXzPMPUusvI= +golang.org/x/oauth2 v0.18.0/go.mod h1:Wf7knwG0MPoWIMMBgFlEaSUDaKskp0dCfrlJRJXbBi8= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -828,16 +861,16 @@ golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= -golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= +golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= -golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8= -golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= +golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw= +golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= @@ -850,8 +883,8 @@ golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= -golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= -golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= +golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -879,8 +912,10 @@ golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200918232735-d647fc253266/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU= golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210114065538-d78b04bdf963/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= @@ -896,14 +931,14 @@ google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.126.0 h1:q4GJq+cAdMAC7XP7njvQ4tvohGLiSlytuL4BQxbIZ+o= -google.golang.org/api v0.126.0/go.mod h1:mBwVAtz+87bEN6CbA1GtZPDOqY2R5ONPqJeIlvyo4Aw= +google.golang.org/api v0.169.0 h1:QwWPy71FgMWqJN/l6jVlFHUa29a7dcUy02I8o799nPY= +google.golang.org/api v0.169.0/go.mod h1:gpNOiMA2tZ4mf5R9Iwf4rK/Dcz0fbdIgWYWVoxmsyLg= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= -google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= -google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= +google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= @@ -916,12 +951,12 @@ google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvx google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/genproto v0.0.0-20211021150943-2b146023228c/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20230530153820-e85fd2cbaebc h1:8DyZCyvI8mE1IdLy/60bS+52xfymkE72wv1asokgtao= -google.golang.org/genproto v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:xZnkP7mREFX5MORlOPEzLMr+90PPZQ2QWzrVTWfAq64= -google.golang.org/genproto/googleapis/api v0.0.0-20230530153820-e85fd2cbaebc h1:kVKPf/IiYSBWEWtkIn6wZXwWGCnLKcC8oWfZvXjsGnM= -google.golang.org/genproto/googleapis/api v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:vHYtlOoi6TsQ3Uk2yxR7NI5z8uoV+3pZtR4jmHIkRig= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc h1:XSJ8Vk1SWuNr8S18z1NZSziL0CPIXLCCMDOEFtHBOFc= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA= +google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9 h1:9+tzLLstTlPTRyJTh+ah5wIMsBW5c4tQwGTN3thOW9Y= +google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9/go.mod h1:mqHbVIp48Muh7Ywss/AD6I5kNVKZMmAa/QEW58Gxp2s= +google.golang.org/genproto/googleapis/api v0.0.0-20240520151616-dc85e6b867a5 h1:P8OJ/WCl/Xo4E4zoe4/bifHpSmmKwARqyqE4nW6J2GQ= +google.golang.org/genproto/googleapis/api v0.0.0-20240520151616-dc85e6b867a5/go.mod h1:RGnPtTG7r4i8sPlNyDeikXF99hMM+hN6QMm4ooG9g2g= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240515191416-fc5f0ca64291 h1:AgADTJarZTBqgjiUzRgfaBchgYB3/WFTC80GPwsMcRI= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240515191416-fc5f0ca64291/go.mod h1:EfXuqaE1J41VCDicxHzUDm+8rk+7ZdXzHV0IhO/I6s0= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= @@ -933,9 +968,8 @@ google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= google.golang.org/grpc v1.41.0/go.mod h1:U3l9uK9J0sini8mHphKoXyaqDA/8VyGnDee1zzIUK6k= -google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= -google.golang.org/grpc v1.55.0 h1:3Oj82/tFSCeUrRTg/5E/7d/W5A1tj6Ky1ABAuZuv5ag= -google.golang.org/grpc v1.55.0/go.mod h1:iYEXKGkEBhg1PjZQvoYEVPTDkHo1/bjTnfwTeGONTY8= +google.golang.org/grpc v1.64.0 h1:KH3VH9y/MgNQg1dE7b3XfVK0GsPSIzJwdF617gUSbvY= +google.golang.org/grpc v1.64.0/go.mod h1:oxjF8E3FBnjp+/gVFYdWacaLDx9na1aqy9oovLpxQYg= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -948,8 +982,8 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= -google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= +google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -959,10 +993,7 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntN gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= -gopkg.in/h2non/gock.v1 v1.0.15 h1:SzLqcIlb/fDfg7UvukMpNcWsu7sI5tWwL+KCATZqks0= -gopkg.in/h2non/gock.v1 v1.0.15/go.mod h1:sX4zAkdYX1TRGJ2JY156cFspQn4yRWn6p9EMdODlynE= gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/ini.v1 v1.51.1/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.66.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= @@ -985,6 +1016,8 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU= +gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/src/mod/dynamicproxy/customHeader.go b/src/mod/dynamicproxy/customHeader.go index 00177f2..d99f6b7 100644 --- a/src/mod/dynamicproxy/customHeader.go +++ b/src/mod/dynamicproxy/customHeader.go @@ -1,5 +1,7 @@ package dynamicproxy +import "strconv" + /* CustomHeader.go @@ -17,8 +19,9 @@ func (ept *ProxyEndpoint) SplitInboundOutboundHeaders() ([][]string, [][]string) } //Use pre-allocation for faster performance + //Downstream +2 for Permission Policy and HSTS upstreamHeaders := make([][]string, len(ept.UserDefinedHeaders)) - downstreamHeaders := make([][]string, len(ept.UserDefinedHeaders)) + downstreamHeaders := make([][]string, len(ept.UserDefinedHeaders)+2) upstreamHeaderCounter := 0 downstreamHeaderCounter := 0 @@ -42,5 +45,17 @@ func (ept *ProxyEndpoint) SplitInboundOutboundHeaders() ([][]string, [][]string) } } + //Check if the endpoint require HSTS headers + if ept.HSTSMaxAge > 0 { + downstreamHeaders[downstreamHeaderCounter] = []string{"Strict-Transport-Security", "max-age=" + strconv.Itoa(int(ept.HSTSMaxAge))} + downstreamHeaderCounter++ + } + + //Check if the endpoint require Permission Policy + if ept.EnablePermissionPolicyHeader && ept.PermissionPolicy != nil { + downstreamHeaders[downstreamHeaderCounter] = ept.PermissionPolicy.ToKeyValueHeader() + downstreamHeaderCounter++ + } + return upstreamHeaders, downstreamHeaders } diff --git a/src/mod/dynamicproxy/permissionpolicy/permissionpolicy.go b/src/mod/dynamicproxy/permissionpolicy/permissionpolicy.go index 8f156ed..a719572 100644 --- a/src/mod/dynamicproxy/permissionpolicy/permissionpolicy.go +++ b/src/mod/dynamicproxy/permissionpolicy/permissionpolicy.go @@ -108,13 +108,8 @@ func GetDefaultPermissionPolicy() *PermissionsPolicy { } } -// InjectPermissionPolicyHeader inject the permission policy into headers -func InjectPermissionPolicyHeader(w http.ResponseWriter, policy *PermissionsPolicy) { - //Keep the original Permission Policy if exists, or there are no policy given - if policy == nil || w.Header().Get("Permissions-Policy") != "" { - return - } - +// ToKeyValueHeader convert a permission policy struct into a key value string header +func (policy *PermissionsPolicy) ToKeyValueHeader() []string { policyHeader := []string{} // Helper function to add policy directives @@ -187,7 +182,16 @@ func InjectPermissionPolicyHeader(w http.ResponseWriter, policy *PermissionsPoli // Join the directives and set the header policyHeaderValue := strings.Join(policyHeader, ", ") + return []string{"Permissions-Policy", policyHeaderValue} +} +// InjectPermissionPolicyHeader inject the permission policy into headers +func InjectPermissionPolicyHeader(w http.ResponseWriter, policy *PermissionsPolicy) { + //Keep the original Permission Policy if exists, or there are no policy given + if policy == nil || w.Header().Get("Permissions-Policy") != "" { + return + } + headerKV := policy.ToKeyValueHeader() //Inject the new policy into the header - w.Header().Set("Permissions-Policy", policyHeaderValue) + w.Header().Set(headerKV[0], headerKV[1]) } diff --git a/src/mod/dynamicproxy/typedef.go b/src/mod/dynamicproxy/typedef.go index 49c3253..d7f2756 100644 --- a/src/mod/dynamicproxy/typedef.go +++ b/src/mod/dynamicproxy/typedef.go @@ -8,6 +8,7 @@ import ( "imuslab.com/zoraxy/mod/access" "imuslab.com/zoraxy/mod/dynamicproxy/dpcore" + "imuslab.com/zoraxy/mod/dynamicproxy/permissionpolicy" "imuslab.com/zoraxy/mod/dynamicproxy/redirection" "imuslab.com/zoraxy/mod/geodb" "imuslab.com/zoraxy/mod/statistic" @@ -118,7 +119,10 @@ type ProxyEndpoint struct { VirtualDirectories []*VirtualDirectoryEndpoint //Custom Headers - UserDefinedHeaders []*UserDefinedHeader //Custom headers to append when proxying requests from this endpoint + UserDefinedHeaders []*UserDefinedHeader //Custom headers to append when proxying requests from this endpoint + HSTSMaxAge int64 //HSTS max age, set to 0 for disable HSTS headers + EnablePermissionPolicyHeader bool //Enable injection of permission policy header + PermissionPolicy *permissionpolicy.PermissionsPolicy //Permission policy header //Authentication RequireBasicAuth bool //Set to true to request basic auth before proxy diff --git a/src/mod/geodb/slowSearch.go b/src/mod/geodb/slowSearch.go index 9d5f4c7..6c4c8c7 100644 --- a/src/mod/geodb/slowSearch.go +++ b/src/mod/geodb/slowSearch.go @@ -53,6 +53,9 @@ func isIPv6InRange(startIP, endIP, testIP string) (bool, error) { // Slow country code lookup for func (s *Store) slowSearchIpv4(ipAddr string) string { + if isReservedIP(ipAddr) { + return "" + } for _, ipRange := range s.geodb { startIp := ipRange[0] endIp := ipRange[1] @@ -67,6 +70,9 @@ func (s *Store) slowSearchIpv4(ipAddr string) string { } func (s *Store) slowSearchIpv6(ipAddr string) string { + if isReservedIP(ipAddr) { + return "" + } for _, ipRange := range s.geodbIpv6 { startIp := ipRange[0] endIp := ipRange[1] diff --git a/src/reverseproxy.go b/src/reverseproxy.go index b2c164f..7e4dc02 100644 --- a/src/reverseproxy.go +++ b/src/reverseproxy.go @@ -11,6 +11,7 @@ import ( "imuslab.com/zoraxy/mod/auth" "imuslab.com/zoraxy/mod/dynamicproxy" + "imuslab.com/zoraxy/mod/dynamicproxy/permissionpolicy" "imuslab.com/zoraxy/mod/uptime" "imuslab.com/zoraxy/mod/utils" ) @@ -1231,3 +1232,90 @@ func HandleCustomHeaderRemove(w http.ResponseWriter, r *http.Request) { utils.SendOK(w) } + +// Handle view or edit HSTS states +func HandleHSTSState(w http.ResponseWriter, r *http.Request) { + domain, err := utils.PostPara(r, "domain") + if err != nil { + domain, err = utils.GetPara(r, "domain") + if err != nil { + utils.SendErrorResponse(w, "domain or matching rule not defined") + return + } + } + + targetProxyEndpoint, err := dynamicProxyRouter.LoadProxy(domain) + if err != nil { + utils.SendErrorResponse(w, "target endpoint not exists") + return + } + + if r.Method == http.MethodGet { + //Return current HSTS enable state + hstsAge := targetProxyEndpoint.HSTSMaxAge + js, _ := json.Marshal(hstsAge) + utils.SendJSONResponse(w, string(js)) + return + } else if r.Method == http.MethodPost { + newMaxAge, err := utils.PostInt(r, "maxage") + if err != nil { + utils.SendErrorResponse(w, "maxage not defeined") + return + } + + if newMaxAge == 0 || newMaxAge >= 31536000 { + targetProxyEndpoint.HSTSMaxAge = int64(newMaxAge) + SaveReverseProxyConfig(targetProxyEndpoint) + targetProxyEndpoint.UpdateToRuntime() + } else { + utils.SendErrorResponse(w, "invalid max age given") + return + } + utils.SendOK(w) + return + } + + http.Error(w, "405 - Method not allowed", http.StatusMethodNotAllowed) +} + +// HandlePermissionPolicy handle read or write to permission policy +func HandlePermissionPolicy(w http.ResponseWriter, r *http.Request) { + domain, err := utils.PostPara(r, "domain") + if err != nil { + domain, err = utils.GetPara(r, "domain") + if err != nil { + utils.SendErrorResponse(w, "domain or matching rule not defined") + return + } + } + + targetProxyEndpoint, err := dynamicProxyRouter.LoadProxy(domain) + if err != nil { + utils.SendErrorResponse(w, "target endpoint not exists") + return + } + + if r.Method == http.MethodGet { + type CurrentPolicyState struct { + PPEnabled bool + CurrentPolicy *permissionpolicy.PermissionsPolicy + } + + currentPolicy := permissionpolicy.GetDefaultPermissionPolicy() + if targetProxyEndpoint.PermissionPolicy != nil { + currentPolicy = targetProxyEndpoint.PermissionPolicy + } + result := CurrentPolicyState{ + PPEnabled: targetProxyEndpoint.EnablePermissionPolicyHeader, + CurrentPolicy: currentPolicy, + } + + js, _ := json.Marshal(result) + utils.SendJSONResponse(w, string(js)) + return + } else if r.Method == http.MethodPost { + + } + + http.Error(w, "405 - Method not allowed", http.StatusMethodNotAllowed) +} diff --git a/src/web/components/httprp.html b/src/web/components/httprp.html index 36bc297..10a2346 100644 --- a/src/web/components/httprp.html +++ b/src/web/components/httprp.html @@ -19,7 +19,7 @@

HTTP Proxy

Host Destination Virtual Directory - Advanced Settings + Advanced Settings Actions @@ -78,7 +78,7 @@

HTTP Proxy

vdList += `
`; if (subd.VirtualDirectories.length == 0){ - vdList = ` No Virtual Directory`; + vdList = `No Virtual Directory`; } let enableChecked = "checked"; @@ -104,9 +104,11 @@

HTTP Proxy

${subd.Domain} ${tlsIcon} ${vdList} - - ${subd.RequireBasicAuth?` Basic Auth`:` Basic Auth`}
- ${subd.RequireRateLimit?` Rate Limit @ ${subd.RateLimit} req/s`:` Rate Limit`} + + ${subd.RequireBasicAuth?` Basic Auth`:``} + ${subd.RequireBasicAuth && subd.RequireRateLimit?"
":""} + ${subd.RequireRateLimit?` Rate Limit @ ${subd.RateLimit} req/s`:``} + ${!subd.RequireBasicAuth && !subd.RequireRateLimit?`No Special Settings`:""}
@@ -246,7 +248,7 @@

HTTP Proxy

-
+
@@ -255,7 +257,8 @@

HTTP Proxy

-
+

+ `; column.empty().append(input); }else if (datatype == "vdir"){ @@ -300,7 +303,6 @@

HTTP Proxy


-
@@ -317,7 +319,8 @@

HTTP Proxy


- +

@@ -462,6 +465,7 @@

HTTP Proxy

$("#vdirBaseRoutingRule").parent().dropdown("set selected", uuid); } + //Open the custom header editor function editCustomHeaders(uuid){ let payload = encodeURIComponent(JSON.stringify({ ept: "host", @@ -470,6 +474,15 @@

HTTP Proxy

showSideWrapper("snippet/customHeaders.html?t=" + Date.now() + "#" + payload); } + //Open the load balance option + function editLoadBalanceOptions(uuid){ + let payload = encodeURIComponent(JSON.stringify({ + ept: "host", + ep: uuid + })); + showSideWrapper("snippet/loadBalancer.html?t=" + Date.now() + "#" + payload); + } + function handleProxyRuleToggle(object){ let endpointUUID = $(object).attr("eptuuid"); let isChecked = object.checked; diff --git a/src/web/components/rules.html b/src/web/components/rules.html index 5948fa8..cbe0b31 100644 --- a/src/web/components/rules.html +++ b/src/web/components/rules.html @@ -26,12 +26,14 @@

New Proxy Rule

-
+ - -
+ E.g. 192.168.0.101:8000 or example.com
+
@@ -430,6 +432,15 @@

New Proxy Rule

initNewProxyRuleAccessDropdownList(); } + /* Docker Optimizations */ + $("/api/docker/available", function(dockerAvailable){ + if (dockerAvailable){ + $(".dockerOptimizations").show(); + }else{ + $(".dockerOptimizations").hide(); + } + }) + function openDockerContainersList(){ showSideWrapper('snippet/dockerContainersList.html'); } @@ -439,9 +450,9 @@

New Proxy Rule

$('#proxyDomain').val(`${item.ip}:${item.port}`) hideSideWrapper(true); } - + + /* UI Element Initialization */ $("#advanceProxyRules").accordion(); $("#newProxyRuleAccessFilter").parent().dropdown(); - \ No newline at end of file diff --git a/src/web/snippet/customHeaders.html b/src/web/snippet/customHeaders.html index 6e07c78..202912b 100644 --- a/src/web/snippet/customHeaders.html +++ b/src/web/snippet/customHeaders.html @@ -5,6 +5,18 @@ +
@@ -16,52 +28,95 @@
-

You can define custom headers to be sent - together with the client request to the backend server in - this reverse proxy endpoint / host.

- - - - - - - - - - - - - -
KeyValueRemove
No Additonal Header
-
-

Edit Custom Header

-

Add or remove custom header(s) over this proxy target

-
-
-
- - -
-
- - -
-
- - - The header key is NOT case sensitive -
-
- - -
-
- + +
+ + + + + + + + + + + + +
KeyValueRemove
No Additonal Header
+

+ Sent additional custom headers to origin server
+ Inject custom headers into origin server responses +

+
+

Edit Custom Header

+

Add or remove custom header(s) over this proxy target

+
+
+
+ + +
+
+ + +
+
+ + + The header key is NOT case sensitive +
+
+ + +
+
+ +
+
-
-
+
+

HTTP Strict Transport Security

+

Force future attempts to access this site to only use HTTPS

+
+ + +
+
+

Permission Policy

+

Explicitly declare what functionality can and cannot be used on this website.

+
+ + +
+
+ + + + + + + + + + + + + + + +
FeatureEnabledAllow All (*)Self Only (self)
James24EngineerEngineer
+
+
+ +
+
@@ -70,6 +125,8 @@

Edit Custom Header





\ No newline at end of file diff --git a/src/web/snippet/loadBalancer.html b/src/web/snippet/loadBalancer.html new file mode 100644 index 0000000..1388250 --- /dev/null +++ b/src/web/snippet/loadBalancer.html @@ -0,0 +1,49 @@ + + + + + + + + + +
+
+
+
+ Load Balance +
+
+
+
+ +
+
+ +
+
+ +



+ +
+ + + \ No newline at end of file From 03974163d487c4c63113c6025ebb0a45fdf32af9 Mon Sep 17 00:00:00 2001 From: Toby Chui Date: Mon, 17 Jun 2024 00:24:24 +0800 Subject: [PATCH 14/18] Added docker conditional compilation - Moved docker UX optimization into module - Added conditional compilation for Windows build - Added Permission Policy header editor - Fixed docker container list ui error message bug --- src/api.go | 4 +- src/docker.go | 84 ------------ src/main.go | 9 +- src/mod/dockerux/docker.go | 60 +++++++++ src/mod/dockerux/docker_windows.go | 32 +++++ src/mod/dockerux/dockerux.go | 24 ++++ src/mod/dynamicproxy/customHeader.go | 25 +++- src/reverseproxy.go | 33 +++++ src/start.go | 8 ++ src/web/components/rules.html | 23 ++-- src/web/login.html | 2 +- src/web/snippet/customHeaders.html | 148 ++++++++++++++++++++-- src/web/snippet/dockerContainersList.html | 2 + 13 files changed, 334 insertions(+), 120 deletions(-) delete mode 100644 src/docker.go create mode 100644 src/mod/dockerux/docker.go create mode 100644 src/mod/dockerux/docker_windows.go create mode 100644 src/mod/dockerux/dockerux.go diff --git a/src/api.go b/src/api.go index 519b532..27bf4ac 100644 --- a/src/api.go +++ b/src/api.go @@ -215,8 +215,8 @@ func initAPIs() { } //Docker UX Optimizations - authRouter.HandleFunc("/api/docker/available", HandleDockerAvailable) - authRouter.HandleFunc("/api/docker/containers", HandleDockerContainersList) + authRouter.HandleFunc("/api/docker/available", DockerUXOptimizer.HandleDockerAvailable) + authRouter.HandleFunc("/api/docker/containers", DockerUXOptimizer.HandleDockerContainersList) //Others http.HandleFunc("/api/info/x", HandleZoraxyInfo) diff --git a/src/docker.go b/src/docker.go deleted file mode 100644 index da68c3a..0000000 --- a/src/docker.go +++ /dev/null @@ -1,84 +0,0 @@ -package main - -import ( - "bufio" - "context" - "encoding/json" - "net/http" - "os" - "os/exec" - "strings" - - "github.com/docker/docker/api/types" - "github.com/docker/docker/api/types/container" - "github.com/docker/docker/client" - "imuslab.com/zoraxy/mod/utils" -) - -// IsDockerized checks if the program is running in a Docker container. -func IsDockerized() bool { - // Check for the /proc/1/cgroup file - file, err := os.Open("/proc/1/cgroup") - if err != nil { - return false - } - defer file.Close() - - scanner := bufio.NewScanner(file) - for scanner.Scan() { - if strings.Contains(scanner.Text(), "docker") { - return true - } - } - - return false -} - -// IsDockerInstalled checks if Docker is installed on the host. -func IsDockerInstalled() bool { - _, err := exec.LookPath("docker") - return err == nil -} - -// HandleDockerAvaible check if teh docker related functions should be avaible in front-end -func HandleDockerAvailable(w http.ResponseWriter, r *http.Request) { - dockerAvailable := IsDockerized() - js, _ := json.Marshal(dockerAvailable) - utils.SendJSONResponse(w, string(js)) -} - -// handleDockerContainersList return the current list of docker containers -// currently listening to the same bridge network interface. See PR #202 for details. -func HandleDockerContainersList(w http.ResponseWriter, r *http.Request) { - apiClient, err := client.NewClientWithOpts(client.WithVersion("1.43")) - if err != nil { - utils.SendErrorResponse(w, err.Error()) - return - } - defer apiClient.Close() - - containers, err := apiClient.ContainerList(context.Background(), container.ListOptions{All: true}) - if err != nil { - utils.SendErrorResponse(w, err.Error()) - return - } - - networks, err := apiClient.NetworkList(context.Background(), types.NetworkListOptions{}) - if err != nil { - utils.SendErrorResponse(w, err.Error()) - return - } - - result := map[string]interface{}{ - "network": networks, - "containers": containers, - } - - js, err := json.Marshal(result) - if err != nil { - utils.SendErrorResponse(w, err.Error()) - return - } - - utils.SendJSONResponse(w, string(js)) -} diff --git a/src/main.go b/src/main.go index 44cf82b..f2a8dc5 100644 --- a/src/main.go +++ b/src/main.go @@ -16,6 +16,7 @@ import ( "imuslab.com/zoraxy/mod/acme" "imuslab.com/zoraxy/mod/auth" "imuslab.com/zoraxy/mod/database" + "imuslab.com/zoraxy/mod/dockerux" "imuslab.com/zoraxy/mod/dynamicproxy/redirection" "imuslab.com/zoraxy/mod/email" "imuslab.com/zoraxy/mod/forwardproxy" @@ -44,6 +45,7 @@ var allowMdnsScanning = flag.Bool("mdns", true, "Enable mDNS scanner and transpo var mdnsName = flag.String("mdnsname", "", "mDNS name, leave empty to use default (zoraxy_{node-uuid}.local)") var ztAuthToken = flag.String("ztauth", "", "ZeroTier authtoken for the local node") var ztAPIPort = flag.Int("ztport", 9993, "ZeroTier controller API port") +var runningInDocker = flag.Bool("docker", false, "Run Zoraxy in docker compatibility mode") var acmeAutoRenewInterval = flag.Int("autorenew", 86400, "ACME auto TLS/SSL certificate renew check interval (seconds)") var enableHighSpeedGeoIPLookup = flag.Bool("fastgeoip", false, "Enable high speed geoip lookup, require 1GB extra memory (Not recommend for low end devices)") var staticWebServerRoot = flag.String("webroot", "./www", "Static web server root folder. Only allow chnage in start paramters") @@ -86,9 +88,10 @@ var ( forwardProxy *forwardproxy.Handler //HTTP Forward proxy, basically VPN for web browser //Helper modules - EmailSender *email.Sender //Email sender that handle email sending - AnalyticLoader *analytic.DataLoader //Data loader for Zoraxy Analytic - SystemWideLogger *logger.Logger //Logger for Zoraxy + EmailSender *email.Sender //Email sender that handle email sending + AnalyticLoader *analytic.DataLoader //Data loader for Zoraxy Analytic + DockerUXOptimizer *dockerux.UXOptimizer //Docker user experience optimizer, community contribution only + SystemWideLogger *logger.Logger //Logger for Zoraxy ) // Kill signal handler. Do something before the system the core terminate. diff --git a/src/mod/dockerux/docker.go b/src/mod/dockerux/docker.go new file mode 100644 index 0000000..90aa0c7 --- /dev/null +++ b/src/mod/dockerux/docker.go @@ -0,0 +1,60 @@ +//go:build !windows +// +build !windows + +package dockerux + +/* Windows docker optimizer*/ + +import ( + "context" + "encoding/json" + "net/http" + + "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/container" + "github.com/docker/docker/client" + "imuslab.com/zoraxy/mod/utils" +) + +// Windows build not support docker +func (d *UXOptimizer) HandleDockerAvailable(w http.ResponseWriter, r *http.Request) { + js, _ := json.Marshal(d.RunninInDocker) + utils.SendJSONResponse(w, string(js)) +} + +func (d *UXOptimizer) HandleDockerContainersList(w http.ResponseWriter, r *http.Request) { + apiClient, err := client.NewClientWithOpts(client.WithVersion("1.43")) + if err != nil { + d.SystemWideLogger.PrintAndLog("Docker", "Unable to create new docker client", err) + utils.SendErrorResponse(w, "Docker client initiation failed") + return + } + defer apiClient.Close() + + containers, err := apiClient.ContainerList(context.Background(), container.ListOptions{All: true}) + if err != nil { + d.SystemWideLogger.PrintAndLog("Docker", "List docker container failed", err) + utils.SendErrorResponse(w, "List docker container failed") + return + } + + networks, err := apiClient.NetworkList(context.Background(), types.NetworkListOptions{}) + if err != nil { + d.SystemWideLogger.PrintAndLog("Docker", "List docker network failed", err) + utils.SendErrorResponse(w, "List docker network failed") + return + } + + result := map[string]interface{}{ + "network": networks, + "containers": containers, + } + + js, err := json.Marshal(result) + if err != nil { + utils.SendErrorResponse(w, err.Error()) + return + } + + utils.SendJSONResponse(w, string(js)) +} diff --git a/src/mod/dockerux/docker_windows.go b/src/mod/dockerux/docker_windows.go new file mode 100644 index 0000000..f70d079 --- /dev/null +++ b/src/mod/dockerux/docker_windows.go @@ -0,0 +1,32 @@ +//go:build windows +// +build windows + +package dockerux + +/* + + Windows docker UX optimizer dummy + + This is a dummy module for Windows as docker features + is useless on Windows and create a larger binary size + + docker on Windows build are trimmed to reduce binary size + and make it compatibile with Windows 7 +*/ + +import ( + "encoding/json" + "net/http" + + "imuslab.com/zoraxy/mod/utils" +) + +// Windows build not support docker +func (d *UXOptimizer) HandleDockerAvailable(w http.ResponseWriter, r *http.Request) { + js, _ := json.Marshal(d.RunninInDocker) + utils.SendJSONResponse(w, string(js)) +} + +func (d *UXOptimizer) HandleDockerContainersList(w http.ResponseWriter, r *http.Request) { + utils.SendErrorResponse(w, "Platform not supported") +} diff --git a/src/mod/dockerux/dockerux.go b/src/mod/dockerux/dockerux.go new file mode 100644 index 0000000..deca314 --- /dev/null +++ b/src/mod/dockerux/dockerux.go @@ -0,0 +1,24 @@ +package dockerux + +import "imuslab.com/zoraxy/mod/info/logger" + +/* + Docker Optimizer + + This script add support for optimizing docker user experience + Note that this module are community contribute only. For bug + report, please directly tag the Pull Request author. +*/ + +type UXOptimizer struct { + RunninInDocker bool + SystemWideLogger *logger.Logger +} + +//Create a new docker optimizer +func NewDockerOptimizer(IsRunningInDocker bool, logger *logger.Logger) *UXOptimizer { + return &UXOptimizer{ + RunninInDocker: IsRunningInDocker, + SystemWideLogger: logger, + } +} diff --git a/src/mod/dynamicproxy/customHeader.go b/src/mod/dynamicproxy/customHeader.go index d99f6b7..796b176 100644 --- a/src/mod/dynamicproxy/customHeader.go +++ b/src/mod/dynamicproxy/customHeader.go @@ -1,6 +1,10 @@ package dynamicproxy -import "strconv" +import ( + "strconv" + + "imuslab.com/zoraxy/mod/dynamicproxy/permissionpolicy" +) /* CustomHeader.go @@ -9,9 +13,9 @@ import "strconv" into the dpcore routing logic */ -//SplitInboundOutboundHeaders split user defined headers into upstream and downstream headers -//return upstream header and downstream header key-value pairs -//if the header is expected to be deleted, the value will be set to empty string +// SplitInboundOutboundHeaders split user defined headers into upstream and downstream headers +// return upstream header and downstream header key-value pairs +// if the header is expected to be deleted, the value will be set to empty string func (ept *ProxyEndpoint) SplitInboundOutboundHeaders() ([][]string, [][]string) { if len(ept.UserDefinedHeaders) == 0 { //Early return if there are no defined headers @@ -52,8 +56,17 @@ func (ept *ProxyEndpoint) SplitInboundOutboundHeaders() ([][]string, [][]string) } //Check if the endpoint require Permission Policy - if ept.EnablePermissionPolicyHeader && ept.PermissionPolicy != nil { - downstreamHeaders[downstreamHeaderCounter] = ept.PermissionPolicy.ToKeyValueHeader() + if ept.EnablePermissionPolicyHeader { + var usingPermissionPolicy *permissionpolicy.PermissionsPolicy + if ept.PermissionPolicy != nil { + //Custom permission policy + usingPermissionPolicy = ept.PermissionPolicy + } else { + //Permission policy is enabled but not customized. Use default + usingPermissionPolicy = permissionpolicy.GetDefaultPermissionPolicy() + } + + downstreamHeaders[downstreamHeaderCounter] = usingPermissionPolicy.ToKeyValueHeader() downstreamHeaderCounter++ } diff --git a/src/reverseproxy.go b/src/reverseproxy.go index 7e4dc02..5c50631 100644 --- a/src/reverseproxy.go +++ b/src/reverseproxy.go @@ -1314,7 +1314,40 @@ func HandlePermissionPolicy(w http.ResponseWriter, r *http.Request) { utils.SendJSONResponse(w, string(js)) return } else if r.Method == http.MethodPost { + //Update the enable state of permission policy + enableState, err := utils.PostBool(r, "enable") + if err != nil { + utils.SendErrorResponse(w, "invalid enable state given") + return + } + + targetProxyEndpoint.EnablePermissionPolicyHeader = enableState + SaveReverseProxyConfig(targetProxyEndpoint) + targetProxyEndpoint.UpdateToRuntime() + utils.SendOK(w) + return + } else if r.Method == http.MethodPut { + //Store the new permission policy + newPermissionPolicyJSONString, err := utils.PostPara(r, "pp") + if err != nil { + utils.SendErrorResponse(w, "missing pp (permission policy) paramter") + return + } + + //Parse the permission policy from JSON string + newPermissionPolicy := permissionpolicy.GetDefaultPermissionPolicy() + err = json.Unmarshal([]byte(newPermissionPolicyJSONString), &newPermissionPolicy) + if err != nil { + utils.SendErrorResponse(w, "permission policy parse error: "+err.Error()) + return + } + //Save it to file + targetProxyEndpoint.PermissionPolicy = newPermissionPolicy + SaveReverseProxyConfig(targetProxyEndpoint) + targetProxyEndpoint.UpdateToRuntime() + utils.SendOK(w) + return } http.Error(w, "405 - Method not allowed", http.StatusMethodNotAllowed) diff --git a/src/start.go b/src/start.go index f7120bd..389bcd4 100644 --- a/src/start.go +++ b/src/start.go @@ -4,6 +4,7 @@ import ( "log" "net/http" "os" + "runtime" "strconv" "strings" "time" @@ -12,6 +13,7 @@ import ( "imuslab.com/zoraxy/mod/acme" "imuslab.com/zoraxy/mod/auth" "imuslab.com/zoraxy/mod/database" + "imuslab.com/zoraxy/mod/dockerux" "imuslab.com/zoraxy/mod/dynamicproxy/redirection" "imuslab.com/zoraxy/mod/forwardproxy" "imuslab.com/zoraxy/mod/ganserv" @@ -269,6 +271,12 @@ func startupSequence() { log.Fatal(err) } + /* Docker UX Optimizer */ + if runtime.GOOS == "windows" && *runningInDocker { + SystemWideLogger.PrintAndLog("WARNING", "Invalid start flag combination: docker=true && runtime.GOOS == windows. Running in docker UX development mode.", nil) + } + DockerUXOptimizer = dockerux.NewDockerOptimizer(*runningInDocker, SystemWideLogger) + } // This sequence start after everything is initialized diff --git a/src/web/components/rules.html b/src/web/components/rules.html index cbe0b31..e42c261 100644 --- a/src/web/components/rules.html +++ b/src/web/components/rules.html @@ -26,13 +26,11 @@

New Proxy Rule

- - E.g. 192.168.0.101:8000 or example.com
-