Merge pull request #208 from tobychui/v3.0.7

V3.0.7 Update
This commit is contained in:
Toby Chui 2024-06-20 09:38:56 +08:00 committed by GitHub
commit 547855f30f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
42 changed files with 5002 additions and 2383 deletions

6
.gitignore vendored
View File

@ -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/

View File

@ -96,6 +96,8 @@ See the [/docker](https://github.com/tobychui/zoraxy/tree/main/docker) folder fo
Usage of zoraxy:
-autorenew int
ACME auto TLS/SSL certificate renew check interval (seconds) (default 86400)
-docker
Run Zoraxy in docker compatibility mode
-fastgeoip
Enable high speed geoip lookup, require 1GB extra memory (Not recommend for low end devices)
-log
@ -188,4 +190,3 @@ If you like the project and want to support us, please consider a donation. You
This project is open-sourced under AGPL. I open-sourced this project so everyone can check for security issues and benefit all users. **This software is intended to be free of charge. If you have acquired this software from a third-party seller, the authors of this repository bears no responsibility for any technical difficulties assistance or support.**

View File

@ -15,4 +15,12 @@ The templates folder contains the template for overriding the build in error or
To use the template, copy and paste the `wwww` folder to the same directory as zoraxy executable (aka the src/ file if you `go build` with the current folder tree).
### Other Templates
There are a few pre-built templates that works with Zoraxy where you can find in the `other-templates` folder. Copy the folder into `www` and rename the folder to `templates` to active them.
It is worth mentioning that the uwu icons for not-found and access-denied are created by @SAWARATSUKI

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,154 @@
<!DOCTYPE html>
<html>
<head>
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="viewport" content="user-scalable=no, width=device-width, initial-scale=1, maximum-scale=1"/>
<meta charset="UTF-8">
<meta name="theme-color" content="#4b75ff">
<link rel="icon" type="image/png" href="img/small_icon.png"/>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/fomantic-ui/2.9.2/semantic.min.css">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+TC:wght@300;400;500;700;900&display=swap" rel="stylesheet">
<script src="https://code.jquery.com/jquery-3.6.4.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/fomantic-ui/2.9.2/semantic.min.js"></script>
<title>404 - Host Not Found</title>
<style>
h1, h2, h3, h4, h5, p, a, span, .ui.list .item{
font-family: 'Noto Sans TC', sans-serif;
font-weight: 300;
color: rgb(88, 88, 88)
}
.diagram{
background-color: #ebebeb;
padding-bottom: 2em;
}
.diagramHeader{
margin-top: 0.2em;
}
@media (max-width:512px) {
.widescreenOnly{
display: none !important;
}
.four.wide.column:not(.widescreenOnly){
width: 50% !important;
}
.ui.grid{
justify-content: center !important;
}
}
</style>
</head>
<body>
<div>
<br><br>
<div class="ui container">
<h1 style="font-size: 4rem;">Error 404</h1>
<p style="font-size: 2rem; margin-bottom: 0.4em;">Target Host Not Found</p>
<small id="timestamp"></small>
</div>
<br><br>
</div>
<div class="diagram">
<div class="ui text container">
<div class="ui grid">
<div class="four wide column widescreenOnly" align="center">
<svg version="1.1" id="client_svg" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="100%" viewBox="0 0 200 200" enable-background="new 0 0 200 200" xml:space="preserve">
<path fill="#C9CACA" d="M184.795,143.037c0,9.941-8.059,18-18,18H33.494c-9.941,0-18-8.059-18-18V44.952c0-9.941,8.059-18,18-18
h133.301c9.941,0,18,8.059,18,18V143.037z"/>
<circle fill="#FFFFFF" cx="37.39" cy="50.88" r="6.998"/>
<circle fill="#FFFFFF" cx="54.115" cy="50.88" r="6.998"/>
<path fill="#FFFFFF" d="M167.188,50.88c0,3.865-3.133,6.998-6.998,6.998H72.379c-3.865,0-6.998-3.133-6.998-6.998l0,0
c0-3.865,3.133-6.998,6.998-6.998h87.811C164.055,43.882,167.188,47.015,167.188,50.88L167.188,50.88z"/>
<rect x="31.296" y="66.907" fill="#FFFFFF" width="132.279" height="77.878"/>
<circle fill="#9BCA3E" cx="96.754" cy="144.785" r="37.574"/>
<polyline fill="none" stroke="#FFFFFF" stroke-width="8" stroke-miterlimit="10" points="108.497,133.047 93.373,153.814
82.989,143.204 "/>
</svg>
<small>You</small>
<h2 class="diagramHeader">Browser</h2>
<p style="font-weight: 500; color: #9bca3e;">Working</p>
</div>
<div class="two wide column widescreenOnly" style="margin-top: 8em; text-align: center;">
<i class="ui big grey exchange alternate icon" style="color:rgb(167, 167, 167) !important;"></i>
</div>
<div class="four wide column widescreenOnly" align="center">
<svg version="1.1" id="cloud_svg" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="100%" viewBox="0 0 200 200" enable-background="new 0 0 200 200" xml:space="preserve">
<ellipse fill="#9FA0A0" cx="46.979" cy="108.234" rx="25.399" ry="25.139"/>
<circle fill="#9FA0A0" cx="109.407" cy="100.066" r="50.314"/>
<circle fill="#9FA0A0" cx="22.733" cy="129.949" r="19.798"/>
<circle fill="#9FA0A0" cx="172.635" cy="125.337" r="24.785"/>
<path fill="#9FA0A0" d="M193.514,133.318c0,9.28-7.522,16.803-16.803,16.803H28.223c-9.281,0-16.803-7.522-16.803-16.803l0,0
c0-9.28,7.522-16.804,16.803-16.804h148.488C185.991,116.515,193.514,124.038,193.514,133.318L193.514,133.318z"/>
<circle fill="#9BCA3D" cx="100" cy="149.572" r="38.267"/>
<polyline fill="none" stroke="#FFFFFF" stroke-width="8" stroke-miterlimit="10" points="113.408,136.402 95.954,160.369
83.971,148.123 "/>
</svg>
<small>Gateway Node</small>
<h2 class="diagramHeader">Reverse Proxy</h2>
<p style="font-weight: 500; color: #9bca3e;">Working</p>
</div>
<div class="two wide column widescreenOnly" style="margin-top: 8em; text-align: center;">
<i class="ui big grey exchange alternate icon" style="color:rgb(167, 167, 167) !important;"></i>
</div>
<div class="four wide column" align="center">
<svg version="1.1" id="host_svg" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="100%" viewBox="0 0 200 200" enable-background="new 0 0 200 200" xml:space="preserve">
<path fill="#999999" d="M168.484,113.413c0,9.941,3.317,46.324-6.624,46.324H35.359c-9.941,0-5.873-39.118-5.715-46.324
l17.053-50.909c1.928-9.879,8.059-18,18-18h69.419c9.941,0,15.464,7.746,18,18L168.484,113.413z"/>
<rect x="38.068" y="118.152" fill="#FFFFFF" width="122.573" height="34.312"/>
<circle fill="#BD2426" cx="141.566" cy="135.873" r="8.014"/>
<circle fill="#BD2426" cx="99.354" cy="152.464" r="36.343"/>
<line fill="none" stroke="#FFFFFF" stroke-width="6" stroke-miterlimit="10" x1="90.5" y1="144.125" x2="107.594" y2="161.946"/>
<line fill="none" stroke="#FFFFFF" stroke-width="6" stroke-miterlimit="10" x1="90.5" y1="161.946" x2="107.594" y2="144.79"/>
</svg>
<small id="host"></small>
<h2 class="diagramHeader">Host</h2>
<p style="font-weight: 500; color: #bd2426;">Not Found</p>
</div>
</div>
</div>
</div>
<div>
<br>
<div class="ui container">
<div class="ui stackable grid">
<div class="eight wide column">
<h1>What happend?</h1>
<p>The reverse proxy target domain is not found.<br>For more information, see the error message on the reverse proxy terminal.</p>
</div>
<div class="eight wide column">
<h1>What can I do?</h1>
<h5 style="font-weight: 500;">If you are a visitor of this website: </h5>
<p>Please try again in a few minutes</p>
<h5 style="font-weight: 500;">If you are the owner of this website:</h5>
<div class="ui bulleted list">
<div class="item">Check if the proxy rules that match this hostname exists</div>
<div class="item">Visit the Reverse Proxy management interface to correct any setting errors</div>
</div>
</div>
</div>
</div>
<br>
</div>
<div class="ui divider"></div>
<div class="ui container" style="color: grey; font-size: 90%">
<p>Powered by Zoraxy</p>
</div>
<br><br>
<script>
$("#timestamp").text(new Date());
$("#host").text(location.href);
</script>
</body>
</html>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -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", DockerUXOptimizer.HandleDockerAvailable)
authRouter.HandleFunc("/api/docker/containers", DockerUXOptimizer.HandleDockerContainersList)
//Others
http.HandleFunc("/api/info/x", HandleZoraxyInfo)
authRouter.HandleFunc("/api/info/geoip", HandleGeoIpLookup)
@ -222,6 +228,8 @@ func initAPIs() {
authRouter.HandleFunc("/api/info/pprof", pprof.Index)
//If you got APIs to add, append them here
// get available docker containers
}
// Function to renders Auth related APIs

View File

@ -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
)

View File

@ -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=

View File

@ -16,6 +16,8 @@ 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/loadbalance"
"imuslab.com/zoraxy/mod/dynamicproxy/redirection"
"imuslab.com/zoraxy/mod/email"
"imuslab.com/zoraxy/mod/forwardproxy"
@ -44,6 +46,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")
@ -52,7 +55,7 @@ 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
bootTime = time.Now().Unix()
@ -66,29 +69,31 @@ var (
/*
Handler Modules
*/
sysdb *database.Database //System database
authAgent *auth.AuthAgent //Authentication agent
tlsCertManager *tlscert.Manager //TLS / SSL management
redirectTable *redirection.RuleTable //Handle special redirection rule sets
pathRuleHandler *pathrule.Handler //Handle specific path blocking or custom headers
geodbStore *geodb.Store //GeoIP database, for resolving IP into country code
accessController *access.Controller //Access controller, handle black list and white list
netstatBuffers *netstat.NetStatBuffers //Realtime graph buffers
statisticCollector *statistic.Collector //Collecting statistic from visitors
uptimeMonitor *uptime.Monitor //Uptime monitor service worker
mdnsScanner *mdns.MDNSHost //mDNS discovery services
ganManager *ganserv.NetworkManager //Global Area Network Manager
webSshManager *sshprox.Manager //Web SSH connection service
streamProxyManager *streamproxy.Manager //Stream Proxy Manager for TCP / UDP forwarding
acmeHandler *acme.ACMEHandler //Handler for ACME Certificate renew
acmeAutoRenewer *acme.AutoRenewer //Handler for ACME auto renew ticking
staticWebServer *webserv.WebServer //Static web server for hosting simple stuffs
forwardProxy *forwardproxy.Handler //HTTP Forward proxy, basically VPN for web browser
sysdb *database.Database //System database
authAgent *auth.AuthAgent //Authentication agent
tlsCertManager *tlscert.Manager //TLS / SSL management
redirectTable *redirection.RuleTable //Handle special redirection rule sets
loadbalancer *loadbalance.RouteManager //Load balancer manager to get routing targets from proxy rules
pathRuleHandler *pathrule.Handler //Handle specific path blocking or custom headers
geodbStore *geodb.Store //GeoIP database, for resolving IP into country code
accessController *access.Controller //Access controller, handle black list and white list
netstatBuffers *netstat.NetStatBuffers //Realtime graph buffers
statisticCollector *statistic.Collector //Collecting statistic from visitors
uptimeMonitor *uptime.Monitor //Uptime monitor service worker
mdnsScanner *mdns.MDNSHost //mDNS discovery services
ganManager *ganserv.NetworkManager //Global Area Network Manager
webSshManager *sshprox.Manager //Web SSH connection service
streamProxyManager *streamproxy.Manager //Stream Proxy Manager for TCP / UDP forwarding
acmeHandler *acme.ACMEHandler //Handler for ACME Certificate renew
acmeAutoRenewer *acme.AutoRenewer //Handler for ACME auto renew ticking
staticWebServer *webserv.WebServer //Static web server for hosting simple stuffs
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.

View File

@ -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))
}

View File

@ -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")
}

View File

@ -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,
}
}

View File

@ -72,6 +72,14 @@ func (h *ProxyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
return
}
// Rate Limit
if sep.RequireRateLimit {
err := h.handleRateLimitRouting(w, r, sep)
if err != nil {
return
}
}
//Validate basic auth
if sep.RequireBasicAuth {
err := h.handleBasicAuthRouting(w, r, sep)

View File

@ -1,5 +1,11 @@
package dynamicproxy
import (
"strconv"
"imuslab.com/zoraxy/mod/dynamicproxy/permissionpolicy"
)
/*
CustomHeader.go
@ -7,18 +13,19 @@ package dynamicproxy
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 {
if len(ept.UserDefinedHeaders) == 0 && ept.HSTSMaxAge == 0 && !ept.EnablePermissionPolicyHeader {
//Early return if there are no defined headers
return [][]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 +49,26 @@ 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 {
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++
}
return upstreamHeaders, downstreamHeaders
}

View File

@ -91,7 +91,6 @@ func addXForwardedForHeader(req *http.Request) {
req.Header.Set("X-Real-Ip", strings.TrimSpace(ips[0]))
}
}
}
}

View File

@ -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,6 +135,13 @@ func (router *Router) StartProxyService() error {
}
}
// Rate Limit
if sep.RequireRateLimit {
if err := router.handleRateLimit(w, r, sep); err != nil {
return
}
}
//Validate basic auth
if sep.RequireBasicAuth {
err := handleBasicAuth(w, r, sep)
@ -232,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
}

View File

@ -0,0 +1,60 @@
package loadbalance
import (
"imuslab.com/zoraxy/mod/geodb"
"imuslab.com/zoraxy/mod/info/logger"
"imuslab.com/zoraxy/mod/uptime"
)
/*
Load Balancer
Handleing load balance request for upstream destinations
*/
type BalancePolicy int
const (
BalancePolicy_RoundRobin BalancePolicy = 0 //Round robin, will ignore upstream if down
BalancePolicy_Fallback BalancePolicy = 1 //Fallback only. Will only switch to next node if the first one failed
BalancePolicy_Random BalancePolicy = 2 //Random, randomly pick one from the list that is online
BalancePolicy_GeoRegion BalancePolicy = 3 //Use the one defined for this geo-location, when down, pick the next avaible node
)
type LoadBalanceRule struct {
Upstreams []string //Reverse proxy upstream servers
LoadBalancePolicy BalancePolicy //Policy in deciding which target IP to proxy
UseRegionLock bool //If this is enabled with BalancePolicy_Geo, when the main site failed, it will not pick another node
UseStickySession bool //Use sticky session, if you are serving EU countries, make sure to add the "Do you want cookie" warning
parent *RouteManager
}
type Options struct {
Geodb *geodb.Store //GeoIP resolver for checking incoming request origin country
UptimeMonitor *uptime.Monitor //For checking if the target is online, this might be nil when the module starts
}
type RouteManager struct {
Options Options
Logger *logger.Logger
}
// Create a new load balance route manager
func NewRouteManager(options *Options, logger *logger.Logger) *RouteManager {
newManager := RouteManager{
Options: *options,
Logger: logger,
}
logger.PrintAndLog("INFO", "Load Balance Route Manager started", nil)
return &newManager
}
func (b *LoadBalanceRule) GetProxyTargetIP() {
//TODO: Implement get proxy target IP logic here
}
// Print debug message
func (m *RouteManager) debugPrint(message string, err error) {
m.Logger.PrintAndLog("LB", message, err)
}

View File

@ -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, ", ")
//Inject the new policy into the header
w.Header().Set("Permissions-Policy", policyHeaderValue)
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(headerKV[0], headerKV[1])
}

View File

@ -0,0 +1,119 @@
package dynamicproxy
import (
"errors"
"net"
"net/http"
"strings"
"sync"
"sync/atomic"
"time"
)
// IpTable is a rate limiter implementation using sync.Map with atomic int64
type RequestCountPerIpTable struct {
table sync.Map
}
// Increment the count of requests for a given IP
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 *RequestCountPerIpTable) 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 *RequestCountPerIpTable) 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 *RequestCountPerIpTable) Clear() {
t.table.Range(func(key, value interface{}) bool {
t.table.Delete(key)
return true
})
}
func (h *ProxyHandler) handleRateLimitRouting(w http.ResponseWriter, r *http.Request, pe *ProxyEndpoint) error {
err := h.Parent.handleRateLimit(w, r, pe)
if err != nil {
h.logRequest(r, false, 429, "ratelimit", pe.Domain)
}
return err
}
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 {
//Default allow passthrough on error
return nil
}
router.rateLimitCounter.Increment(ip)
if router.rateLimitCounter.Exceeded(ip, int64(pe.RateLimit)) {
w.WriteHeader(429)
return errors.New("rate limit exceeded")
}
// log.Println("Rate limit check", ip, ipTable.GetCount(ip))
return nil
}
// 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
}

View File

@ -1,7 +1,7 @@
package redirection
import (
"log"
"errors"
"net/http"
"strings"
)
@ -52,7 +52,7 @@ func (t *RuleTable) HandleRedirect(w http.ResponseWriter, r *http.Request) int {
//Invalid usage
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte("500 - Internal Server Error"))
log.Println("Target request URL do not have matching redirect rule. Check with IsRedirectable before calling HandleRedirect!")
t.log("Target request URL do not have matching redirect rule. Check with IsRedirectable before calling HandleRedirect!", errors.New("invalid usage"))
return 500
}
}

View File

@ -30,11 +30,12 @@ type RedirectRules struct {
StatusCode int //Status Code for redirection
}
func NewRuleTable(configPath string, allowRegex bool) (*RuleTable, error) {
func NewRuleTable(configPath string, allowRegex bool, logger *logger.Logger) (*RuleTable, error) {
thisRuleTable := RuleTable{
rules: sync.Map{},
configPath: configPath,
AllowRegex: allowRegex,
Logger: logger,
}
//Load all the rules from the config path
if !utils.FileExists(configPath) {
@ -67,7 +68,7 @@ func NewRuleTable(configPath string, allowRegex bool) (*RuleTable, error) {
//Map the rules into the sync map
for _, rule := range rules {
log.Println("Redirection rule added: " + rule.RedirectURL + " -> " + rule.TargetURL)
thisRuleTable.log("Redirection rule added: "+rule.RedirectURL+" -> "+rule.TargetURL, nil)
thisRuleTable.rules.Store(rule.RedirectURL, rule)
}
@ -92,7 +93,7 @@ func (t *RuleTable) AddRedirectRule(redirectURL string, destURL string, forwardP
// Create a new file for writing the JSON data
file, err := os.Create(filepath)
if err != nil {
log.Printf("Error creating file %s: %s", filepath, err)
t.log("Error creating file "+filepath, err)
return err
}
defer file.Close()
@ -100,7 +101,7 @@ func (t *RuleTable) AddRedirectRule(redirectURL string, destURL string, forwardP
// Encode the RedirectRules object to JSON and write it to the file
err = json.NewEncoder(file).Encode(newRule)
if err != nil {
log.Printf("Error encoding JSON to file %s: %s", filepath, err)
t.log("Error encoding JSON to file "+filepath, err)
return err
}
@ -125,7 +126,7 @@ func (t *RuleTable) DeleteRedirectRule(redirectURL string) error {
// Delete the file
if err := os.Remove(filepath); err != nil {
log.Printf("Error deleting file %s: %s", filepath, err)
t.log("Error deleting file "+filepath, err)
return err
}

View File

@ -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"
@ -51,8 +52,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
@ -117,13 +119,20 @@ 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
BasicAuthCredentials []*BasicAuthCredentials //Basic auth credentials
BasicAuthExceptionRules []*BasicAuthExceptionRule //Path to exclude in a basic auth enabled proxy target
// Rate Limiting
RequireRateLimit bool
RateLimit int64 // Rate limit in requests per second
//Access Control
AccessFilterUUID string //Access filter ID

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -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]

View File

@ -227,7 +227,7 @@ func getWebsiteStatus(url string) (int, error) {
client := http.Client{
Jar: jar,
Timeout: 10 * time.Second,
Timeout: 5 * time.Second,
}
req, _ := http.NewRequest("GET", url, nil)

View File

@ -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)

View File

@ -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"
)
@ -143,9 +144,12 @@ func ReverseProxtInit() {
Interval: 300, //5 minutes
MaxRecordsStore: 288, //1 day
})
//Pass the pointer of this uptime monitor into the load balancer
loadbalancer.Options.UptimeMonitor = uptimeMonitor
SystemWideLogger.Println("Uptime Monitor background service started")
}()
}
func ReverseProxyHandleOnOff(w http.ResponseWriter, r *http.Request) {
@ -221,7 +225,7 @@ func ReverseProxyHandleAddEndpoint(w http.ResponseWriter, r *http.Request) {
return
}
//Require basic auth?
// Require basic auth?
rba, _ := utils.PostPara(r, "bauth")
if rba == "" {
rba = "false"
@ -229,6 +233,29 @@ func ReverseProxyHandleAddEndpoint(w http.ResponseWriter, r *http.Request) {
requireBasicAuth := (rba == "true")
// Require Rate Limiting?
requireRateLimit := false
proxyRateLimit := 1000
requireRateLimit, err = utils.PostBool(r, "rate")
if err != nil {
requireRateLimit = false
}
if requireRateLimit {
proxyRateLimit, err = utils.PostInt(r, "ratenum")
if err != nil {
proxyRateLimit = 0
}
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 == "" {
@ -309,6 +336,9 @@ func ReverseProxyHandleAddEndpoint(w http.ResponseWriter, r *http.Request) {
BasicAuthExceptionRules: []*dynamicproxy.BasicAuthExceptionRule{},
DefaultSiteOption: 0,
DefaultSiteValue: "",
// Rate Limit
RequireRateLimit: requireRateLimit,
RateLimit: int64(proxyRateLimit),
}
preparedEndpoint, err := dynamicProxyRouter.PrepareProxyRoute(&thisProxyEndpoint)
@ -430,6 +460,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 == "" {
@ -451,6 +501,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
@ -1187,3 +1239,123 @@ 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 {
//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)
}

View File

@ -4,6 +4,7 @@ import (
"log"
"net/http"
"os"
"runtime"
"strconv"
"strings"
"time"
@ -12,6 +13,8 @@ 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/loadbalance"
"imuslab.com/zoraxy/mod/dynamicproxy/redirection"
"imuslab.com/zoraxy/mod/forwardproxy"
"imuslab.com/zoraxy/mod/ganserv"
@ -73,15 +76,22 @@ func startupSequence() {
panic(err)
}
//Create a system wide logger
l, err := logger.NewLogger("zr", "./log", *logOutputToFile)
if err == nil {
SystemWideLogger = l
} else {
panic(err)
}
//Create a redirection rule table
db.NewTable("redirect")
redirectAllowRegexp := false
db.Read("redirect", "regex", &redirectAllowRegexp)
redirectTable, err = redirection.NewRuleTable("./conf/redirect", redirectAllowRegexp)
redirectTable, err = redirection.NewRuleTable("./conf/redirect", redirectAllowRegexp, SystemWideLogger)
if err != nil {
panic(err)
}
redirectTable.Logger = SystemWideLogger
//Create a geodb store
geodbStore, err = geodb.NewGeoDb(sysdb, &geodb.StoreOptions{
@ -92,6 +102,11 @@ func startupSequence() {
panic(err)
}
//Create a load balance route manager
loadbalancer = loadbalance.NewRouteManager(&loadbalance.Options{
Geodb: geodbStore,
}, SystemWideLogger)
//Create the access controller
accessController, err = access.NewAccessController(&access.Options{
Database: sysdb,
@ -110,14 +125,6 @@ func startupSequence() {
panic(err)
}
//Create a system wide logger
l, err := logger.NewLogger("zr", "./log", *logOutputToFile)
if err == nil {
SystemWideLogger = l
} else {
panic(err)
}
//Start the static web server
staticWebServer = webserv.NewWebServer(&webserv.WebServerOptions{
Sysdb: sysdb,
@ -269,6 +276,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

View File

@ -19,7 +19,7 @@
<th>Host</th>
<th>Destination</th>
<th>Virtual Directory</th>
<th>Basic Auth</th>
<th style="max-width: 300px;">Advanced Settings</th>
<th class="no-sort" style="min-width:150px;">Actions</th>
</tr>
</thead>
@ -78,7 +78,7 @@
vdList += `</div>`;
if (subd.VirtualDirectories.length == 0){
vdList = `<small style="opacity: 0.3; pointer-events: none; user-select: none;"><i class="check icon"></i> No Virtual Directory</small>`;
vdList = `<small style="opacity: 0.3; pointer-events: none; user-select: none;">No Virtual Directory</small>`;
}
let enableChecked = "checked";
@ -104,8 +104,11 @@
</td>
<td data-label="" editable="true" datatype="domain">${subd.Domain} ${tlsIcon}</td>
<td data-label="" editable="true" datatype="vdir">${vdList}</td>
<td data-label="" editable="true" datatype="basicauth">
${subd.RequireBasicAuth?`<i class="ui green check icon"></i>`:`<i class="ui grey remove icon"></i>`}
<td data-label="" editable="true" datatype="advanced" style="width: 350px;">
${subd.RequireBasicAuth?`<i class="ui green check icon"></i> Basic Auth`:``}
${subd.RequireBasicAuth && subd.RequireRateLimit?"<br>":""}
${subd.RequireRateLimit?`<i class="ui green check icon"></i> Rate Limit @ ${subd.RateLimit} req/s`:``}
${!subd.RequireBasicAuth && !subd.RequireRateLimit?`<small style="opacity: 0.3; pointer-events: none; user-select: none;">No Special Settings</small>`:""}
</td>
<td class="center aligned ignoremw" editable="true" datatype="action" data-label="">
<div class="ui toggle tiny fitted checkbox" style="margin-bottom: -0.5em; margin-right: 0.4em;" title="Enable / Disable Rule">
@ -243,9 +246,9 @@
input = `
<div class="ui mini fluid input">
<input type="text" class="Domain" value="${domain}">
<input type="text" class="Domain" onchange="cleanProxyTargetValue(this)" value="${domain}">
</div>
<div class="ui checkbox" style="margin-top: 0.4em;">
<div class="ui checkbox" style="margin-top: 0.6em;">
<input type="checkbox" class="RequireTLS" ${tls}>
<label>Require TLS<br>
<small>Proxy target require HTTPS connection</small></label>
@ -254,7 +257,8 @@
<input type="checkbox" class="SkipCertValidations" ${checkstate}>
<label>Skip Verification<br>
<small>Check this if proxy target is using self signed certificates</small></label>
</div>
</div><br>
<!-- <button class="ui basic compact tiny button" style="margin-left: 0.4em; margin-top: 0.4em;" onclick="editLoadBalanceOptions('${uuid}');"><i class="purple server icon"></i> Load Balance</button> -->
`;
column.empty().append(input);
}else if (datatype == "vdir"){
@ -263,11 +267,11 @@
<i class="ui yellow folder icon"></i> Edit Virtual Directories
</button>`);
}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;
@ -276,16 +280,35 @@
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(`<div class="ui checkbox" style="margin-top: 0.4em;">
<input type="checkbox" class="RequireBasicAuth" ${checkstate}>
<input type="checkbox" class="RequireBasicAuth" ${basicAuthCheckstate}>
<label>Require Basic Auth</label>
</div>
<button class="ui basic tiny button" style="margin-left: 0.4em; margin-top: 0.4em;" onclick="editBasicAuthCredentials('${uuid}');"><i class="ui blue user circle icon"></i> Edit Credentials</button>
<br>
<button class="ui basic compact tiny button" style="margin-left: 0.4em; margin-top: 0.4em;" onclick="editBasicAuthCredentials('${uuid}');"><i class="ui blue user circle icon"></i> Edit Credentials</button>
<br>
<button class="ui basic compact tiny button" style="margin-left: 0.4em; margin-top: 0.4em;" onclick="editCustomHeaders('${uuid}');"><i class="heading icon"></i> Custom Headers</button>
<div class="ui basic advance segment" style="padding: 0.4em !important; border-radius: 0.4em;">
<div class="ui endpointAdvanceConfig accordion" style="padding-right: 0.6em;">
<div class="title">
<i class="dropdown icon"></i>
Advance Configs
Security Options
</div>
<div class="content">
<div class="ui checkbox" style="margin-top: 0.4em;">
@ -294,13 +317,34 @@
<small>Check this to allow cross-origin websocket requests</small></label>
</div>
<br>
<button class="ui basic compact tiny button" style="margin-left: 0.4em; margin-top: 0.4em;" onclick="editCustomHeaders('${uuid}');"><i class="heading icon"></i> Custom Headers</button>
<!-- <button class="ui basic compact tiny button" style="margin-left: 0.4em; margin-top: 0.4em;" onclick="editLoadBalanceOptions('${uuid}');"><i class="blue server icon"></i> Load Balance</button> -->
<div class="ui checkbox" style="margin-top: 0.4em;">
<input type="checkbox" onchange="handleToggleRateLimitInput();" class="RequireRateLimit" ${rateLimitCheckState}>
<label>Require Rate Limit<br>
<small>Check this to enable rate limit on this inbound hostname</small></label>
</div><br>
<div class="ui mini right labeled fluid input ${rateLimitDisableState}" style="margin-top: 0.4em;">
<input type="number" class="RateLimit" value="${rateLimit}" min="1" >
<label class="ui basic label">
req / sec / IP
</label>
</div>
</div>
</div>
<div>
`);
} else if (datatype == "ratelimit"){
column.empty().append(`
<div class="ui checkbox" style="margin-top: 0.4em;">
<input type="checkbox" class="RequireRateLimit" ${checkstate}>
<label>Require Rate Limit</label>
</div>
<div class="ui mini fluid input">
<input type="number" class="RateLimit" value="${rateLimit}" placeholder="100" min="1" max="1000" >
</div>
`);
}else if (datatype == 'action'){
column.empty().append(`
<button title="Save" onclick="saveProxyInlineEdit('${uuid.hexEncode()}');" class="ui basic small icon circular button inlineEditActionBtn"><i class="ui green save icon"></i></button>
@ -331,6 +375,17 @@
$("#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");
@ -348,6 +403,8 @@
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)
@ -364,6 +421,8 @@
"tlsval": skipCertValidations,
"bpwsorg" : bypassWebsocketOrigin,
"bauth" :requireBasicAuth,
"rate" :requireRateLimit,
"ratenum" :rateLimit,
},
success: function(data){
if (data.error !== undefined){
@ -375,6 +434,21 @@
}
})
}
//Clearn the proxy target value, make sure user do not enter http:// or https://
//and auto select TLS checkbox if https:// exists
function cleanProxyTargetValue(input){
let targetDomain = $(input).val().trim();
if (targetDomain.startsWith("http://")){
targetDomain = targetDomain.substr(7);
$(input).val(targetDomain);
$("#httpProxyList input.RequireTLS").parent().checkbox("set unchecked");
}else if (targetDomain.startsWith("https://")){
targetDomain = targetDomain.substr(8);
$(input).val(targetDomain);
$("#httpProxyList input.RequireTLS").parent().checkbox("set checked");
}
}
/* button events */
function editBasicAuthCredentials(uuid){
@ -406,6 +480,7 @@
$("#vdirBaseRoutingRule").parent().dropdown("set selected", uuid);
}
//Open the custom header editor
function editCustomHeaders(uuid){
let payload = encodeURIComponent(JSON.stringify({
ept: "host",
@ -414,6 +489,15 @@
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;
@ -438,10 +522,6 @@
})
}
/* Access List handling */
//Bind on tab switch events
tabSwitchEventBind["httprp"] = function(){
listProxyEndpoints();

View File

@ -26,9 +26,12 @@
</div>
<div class="field">
<label>Target IP Address or Domain Name with port</label>
<input type="text" id="proxyDomain" onchange="autoCheckTls(this.value);">
<input type="text" id="proxyDomain" onchange="autoCheckTls(this.value);">
<small>E.g. 192.168.0.101:8000 or example.com</small>
</div>
<div class="field dockerOptimizations" style="display:none;">
<button style="margin-top: -2em;" class="ui basic small button" onclick="openDockerContainersList();"><i class="blue docker icon"></i> Pick from Docker Containers</button>
</div>
<div class="field">
<div class="ui checkbox">
<input type="checkbox" id="reqTls">
@ -73,6 +76,22 @@
<label>Allow plain HTTP access<br><small>Allow this subdomain to be connected without TLS (Require HTTP server enabled on port 80)</small></label>
</div>
</div>
<div class="field">
<div class="ui checkbox">
<input type="checkbox" id="requireRateLimit">
<label>Require Rate Limit<br><small>This proxy endpoint will be rate limited.</small></label>
</div>
</div>
<div class="field">
<label>Rate Limit</label>
<div class="ui fluid right labeled input">
<input type="number" id="proxyRateLimit" placeholder="100" min="1" max="1000" value="100">
<div class="ui basic label">
req / sec / IP
</div>
</div>
<small>Return a 429 error code if request rate exceed the rate limit.</small>
</div>
<div class="field">
<div class="ui checkbox">
<input type="checkbox" id="requireBasicAuth">
@ -147,6 +166,8 @@
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 +197,8 @@
bpwsorg: skipWebSocketOriginCheck,
bypassGlobalTLS: bypassGlobalTLS,
bauth: requireBasicAuth,
rate: requireRateLimit,
ratenum: proxyRateLimit,
cred: JSON.stringify(credentials),
access: accessRuleToUse,
},
@ -264,6 +287,16 @@
}
$("#requireBasicAuth").on('change', toggleBasicAuth);
toggleBasicAuth();
function toggleRateLimit() {
if ($("#requireRateLimit").parent().checkbox("is checked")) {
$("#proxyRateLimit").parent().parent().removeClass("disabled");
} else {
$("#proxyRateLimit").parent().parent().addClass("disabled");
}
}
$("#requireRateLimit").on('change', toggleRateLimit);
toggleRateLimit();
/*
@ -397,9 +430,30 @@
initNewProxyRuleAccessDropdownList();
}
$(document).ready(function(){
$("#advanceProxyRules").accordion();
$("#newProxyRuleAccessFilter").parent().dropdown();
});
/* Docker Optimizations */
function initDockerUXOptimizations(){
$.get("/api/docker/available", function(dockerAvailable){
if (dockerAvailable){
$(".dockerOptimizations").show();
}else{
$(".dockerOptimizations").hide();
}
});
}
initDockerUXOptimizations();
function openDockerContainersList(){
showSideWrapper('snippet/dockerContainersList.html');
}
function addContainerItem(item) {
$('#rootname').val(item.name);
$('#proxyDomain').val(`${item.ip}:${item.port}`)
hideSideWrapper(true);
}
/* UI Element Initialization */
$("#advanceProxyRules").accordion();
$("#newProxyRuleAccessFilter").parent().dropdown();
</script>

View File

@ -74,7 +74,7 @@
<input type="text" id="incomingPort" placeholder="Incoming Port" value="80">
<button class="ui green notloopbackOnly button" style="background: linear-gradient(60deg, #27e7ff, #00ca52);" onclick="handlePortChange();"><i class="ui checkmark icon"></i> Apply</button>
</div>
<br><br>
<br>
<div id="tls" class="ui toggle notloopbackOnly checkbox">
<input type="checkbox">
<label>Use TLS to serve proxy request</label>

View File

@ -123,6 +123,7 @@
$('#streamProxyForm input, #streamProxyForm select').val('');
$('#streamProxyForm select').dropdown('clear');
$("#streamProxyForm input[name=timeout]").val(10);
$("#streamProxyForm .toggle.checkbox").checkbox("set unchecked");
}
function cancelStreamProxyEdit(event=undefined) {
@ -305,6 +306,7 @@
}
initProxyConfigList();
cancelStreamProxyEdit();
clearStreamProxyAddEditForm();
},
error: function() {

View File

@ -14,7 +14,7 @@
<script src="https://cdnjs.cloudflare.com/ajax/libs/fomantic-ui/2.9.2/semantic.min.js"></script>
<title>404 - Host Not Found</title>
<style>
h1, h2, h3, h4, h5, p, a, span{
h1, h2, h3, h4, h5, p, a, span, .ui.list .item{
font-family: 'Noto Sans TC', sans-serif;
font-weight: 300;
color: rgb(88, 88, 88)
@ -22,9 +22,6 @@
.diagram{
background-color: #ebebeb;
box-shadow:
inset 0px 11px 8px -10px #CCC,
inset 0px -11px 8px -10px #CCC;
padding-bottom: 2em;
}
@ -135,7 +132,7 @@
<p>Please try again in a few minutes</p>
<h5 style="font-weight: 500;">If you are the owner of this website:</h5>
<div class="ui bulleted list">
<div class="item">Check if the target web server is online</div>
<div class="item">Check if the proxy rules that match this hostname exists</div>
<div class="item">Visit the Reverse Proxy management interface to correct any setting errors</div>
</div>
</div>

View File

@ -114,7 +114,7 @@
min-height:40px;
}
}
</style>
</head>
<body>

View File

@ -5,6 +5,22 @@
<link rel="stylesheet" href="../script/semantic/semantic.min.css">
<script src="../script/jquery-3.6.0.min.js"></script>
<script src="../script/semantic/semantic.min.js"></script>
<style>
.ui.tabular.menu .item.narrowpadding{
padding: 0.6em !important;
margin: 0.15em !important;
}
#permissionPolicyEditor.disabled{
opacity: 0.4;
pointer-events: none;
user-select: none;
}
#permissionPolicyEditor .experimental{
background-color: rgb(241, 241, 241);
}
</style>
</head>
<body>
<br>
@ -16,52 +32,93 @@
</div>
</div>
<div class="ui divider"></div>
<p>You can define custom headers to be sent
together with the client request to the backend server in
this reverse proxy endpoint / host.</p>
<table class="ui very basic compacted unstackable celled table">
<thead>
<tr>
<th>Key</th>
<th>Value</th>
<th>Remove</th>
</tr></thead>
<tbody id="headerTable">
<tr>
<td colspan="3"><i class="ui green circle check icon"></i> No Additonal Header</td>
</tr>
</tbody>
</table>
<div class="ui divider"></div>
<h4>Edit Custom Header</h4>
<p>Add or remove custom header(s) over this proxy target</p>
<div class="scrolling content ui form">
<div class="five small fields credentialEntry">
<div class="field" align="center">
<button id="toOriginButton" title="Downstream to Upstream" class="ui circular basic active button">Zoraxy <i class="angle double right blue icon" style="margin-right: 0.4em;"></i> Origin</button>
<button id="toClientButton" title="Upstream to Downstream" class="ui circular basic button">Client <i class="angle double left orange icon" style="margin-left: 0.4em;"></i> Zoraxy</button>
<div class="ui small pointing secondary menu">
<a class="item active narrowpadding" data-tab="customheaders">Custom Headers</a>
<a class="item narrowpadding" data-tab="security">Security Headers</a>
</div>
<div class="ui tab basic segment active" data-tab="customheaders">
<table class="ui very basic compacted unstackable celled table">
<thead>
<tr>
<th>Key</th>
<th>Value</th>
<th>Remove</th>
</tr></thead>
<tbody id="headerTable">
<tr>
<td colspan="3"><i class="ui green circle check icon"></i> No Additonal Header</td>
</tr>
</tbody>
</table>
<p>
<i class="angle double right blue icon"></i> Sent additional custom headers to origin server <br>
<i class="angle double left orange icon"></i> Inject custom headers into origin server responses
</p>
<div class="ui divider"></div>
<h4>Edit Custom Header</h4>
<p>Add or remove custom header(s) over this proxy target</p>
<div class="scrolling content ui form">
<div class="five small fields credentialEntry">
<div class="field" align="center">
<button id="toOriginButton" style="margin-top: 0.6em;" title="Downstream to Upstream" class="ui circular basic active button">Zoraxy <i class="angle double right blue icon" style="margin-right: 0.4em;"></i> Origin</button>
<button id="toClientButton" style="margin-top: 0.6em;" title="Upstream to Downstream" class="ui circular basic button">Client <i class="angle double left orange icon" style="margin-left: 0.4em;"></i> Zoraxy</button>
</div>
<div class="field" align="center">
<button id="headerModeAdd" style="margin-top: 0.6em;" class="ui circular basic active button"><i class="ui green circle add icon"></i> Add Header</button>
<button id="headerModeRemove" style="margin-top: 0.6em;" class="ui circular basic button"><i class="ui red circle times icon"></i> Remove Header</button>
</div>
<div class="field">
<label>Header Key</label>
<input id="headerName" type="text" placeholder="X-Custom-Header" autocomplete="off">
<small>The header key is <b>NOT</b> case sensitive</small>
</div>
<div class="field">
<label>Header Value</label>
<input id="headerValue" type="text" placeholder="value1,value2,value3" autocomplete="off">
</div>
<div class="field" >
<button class="ui basic button" onclick="addCustomHeader();"><i class="green add icon"></i> Add Header Rewrite Rule</button>
</div>
<div class="ui divider"></div>
</div>
<div class="field" align="center">
<button id="headerModeAdd" class="ui circular basic active button"><i class="ui green circle add icon"></i> Add Header</button>
<button id="headerModeRemove" class="ui circular basic button"><i class="ui red circle times icon"></i> Remove Header</button>
</div>
<div class="field">
<label>Header Key</label>
<input id="headerName" type="text" placeholder="X-Custom-Header" autocomplete="off">
<small>The header key is <b>NOT</b> case sensitive</small>
</div>
<div class="field">
<label>Header Value</label>
<input id="headerValue" type="text" placeholder="value1,value2,value3" autocomplete="off">
</div>
<div class="field" >
<button class="ui basic button" onclick="addCustomHeader();"><i class="green add icon"></i> Add Header Rewrite Rule</button>
</div>
<div class="ui divider"></div>
</div>
</div>
<div class="ui divider"></div>
<div class="ui tab basic segment" data-tab="security">
<h4>HTTP Strict Transport Security</h4>
<p>Force future attempts to access this site to only use HTTPS</p>
<div class="ui toggle checkbox">
<input type="checkbox" id="enableHSTS" name="enableHSTS">
<label>Enable HSTS<br>
<small>HSTS header will be automatically ignored if the site is accessed using HTTP</small></label>
</div>
<div class="ui divider"></div>
<h4>Permission Policy</h4>
<p>Explicitly declare what functionality can and cannot be used on this website. </p>
<div class="ui toggle checkbox" style="margin-top: 0.6em;">
<input type="checkbox" id="enablePP" name="enablePP">
<label>Enable Permission Policy<br>
<small>Enable Permission-Policy header with all allowed state.</small></label>
</div>
<div style="margin-top: 1em;" id="permissionPolicyEditor">
<table class="ui celled unstackable very compact table">
<thead>
<tr><th>Feature</th>
<th>Enabled</th>
<th>Allow All (*)</th>
<th>Self Only (self)</th>
</tr></thead>
<tbody id="permissionPolicyEditTable">
<tr>
<td colspan="4"><i class="ui loading spinner icon"></i> Generating</td>
</tr>
</tbody>
</table>
</div>
<small><i class="ui yellow exclamation triangle icon"></i> Grey out fields are non-standard permission policies</small>
<br><br>
<button class="ui basic button" onclick="savePermissionPolicy();"><i class="green save icon"></i> Save</button>
</div>
<div class="field" >
<button class="ui basic button" style="float: right;" onclick="closeThisWrapper();">Close</button>
</div>
@ -70,6 +127,9 @@
<br><br><br><br>
<script>
$('.menu .item').tab();
let permissionPolicyKeys = [];
let editingEndpoint = {};
if (window.location.hash.length > 1){
let payloadHash = window.location.hash.substr(1);
@ -239,6 +299,218 @@
});
}
listCustomHeaders();
//Start HSTS state
function initHSTSState(){
$.get("/api/proxy/header/handleHSTS?domain=" + editingEndpoint.ep, function(data){
if (data == 0){
//HSTS disabled
$("#enableHSTS").parent().checkbox("set unchecked");
}else{
//HSTS enabled
$("#enableHSTS").parent().checkbox("set checked");
}
/* Bind events to toggles */
$("#enableHSTS").on("change", function(){
let HSTSEnabled = $("#enableHSTS")[0].checked;
$.ajax({
url: "/api/proxy/header/handleHSTS",
method: "POST",
data: {
"domain": editingEndpoint.ep,
"maxage": 31536000
},
success: function(data){
if (data.error != undefined){
parent.msgbox(data.error, false);
}else{
parent.msgbox(`HSTS ${HSTSEnabled?"Enabled":"Disabled"}`);
}
}
})
});
});
}
initHSTSState();
//Return true if this is an proposed permission policy feature
function isExperimentalFeature(header) {
// List of experimental features
const experimentalFeatures = [
"clipboard-read",
"clipboard-write",
"gamepad",
"speaker-selection",
"conversion-measurement",
"focus-without-user-activation",
"hid",
"idle-detection",
"interest-cohort",
"serial",
"sync-script",
"trust-token-redemption",
"unload",
"window-placement",
"vertical-scroll"
];
header = header.replaceAll("_","-");
// Check if the header is in the list of experimental features
return experimentalFeatures.includes(header);
}
/* List permission policy header from server */
function initPermissionPolicy(){
$.get("/api/proxy/header/handlePermissionPolicy?domain=" + editingEndpoint.ep, function(data){
if (data.error != undefined){
console.log(data.error);
$("#enablePP").parent().addClass('disabled');
return;
}
//Set checkbox initial state
if (data.PPEnabled){
$("#enablePP").parent().checkbox("set checked");
$("#permissionPolicyEditor").removeClass("disabled");
}else{
$("#enablePP").parent().checkbox("set unchecked");
$("#permissionPolicyEditor").addClass("disabled");
}
//Bind toggle change events
$("#enablePP").on("change", function(evt){
//Set checkbox state
let ppEnabled = $("#enablePP")[0].checked;
if (ppEnabled){
$("#permissionPolicyEditor").removeClass("disabled");
}else{
$("#permissionPolicyEditor").addClass("disabled");
}
$.ajax({
url: "/api/proxy/header/handlePermissionPolicy",
method: "POST",
data: {
enable: ppEnabled,
domain: editingEndpoint.ep
},
success: function(data){
if (data.error != undefined){
parent.msgbox(data.error, false);
}else{
parent.msgbox(`Permission Policy ${ppEnabled?"Enabled":"Disabled"}`)
}
}
})
});
//Render the table to list
$("#permissionPolicyEditTable").html("");
for (const [key, value] of Object.entries(data.CurrentPolicy)) {
let allowall = "";
let allowself = "";
let enabled = "checked";
if (value.length == 1 && value[0] == "*"){
allowall = "checked";
}else if (value.length == 1 && value[0] == "self"){
allowself = "checked";
}
if (value.length == 0){
enabled = ""
allowall = "checked"; //default state
}
let isExperimental = isExperimentalFeature(key);
$("#permissionPolicyEditTable").append(`<tr class="${isExperimental?"experimental":""}">
<td>${key.replaceAll("_","-")}</td>
<td>
<div class="ui checkbox">
<input class="enabled" type="checkbox" name="${key}" ${enabled}>
<label></label>
</div>
</td>
<td>
<div class="ui radio checkbox targetinput ${!enabled?"disabled":""}">
<input type="radio" value="all" name="${key}-target" ${allowall} ${!enabled?"disabled=\"\"":""}>
<label></label>
</div>
</td>
<td>
<div class="ui radio checkbox targetinput ${!enabled?"disabled":""}">
<input type="radio" value="self" name="${key}-target" ${allowself} ${!enabled?"disabled=\"\"":""}>
<label></label>
</div>
</td>
</tr>`);
permissionPolicyKeys.push(key);
}
$("#permissionPolicyEditTable .enabled").on("change", function(){
console.log($(this)[0].checked);
let fieldGroup = $(this).parent().parent().parent();
if ($(this)[0].checked){
fieldGroup.find(".targetinput").removeClass("disabled");
fieldGroup.find("input[type=radio]").prop('disabled', false);
}else{
fieldGroup.find(".targetinput").addClass("disabled");
fieldGroup.find("input[type=radio]").prop('disabled', true);
}
})
});
}
initPermissionPolicy();
//Generate the permission policy object for sending to backend
function generatePermissionPolicyObject(){
function getStructuredFieldValueFromDOM(fieldKey){
var policyTarget = $(`#permissionPolicyEditTable input[name="${fieldKey}-target"]:checked`).val();
var isPolicyEnabled = $(`#permissionPolicyEditTable input[name="${fieldKey}"]`).is(':checked');
if (!isPolicyEnabled){
return [];
}
if (policyTarget == "all"){
//Rewrite all to correct syntax
policyTarget = "*";
}
return [policyTarget];
}
let newPermissionPolicyKeyValuePair = {};
permissionPolicyKeys.forEach(policyKey => {
newPermissionPolicyKeyValuePair[policyKey] = getStructuredFieldValueFromDOM(policyKey);
});
console.log(newPermissionPolicyKeyValuePair);
return newPermissionPolicyKeyValuePair;
}
//Handle saving of permission policy
function savePermissionPolicy(){
let permissionPolicy = generatePermissionPolicyObject();
let domain = editingEndpoint.ep;
$.ajax({
url: "/api/proxy/header/handlePermissionPolicy",
method: "PUT",
data: {
"domain": domain,
"pp": JSON.stringify(permissionPolicy),
},
success: function(data){
if (data.error != undefined){
parent.msgbox(data.error, false);
}else{
parent.msgbox("Permission Policy Updated");
}
}
})
}
</script>
</body>
</html>

View File

@ -0,0 +1,139 @@
<!DOCTYPE html>
<html>
<head>
<!-- Notes: This should be open in its original path-->
<link rel="stylesheet" href="../script/semantic/semantic.min.css" />
<script src="../script/jquery-3.6.0.min.js"></script>
<script src="../script/semantic/semantic.min.js"></script>
</head>
<body>
<br />
<div class="ui container">
<div class="ui header">
<div class="content">
List of Docker Containers
<div class="sub header">
Below is a list of all detected Docker containers currently running
on the system.
</div>
</div>
</div>
<div id="containersList" class="ui middle aligned divided list active">
<div class="ui loader active"></div>
</div>
<div class="ui horizontal divider"></div>
<div id="containersAddedListHeader" class="ui header" hidden>
Already added containers:
</div>
<div
id="containersAddedList"
class="ui middle aligned divided list"
></div>
</div>
<script>
const lines = {};
const linesAded = [];
function getDockerContainers() {
const hostRequest = $.get("/api/proxy/list?type=host");
const dockerRequest = $.get("/api/docker/containers");
// Wait for both requests to complete
Promise.all([hostRequest, dockerRequest])
.then(([hostData, dockerData]) => {
if (
dockerData.error === undefined &&
hostData.error === undefined
) {
const { containers, network } = dockerData;
const bridge = network.find(({ Name }) => Name === "bridge");
const {
IPAM: {
Config: [{ Gateway: gateway }],
},
} = bridge;
const existedDomains = hostData.map(({ Domain }) => Domain);
for (const container of containers) {
const {
Ports,
Names: [name],
} = container;
for (const portObject of Ports.filter(
({ IP: ip }) => ip === "::"
)) {
const { IP: ip, PublicPort: port } = portObject;
const key = `${name}-${port}`;
if (
existedDomains.some((item) => item === `${gateway}:${port}`)
) {
linesAded.push({
name: name.replace(/^\//, ""),
ip: gateway,
port,
});
} else if (!lines[key]) {
lines[key] = {
name: name.replace(/^\//, ""),
ip: gateway,
port,
};
}
}
}
for (const [key, line] of Object.entries(lines)) {
$("#containersList").append(
`<div class="item">
<div class="right floated content">
<div class="ui button" onclick="addContainerItem('${key}');">Add</div>
</div>
<div class="content">
<div class="header">${line.name}</div>
<div class="description">
${line.ip}:${line.port}
</div>
</div>`
);
}
for (const line of linesAded) {
$("#containersAddedList").append(
`<div class="item">
<div class="content">
<div class="header">${line.name}</div>
<div class="description">
${line.ip}:${line.port}
</div>
</div>`
);
}
linesAded.length &&
$("#containersAddedListHeader").removeAttr("hidden");
$("#containersList .loader").removeClass("active");
} else {
parent.msgbox(
`Error loading data: ${dockerData.error || hostData.error}`,
false
);
$("#containersList").html(`<div class="ui basic segment"><i class="ui red times icon"></i> ${dockerData.error || hostData.error}</div>`);
}
})
.catch((error) => {
console.log(error.responseText);
parent.msgbox("Error loading data: " + error.message, false);
});
}
getDockerContainers();
function addContainerItem(item) {
if (lines[item]) {
parent.addContainerItem(lines[item]);
}
}
</script>
</body>
</html>

View File

@ -0,0 +1,49 @@
<!DOCTYPE html>
<html>
<head>
<!-- Notes: This should be open in its original path-->
<link rel="stylesheet" href="../script/semantic/semantic.min.css">
<script src="../script/jquery-3.6.0.min.js"></script>
<script src="../script/semantic/semantic.min.js"></script>
</head>
<body>
<br>
<div class="ui container">
<div class="ui header">
<div class="content">
Load Balance
<div class="sub header epname"></div>
</div>
</div>
<div class="ui divider"></div>
<div class="ui divider"></div>
<div class="field" >
<button class="ui basic button" style="float: right;" onclick="closeThisWrapper();">Close</button>
</div>
</div>
<br><br><br><br>
</div>
<script>
let aliasList = [];
let editingEndpoint = {};
if (window.location.hash.length > 1){
let payloadHash = window.location.hash.substr(1);
try{
payloadHash = JSON.parse(decodeURIComponent(payloadHash));
$(".epname").text(payloadHash.ep);
editingEndpoint = payloadHash;
}catch(ex){
console.log("Unable to load endpoint data from hash")
}
}
function closeThisWrapper(){
parent.hideSideWrapper(true);
}
</script>
</body>
</html>