34 Commits
3.0.8 ... 3.1.0

Author SHA1 Message Date
0a734e0bd3 Merge pull request #275 from tobychui/v3.1.0
v3.1.0 Update
2024-07-31 22:39:01 +08:00
f4fa92635c Added example go.mod files for windows 7 2024-07-31 22:35:25 +08:00
7d5151bb00 Add EarlyRenew flag to Dockerfile 2024-07-31 10:21:57 -04:00
54475e4b99 Fixed #271
- Fixed implementation in geoip resolver trie tree
2024-07-31 21:57:59 +08:00
6ac16caf37 Update main.go
- Updated main to internal web fs
2024-07-31 16:15:59 +08:00
97502db607 Update extract.go
- Updated lego config extractor
2024-07-31 16:12:28 +08:00
0747cf4b0f Fixed gandi DNS bug
- Fixed gandi DNS challenge extra input field
- Updated geoip list
2024-07-31 16:11:50 +08:00
94483acc92 Added log viewer filter
+ Added filter to log viewer #243
+ Added auto log refresh
2024-07-31 16:01:49 +08:00
7626857c02 Updated acme dns list
- Updated acme dns configs
- Updated dns propagation timeout from default (2min) to 5 minutes
2024-07-29 12:55:37 +08:00
0f772a715b Update extract.go
Updared extractor to compatible with later version of lego
2024-07-29 12:50:57 +08:00
fd1439f746 Fixed csrf token error in cert upload ui
- Fixed csrf token error in cert upload interface
- Added system wide logger into tls cert manager
2024-07-29 12:28:21 +08:00
ca37bfbfa6 Fixed #106
- Added experimental proxmox fixes
- Fixed upstream error resp code not logging bug
2024-07-27 17:33:41 +08:00
c1e16d55ab Optimized csrf mux
- Forced same site to lax mode for better browser compatibility
- Set zoraxy-csrf as cookie name
2024-07-24 22:47:49 +08:00
f595da92a1 Fixed #267
- Added csrf middleware to management portal mux
- Added csrf token to all html templates
- Added csrf validation to all endpoints
- Optimized some old endpoints implementation
2024-07-24 21:58:44 +08:00
02ff288280 Doc: Note about PORT usage for Docker run and compose 2024-07-22 14:03:10 -04:00
b1c5bc2963 Fixed #255
- Added host header manual overwrite feature
- Added toggle for automatic hop-by-hop header removing
2024-07-21 17:06:09 +08:00
d3dbbf9052 Merge branch 'v3.1.0' of https://github.com/tobychui/zoraxy into v3.1.0 2024-07-21 15:11:27 +08:00
f4a5c905e7 Fixed #256
- Added startup paramter to change the early renew days of certificates
- Changed the default early renew days of certificates from 14 days to 30 days
- Fixed vdir update not updating uptime monitor bug
2024-07-21 15:11:13 +08:00
245379e91f Fixed #254
- Added uptime cleaning logic to update function
2024-07-19 10:21:26 +08:00
955a2232df Update Makefile
- Fixed bug in CICD pipeline
2024-07-18 18:50:45 +08:00
7eb7ae7ced Merge pull request #251 from PassiveLemon/docker-timezone
Doc: Document on how to use host time in the container
2024-07-16 23:12:14 +08:00
3aa0f2d914 Target latest alpine image 2024-07-16 11:07:47 -04:00
39b0c8c674 Doc: Document on how to use host time in the container 2024-07-16 10:56:12 -04:00
bddeae8365 Fixed manual renew certificate bug
- Fixed manual renew certificate bug in wildcard certs
- Updated version no
2024-07-16 22:08:51 +08:00
8e0e9531e7 Merge pull request #250 from Morethanevil/main
Update CHANGELOG.md
2024-07-16 20:35:04 +08:00
6ff22865e0 Update CHANGELOG.md 2024-07-16 14:26:19 +02:00
0828fd1958 Update update.go
Fixed bug in skip version upgrade
2024-07-16 15:14:49 +08:00
82f84470f7 Merge pull request #246 from tobychui/3.0.9
Update 3.0.9
2024-07-16 13:15:02 +08:00
cf9a05f130 Updated v3.0.9
- Added certificate download
- Updated netcup timeout value
- Updated geoip db
- Removed debug print from log viewer
- Upgraded netstat log printing to new log formatter
- Improved updater implementation
2024-07-16 11:30:12 +08:00
301072db90 Fixed #231
- Added higher propagation timeout for netcup
- Fixed bug in CICD script
2024-07-16 10:37:10 +08:00
cfcd10d64f Update README.md
Updated new start parameters and feature list
2024-07-15 23:00:59 +08:00
c85760c73a Merge pull request #242 from Morethanevil/main
Update CHANGELOG.md
2024-07-15 21:39:01 +08:00
b7bb918aa3 Fix: Container issue due to deprecated flag 2024-07-15 09:21:14 -04:00
962f3e0566 Update CHANGELOG.md 2024-07-15 14:16:46 +02:00
83 changed files with 6960 additions and 16796 deletions

View File

@ -1,3 +1,26 @@
# v3.0.9 16 Jul 2024
+ Added certificate download [#227](https://github.com/tobychui/zoraxy/issues/227)
+ Updated netcup timeout value [#231](https://github.com/tobychui/zoraxy/issues/231)
+ Updated geoip db
+ Removed debug print from log viewer
+ Upgraded netstat log printing to new log formatter
+ Improved update module implementation
# v3.0.8 15 Jul 2024
+ Added apache style logging mechanism (and build-in log viewer) [#218](https://github.com/tobychui/zoraxy/issues/218)
+ Fixed keep alive flushing issues [#235](https://github.com/tobychui/zoraxy/issues/235)
+ Added multi-upstream supports [#100](https://github.com/tobychui/zoraxy/issues/100)
+ Added stick session load balancer
+ Added weighted random load balancer
+ Added domain cleaning logic to domain / IP input fields
+ Added HSTS "include subdomain" auto injector
+ Added work-in-progress SSO / Oauth Server UI
+ Fixed uptime monitor not updating on proxy rule change bug
+ Optimized UI for create new proxy rule
+ Removed service expose proxy feature
# v3.0.7 20 Jun 2024 # v3.0.7 20 Jun 2024
+ Fixed redirection enable bug [#199](https://github.com/tobychui/zoraxy/issues/199) + Fixed redirection enable bug [#199](https://github.com/tobychui/zoraxy/issues/199)

View File

@ -21,12 +21,14 @@ A general purpose HTTP reverse proxy and forwarding tool. Now written in Go!
- DNS Challenge for Let's Encrypt and [these DNS providers](https://go-acme.github.io/lego/dns/) - DNS Challenge for Let's Encrypt and [these DNS providers](https://go-acme.github.io/lego/dns/)
- Blacklist / Whitelist by country or IP address (single IP, CIDR or wildcard for beginners) - Blacklist / Whitelist by country or IP address (single IP, CIDR or wildcard for beginners)
- Global Area Network Controller Web UI (ZeroTier not included) - Global Area Network Controller Web UI (ZeroTier not included)
- TCP Tunneling / Proxy - Stream Proxy (TCP & UDP)
- Integrated Up-time Monitor - Integrated Up-time Monitor
- Web-SSH Terminal - Web-SSH Terminal
- Utilities - Utilities
- CIDR IP converters - CIDR IP converters
- mDNS Scanner - mDNS Scanner
- Wake-On-Lan
- Debug Forward Proxy
- IP Scanner - IP Scanner
- Others - Others
- Basic single-admin management mode - Basic single-admin management mode
@ -96,12 +98,12 @@ See the [/docker](https://github.com/tobychui/zoraxy/tree/main/docker) folder fo
Usage of zoraxy: Usage of zoraxy:
-autorenew int -autorenew int
ACME auto TLS/SSL certificate renew check interval (seconds) (default 86400) ACME auto TLS/SSL certificate renew check interval (seconds) (default 86400)
-cfgupgrade
Enable auto config upgrade if breaking change is detected (default true)
-docker -docker
Run Zoraxy in docker compatibility mode Run Zoraxy in docker compatibility mode
-fastgeoip -fastgeoip
Enable high speed geoip lookup, require 1GB extra memory (Not recommend for low end devices) Enable high speed geoip lookup, require 1GB extra memory (Not recommend for low end devices)
-log
Log terminal output to file (default true)
-mdns -mdns
Enable mDNS scanner and transponder (default true) Enable mDNS scanner and transponder (default true)
-mdnsname string -mdnsname string

View File

@ -18,7 +18,7 @@ RUN go mod tidy &&\
RUN chmod 755 /usr/local/bin/zoraxy &&\ RUN chmod 755 /usr/local/bin/zoraxy &&\
chmod +x /usr/local/bin/zoraxy chmod +x /usr/local/bin/zoraxy
FROM docker.io/alpine:3.20 FROM docker.io/alpine:latest
RUN apk add --no-cache bash netcat-openbsd sudo RUN apk add --no-cache bash netcat-openbsd sudo
@ -30,8 +30,8 @@ VOLUME [ "/opt/zoraxy/config/" ]
WORKDIR /opt/zoraxy/config/ WORKDIR /opt/zoraxy/config/
ENV AUTORENEW="86400" ENV AUTORENEW="86400"
ENV EARLYRENEW="30"
ENV FASTGEOIP="false" ENV FASTGEOIP="false"
ENV LOG="true"
ENV MDNS="true" ENV MDNS="true"
ENV MDNSNAME="''" ENV MDNSNAME="''"
ENV NOAUTH="false" ENV NOAUTH="false"
@ -43,6 +43,6 @@ ENV WEBROOT="./www"
ENV ZTAUTH="''" ENV ZTAUTH="''"
ENV ZTPORT="9993" ENV ZTPORT="9993"
ENTRYPOINT "zoraxy" "-docker=true" "-autorenew=${AUTORENEW}" "-fastgeoip=${FASTGEOIP}" "-log=${LOG}" "-mdns=${MDNS}" "-mdnsname=${MDNSNAME}" "-noauth=${NOAUTH}" "-port=:${PORT}" "-sshlb=${SSHLB}" "-version=${VERSION}" "-webfm=${WEBFM}" "-webroot=${WEBROOT}" "-ztauth=${ZTAUTH}" "-ztport=${ZTPORT}" ENTRYPOINT "zoraxy" "-docker=true" "-autorenew=${AUTORENEW}" "-earlyrenew=${EARLYRENEW}" "-fastgeoip=${FASTGEOIP}" "-mdns=${MDNS}" "-mdnsname=${MDNSNAME}" "-noauth=${NOAUTH}" "-port=:${PORT}" "-sshlb=${SSHLB}" "-version=${VERSION}" "-webfm=${WEBFM}" "-webroot=${WEBROOT}" "-ztauth=${ZTAUTH}" "-ztport=${ZTPORT}"
HEALTHCHECK --interval=15s --timeout=5s --start-period=10s --retries=3 CMD nc -vz 127.0.0.1 $PORT || exit 1 HEALTHCHECK --interval=15s --timeout=5s --start-period=10s --retries=3 CMD nc -vz 127.0.0.1 $PORT || exit 1

View File

@ -40,16 +40,17 @@ services:
| `-p (ports)` | Yes | Depending on how your network is setup, you may need to portforward 80, 443, and the management port. | | `-p (ports)` | Yes | Depending on how your network is setup, you may need to portforward 80, 443, and the management port. |
| `-v (path to storage directory):/opt/zoraxy/config/` | Recommend | Sets the folder that holds your files. This should be the place you just chose. By default, it will create a Docker volume for the files for persistency but they will not be accessible. | | `-v (path to storage directory):/opt/zoraxy/config/` | Recommend | Sets the folder that holds your files. This should be the place you just chose. By default, it will create a Docker volume for the files for persistency but they will not be accessible. |
| `-v /var/run/docker.sock:/var/run/docker.sock` | No | Used for autodiscovery. | | `-v /var/run/docker.sock:/var/run/docker.sock` | No | Used for autodiscovery. |
| `-v /etc/localtime:/etc/localtime` | No | Uses the hosts time in the container. |
| `-e (flag)="(value)"` | No | Arguments to run Zoraxy with. They are simply just capitalized Zoraxy flags. `-docker=true` is always set by default. See examples below. | | `-e (flag)="(value)"` | No | Arguments to run Zoraxy with. They are simply just capitalized Zoraxy flags. `-docker=true` is always set by default. See examples below. |
| `zoraxydocker/zoraxy:latest` | Yes | The repository on Docker hub. By default, it is the latest version that is published. | | `zoraxydocker/zoraxy:latest` | Yes | The repository on Docker hub. By default, it is the latest version that is published. |
> [!IMPORTANT] > [!IMPORTANT]
> Docker usage of the port flag should not include the colon. Ex: PORT="8000" > Docker usage of the port flag should not include the colon. Ex: `-e PORT="8000"` for Docker run and `PORT: "8000"` for Docker compose.
## Examples: </br> ## Examples: </br>
### Docker Run </br> ### Docker Run </br>
``` ```
docker run -d --name zoraxy -p 80:80 -p 443:443 -p 8005:8005 -v /home/docker/Containers/Zoraxy:/opt/zoraxy/config/ -v /var/run/docker.sock:/var/run/docker.sock -e PORT="8005" -e FASTGEOIP="true" zoraxydocker/zoraxy:latest docker run -d --name zoraxy -p 80:80 -p 443:443 -p 8005:8005 -v /home/docker/Containers/Zoraxy:/opt/zoraxy/config/ -v /var/run/docker.sock:/var/run/docker.sock -v /etc/localtime:/etc/localtime -e PORT="8005" -e FASTGEOIP="true" zoraxydocker/zoraxy:latest
``` ```
### Docker Compose </br> ### Docker Compose </br>
@ -65,6 +66,7 @@ services:
volumes: volumes:
- /home/docker/Containers/Zoraxy:/opt/zoraxy/config/ - /home/docker/Containers/Zoraxy:/opt/zoraxy/config/
- /var/run/docker.sock:/var/run/docker.sock - /var/run/docker.sock:/var/run/docker.sock
- /etc/localtime:/etc/localtime
environment: environment:
PORT: "8005" PORT: "8005"
FASTGEOIP: "true" FASTGEOIP: "true"

View File

@ -19,7 +19,7 @@ clean:
$(PLATFORMS): $(PLATFORMS):
@echo "Building $(os)/$(arch)" @echo "Building $(os)/$(arch)"
GOROOT_FINAL=Git/ GOOS=$(os) GOARCH=$(arch) $(if $(filter linux/arm,$(os)/$(arch)),GOARM=6,) go build -o './dist/zoraxy_$(os)_$(arch)' -ldflags "-s -w" -trimpath GOROOT_FINAL=Git/ GOOS=$(os) GOARCH=$(arch) $(if $(filter linux/arm,$(os)/$(arch)),GOARM=6,) CGO_ENABLED="0" go build -o './dist/zoraxy_$(os)_$(arch)' -ldflags "-s -w" -trimpath
# GOROOT_FINAL=Git/ GOOS=$(os) GOARCH=$(arch) GOARM=6 go build -o './dist/zoraxy_$(os)_$(arch)' -ldflags "-s -w" -trimpath # GOROOT_FINAL=Git/ GOOS=$(os) GOARCH=$(arch) GOARM=6 go build -o './dist/zoraxy_$(os)_$(arch)' -ldflags "-s -w" -trimpath

View File

@ -22,11 +22,11 @@ import (
var requireAuth = true var requireAuth = true
func initAPIs() { func initAPIs(targetMux *http.ServeMux) {
authRouter := auth.NewManagedHTTPRouter(auth.RouterOption{ authRouter := auth.NewManagedHTTPRouter(auth.RouterOption{
AuthAgent: authAgent, AuthAgent: authAgent,
RequireAuth: requireAuth, RequireAuth: requireAuth,
TargetMux: targetMux,
DeniedHandler: func(w http.ResponseWriter, r *http.Request) { DeniedHandler: func(w http.ResponseWriter, r *http.Request) {
http.Error(w, "401 - Unauthorized", http.StatusUnauthorized) http.Error(w, "401 - Unauthorized", http.StatusUnauthorized)
}, },
@ -37,12 +37,12 @@ func initAPIs() {
if development { if development {
fs = http.FileServer(http.Dir("web/")) fs = http.FileServer(http.Dir("web/"))
} }
//Add a layer of middleware for advance control //Add a layer of middleware for advance control
advHandler := FSHandler(fs) advHandler := FSHandler(fs)
http.Handle("/", advHandler) targetMux.Handle("/", advHandler)
//Authentication APIs //Authentication APIs
registerAuthAPIs(requireAuth) registerAuthAPIs(requireAuth, targetMux)
//Reverse proxy //Reverse proxy
authRouter.HandleFunc("/api/proxy/enable", ReverseProxyHandleOnOff) authRouter.HandleFunc("/api/proxy/enable", ReverseProxyHandleOnOff)
@ -77,6 +77,8 @@ func initAPIs() {
authRouter.HandleFunc("/api/proxy/header/add", HandleCustomHeaderAdd) authRouter.HandleFunc("/api/proxy/header/add", HandleCustomHeaderAdd)
authRouter.HandleFunc("/api/proxy/header/remove", HandleCustomHeaderRemove) authRouter.HandleFunc("/api/proxy/header/remove", HandleCustomHeaderRemove)
authRouter.HandleFunc("/api/proxy/header/handleHSTS", HandleHSTSState) authRouter.HandleFunc("/api/proxy/header/handleHSTS", HandleHSTSState)
authRouter.HandleFunc("/api/proxy/header/handleHopByHop", HandleHopByHop)
authRouter.HandleFunc("/api/proxy/header/handleHostOverwrite", HandleHostOverwrite)
authRouter.HandleFunc("/api/proxy/header/handlePermissionPolicy", HandlePermissionPolicy) authRouter.HandleFunc("/api/proxy/header/handlePermissionPolicy", HandlePermissionPolicy)
//Reverse proxy auth related APIs //Reverse proxy auth related APIs
authRouter.HandleFunc("/api/proxy/auth/exceptions/list", ListProxyBasicAuthExceptionPaths) authRouter.HandleFunc("/api/proxy/auth/exceptions/list", ListProxyBasicAuthExceptionPaths)
@ -87,6 +89,7 @@ func initAPIs() {
authRouter.HandleFunc("/api/cert/tls", handleToggleTLSProxy) authRouter.HandleFunc("/api/cert/tls", handleToggleTLSProxy)
authRouter.HandleFunc("/api/cert/tlsRequireLatest", handleSetTlsRequireLatest) authRouter.HandleFunc("/api/cert/tlsRequireLatest", handleSetTlsRequireLatest)
authRouter.HandleFunc("/api/cert/upload", handleCertUpload) authRouter.HandleFunc("/api/cert/upload", handleCertUpload)
authRouter.HandleFunc("/api/cert/download", handleCertDownload)
authRouter.HandleFunc("/api/cert/list", handleListCertificate) authRouter.HandleFunc("/api/cert/list", handleListCertificate)
authRouter.HandleFunc("/api/cert/listdomains", handleListDomains) authRouter.HandleFunc("/api/cert/listdomains", handleListDomains)
authRouter.HandleFunc("/api/cert/checkDefault", handleDefaultCertCheck) authRouter.HandleFunc("/api/cert/checkDefault", handleDefaultCertCheck)
@ -127,7 +130,7 @@ func initAPIs() {
//Statistic & uptime monitoring API //Statistic & uptime monitoring API
authRouter.HandleFunc("/api/stats/summary", statisticCollector.HandleTodayStatLoad) authRouter.HandleFunc("/api/stats/summary", statisticCollector.HandleTodayStatLoad)
authRouter.HandleFunc("/api/stats/countries", HandleCountryDistrSummary) authRouter.HandleFunc("/api/stats/countries", HandleCountryDistrSummary)
authRouter.HandleFunc("/api/stats/netstat", netstat.HandleGetNetworkInterfaceStats) authRouter.HandleFunc("/api/stats/netstat", netstatBuffers.HandleGetNetworkInterfaceStats)
authRouter.HandleFunc("/api/stats/netstatgraph", netstatBuffers.HandleGetBufferedNetworkInterfaceStats) authRouter.HandleFunc("/api/stats/netstatgraph", netstatBuffers.HandleGetBufferedNetworkInterfaceStats)
authRouter.HandleFunc("/api/stats/listnic", netstat.HandleListNetworkInterfaces) authRouter.HandleFunc("/api/stats/listnic", netstat.HandleListNetworkInterfaces)
authRouter.HandleFunc("/api/utm/list", HandleUptimeMonitorListing) authRouter.HandleFunc("/api/utm/list", HandleUptimeMonitorListing)
@ -184,8 +187,8 @@ func initAPIs() {
authRouter.HandleFunc("/api/tools/fwdproxy/port", forwardProxy.HandlePort) authRouter.HandleFunc("/api/tools/fwdproxy/port", forwardProxy.HandlePort)
//Account Reset //Account Reset
http.HandleFunc("/api/account/reset", HandleAdminAccountResetEmail) targetMux.HandleFunc("/api/account/reset", HandleAdminAccountResetEmail)
http.HandleFunc("/api/account/new", HandleNewPasswordSetup) targetMux.HandleFunc("/api/account/new", HandleNewPasswordSetup)
//ACME & Auto Renewer //ACME & Auto Renewer
authRouter.HandleFunc("/api/acme/listExpiredDomains", acmeHandler.HandleGetExpiredDomains) authRouter.HandleFunc("/api/acme/listExpiredDomains", acmeHandler.HandleGetExpiredDomains)
@ -225,7 +228,7 @@ func initAPIs() {
authRouter.HandleFunc("/api/docker/containers", DockerUXOptimizer.HandleDockerContainersList) authRouter.HandleFunc("/api/docker/containers", DockerUXOptimizer.HandleDockerContainersList)
//Others //Others
http.HandleFunc("/api/info/x", HandleZoraxyInfo) targetMux.HandleFunc("/api/info/x", HandleZoraxyInfo)
authRouter.HandleFunc("/api/info/geoip", HandleGeoIpLookup) authRouter.HandleFunc("/api/info/geoip", HandleGeoIpLookup)
authRouter.HandleFunc("/api/conf/export", ExportConfigAsZip) authRouter.HandleFunc("/api/conf/export", ExportConfigAsZip)
authRouter.HandleFunc("/api/conf/import", ImportConfigFromZip) authRouter.HandleFunc("/api/conf/import", ImportConfigFromZip)
@ -240,18 +243,18 @@ func initAPIs() {
} }
// Function to renders Auth related APIs // Function to renders Auth related APIs
func registerAuthAPIs(requireAuth bool) { func registerAuthAPIs(requireAuth bool, targetMux *http.ServeMux) {
//Auth APIs //Auth APIs
http.HandleFunc("/api/auth/login", authAgent.HandleLogin) targetMux.HandleFunc("/api/auth/login", authAgent.HandleLogin)
http.HandleFunc("/api/auth/logout", authAgent.HandleLogout) targetMux.HandleFunc("/api/auth/logout", authAgent.HandleLogout)
http.HandleFunc("/api/auth/checkLogin", func(w http.ResponseWriter, r *http.Request) { targetMux.HandleFunc("/api/auth/checkLogin", func(w http.ResponseWriter, r *http.Request) {
if requireAuth { if requireAuth {
authAgent.CheckLogin(w, r) authAgent.CheckLogin(w, r)
} else { } else {
utils.SendJSONResponse(w, "true") utils.SendJSONResponse(w, "true")
} }
}) })
http.HandleFunc("/api/auth/username", func(w http.ResponseWriter, r *http.Request) { targetMux.HandleFunc("/api/auth/username", func(w http.ResponseWriter, r *http.Request) {
username, err := authAgent.GetUserName(w, r) username, err := authAgent.GetUserName(w, r)
if err != nil { if err != nil {
http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized) http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
@ -261,12 +264,12 @@ func registerAuthAPIs(requireAuth bool) {
js, _ := json.Marshal(username) js, _ := json.Marshal(username)
utils.SendJSONResponse(w, string(js)) utils.SendJSONResponse(w, string(js))
}) })
http.HandleFunc("/api/auth/userCount", func(w http.ResponseWriter, r *http.Request) { targetMux.HandleFunc("/api/auth/userCount", func(w http.ResponseWriter, r *http.Request) {
uc := authAgent.GetUserCounts() uc := authAgent.GetUserCounts()
js, _ := json.Marshal(uc) js, _ := json.Marshal(uc)
utils.SendJSONResponse(w, string(js)) utils.SendJSONResponse(w, string(js))
}) })
http.HandleFunc("/api/auth/register", func(w http.ResponseWriter, r *http.Request) { targetMux.HandleFunc("/api/auth/register", func(w http.ResponseWriter, r *http.Request) {
if authAgent.GetUserCounts() == 0 { if authAgent.GetUserCounts() == 0 {
//Allow register root admin //Allow register root admin
authAgent.HandleRegisterWithoutEmail(w, r, func(username, reserved string) { authAgent.HandleRegisterWithoutEmail(w, r, func(username, reserved string) {
@ -277,7 +280,7 @@ func registerAuthAPIs(requireAuth bool) {
utils.SendErrorResponse(w, "Root management account already exists") utils.SendErrorResponse(w, "Root management account already exists")
} }
}) })
http.HandleFunc("/api/auth/changePassword", func(w http.ResponseWriter, r *http.Request) { targetMux.HandleFunc("/api/auth/changePassword", func(w http.ResponseWriter, r *http.Request) {
username, err := authAgent.GetUserName(w, r) username, err := authAgent.GetUserName(w, r)
if err != nil { if err != nil {
http.Error(w, "401 - Unauthorized", http.StatusUnauthorized) http.Error(w, "401 - Unauthorized", http.StatusUnauthorized)

View File

@ -182,27 +182,28 @@ func handleToggleTLSProxy(w http.ResponseWriter, r *http.Request) {
sysdb.Read("settings", "usetls", &currentTlsSetting) sysdb.Read("settings", "usetls", &currentTlsSetting)
} }
newState, err := utils.PostPara(r, "set") if r.Method == http.MethodGet {
if err != nil { //Get the current status
//No setting. Get the current status
js, _ := json.Marshal(currentTlsSetting) js, _ := json.Marshal(currentTlsSetting)
utils.SendJSONResponse(w, string(js)) utils.SendJSONResponse(w, string(js))
} else { } else if r.Method == http.MethodPost {
if newState == "true" { newState, err := utils.PostBool(r, "set")
if err != nil {
utils.SendErrorResponse(w, "new state not set or invalid")
return
}
if newState {
sysdb.Write("settings", "usetls", true) sysdb.Write("settings", "usetls", true)
SystemWideLogger.Println("Enabling TLS mode on reverse proxy") SystemWideLogger.Println("Enabling TLS mode on reverse proxy")
dynamicProxyRouter.UpdateTLSSetting(true) dynamicProxyRouter.UpdateTLSSetting(true)
} else if newState == "false" { } else {
sysdb.Write("settings", "usetls", false) sysdb.Write("settings", "usetls", false)
SystemWideLogger.Println("Disabling TLS mode on reverse proxy") SystemWideLogger.Println("Disabling TLS mode on reverse proxy")
dynamicProxyRouter.UpdateTLSSetting(false) dynamicProxyRouter.UpdateTLSSetting(false)
} else {
utils.SendErrorResponse(w, "invalid state given. Only support true or false")
return
} }
utils.SendOK(w) utils.SendOK(w)
} else {
http.Error(w, "405 - Method not allowed", http.StatusMethodNotAllowed)
} }
} }
@ -233,6 +234,51 @@ func handleSetTlsRequireLatest(w http.ResponseWriter, r *http.Request) {
} }
} }
// Handle download of the selected certificate
func handleCertDownload(w http.ResponseWriter, r *http.Request) {
// get the certificate name
certname, err := utils.GetPara(r, "certname")
if err != nil {
utils.SendErrorResponse(w, "invalid certname given")
return
}
certname = filepath.Base(certname) //prevent path escape
// check if the cert exists
pubKey := filepath.Join(filepath.Join("./conf/certs"), certname+".key")
priKey := filepath.Join(filepath.Join("./conf/certs"), certname+".pem")
if utils.FileExists(pubKey) && utils.FileExists(priKey) {
//Zip them and serve them via http download
seeking, _ := utils.GetBool(r, "seek")
if seeking {
//This request only check if the key exists. Do not provide download
utils.SendOK(w)
return
}
//Serve both file in zip
zipTmpFolder := "./tmp/download"
os.MkdirAll(zipTmpFolder, 0775)
zipFileName := filepath.Join(zipTmpFolder, certname+".zip")
err := utils.ZipFiles(zipFileName, pubKey, priKey)
if err != nil {
http.Error(w, "Failed to create zip file", http.StatusInternalServerError)
return
}
defer os.Remove(zipFileName) // Clean up the zip file after serving
// Serve the zip file
w.Header().Set("Content-Disposition", "attachment; filename=\""+certname+"_export.zip\"")
w.Header().Set("Content-Type", "application/zip")
http.ServeFile(w, r, zipFileName)
} else {
//Not both key exists
utils.SendErrorResponse(w, "invalid key-pairs: private key or public key not found in key store")
return
}
}
// Handle upload of the certificate // Handle upload of the certificate
func handleCertUpload(w http.ResponseWriter, r *http.Request) { func handleCertUpload(w http.ResponseWriter, r *http.Request) {
// check if request method is POST // check if request method is POST

View File

@ -95,6 +95,7 @@ require (
github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect
github.com/googleapis/gax-go/v2 v2.12.2 // indirect github.com/googleapis/gax-go/v2 v2.12.2 // indirect
github.com/gophercloud/gophercloud v1.0.0 // indirect github.com/gophercloud/gophercloud v1.0.0 // indirect
github.com/gorilla/csrf v1.7.2 // indirect
github.com/gorilla/css v1.0.1 // indirect github.com/gorilla/css v1.0.1 // indirect
github.com/gorilla/securecookie v1.1.2 // indirect github.com/gorilla/securecookie v1.1.2 // indirect
github.com/hashicorp/errwrap v1.0.0 // indirect github.com/hashicorp/errwrap v1.0.0 // indirect

View File

@ -317,6 +317,8 @@ github.com/googleapis/gax-go/v2 v2.12.2/go.mod h1:61M8vcyyXR2kqKFxKrfA22jaA8JGF7
github.com/gophercloud/gophercloud v1.0.0 h1:9nTGx0jizmHxDobe4mck89FyQHVyA3CaXLIUSGJjP9k= github.com/gophercloud/gophercloud v1.0.0 h1:9nTGx0jizmHxDobe4mck89FyQHVyA3CaXLIUSGJjP9k=
github.com/gophercloud/gophercloud v1.0.0/go.mod h1:Q8fZtyi5zZxPS/j9aj3sSxtvj41AdQMDwyo1myduD5c= github.com/gophercloud/gophercloud v1.0.0/go.mod h1:Q8fZtyi5zZxPS/j9aj3sSxtvj41AdQMDwyo1myduD5c=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/csrf v1.7.2 h1:oTUjx0vyf2T+wkrx09Trsev1TE+/EbDAeHtSTbtC2eI=
github.com/gorilla/csrf v1.7.2/go.mod h1:F1Fj3KG23WYHE6gozCmBAezKookxbIvUJT+121wTuLk=
github.com/gorilla/css v1.0.1 h1:ntNaBIghp6JmvWnxbZKANoLyuXTPZ4cAMlo6RyhlbO8= github.com/gorilla/css v1.0.1 h1:ntNaBIghp6JmvWnxbZKANoLyuXTPZ4cAMlo6RyhlbO8=
github.com/gorilla/css v1.0.1/go.mod h1:BvnYkspnSzMmwRK+b8/xgNPLiIuNZr6vbZBTPQ2A3b0= github.com/gorilla/css v1.0.1/go.mod h1:BvnYkspnSzMmwRK+b8/xgNPLiIuNZr6vbZBTPQ2A3b0=
github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=

View File

@ -12,6 +12,7 @@ import (
"time" "time"
"github.com/google/uuid" "github.com/google/uuid"
"github.com/gorilla/csrf"
"imuslab.com/zoraxy/mod/access" "imuslab.com/zoraxy/mod/access"
"imuslab.com/zoraxy/mod/acme" "imuslab.com/zoraxy/mod/acme"
"imuslab.com/zoraxy/mod/auth" "imuslab.com/zoraxy/mod/auth"
@ -50,6 +51,7 @@ var ztAuthToken = flag.String("ztauth", "", "ZeroTier authtoken for the local no
var ztAPIPort = flag.Int("ztport", 9993, "ZeroTier controller API port") var ztAPIPort = flag.Int("ztport", 9993, "ZeroTier controller API port")
var runningInDocker = flag.Bool("docker", false, "Run Zoraxy in docker compatibility mode") 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 acmeAutoRenewInterval = flag.Int("autorenew", 86400, "ACME auto TLS/SSL certificate renew check interval (seconds)")
var acmeCertAutoRenewDays = flag.Int("earlyrenew", 30, "Number of days to early renew a soon expiring certificate (days)")
var enableHighSpeedGeoIPLookup = flag.Bool("fastgeoip", false, "Enable high speed geoip lookup, require 1GB extra memory (Not recommend for low end devices)") 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") var staticWebServerRoot = flag.String("webroot", "./www", "Static web server root folder. Only allow chnage in start paramters")
var allowWebFileManager = flag.Bool("webfm", true, "Enable web file manager for static web server root folder") var allowWebFileManager = flag.Bool("webfm", true, "Enable web file manager for static web server root folder")
@ -57,7 +59,7 @@ var enableAutoUpdate = flag.Bool("cfgupgrade", true, "Enable auto config upgrade
var ( var (
name = "Zoraxy" name = "Zoraxy"
version = "3.0.8" version = "3.1.0"
nodeUUID = "generic" //System uuid, in uuidv4 format nodeUUID = "generic" //System uuid, in uuidv4 format
development = false //Set this to false to use embedded web fs development = false //Set this to false to use embedded web fs
bootTime = time.Now().Unix() bootTime = time.Now().Unix()
@ -71,10 +73,12 @@ var (
/* /*
Handler Modules Handler Modules
*/ */
sysdb *database.Database //System database sysdb *database.Database //System database
authAgent *auth.AuthAgent //Authentication agent authAgent *auth.AuthAgent //Authentication agent
tlsCertManager *tlscert.Manager //TLS / SSL management tlsCertManager *tlscert.Manager //TLS / SSL management
redirectTable *redirection.RuleTable //Handle special redirection rule sets redirectTable *redirection.RuleTable //Handle special redirection rule sets
webminPanelMux *http.ServeMux //Server mux for handling webmin panel APIs
csrfMiddleware func(http.Handler) http.Handler //CSRF protection middleware
pathRuleHandler *pathrule.Handler //Handle specific path blocking or custom headers pathRuleHandler *pathrule.Handler //Handle specific path blocking or custom headers
geodbStore *geodb.Store //GeoIP database, for resolving IP into country code geodbStore *geodb.Store //GeoIP database, for resolving IP into country code
@ -175,12 +179,22 @@ func main() {
} }
nodeUUID = string(uuidBytes) nodeUUID = string(uuidBytes)
//Create a new webmin mux and csrf middleware layer
webminPanelMux = http.NewServeMux()
csrfMiddleware = csrf.Protect(
[]byte(nodeUUID),
csrf.CookieName("zoraxy-csrf"),
csrf.Secure(false),
csrf.Path("/"),
csrf.SameSite(csrf.SameSiteLaxMode),
)
//Startup all modules //Startup all modules
startupSequence() startupSequence()
//Initiate management interface APIs //Initiate management interface APIs
requireAuth = !(*noauth) requireAuth = !(*noauth)
initAPIs() initAPIs(webminPanelMux)
//Start the reverse proxy server in go routine //Start the reverse proxy server in go routine
go func() { go func() {
@ -193,7 +207,7 @@ func main() {
finalSequence() finalSequence()
SystemWideLogger.Println("Zoraxy started. Visit control panel at http://localhost" + *webUIPort) SystemWideLogger.Println("Zoraxy started. Visit control panel at http://localhost" + *webUIPort)
err = http.ListenAndServe(*webUIPort, nil) err = http.ListenAndServe(*webUIPort, csrfMiddleware(webminPanelMux))
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)

View File

@ -1,11 +1,6 @@
package acme package acme
import ( import (
"errors"
"log"
"os"
"strings"
"github.com/go-acme/lego/v4/challenge" "github.com/go-acme/lego/v4/challenge"
"imuslab.com/zoraxy/mod/acme/acmedns" "imuslab.com/zoraxy/mod/acme/acmedns"
) )
@ -29,7 +24,7 @@ func GetDnsChallengeProviderByName(dnsProvider string, dnsCredentials string) (c
/* /*
Original implementation of DNS ACME using OS.Env as payload Original implementation of DNS ACME using OS.Env as payload
*/ */
/*
func setCredentialsIntoEnvironmentVariables(credentials map[string]string) { func setCredentialsIntoEnvironmentVariables(credentials map[string]string) {
for key, value := range credentials { for key, value := range credentials {
err := os.Setenv(key, value) err := os.Setenv(key, value)
@ -41,6 +36,7 @@ func setCredentialsIntoEnvironmentVariables(credentials map[string]string) {
} }
} }
func extractDnsCredentials(input string) (map[string]string, error) { func extractDnsCredentials(input string) (map[string]string, error) {
result := make(map[string]string) result := make(map[string]string)
@ -70,3 +66,5 @@ func extractDnsCredentials(input string) (map[string]string, error) {
return result, nil return result, nil
} }
*/

View File

@ -6,6 +6,7 @@ package acmedns
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"time"
"github.com/go-acme/lego/v4/challenge" "github.com/go-acme/lego/v4/challenge"
"github.com/go-acme/lego/v4/providers/dns/alidns" "github.com/go-acme/lego/v4/providers/dns/alidns"
@ -142,6 +143,7 @@ func GetDNSProviderByJsonConfig(name string, js string)(challenge.Provider, erro
if err != nil { if err != nil {
return nil, err return nil, err
} }
cfg.PropagationTimeout = 5*time.Minute
return alidns.NewDNSProviderConfig(cfg) return alidns.NewDNSProviderConfig(cfg)
case "allinkl": case "allinkl":
cfg := allinkl.NewDefaultConfig() cfg := allinkl.NewDefaultConfig()
@ -149,6 +151,7 @@ func GetDNSProviderByJsonConfig(name string, js string)(challenge.Provider, erro
if err != nil { if err != nil {
return nil, err return nil, err
} }
cfg.PropagationTimeout = 5*time.Minute
return allinkl.NewDNSProviderConfig(cfg) return allinkl.NewDNSProviderConfig(cfg)
case "arvancloud": case "arvancloud":
cfg := arvancloud.NewDefaultConfig() cfg := arvancloud.NewDefaultConfig()
@ -156,6 +159,7 @@ func GetDNSProviderByJsonConfig(name string, js string)(challenge.Provider, erro
if err != nil { if err != nil {
return nil, err return nil, err
} }
cfg.PropagationTimeout = 5*time.Minute
return arvancloud.NewDNSProviderConfig(cfg) return arvancloud.NewDNSProviderConfig(cfg)
case "auroradns": case "auroradns":
cfg := auroradns.NewDefaultConfig() cfg := auroradns.NewDefaultConfig()
@ -163,6 +167,7 @@ func GetDNSProviderByJsonConfig(name string, js string)(challenge.Provider, erro
if err != nil { if err != nil {
return nil, err return nil, err
} }
cfg.PropagationTimeout = 5*time.Minute
return auroradns.NewDNSProviderConfig(cfg) return auroradns.NewDNSProviderConfig(cfg)
case "autodns": case "autodns":
cfg := autodns.NewDefaultConfig() cfg := autodns.NewDefaultConfig()
@ -170,6 +175,7 @@ func GetDNSProviderByJsonConfig(name string, js string)(challenge.Provider, erro
if err != nil { if err != nil {
return nil, err return nil, err
} }
cfg.PropagationTimeout = 5*time.Minute
return autodns.NewDNSProviderConfig(cfg) return autodns.NewDNSProviderConfig(cfg)
case "azure": case "azure":
cfg := azure.NewDefaultConfig() cfg := azure.NewDefaultConfig()
@ -177,6 +183,7 @@ func GetDNSProviderByJsonConfig(name string, js string)(challenge.Provider, erro
if err != nil { if err != nil {
return nil, err return nil, err
} }
cfg.PropagationTimeout = 5*time.Minute
return azure.NewDNSProviderConfig(cfg) return azure.NewDNSProviderConfig(cfg)
case "azuredns": case "azuredns":
cfg := azuredns.NewDefaultConfig() cfg := azuredns.NewDefaultConfig()
@ -184,6 +191,7 @@ func GetDNSProviderByJsonConfig(name string, js string)(challenge.Provider, erro
if err != nil { if err != nil {
return nil, err return nil, err
} }
cfg.PropagationTimeout = 5*time.Minute
return azuredns.NewDNSProviderConfig(cfg) return azuredns.NewDNSProviderConfig(cfg)
case "bindman": case "bindman":
cfg := bindman.NewDefaultConfig() cfg := bindman.NewDefaultConfig()
@ -191,6 +199,7 @@ func GetDNSProviderByJsonConfig(name string, js string)(challenge.Provider, erro
if err != nil { if err != nil {
return nil, err return nil, err
} }
cfg.PropagationTimeout = 5*time.Minute
return bindman.NewDNSProviderConfig(cfg) return bindman.NewDNSProviderConfig(cfg)
case "bluecat": case "bluecat":
cfg := bluecat.NewDefaultConfig() cfg := bluecat.NewDefaultConfig()
@ -198,6 +207,7 @@ func GetDNSProviderByJsonConfig(name string, js string)(challenge.Provider, erro
if err != nil { if err != nil {
return nil, err return nil, err
} }
cfg.PropagationTimeout = 5*time.Minute
return bluecat.NewDNSProviderConfig(cfg) return bluecat.NewDNSProviderConfig(cfg)
case "brandit": case "brandit":
cfg := brandit.NewDefaultConfig() cfg := brandit.NewDefaultConfig()
@ -205,6 +215,7 @@ func GetDNSProviderByJsonConfig(name string, js string)(challenge.Provider, erro
if err != nil { if err != nil {
return nil, err return nil, err
} }
cfg.PropagationTimeout = 5*time.Minute
return brandit.NewDNSProviderConfig(cfg) return brandit.NewDNSProviderConfig(cfg)
case "bunny": case "bunny":
cfg := bunny.NewDefaultConfig() cfg := bunny.NewDefaultConfig()
@ -212,6 +223,7 @@ func GetDNSProviderByJsonConfig(name string, js string)(challenge.Provider, erro
if err != nil { if err != nil {
return nil, err return nil, err
} }
cfg.PropagationTimeout = 5*time.Minute
return bunny.NewDNSProviderConfig(cfg) return bunny.NewDNSProviderConfig(cfg)
case "checkdomain": case "checkdomain":
cfg := checkdomain.NewDefaultConfig() cfg := checkdomain.NewDefaultConfig()
@ -219,6 +231,7 @@ func GetDNSProviderByJsonConfig(name string, js string)(challenge.Provider, erro
if err != nil { if err != nil {
return nil, err return nil, err
} }
cfg.PropagationTimeout = 5*time.Minute
return checkdomain.NewDNSProviderConfig(cfg) return checkdomain.NewDNSProviderConfig(cfg)
case "civo": case "civo":
cfg := civo.NewDefaultConfig() cfg := civo.NewDefaultConfig()
@ -226,6 +239,7 @@ func GetDNSProviderByJsonConfig(name string, js string)(challenge.Provider, erro
if err != nil { if err != nil {
return nil, err return nil, err
} }
cfg.PropagationTimeout = 5*time.Minute
return civo.NewDNSProviderConfig(cfg) return civo.NewDNSProviderConfig(cfg)
case "clouddns": case "clouddns":
cfg := clouddns.NewDefaultConfig() cfg := clouddns.NewDefaultConfig()
@ -233,6 +247,7 @@ func GetDNSProviderByJsonConfig(name string, js string)(challenge.Provider, erro
if err != nil { if err != nil {
return nil, err return nil, err
} }
cfg.PropagationTimeout = 5*time.Minute
return clouddns.NewDNSProviderConfig(cfg) return clouddns.NewDNSProviderConfig(cfg)
case "cloudflare": case "cloudflare":
cfg := cloudflare.NewDefaultConfig() cfg := cloudflare.NewDefaultConfig()
@ -240,6 +255,7 @@ func GetDNSProviderByJsonConfig(name string, js string)(challenge.Provider, erro
if err != nil { if err != nil {
return nil, err return nil, err
} }
cfg.PropagationTimeout = 5*time.Minute
return cloudflare.NewDNSProviderConfig(cfg) return cloudflare.NewDNSProviderConfig(cfg)
case "cloudns": case "cloudns":
cfg := cloudns.NewDefaultConfig() cfg := cloudns.NewDefaultConfig()
@ -247,6 +263,7 @@ func GetDNSProviderByJsonConfig(name string, js string)(challenge.Provider, erro
if err != nil { if err != nil {
return nil, err return nil, err
} }
cfg.PropagationTimeout = 5*time.Minute
return cloudns.NewDNSProviderConfig(cfg) return cloudns.NewDNSProviderConfig(cfg)
case "cloudru": case "cloudru":
cfg := cloudru.NewDefaultConfig() cfg := cloudru.NewDefaultConfig()
@ -254,6 +271,7 @@ func GetDNSProviderByJsonConfig(name string, js string)(challenge.Provider, erro
if err != nil { if err != nil {
return nil, err return nil, err
} }
cfg.PropagationTimeout = 5*time.Minute
return cloudru.NewDNSProviderConfig(cfg) return cloudru.NewDNSProviderConfig(cfg)
case "cloudxns": case "cloudxns":
cfg := cloudxns.NewDefaultConfig() cfg := cloudxns.NewDefaultConfig()
@ -261,6 +279,7 @@ func GetDNSProviderByJsonConfig(name string, js string)(challenge.Provider, erro
if err != nil { if err != nil {
return nil, err return nil, err
} }
cfg.PropagationTimeout = 5*time.Minute
return cloudxns.NewDNSProviderConfig(cfg) return cloudxns.NewDNSProviderConfig(cfg)
case "conoha": case "conoha":
cfg := conoha.NewDefaultConfig() cfg := conoha.NewDefaultConfig()
@ -268,6 +287,7 @@ func GetDNSProviderByJsonConfig(name string, js string)(challenge.Provider, erro
if err != nil { if err != nil {
return nil, err return nil, err
} }
cfg.PropagationTimeout = 5*time.Minute
return conoha.NewDNSProviderConfig(cfg) return conoha.NewDNSProviderConfig(cfg)
case "constellix": case "constellix":
cfg := constellix.NewDefaultConfig() cfg := constellix.NewDefaultConfig()
@ -275,6 +295,7 @@ func GetDNSProviderByJsonConfig(name string, js string)(challenge.Provider, erro
if err != nil { if err != nil {
return nil, err return nil, err
} }
cfg.PropagationTimeout = 5*time.Minute
return constellix.NewDNSProviderConfig(cfg) return constellix.NewDNSProviderConfig(cfg)
case "cpanel": case "cpanel":
cfg := cpanel.NewDefaultConfig() cfg := cpanel.NewDefaultConfig()
@ -282,6 +303,7 @@ func GetDNSProviderByJsonConfig(name string, js string)(challenge.Provider, erro
if err != nil { if err != nil {
return nil, err return nil, err
} }
cfg.PropagationTimeout = 5*time.Minute
return cpanel.NewDNSProviderConfig(cfg) return cpanel.NewDNSProviderConfig(cfg)
case "derak": case "derak":
cfg := derak.NewDefaultConfig() cfg := derak.NewDefaultConfig()
@ -289,6 +311,7 @@ func GetDNSProviderByJsonConfig(name string, js string)(challenge.Provider, erro
if err != nil { if err != nil {
return nil, err return nil, err
} }
cfg.PropagationTimeout = 5*time.Minute
return derak.NewDNSProviderConfig(cfg) return derak.NewDNSProviderConfig(cfg)
case "desec": case "desec":
cfg := desec.NewDefaultConfig() cfg := desec.NewDefaultConfig()
@ -296,6 +319,7 @@ func GetDNSProviderByJsonConfig(name string, js string)(challenge.Provider, erro
if err != nil { if err != nil {
return nil, err return nil, err
} }
cfg.PropagationTimeout = 5*time.Minute
return desec.NewDNSProviderConfig(cfg) return desec.NewDNSProviderConfig(cfg)
case "digitalocean": case "digitalocean":
cfg := digitalocean.NewDefaultConfig() cfg := digitalocean.NewDefaultConfig()
@ -303,6 +327,7 @@ func GetDNSProviderByJsonConfig(name string, js string)(challenge.Provider, erro
if err != nil { if err != nil {
return nil, err return nil, err
} }
cfg.PropagationTimeout = 5*time.Minute
return digitalocean.NewDNSProviderConfig(cfg) return digitalocean.NewDNSProviderConfig(cfg)
case "dnshomede": case "dnshomede":
cfg := dnshomede.NewDefaultConfig() cfg := dnshomede.NewDefaultConfig()
@ -310,6 +335,7 @@ func GetDNSProviderByJsonConfig(name string, js string)(challenge.Provider, erro
if err != nil { if err != nil {
return nil, err return nil, err
} }
cfg.PropagationTimeout = 5*time.Minute
return dnshomede.NewDNSProviderConfig(cfg) return dnshomede.NewDNSProviderConfig(cfg)
case "dnsimple": case "dnsimple":
cfg := dnsimple.NewDefaultConfig() cfg := dnsimple.NewDefaultConfig()
@ -317,6 +343,7 @@ func GetDNSProviderByJsonConfig(name string, js string)(challenge.Provider, erro
if err != nil { if err != nil {
return nil, err return nil, err
} }
cfg.PropagationTimeout = 5*time.Minute
return dnsimple.NewDNSProviderConfig(cfg) return dnsimple.NewDNSProviderConfig(cfg)
case "dnsmadeeasy": case "dnsmadeeasy":
cfg := dnsmadeeasy.NewDefaultConfig() cfg := dnsmadeeasy.NewDefaultConfig()
@ -324,6 +351,7 @@ func GetDNSProviderByJsonConfig(name string, js string)(challenge.Provider, erro
if err != nil { if err != nil {
return nil, err return nil, err
} }
cfg.PropagationTimeout = 5*time.Minute
return dnsmadeeasy.NewDNSProviderConfig(cfg) return dnsmadeeasy.NewDNSProviderConfig(cfg)
case "dnspod": case "dnspod":
cfg := dnspod.NewDefaultConfig() cfg := dnspod.NewDefaultConfig()
@ -331,6 +359,7 @@ func GetDNSProviderByJsonConfig(name string, js string)(challenge.Provider, erro
if err != nil { if err != nil {
return nil, err return nil, err
} }
cfg.PropagationTimeout = 5*time.Minute
return dnspod.NewDNSProviderConfig(cfg) return dnspod.NewDNSProviderConfig(cfg)
case "dode": case "dode":
cfg := dode.NewDefaultConfig() cfg := dode.NewDefaultConfig()
@ -338,6 +367,7 @@ func GetDNSProviderByJsonConfig(name string, js string)(challenge.Provider, erro
if err != nil { if err != nil {
return nil, err return nil, err
} }
cfg.PropagationTimeout = 5*time.Minute
return dode.NewDNSProviderConfig(cfg) return dode.NewDNSProviderConfig(cfg)
case "domeneshop": case "domeneshop":
cfg := domeneshop.NewDefaultConfig() cfg := domeneshop.NewDefaultConfig()
@ -345,6 +375,7 @@ func GetDNSProviderByJsonConfig(name string, js string)(challenge.Provider, erro
if err != nil { if err != nil {
return nil, err return nil, err
} }
cfg.PropagationTimeout = 5*time.Minute
return domeneshop.NewDNSProviderConfig(cfg) return domeneshop.NewDNSProviderConfig(cfg)
case "dreamhost": case "dreamhost":
cfg := dreamhost.NewDefaultConfig() cfg := dreamhost.NewDefaultConfig()
@ -352,6 +383,7 @@ func GetDNSProviderByJsonConfig(name string, js string)(challenge.Provider, erro
if err != nil { if err != nil {
return nil, err return nil, err
} }
cfg.PropagationTimeout = 5*time.Minute
return dreamhost.NewDNSProviderConfig(cfg) return dreamhost.NewDNSProviderConfig(cfg)
case "duckdns": case "duckdns":
cfg := duckdns.NewDefaultConfig() cfg := duckdns.NewDefaultConfig()
@ -359,6 +391,7 @@ func GetDNSProviderByJsonConfig(name string, js string)(challenge.Provider, erro
if err != nil { if err != nil {
return nil, err return nil, err
} }
cfg.PropagationTimeout = 5*time.Minute
return duckdns.NewDNSProviderConfig(cfg) return duckdns.NewDNSProviderConfig(cfg)
case "dyn": case "dyn":
cfg := dyn.NewDefaultConfig() cfg := dyn.NewDefaultConfig()
@ -366,6 +399,7 @@ func GetDNSProviderByJsonConfig(name string, js string)(challenge.Provider, erro
if err != nil { if err != nil {
return nil, err return nil, err
} }
cfg.PropagationTimeout = 5*time.Minute
return dyn.NewDNSProviderConfig(cfg) return dyn.NewDNSProviderConfig(cfg)
case "dynu": case "dynu":
cfg := dynu.NewDefaultConfig() cfg := dynu.NewDefaultConfig()
@ -373,6 +407,7 @@ func GetDNSProviderByJsonConfig(name string, js string)(challenge.Provider, erro
if err != nil { if err != nil {
return nil, err return nil, err
} }
cfg.PropagationTimeout = 5*time.Minute
return dynu.NewDNSProviderConfig(cfg) return dynu.NewDNSProviderConfig(cfg)
case "easydns": case "easydns":
cfg := easydns.NewDefaultConfig() cfg := easydns.NewDefaultConfig()
@ -380,6 +415,7 @@ func GetDNSProviderByJsonConfig(name string, js string)(challenge.Provider, erro
if err != nil { if err != nil {
return nil, err return nil, err
} }
cfg.PropagationTimeout = 5*time.Minute
return easydns.NewDNSProviderConfig(cfg) return easydns.NewDNSProviderConfig(cfg)
case "efficientip": case "efficientip":
cfg := efficientip.NewDefaultConfig() cfg := efficientip.NewDefaultConfig()
@ -387,6 +423,7 @@ func GetDNSProviderByJsonConfig(name string, js string)(challenge.Provider, erro
if err != nil { if err != nil {
return nil, err return nil, err
} }
cfg.PropagationTimeout = 5*time.Minute
return efficientip.NewDNSProviderConfig(cfg) return efficientip.NewDNSProviderConfig(cfg)
case "epik": case "epik":
cfg := epik.NewDefaultConfig() cfg := epik.NewDefaultConfig()
@ -394,6 +431,7 @@ func GetDNSProviderByJsonConfig(name string, js string)(challenge.Provider, erro
if err != nil { if err != nil {
return nil, err return nil, err
} }
cfg.PropagationTimeout = 5*time.Minute
return epik.NewDNSProviderConfig(cfg) return epik.NewDNSProviderConfig(cfg)
case "exoscale": case "exoscale":
cfg := exoscale.NewDefaultConfig() cfg := exoscale.NewDefaultConfig()
@ -401,6 +439,7 @@ func GetDNSProviderByJsonConfig(name string, js string)(challenge.Provider, erro
if err != nil { if err != nil {
return nil, err return nil, err
} }
cfg.PropagationTimeout = 5*time.Minute
return exoscale.NewDNSProviderConfig(cfg) return exoscale.NewDNSProviderConfig(cfg)
case "freemyip": case "freemyip":
cfg := freemyip.NewDefaultConfig() cfg := freemyip.NewDefaultConfig()
@ -408,6 +447,7 @@ func GetDNSProviderByJsonConfig(name string, js string)(challenge.Provider, erro
if err != nil { if err != nil {
return nil, err return nil, err
} }
cfg.PropagationTimeout = 5*time.Minute
return freemyip.NewDNSProviderConfig(cfg) return freemyip.NewDNSProviderConfig(cfg)
case "gandi": case "gandi":
cfg := gandi.NewDefaultConfig() cfg := gandi.NewDefaultConfig()
@ -415,6 +455,7 @@ func GetDNSProviderByJsonConfig(name string, js string)(challenge.Provider, erro
if err != nil { if err != nil {
return nil, err return nil, err
} }
cfg.PropagationTimeout = 5*time.Minute
return gandi.NewDNSProviderConfig(cfg) return gandi.NewDNSProviderConfig(cfg)
case "gandiv5": case "gandiv5":
cfg := gandiv5.NewDefaultConfig() cfg := gandiv5.NewDefaultConfig()
@ -422,6 +463,7 @@ func GetDNSProviderByJsonConfig(name string, js string)(challenge.Provider, erro
if err != nil { if err != nil {
return nil, err return nil, err
} }
cfg.PropagationTimeout = 5*time.Minute
return gandiv5.NewDNSProviderConfig(cfg) return gandiv5.NewDNSProviderConfig(cfg)
case "gcore": case "gcore":
cfg := gcore.NewDefaultConfig() cfg := gcore.NewDefaultConfig()
@ -429,6 +471,7 @@ func GetDNSProviderByJsonConfig(name string, js string)(challenge.Provider, erro
if err != nil { if err != nil {
return nil, err return nil, err
} }
cfg.PropagationTimeout = 5*time.Minute
return gcore.NewDNSProviderConfig(cfg) return gcore.NewDNSProviderConfig(cfg)
case "glesys": case "glesys":
cfg := glesys.NewDefaultConfig() cfg := glesys.NewDefaultConfig()
@ -436,6 +479,7 @@ func GetDNSProviderByJsonConfig(name string, js string)(challenge.Provider, erro
if err != nil { if err != nil {
return nil, err return nil, err
} }
cfg.PropagationTimeout = 5*time.Minute
return glesys.NewDNSProviderConfig(cfg) return glesys.NewDNSProviderConfig(cfg)
case "godaddy": case "godaddy":
cfg := godaddy.NewDefaultConfig() cfg := godaddy.NewDefaultConfig()
@ -443,6 +487,7 @@ func GetDNSProviderByJsonConfig(name string, js string)(challenge.Provider, erro
if err != nil { if err != nil {
return nil, err return nil, err
} }
cfg.PropagationTimeout = 5*time.Minute
return godaddy.NewDNSProviderConfig(cfg) return godaddy.NewDNSProviderConfig(cfg)
case "googledomains": case "googledomains":
cfg := googledomains.NewDefaultConfig() cfg := googledomains.NewDefaultConfig()
@ -450,6 +495,7 @@ func GetDNSProviderByJsonConfig(name string, js string)(challenge.Provider, erro
if err != nil { if err != nil {
return nil, err return nil, err
} }
cfg.PropagationTimeout = 5*time.Minute
return googledomains.NewDNSProviderConfig(cfg) return googledomains.NewDNSProviderConfig(cfg)
case "hetzner": case "hetzner":
cfg := hetzner.NewDefaultConfig() cfg := hetzner.NewDefaultConfig()
@ -457,6 +503,7 @@ func GetDNSProviderByJsonConfig(name string, js string)(challenge.Provider, erro
if err != nil { if err != nil {
return nil, err return nil, err
} }
cfg.PropagationTimeout = 5*time.Minute
return hetzner.NewDNSProviderConfig(cfg) return hetzner.NewDNSProviderConfig(cfg)
case "hostingde": case "hostingde":
cfg := hostingde.NewDefaultConfig() cfg := hostingde.NewDefaultConfig()
@ -464,6 +511,7 @@ func GetDNSProviderByJsonConfig(name string, js string)(challenge.Provider, erro
if err != nil { if err != nil {
return nil, err return nil, err
} }
cfg.PropagationTimeout = 5*time.Minute
return hostingde.NewDNSProviderConfig(cfg) return hostingde.NewDNSProviderConfig(cfg)
case "hosttech": case "hosttech":
cfg := hosttech.NewDefaultConfig() cfg := hosttech.NewDefaultConfig()
@ -471,6 +519,7 @@ func GetDNSProviderByJsonConfig(name string, js string)(challenge.Provider, erro
if err != nil { if err != nil {
return nil, err return nil, err
} }
cfg.PropagationTimeout = 5*time.Minute
return hosttech.NewDNSProviderConfig(cfg) return hosttech.NewDNSProviderConfig(cfg)
case "httpnet": case "httpnet":
cfg := httpnet.NewDefaultConfig() cfg := httpnet.NewDefaultConfig()
@ -478,6 +527,7 @@ func GetDNSProviderByJsonConfig(name string, js string)(challenge.Provider, erro
if err != nil { if err != nil {
return nil, err return nil, err
} }
cfg.PropagationTimeout = 5*time.Minute
return httpnet.NewDNSProviderConfig(cfg) return httpnet.NewDNSProviderConfig(cfg)
case "hyperone": case "hyperone":
cfg := hyperone.NewDefaultConfig() cfg := hyperone.NewDefaultConfig()
@ -485,6 +535,7 @@ func GetDNSProviderByJsonConfig(name string, js string)(challenge.Provider, erro
if err != nil { if err != nil {
return nil, err return nil, err
} }
cfg.PropagationTimeout = 5*time.Minute
return hyperone.NewDNSProviderConfig(cfg) return hyperone.NewDNSProviderConfig(cfg)
case "ibmcloud": case "ibmcloud":
cfg := ibmcloud.NewDefaultConfig() cfg := ibmcloud.NewDefaultConfig()
@ -492,6 +543,7 @@ func GetDNSProviderByJsonConfig(name string, js string)(challenge.Provider, erro
if err != nil { if err != nil {
return nil, err return nil, err
} }
cfg.PropagationTimeout = 5*time.Minute
return ibmcloud.NewDNSProviderConfig(cfg) return ibmcloud.NewDNSProviderConfig(cfg)
case "iij": case "iij":
cfg := iij.NewDefaultConfig() cfg := iij.NewDefaultConfig()
@ -499,6 +551,7 @@ func GetDNSProviderByJsonConfig(name string, js string)(challenge.Provider, erro
if err != nil { if err != nil {
return nil, err return nil, err
} }
cfg.PropagationTimeout = 5*time.Minute
return iij.NewDNSProviderConfig(cfg) return iij.NewDNSProviderConfig(cfg)
case "iijdpf": case "iijdpf":
cfg := iijdpf.NewDefaultConfig() cfg := iijdpf.NewDefaultConfig()
@ -506,6 +559,7 @@ func GetDNSProviderByJsonConfig(name string, js string)(challenge.Provider, erro
if err != nil { if err != nil {
return nil, err return nil, err
} }
cfg.PropagationTimeout = 5*time.Minute
return iijdpf.NewDNSProviderConfig(cfg) return iijdpf.NewDNSProviderConfig(cfg)
case "infoblox": case "infoblox":
cfg := infoblox.NewDefaultConfig() cfg := infoblox.NewDefaultConfig()
@ -513,6 +567,7 @@ func GetDNSProviderByJsonConfig(name string, js string)(challenge.Provider, erro
if err != nil { if err != nil {
return nil, err return nil, err
} }
cfg.PropagationTimeout = 5*time.Minute
return infoblox.NewDNSProviderConfig(cfg) return infoblox.NewDNSProviderConfig(cfg)
case "infomaniak": case "infomaniak":
cfg := infomaniak.NewDefaultConfig() cfg := infomaniak.NewDefaultConfig()
@ -520,6 +575,7 @@ func GetDNSProviderByJsonConfig(name string, js string)(challenge.Provider, erro
if err != nil { if err != nil {
return nil, err return nil, err
} }
cfg.PropagationTimeout = 5*time.Minute
return infomaniak.NewDNSProviderConfig(cfg) return infomaniak.NewDNSProviderConfig(cfg)
case "internetbs": case "internetbs":
cfg := internetbs.NewDefaultConfig() cfg := internetbs.NewDefaultConfig()
@ -527,6 +583,7 @@ func GetDNSProviderByJsonConfig(name string, js string)(challenge.Provider, erro
if err != nil { if err != nil {
return nil, err return nil, err
} }
cfg.PropagationTimeout = 5*time.Minute
return internetbs.NewDNSProviderConfig(cfg) return internetbs.NewDNSProviderConfig(cfg)
case "inwx": case "inwx":
cfg := inwx.NewDefaultConfig() cfg := inwx.NewDefaultConfig()
@ -534,6 +591,7 @@ func GetDNSProviderByJsonConfig(name string, js string)(challenge.Provider, erro
if err != nil { if err != nil {
return nil, err return nil, err
} }
cfg.PropagationTimeout = 5*time.Minute
return inwx.NewDNSProviderConfig(cfg) return inwx.NewDNSProviderConfig(cfg)
case "ionos": case "ionos":
cfg := ionos.NewDefaultConfig() cfg := ionos.NewDefaultConfig()
@ -541,6 +599,7 @@ func GetDNSProviderByJsonConfig(name string, js string)(challenge.Provider, erro
if err != nil { if err != nil {
return nil, err return nil, err
} }
cfg.PropagationTimeout = 5*time.Minute
return ionos.NewDNSProviderConfig(cfg) return ionos.NewDNSProviderConfig(cfg)
case "ipv64": case "ipv64":
cfg := ipv64.NewDefaultConfig() cfg := ipv64.NewDefaultConfig()
@ -548,6 +607,7 @@ func GetDNSProviderByJsonConfig(name string, js string)(challenge.Provider, erro
if err != nil { if err != nil {
return nil, err return nil, err
} }
cfg.PropagationTimeout = 5*time.Minute
return ipv64.NewDNSProviderConfig(cfg) return ipv64.NewDNSProviderConfig(cfg)
case "iwantmyname": case "iwantmyname":
cfg := iwantmyname.NewDefaultConfig() cfg := iwantmyname.NewDefaultConfig()
@ -555,6 +615,7 @@ func GetDNSProviderByJsonConfig(name string, js string)(challenge.Provider, erro
if err != nil { if err != nil {
return nil, err return nil, err
} }
cfg.PropagationTimeout = 5*time.Minute
return iwantmyname.NewDNSProviderConfig(cfg) return iwantmyname.NewDNSProviderConfig(cfg)
case "joker": case "joker":
cfg := joker.NewDefaultConfig() cfg := joker.NewDefaultConfig()
@ -562,6 +623,7 @@ func GetDNSProviderByJsonConfig(name string, js string)(challenge.Provider, erro
if err != nil { if err != nil {
return nil, err return nil, err
} }
cfg.PropagationTimeout = 5*time.Minute
return joker.NewDNSProviderConfig(cfg) return joker.NewDNSProviderConfig(cfg)
case "liara": case "liara":
cfg := liara.NewDefaultConfig() cfg := liara.NewDefaultConfig()
@ -569,6 +631,7 @@ func GetDNSProviderByJsonConfig(name string, js string)(challenge.Provider, erro
if err != nil { if err != nil {
return nil, err return nil, err
} }
cfg.PropagationTimeout = 5*time.Minute
return liara.NewDNSProviderConfig(cfg) return liara.NewDNSProviderConfig(cfg)
case "lightsail": case "lightsail":
cfg := lightsail.NewDefaultConfig() cfg := lightsail.NewDefaultConfig()
@ -576,6 +639,7 @@ func GetDNSProviderByJsonConfig(name string, js string)(challenge.Provider, erro
if err != nil { if err != nil {
return nil, err return nil, err
} }
cfg.PropagationTimeout = 5*time.Minute
return lightsail.NewDNSProviderConfig(cfg) return lightsail.NewDNSProviderConfig(cfg)
case "linode": case "linode":
cfg := linode.NewDefaultConfig() cfg := linode.NewDefaultConfig()
@ -583,6 +647,7 @@ func GetDNSProviderByJsonConfig(name string, js string)(challenge.Provider, erro
if err != nil { if err != nil {
return nil, err return nil, err
} }
cfg.PropagationTimeout = 5*time.Minute
return linode.NewDNSProviderConfig(cfg) return linode.NewDNSProviderConfig(cfg)
case "liquidweb": case "liquidweb":
cfg := liquidweb.NewDefaultConfig() cfg := liquidweb.NewDefaultConfig()
@ -590,6 +655,7 @@ func GetDNSProviderByJsonConfig(name string, js string)(challenge.Provider, erro
if err != nil { if err != nil {
return nil, err return nil, err
} }
cfg.PropagationTimeout = 5*time.Minute
return liquidweb.NewDNSProviderConfig(cfg) return liquidweb.NewDNSProviderConfig(cfg)
case "loopia": case "loopia":
cfg := loopia.NewDefaultConfig() cfg := loopia.NewDefaultConfig()
@ -597,6 +663,7 @@ func GetDNSProviderByJsonConfig(name string, js string)(challenge.Provider, erro
if err != nil { if err != nil {
return nil, err return nil, err
} }
cfg.PropagationTimeout = 5*time.Minute
return loopia.NewDNSProviderConfig(cfg) return loopia.NewDNSProviderConfig(cfg)
case "luadns": case "luadns":
cfg := luadns.NewDefaultConfig() cfg := luadns.NewDefaultConfig()
@ -604,6 +671,7 @@ func GetDNSProviderByJsonConfig(name string, js string)(challenge.Provider, erro
if err != nil { if err != nil {
return nil, err return nil, err
} }
cfg.PropagationTimeout = 5*time.Minute
return luadns.NewDNSProviderConfig(cfg) return luadns.NewDNSProviderConfig(cfg)
case "mailinabox": case "mailinabox":
cfg := mailinabox.NewDefaultConfig() cfg := mailinabox.NewDefaultConfig()
@ -611,6 +679,7 @@ func GetDNSProviderByJsonConfig(name string, js string)(challenge.Provider, erro
if err != nil { if err != nil {
return nil, err return nil, err
} }
cfg.PropagationTimeout = 5*time.Minute
return mailinabox.NewDNSProviderConfig(cfg) return mailinabox.NewDNSProviderConfig(cfg)
case "metaname": case "metaname":
cfg := metaname.NewDefaultConfig() cfg := metaname.NewDefaultConfig()
@ -618,6 +687,7 @@ func GetDNSProviderByJsonConfig(name string, js string)(challenge.Provider, erro
if err != nil { if err != nil {
return nil, err return nil, err
} }
cfg.PropagationTimeout = 5*time.Minute
return metaname.NewDNSProviderConfig(cfg) return metaname.NewDNSProviderConfig(cfg)
case "mydnsjp": case "mydnsjp":
cfg := mydnsjp.NewDefaultConfig() cfg := mydnsjp.NewDefaultConfig()
@ -625,6 +695,7 @@ func GetDNSProviderByJsonConfig(name string, js string)(challenge.Provider, erro
if err != nil { if err != nil {
return nil, err return nil, err
} }
cfg.PropagationTimeout = 5*time.Minute
return mydnsjp.NewDNSProviderConfig(cfg) return mydnsjp.NewDNSProviderConfig(cfg)
case "namecheap": case "namecheap":
cfg := namecheap.NewDefaultConfig() cfg := namecheap.NewDefaultConfig()
@ -632,6 +703,7 @@ func GetDNSProviderByJsonConfig(name string, js string)(challenge.Provider, erro
if err != nil { if err != nil {
return nil, err return nil, err
} }
cfg.PropagationTimeout = 5*time.Minute
return namecheap.NewDNSProviderConfig(cfg) return namecheap.NewDNSProviderConfig(cfg)
case "namedotcom": case "namedotcom":
cfg := namedotcom.NewDefaultConfig() cfg := namedotcom.NewDefaultConfig()
@ -639,6 +711,7 @@ func GetDNSProviderByJsonConfig(name string, js string)(challenge.Provider, erro
if err != nil { if err != nil {
return nil, err return nil, err
} }
cfg.PropagationTimeout = 5*time.Minute
return namedotcom.NewDNSProviderConfig(cfg) return namedotcom.NewDNSProviderConfig(cfg)
case "namesilo": case "namesilo":
cfg := namesilo.NewDefaultConfig() cfg := namesilo.NewDefaultConfig()
@ -646,6 +719,7 @@ func GetDNSProviderByJsonConfig(name string, js string)(challenge.Provider, erro
if err != nil { if err != nil {
return nil, err return nil, err
} }
cfg.PropagationTimeout = 5*time.Minute
return namesilo.NewDNSProviderConfig(cfg) return namesilo.NewDNSProviderConfig(cfg)
case "nearlyfreespeech": case "nearlyfreespeech":
cfg := nearlyfreespeech.NewDefaultConfig() cfg := nearlyfreespeech.NewDefaultConfig()
@ -653,20 +727,23 @@ func GetDNSProviderByJsonConfig(name string, js string)(challenge.Provider, erro
if err != nil { if err != nil {
return nil, err return nil, err
} }
cfg.PropagationTimeout = 5*time.Minute
return nearlyfreespeech.NewDNSProviderConfig(cfg) return nearlyfreespeech.NewDNSProviderConfig(cfg)
case "netcup": case "netcup":
cfg := netcup.NewDefaultConfig() cfg := netcup.NewDefaultConfig()
err := json.Unmarshal([]byte(js), &cfg) err := json.Unmarshal([]byte(js), &cfg)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return netcup.NewDNSProviderConfig(cfg) cfg.PropagationTimeout = 20*time.Minute
return netcup.NewDNSProviderConfig(cfg)
case "netlify": case "netlify":
cfg := netlify.NewDefaultConfig() cfg := netlify.NewDefaultConfig()
err := json.Unmarshal([]byte(js), &cfg) err := json.Unmarshal([]byte(js), &cfg)
if err != nil { if err != nil {
return nil, err return nil, err
} }
cfg.PropagationTimeout = 5*time.Minute
return netlify.NewDNSProviderConfig(cfg) return netlify.NewDNSProviderConfig(cfg)
case "nicmanager": case "nicmanager":
cfg := nicmanager.NewDefaultConfig() cfg := nicmanager.NewDefaultConfig()
@ -674,6 +751,7 @@ func GetDNSProviderByJsonConfig(name string, js string)(challenge.Provider, erro
if err != nil { if err != nil {
return nil, err return nil, err
} }
cfg.PropagationTimeout = 5*time.Minute
return nicmanager.NewDNSProviderConfig(cfg) return nicmanager.NewDNSProviderConfig(cfg)
case "nifcloud": case "nifcloud":
cfg := nifcloud.NewDefaultConfig() cfg := nifcloud.NewDefaultConfig()
@ -681,6 +759,7 @@ func GetDNSProviderByJsonConfig(name string, js string)(challenge.Provider, erro
if err != nil { if err != nil {
return nil, err return nil, err
} }
cfg.PropagationTimeout = 5*time.Minute
return nifcloud.NewDNSProviderConfig(cfg) return nifcloud.NewDNSProviderConfig(cfg)
case "njalla": case "njalla":
cfg := njalla.NewDefaultConfig() cfg := njalla.NewDefaultConfig()
@ -688,6 +767,7 @@ func GetDNSProviderByJsonConfig(name string, js string)(challenge.Provider, erro
if err != nil { if err != nil {
return nil, err return nil, err
} }
cfg.PropagationTimeout = 5*time.Minute
return njalla.NewDNSProviderConfig(cfg) return njalla.NewDNSProviderConfig(cfg)
case "nodion": case "nodion":
cfg := nodion.NewDefaultConfig() cfg := nodion.NewDefaultConfig()
@ -695,6 +775,7 @@ func GetDNSProviderByJsonConfig(name string, js string)(challenge.Provider, erro
if err != nil { if err != nil {
return nil, err return nil, err
} }
cfg.PropagationTimeout = 5*time.Minute
return nodion.NewDNSProviderConfig(cfg) return nodion.NewDNSProviderConfig(cfg)
case "ns1": case "ns1":
cfg := ns1.NewDefaultConfig() cfg := ns1.NewDefaultConfig()
@ -702,6 +783,7 @@ func GetDNSProviderByJsonConfig(name string, js string)(challenge.Provider, erro
if err != nil { if err != nil {
return nil, err return nil, err
} }
cfg.PropagationTimeout = 5*time.Minute
return ns1.NewDNSProviderConfig(cfg) return ns1.NewDNSProviderConfig(cfg)
case "otc": case "otc":
cfg := otc.NewDefaultConfig() cfg := otc.NewDefaultConfig()
@ -709,6 +791,7 @@ func GetDNSProviderByJsonConfig(name string, js string)(challenge.Provider, erro
if err != nil { if err != nil {
return nil, err return nil, err
} }
cfg.PropagationTimeout = 5*time.Minute
return otc.NewDNSProviderConfig(cfg) return otc.NewDNSProviderConfig(cfg)
case "ovh": case "ovh":
cfg := ovh.NewDefaultConfig() cfg := ovh.NewDefaultConfig()
@ -716,6 +799,7 @@ func GetDNSProviderByJsonConfig(name string, js string)(challenge.Provider, erro
if err != nil { if err != nil {
return nil, err return nil, err
} }
cfg.PropagationTimeout = 5*time.Minute
return ovh.NewDNSProviderConfig(cfg) return ovh.NewDNSProviderConfig(cfg)
case "pdns": case "pdns":
cfg := pdns.NewDefaultConfig() cfg := pdns.NewDefaultConfig()
@ -723,6 +807,7 @@ func GetDNSProviderByJsonConfig(name string, js string)(challenge.Provider, erro
if err != nil { if err != nil {
return nil, err return nil, err
} }
cfg.PropagationTimeout = 5*time.Minute
return pdns.NewDNSProviderConfig(cfg) return pdns.NewDNSProviderConfig(cfg)
case "plesk": case "plesk":
cfg := plesk.NewDefaultConfig() cfg := plesk.NewDefaultConfig()
@ -730,6 +815,7 @@ func GetDNSProviderByJsonConfig(name string, js string)(challenge.Provider, erro
if err != nil { if err != nil {
return nil, err return nil, err
} }
cfg.PropagationTimeout = 5*time.Minute
return plesk.NewDNSProviderConfig(cfg) return plesk.NewDNSProviderConfig(cfg)
case "porkbun": case "porkbun":
cfg := porkbun.NewDefaultConfig() cfg := porkbun.NewDefaultConfig()
@ -737,6 +823,7 @@ func GetDNSProviderByJsonConfig(name string, js string)(challenge.Provider, erro
if err != nil { if err != nil {
return nil, err return nil, err
} }
cfg.PropagationTimeout = 5*time.Minute
return porkbun.NewDNSProviderConfig(cfg) return porkbun.NewDNSProviderConfig(cfg)
case "rackspace": case "rackspace":
cfg := rackspace.NewDefaultConfig() cfg := rackspace.NewDefaultConfig()
@ -744,6 +831,7 @@ func GetDNSProviderByJsonConfig(name string, js string)(challenge.Provider, erro
if err != nil { if err != nil {
return nil, err return nil, err
} }
cfg.PropagationTimeout = 5*time.Minute
return rackspace.NewDNSProviderConfig(cfg) return rackspace.NewDNSProviderConfig(cfg)
case "rcodezero": case "rcodezero":
cfg := rcodezero.NewDefaultConfig() cfg := rcodezero.NewDefaultConfig()
@ -751,6 +839,7 @@ func GetDNSProviderByJsonConfig(name string, js string)(challenge.Provider, erro
if err != nil { if err != nil {
return nil, err return nil, err
} }
cfg.PropagationTimeout = 5*time.Minute
return rcodezero.NewDNSProviderConfig(cfg) return rcodezero.NewDNSProviderConfig(cfg)
case "regru": case "regru":
cfg := regru.NewDefaultConfig() cfg := regru.NewDefaultConfig()
@ -758,6 +847,7 @@ func GetDNSProviderByJsonConfig(name string, js string)(challenge.Provider, erro
if err != nil { if err != nil {
return nil, err return nil, err
} }
cfg.PropagationTimeout = 5*time.Minute
return regru.NewDNSProviderConfig(cfg) return regru.NewDNSProviderConfig(cfg)
case "rfc2136": case "rfc2136":
cfg := rfc2136.NewDefaultConfig() cfg := rfc2136.NewDefaultConfig()
@ -765,6 +855,7 @@ func GetDNSProviderByJsonConfig(name string, js string)(challenge.Provider, erro
if err != nil { if err != nil {
return nil, err return nil, err
} }
cfg.PropagationTimeout = 5*time.Minute
return rfc2136.NewDNSProviderConfig(cfg) return rfc2136.NewDNSProviderConfig(cfg)
case "rimuhosting": case "rimuhosting":
cfg := rimuhosting.NewDefaultConfig() cfg := rimuhosting.NewDefaultConfig()
@ -772,6 +863,7 @@ func GetDNSProviderByJsonConfig(name string, js string)(challenge.Provider, erro
if err != nil { if err != nil {
return nil, err return nil, err
} }
cfg.PropagationTimeout = 5*time.Minute
return rimuhosting.NewDNSProviderConfig(cfg) return rimuhosting.NewDNSProviderConfig(cfg)
case "route53": case "route53":
cfg := route53.NewDefaultConfig() cfg := route53.NewDefaultConfig()
@ -779,6 +871,7 @@ func GetDNSProviderByJsonConfig(name string, js string)(challenge.Provider, erro
if err != nil { if err != nil {
return nil, err return nil, err
} }
cfg.PropagationTimeout = 5*time.Minute
return route53.NewDNSProviderConfig(cfg) return route53.NewDNSProviderConfig(cfg)
case "safedns": case "safedns":
cfg := safedns.NewDefaultConfig() cfg := safedns.NewDefaultConfig()
@ -786,6 +879,7 @@ func GetDNSProviderByJsonConfig(name string, js string)(challenge.Provider, erro
if err != nil { if err != nil {
return nil, err return nil, err
} }
cfg.PropagationTimeout = 5*time.Minute
return safedns.NewDNSProviderConfig(cfg) return safedns.NewDNSProviderConfig(cfg)
case "sakuracloud": case "sakuracloud":
cfg := sakuracloud.NewDefaultConfig() cfg := sakuracloud.NewDefaultConfig()
@ -793,6 +887,7 @@ func GetDNSProviderByJsonConfig(name string, js string)(challenge.Provider, erro
if err != nil { if err != nil {
return nil, err return nil, err
} }
cfg.PropagationTimeout = 5*time.Minute
return sakuracloud.NewDNSProviderConfig(cfg) return sakuracloud.NewDNSProviderConfig(cfg)
case "scaleway": case "scaleway":
cfg := scaleway.NewDefaultConfig() cfg := scaleway.NewDefaultConfig()
@ -800,6 +895,7 @@ func GetDNSProviderByJsonConfig(name string, js string)(challenge.Provider, erro
if err != nil { if err != nil {
return nil, err return nil, err
} }
cfg.PropagationTimeout = 5*time.Minute
return scaleway.NewDNSProviderConfig(cfg) return scaleway.NewDNSProviderConfig(cfg)
case "selectel": case "selectel":
cfg := selectel.NewDefaultConfig() cfg := selectel.NewDefaultConfig()
@ -807,6 +903,7 @@ func GetDNSProviderByJsonConfig(name string, js string)(challenge.Provider, erro
if err != nil { if err != nil {
return nil, err return nil, err
} }
cfg.PropagationTimeout = 5*time.Minute
return selectel.NewDNSProviderConfig(cfg) return selectel.NewDNSProviderConfig(cfg)
case "servercow": case "servercow":
cfg := servercow.NewDefaultConfig() cfg := servercow.NewDefaultConfig()
@ -814,6 +911,7 @@ func GetDNSProviderByJsonConfig(name string, js string)(challenge.Provider, erro
if err != nil { if err != nil {
return nil, err return nil, err
} }
cfg.PropagationTimeout = 5*time.Minute
return servercow.NewDNSProviderConfig(cfg) return servercow.NewDNSProviderConfig(cfg)
case "shellrent": case "shellrent":
cfg := shellrent.NewDefaultConfig() cfg := shellrent.NewDefaultConfig()
@ -821,6 +919,7 @@ func GetDNSProviderByJsonConfig(name string, js string)(challenge.Provider, erro
if err != nil { if err != nil {
return nil, err return nil, err
} }
cfg.PropagationTimeout = 5*time.Minute
return shellrent.NewDNSProviderConfig(cfg) return shellrent.NewDNSProviderConfig(cfg)
case "simply": case "simply":
cfg := simply.NewDefaultConfig() cfg := simply.NewDefaultConfig()
@ -828,6 +927,7 @@ func GetDNSProviderByJsonConfig(name string, js string)(challenge.Provider, erro
if err != nil { if err != nil {
return nil, err return nil, err
} }
cfg.PropagationTimeout = 5*time.Minute
return simply.NewDNSProviderConfig(cfg) return simply.NewDNSProviderConfig(cfg)
case "sonic": case "sonic":
cfg := sonic.NewDefaultConfig() cfg := sonic.NewDefaultConfig()
@ -835,6 +935,7 @@ func GetDNSProviderByJsonConfig(name string, js string)(challenge.Provider, erro
if err != nil { if err != nil {
return nil, err return nil, err
} }
cfg.PropagationTimeout = 5*time.Minute
return sonic.NewDNSProviderConfig(cfg) return sonic.NewDNSProviderConfig(cfg)
case "stackpath": case "stackpath":
cfg := stackpath.NewDefaultConfig() cfg := stackpath.NewDefaultConfig()
@ -842,6 +943,7 @@ func GetDNSProviderByJsonConfig(name string, js string)(challenge.Provider, erro
if err != nil { if err != nil {
return nil, err return nil, err
} }
cfg.PropagationTimeout = 5*time.Minute
return stackpath.NewDNSProviderConfig(cfg) return stackpath.NewDNSProviderConfig(cfg)
case "tencentcloud": case "tencentcloud":
cfg := tencentcloud.NewDefaultConfig() cfg := tencentcloud.NewDefaultConfig()
@ -849,6 +951,7 @@ func GetDNSProviderByJsonConfig(name string, js string)(challenge.Provider, erro
if err != nil { if err != nil {
return nil, err return nil, err
} }
cfg.PropagationTimeout = 5*time.Minute
return tencentcloud.NewDNSProviderConfig(cfg) return tencentcloud.NewDNSProviderConfig(cfg)
case "transip": case "transip":
cfg := transip.NewDefaultConfig() cfg := transip.NewDefaultConfig()
@ -856,6 +959,7 @@ func GetDNSProviderByJsonConfig(name string, js string)(challenge.Provider, erro
if err != nil { if err != nil {
return nil, err return nil, err
} }
cfg.PropagationTimeout = 5*time.Minute
return transip.NewDNSProviderConfig(cfg) return transip.NewDNSProviderConfig(cfg)
case "ultradns": case "ultradns":
cfg := ultradns.NewDefaultConfig() cfg := ultradns.NewDefaultConfig()
@ -863,6 +967,7 @@ func GetDNSProviderByJsonConfig(name string, js string)(challenge.Provider, erro
if err != nil { if err != nil {
return nil, err return nil, err
} }
cfg.PropagationTimeout = 5*time.Minute
return ultradns.NewDNSProviderConfig(cfg) return ultradns.NewDNSProviderConfig(cfg)
case "variomedia": case "variomedia":
cfg := variomedia.NewDefaultConfig() cfg := variomedia.NewDefaultConfig()
@ -870,6 +975,7 @@ func GetDNSProviderByJsonConfig(name string, js string)(challenge.Provider, erro
if err != nil { if err != nil {
return nil, err return nil, err
} }
cfg.PropagationTimeout = 5*time.Minute
return variomedia.NewDNSProviderConfig(cfg) return variomedia.NewDNSProviderConfig(cfg)
case "vegadns": case "vegadns":
cfg := vegadns.NewDefaultConfig() cfg := vegadns.NewDefaultConfig()
@ -877,6 +983,7 @@ func GetDNSProviderByJsonConfig(name string, js string)(challenge.Provider, erro
if err != nil { if err != nil {
return nil, err return nil, err
} }
cfg.PropagationTimeout = 5*time.Minute
return vegadns.NewDNSProviderConfig(cfg) return vegadns.NewDNSProviderConfig(cfg)
case "vercel": case "vercel":
cfg := vercel.NewDefaultConfig() cfg := vercel.NewDefaultConfig()
@ -884,6 +991,7 @@ func GetDNSProviderByJsonConfig(name string, js string)(challenge.Provider, erro
if err != nil { if err != nil {
return nil, err return nil, err
} }
cfg.PropagationTimeout = 5*time.Minute
return vercel.NewDNSProviderConfig(cfg) return vercel.NewDNSProviderConfig(cfg)
case "versio": case "versio":
cfg := versio.NewDefaultConfig() cfg := versio.NewDefaultConfig()
@ -891,6 +999,7 @@ func GetDNSProviderByJsonConfig(name string, js string)(challenge.Provider, erro
if err != nil { if err != nil {
return nil, err return nil, err
} }
cfg.PropagationTimeout = 5*time.Minute
return versio.NewDNSProviderConfig(cfg) return versio.NewDNSProviderConfig(cfg)
case "vinyldns": case "vinyldns":
cfg := vinyldns.NewDefaultConfig() cfg := vinyldns.NewDefaultConfig()
@ -898,6 +1007,7 @@ func GetDNSProviderByJsonConfig(name string, js string)(challenge.Provider, erro
if err != nil { if err != nil {
return nil, err return nil, err
} }
cfg.PropagationTimeout = 5*time.Minute
return vinyldns.NewDNSProviderConfig(cfg) return vinyldns.NewDNSProviderConfig(cfg)
case "vkcloud": case "vkcloud":
cfg := vkcloud.NewDefaultConfig() cfg := vkcloud.NewDefaultConfig()
@ -905,6 +1015,7 @@ func GetDNSProviderByJsonConfig(name string, js string)(challenge.Provider, erro
if err != nil { if err != nil {
return nil, err return nil, err
} }
cfg.PropagationTimeout = 5*time.Minute
return vkcloud.NewDNSProviderConfig(cfg) return vkcloud.NewDNSProviderConfig(cfg)
case "vscale": case "vscale":
cfg := vscale.NewDefaultConfig() cfg := vscale.NewDefaultConfig()
@ -912,6 +1023,7 @@ func GetDNSProviderByJsonConfig(name string, js string)(challenge.Provider, erro
if err != nil { if err != nil {
return nil, err return nil, err
} }
cfg.PropagationTimeout = 5*time.Minute
return vscale.NewDNSProviderConfig(cfg) return vscale.NewDNSProviderConfig(cfg)
case "vultr": case "vultr":
cfg := vultr.NewDefaultConfig() cfg := vultr.NewDefaultConfig()
@ -919,6 +1031,7 @@ func GetDNSProviderByJsonConfig(name string, js string)(challenge.Provider, erro
if err != nil { if err != nil {
return nil, err return nil, err
} }
cfg.PropagationTimeout = 5*time.Minute
return vultr.NewDNSProviderConfig(cfg) return vultr.NewDNSProviderConfig(cfg)
case "webnames": case "webnames":
cfg := webnames.NewDefaultConfig() cfg := webnames.NewDefaultConfig()
@ -926,6 +1039,7 @@ func GetDNSProviderByJsonConfig(name string, js string)(challenge.Provider, erro
if err != nil { if err != nil {
return nil, err return nil, err
} }
cfg.PropagationTimeout = 5*time.Minute
return webnames.NewDNSProviderConfig(cfg) return webnames.NewDNSProviderConfig(cfg)
case "websupport": case "websupport":
cfg := websupport.NewDefaultConfig() cfg := websupport.NewDefaultConfig()
@ -933,6 +1047,7 @@ func GetDNSProviderByJsonConfig(name string, js string)(challenge.Provider, erro
if err != nil { if err != nil {
return nil, err return nil, err
} }
cfg.PropagationTimeout = 5*time.Minute
return websupport.NewDNSProviderConfig(cfg) return websupport.NewDNSProviderConfig(cfg)
case "wedos": case "wedos":
cfg := wedos.NewDefaultConfig() cfg := wedos.NewDefaultConfig()
@ -940,6 +1055,7 @@ func GetDNSProviderByJsonConfig(name string, js string)(challenge.Provider, erro
if err != nil { if err != nil {
return nil, err return nil, err
} }
cfg.PropagationTimeout = 5*time.Minute
return wedos.NewDNSProviderConfig(cfg) return wedos.NewDNSProviderConfig(cfg)
case "yandex": case "yandex":
cfg := yandex.NewDefaultConfig() cfg := yandex.NewDefaultConfig()
@ -947,6 +1063,7 @@ func GetDNSProviderByJsonConfig(name string, js string)(challenge.Provider, erro
if err != nil { if err != nil {
return nil, err return nil, err
} }
cfg.PropagationTimeout = 5*time.Minute
return yandex.NewDNSProviderConfig(cfg) return yandex.NewDNSProviderConfig(cfg)
case "yandex360": case "yandex360":
cfg := yandex360.NewDefaultConfig() cfg := yandex360.NewDefaultConfig()
@ -954,6 +1071,7 @@ func GetDNSProviderByJsonConfig(name string, js string)(challenge.Provider, erro
if err != nil { if err != nil {
return nil, err return nil, err
} }
cfg.PropagationTimeout = 5*time.Minute
return yandex360.NewDNSProviderConfig(cfg) return yandex360.NewDNSProviderConfig(cfg)
case "yandexcloud": case "yandexcloud":
cfg := yandexcloud.NewDefaultConfig() cfg := yandexcloud.NewDefaultConfig()
@ -961,6 +1079,7 @@ func GetDNSProviderByJsonConfig(name string, js string)(challenge.Provider, erro
if err != nil { if err != nil {
return nil, err return nil, err
} }
cfg.PropagationTimeout = 5*time.Minute
return yandexcloud.NewDNSProviderConfig(cfg) return yandexcloud.NewDNSProviderConfig(cfg)
case "zoneee": case "zoneee":
cfg := zoneee.NewDefaultConfig() cfg := zoneee.NewDefaultConfig()
@ -968,6 +1087,7 @@ func GetDNSProviderByJsonConfig(name string, js string)(challenge.Provider, erro
if err != nil { if err != nil {
return nil, err return nil, err
} }
cfg.PropagationTimeout = 5*time.Minute
return zoneee.NewDNSProviderConfig(cfg) return zoneee.NewDNSProviderConfig(cfg)
case "zonomi": case "zonomi":
cfg := zonomi.NewDefaultConfig() cfg := zonomi.NewDefaultConfig()
@ -975,6 +1095,7 @@ func GetDNSProviderByJsonConfig(name string, js string)(challenge.Provider, erro
if err != nil { if err != nil {
return nil, err return nil, err
} }
cfg.PropagationTimeout = 5*time.Minute
return zonomi.NewDNSProviderConfig(cfg) return zonomi.NewDNSProviderConfig(cfg)
default: default:
return nil, fmt.Errorf("unrecognized DNS provider: %s", name) return nil, fmt.Errorf("unrecognized DNS provider: %s", name)

View File

@ -153,6 +153,10 @@
"azure": { "azure": {
"Name": "azure", "Name": "azure",
"ConfigableFields": [ "ConfigableFields": [
{
"Title": "ZoneName",
"Datatype": "string"
},
{ {
"Title": "ClientID", "Title": "ClientID",
"Datatype": "string" "Datatype": "string"
@ -208,6 +212,10 @@
"azuredns": { "azuredns": {
"Name": "azuredns", "Name": "azuredns",
"ConfigableFields": [ "ConfigableFields": [
{
"Title": "ZoneName",
"Datatype": "string"
},
{ {
"Title": "SubscriptionID", "Title": "SubscriptionID",
"Datatype": "string" "Datatype": "string"
@ -343,6 +351,10 @@
{ {
"Title": "HTTPClient", "Title": "HTTPClient",
"Datatype": "*http.Client" "Datatype": "*http.Client"
},
{
"Title": "SkipDeploy",
"Datatype": "bool"
} }
] ]
}, },
@ -1214,10 +1226,6 @@
"gandi": { "gandi": {
"Name": "gandi", "Name": "gandi",
"ConfigableFields": [ "ConfigableFields": [
{
"Title": "BaseURL",
"Datatype": "string"
},
{ {
"Title": "APIKey", "Title": "APIKey",
"Datatype": "string" "Datatype": "string"
@ -1241,10 +1249,6 @@
"gandiv5": { "gandiv5": {
"Name": "gandiv5", "Name": "gandiv5",
"ConfigableFields": [ "ConfigableFields": [
{
"Title": "BaseURL",
"Datatype": "string"
},
{ {
"Title": "APIKey", "Title": "APIKey",
"Datatype": "string" "Datatype": "string"

View File

@ -34,6 +34,7 @@ type AutoRenewer struct {
AcmeHandler *ACMEHandler AcmeHandler *ACMEHandler
RenewerConfig *AutoRenewConfig RenewerConfig *AutoRenewConfig
RenewTickInterval int64 RenewTickInterval int64
EarlyRenewDays int //How many days before cert expire to renew certificate
TickerstopChan chan bool TickerstopChan chan bool
} }
@ -44,11 +45,15 @@ type ExpiredCerts struct {
// Create an auto renew agent, require config filepath and auto scan & renew interval (seconds) // Create an auto renew agent, require config filepath and auto scan & renew interval (seconds)
// Set renew check interval to 0 for auto (1 day) // Set renew check interval to 0 for auto (1 day)
func NewAutoRenewer(config string, certFolder string, renewCheckInterval int64, AcmeHandler *ACMEHandler) (*AutoRenewer, error) { func NewAutoRenewer(config string, certFolder string, renewCheckInterval int64, earlyRenewDays int, AcmeHandler *ACMEHandler) (*AutoRenewer, error) {
if renewCheckInterval == 0 { if renewCheckInterval == 0 {
renewCheckInterval = 86400 //1 day renewCheckInterval = 86400 //1 day
} }
if earlyRenewDays == 0 {
earlyRenewDays = 30
}
//Load the config file. If not found, create one //Load the config file. If not found, create one
if !utils.FileExists(config) { if !utils.FileExists(config) {
//Create one //Create one
@ -135,7 +140,7 @@ func (a *AutoRenewer) StopAutoRenewTicker() {
// opr = setSelected -> Enter a list of file names (or matching rules) for auto renew // opr = setSelected -> Enter a list of file names (or matching rules) for auto renew
// opr = setAuto -> Set to use auto detect certificates and renew // opr = setAuto -> Set to use auto detect certificates and renew
func (a *AutoRenewer) HandleSetAutoRenewDomains(w http.ResponseWriter, r *http.Request) { func (a *AutoRenewer) HandleSetAutoRenewDomains(w http.ResponseWriter, r *http.Request) {
opr, err := utils.GetPara(r, "opr") opr, err := utils.PostPara(r, "opr")
if err != nil { if err != nil {
utils.SendErrorResponse(w, "Operation not set") utils.SendErrorResponse(w, "Operation not set")
return return
@ -165,6 +170,8 @@ func (a *AutoRenewer) HandleSetAutoRenewDomains(w http.ResponseWriter, r *http.R
a.RenewerConfig.RenewAll = true a.RenewerConfig.RenewAll = true
a.saveRenewConfigToFile() a.saveRenewConfigToFile()
utils.SendOK(w) utils.SendOK(w)
} else {
utils.SendErrorResponse(w, "invalid operation given")
} }
} }
@ -208,19 +215,22 @@ func (a *AutoRenewer) HandleRenewNow(w http.ResponseWriter, r *http.Request) {
utils.SendJSONResponse(w, string(js)) utils.SendJSONResponse(w, string(js))
} }
// HandleAutoRenewEnable get and set the auto renew enable state
func (a *AutoRenewer) HandleAutoRenewEnable(w http.ResponseWriter, r *http.Request) { func (a *AutoRenewer) HandleAutoRenewEnable(w http.ResponseWriter, r *http.Request) {
val, err := utils.PostPara(r, "enable") if r.Method == http.MethodGet {
if err != nil {
js, _ := json.Marshal(a.RenewerConfig.Enabled) js, _ := json.Marshal(a.RenewerConfig.Enabled)
utils.SendJSONResponse(w, string(js)) utils.SendJSONResponse(w, string(js))
} else { } else if r.Method == http.MethodPost {
if val == "true" { val, err := utils.PostBool(r, "enable")
if err != nil {
utils.SendErrorResponse(w, "invalid or empty enable state")
}
if val {
//Check if the email is not empty //Check if the email is not empty
if a.RenewerConfig.Email == "" { if a.RenewerConfig.Email == "" {
utils.SendErrorResponse(w, "Email is not set") utils.SendErrorResponse(w, "Email is not set")
return return
} }
a.RenewerConfig.Enabled = true a.RenewerConfig.Enabled = true
a.saveRenewConfigToFile() a.saveRenewConfigToFile()
log.Println("[ACME] ACME auto renew enabled") log.Println("[ACME] ACME auto renew enabled")
@ -231,19 +241,26 @@ func (a *AutoRenewer) HandleAutoRenewEnable(w http.ResponseWriter, r *http.Reque
log.Println("[ACME] ACME auto renew disabled") log.Println("[ACME] ACME auto renew disabled")
a.StopAutoRenewTicker() a.StopAutoRenewTicker()
} }
} else {
http.Error(w, "405 - Method not allowed", http.StatusMethodNotAllowed)
} }
} }
func (a *AutoRenewer) HandleACMEEmail(w http.ResponseWriter, r *http.Request) { func (a *AutoRenewer) HandleACMEEmail(w http.ResponseWriter, r *http.Request) {
if r.Method == http.MethodGet {
email, err := utils.PostPara(r, "set")
if err != nil {
//Return the current email to user //Return the current email to user
js, _ := json.Marshal(a.RenewerConfig.Email) js, _ := json.Marshal(a.RenewerConfig.Email)
utils.SendJSONResponse(w, string(js)) utils.SendJSONResponse(w, string(js))
} else { } else if r.Method == http.MethodPost {
email, err := utils.PostPara(r, "set")
if err != nil {
utils.SendErrorResponse(w, "invalid or empty email given")
return
}
//Check if the email is valid //Check if the email is valid
_, err := mail.ParseAddress(email) _, err = mail.ParseAddress(email)
if err != nil { if err != nil {
utils.SendErrorResponse(w, err.Error()) utils.SendErrorResponse(w, err.Error())
return return
@ -252,8 +269,11 @@ func (a *AutoRenewer) HandleACMEEmail(w http.ResponseWriter, r *http.Request) {
//Set the new config //Set the new config
a.RenewerConfig.Email = email a.RenewerConfig.Email = email
a.saveRenewConfigToFile() a.saveRenewConfigToFile()
}
utils.SendOK(w)
} else {
http.Error(w, "405 - Method not allowed", http.StatusMethodNotAllowed)
}
} }
// Check and renew certificates. This check all the certificates in the // Check and renew certificates. This check all the certificates in the
@ -277,7 +297,7 @@ func (a *AutoRenewer) CheckAndRenewCertificates() ([]string, error) {
if err != nil { if err != nil {
continue continue
} }
if CertExpireSoon(certBytes) || CertIsExpired(certBytes) { if CertExpireSoon(certBytes, a.EarlyRenewDays) || CertIsExpired(certBytes) {
//This cert is expired //This cert is expired
DNSName, err := ExtractDomains(certBytes) DNSName, err := ExtractDomains(certBytes)
@ -305,7 +325,7 @@ func (a *AutoRenewer) CheckAndRenewCertificates() ([]string, error) {
if err != nil { if err != nil {
continue continue
} }
if CertExpireSoon(certBytes) || CertIsExpired(certBytes) { if CertExpireSoon(certBytes, a.EarlyRenewDays) || CertIsExpired(certBytes) {
//This cert is expired //This cert is expired
DNSName, err := ExtractDomains(certBytes) DNSName, err := ExtractDomains(certBytes)

View File

@ -81,13 +81,14 @@ func CertIsExpired(certBytes []byte) bool {
return false return false
} }
func CertExpireSoon(certBytes []byte) bool { // CertExpireSoon check if the given cert bytes will expires within the given number of days from now
func CertExpireSoon(certBytes []byte, numberOfDays int) bool {
block, _ := pem.Decode(certBytes) block, _ := pem.Decode(certBytes)
if block != nil { if block != nil {
cert, err := x509.ParseCertificate(block.Bytes) cert, err := x509.ParseCertificate(block.Bytes)
if err == nil { if err == nil {
expirationDate := cert.NotAfter expirationDate := cert.NotAfter
threshold := 14 * 24 * time.Hour // 14 days threshold := time.Duration(numberOfDays) * 24 * time.Hour
timeRemaining := time.Until(expirationDate) timeRemaining := time.Until(expirationDate)
if timeRemaining <= threshold { if timeRemaining <= threshold {

View File

@ -10,7 +10,7 @@ type RouterOption struct {
AuthAgent *AuthAgent AuthAgent *AuthAgent
RequireAuth bool //This router require authentication RequireAuth bool //This router require authentication
DeniedHandler func(http.ResponseWriter, *http.Request) //Things to do when request is rejected DeniedHandler func(http.ResponseWriter, *http.Request) //Things to do when request is rejected
TargetMux *http.ServeMux
} }
type RouterDef struct { type RouterDef struct {
@ -35,17 +35,31 @@ func (router *RouterDef) HandleFunc(endpoint string, handler func(http.ResponseW
authAgent := router.option.AuthAgent authAgent := router.option.AuthAgent
//OK. Register handler //OK. Register handler
http.HandleFunc(endpoint, func(w http.ResponseWriter, r *http.Request) { if router.option.TargetMux == nil {
//Check authentication of the user http.HandleFunc(endpoint, func(w http.ResponseWriter, r *http.Request) {
if router.option.RequireAuth { //Check authentication of the user
authAgent.HandleCheckAuth(w, r, func(w http.ResponseWriter, r *http.Request) { if router.option.RequireAuth {
authAgent.HandleCheckAuth(w, r, func(w http.ResponseWriter, r *http.Request) {
handler(w, r)
})
} else {
handler(w, r) handler(w, r)
}) }
} else {
handler(w, r)
}
}) })
} else {
router.option.TargetMux.HandleFunc(endpoint, func(w http.ResponseWriter, r *http.Request) {
//Check authentication of the user
if router.option.RequireAuth {
authAgent.HandleCheckAuth(w, r, func(w http.ResponseWriter, r *http.Request) {
handler(w, r)
})
} else {
handler(w, r)
}
})
}
router.endpoints[endpoint] = handler router.endpoints[endpoint] = handler

View File

@ -3,8 +3,6 @@
package dockerux package dockerux
/* Windows docker optimizer*/
import ( import (
"context" "context"
"encoding/json" "encoding/json"
@ -16,7 +14,6 @@ import (
"imuslab.com/zoraxy/mod/utils" "imuslab.com/zoraxy/mod/utils"
) )
// Windows build not support docker
func (d *UXOptimizer) HandleDockerAvailable(w http.ResponseWriter, r *http.Request) { func (d *UXOptimizer) HandleDockerAvailable(w http.ResponseWriter, r *http.Request) {
js, _ := json.Marshal(d.RunninInDocker) js, _ := json.Marshal(d.RunninInDocker)
utils.SendJSONResponse(w, string(js)) utils.SendJSONResponse(w, string(js))

View File

@ -0,0 +1,21 @@
package domainsniff
import "net/http"
/*
Promox API sniffer
This handler sniff proxmox API endpoint and
adjust the request accordingly to fix shits
in the proxmox API server
*/
func IsProxmox(r *http.Request) bool {
// Check if any of the cookies is named PVEAuthCookie
for _, cookie := range r.Cookies() {
if cookie.Name == "PVEAuthCookie" {
return true
}
}
return false
}

View File

@ -11,6 +11,7 @@ import (
"strings" "strings"
"time" "time"
"imuslab.com/zoraxy/mod/dynamicproxy/domainsniff"
"imuslab.com/zoraxy/mod/dynamicproxy/permissionpolicy" "imuslab.com/zoraxy/mod/dynamicproxy/permissionpolicy"
) )
@ -50,13 +51,13 @@ type ReverseProxy struct {
ModifyResponse func(*http.Response) error ModifyResponse func(*http.Response) error
//Prepender is an optional prepend text for URL rewrite //Prepender is an optional prepend text for URL rewrite
//
Prepender string Prepender string
Verbal bool Verbal bool
} }
type ResponseRewriteRuleSet struct { type ResponseRewriteRuleSet struct {
/* Basic Rewrite Rulesets */
ProxyDomain string ProxyDomain string
OriginalHost string OriginalHost string
UseTLS bool UseTLS bool
@ -64,8 +65,13 @@ type ResponseRewriteRuleSet struct {
PathPrefix string //Vdir prefix for root, / will be rewrite to this PathPrefix string //Vdir prefix for root, / will be rewrite to this
UpstreamHeaders [][]string UpstreamHeaders [][]string
DownstreamHeaders [][]string DownstreamHeaders [][]string
NoRemoveHopByHop bool //Do not remove hop-by-hop headers, dangerous
Version string //Version number of Zoraxy, use for X-Proxy-By /* Advance Usecase Options */
HostHeaderOverwrite string //Force overwrite of request "Host" header (advanced usecase)
NoRemoveHopByHop bool //Do not remove hop-by-hop headers (advanced usecase)
/* System Information Payload */
Version string //Version number of Zoraxy, use for X-Proxy-By
} }
type requestCanceler interface { type requestCanceler interface {
@ -73,8 +79,8 @@ type requestCanceler interface {
} }
type DpcoreOptions struct { type DpcoreOptions struct {
IgnoreTLSVerification bool IgnoreTLSVerification bool //Disable all TLS verification when request pass through this proxy router
FlushInterval time.Duration FlushInterval time.Duration //Duration to flush in normal requests. Stream request or keep-alive request will always flush with interval of -1 (immediately)
} }
func NewDynamicProxyCore(target *url.URL, prepender string, dpcOptions *DpcoreOptions) *ReverseProxy { func NewDynamicProxyCore(target *url.URL, prepender string, dpcOptions *DpcoreOptions) *ReverseProxy {
@ -252,7 +258,7 @@ func (p *ReverseProxy) logf(format string, args ...interface{}) {
} }
} }
func (p *ReverseProxy) ProxyHTTP(rw http.ResponseWriter, req *http.Request, rrr *ResponseRewriteRuleSet) error { func (p *ReverseProxy) ProxyHTTP(rw http.ResponseWriter, req *http.Request, rrr *ResponseRewriteRuleSet) (int, error) {
transport := p.Transport transport := p.Transport
outreq := new(http.Request) outreq := new(http.Request)
@ -281,7 +287,10 @@ func (p *ReverseProxy) ProxyHTTP(rw http.ResponseWriter, req *http.Request, rrr
outreq.Close = false outreq.Close = false
//Only skip origin rewrite iff proxy target require TLS and it is external domain name like github.com //Only skip origin rewrite iff proxy target require TLS and it is external domain name like github.com
if !(rrr.UseTLS && isExternalDomainName(rrr.ProxyDomain)) { if rrr.HostHeaderOverwrite != "" {
//Use user defined overwrite header value, see issue #255
outreq.Host = rrr.HostHeaderOverwrite
} else if !(rrr.UseTLS && isExternalDomainName(rrr.ProxyDomain)) {
// Always use the original host, see issue #164 // Always use the original host, see issue #164
outreq.Host = rrr.OriginalHost outreq.Host = rrr.OriginalHost
} }
@ -291,7 +300,9 @@ func (p *ReverseProxy) ProxyHTTP(rw http.ResponseWriter, req *http.Request, rrr
copyHeader(outreq.Header, req.Header) copyHeader(outreq.Header, req.Header)
// Remove hop-by-hop headers. // Remove hop-by-hop headers.
removeHeaders(outreq.Header, rrr.NoCache) if !rrr.NoRemoveHopByHop {
removeHeaders(outreq.Header, rrr.NoCache)
}
// Add X-Forwarded-For Header. // Add X-Forwarded-For Header.
addXForwardedForHeader(outreq) addXForwardedForHeader(outreq)
@ -302,6 +313,11 @@ func (p *ReverseProxy) ProxyHTTP(rw http.ResponseWriter, req *http.Request, rrr
// Rewrite outbound UA, must be after user headers // Rewrite outbound UA, must be after user headers
rewriteUserAgent(outreq.Header, "Zoraxy/"+rrr.Version) rewriteUserAgent(outreq.Header, "Zoraxy/"+rrr.Version)
//Fix proxmox transfer encoding bug if detected Proxmox Cookie
if domainsniff.IsProxmox(req) {
outreq.TransferEncoding = []string{"identity"}
}
res, err := transport.RoundTrip(outreq) res, err := transport.RoundTrip(outreq)
if err != nil { if err != nil {
if p.Verbal { if p.Verbal {
@ -309,11 +325,13 @@ func (p *ReverseProxy) ProxyHTTP(rw http.ResponseWriter, req *http.Request, rrr
} }
//rw.WriteHeader(http.StatusBadGateway) //rw.WriteHeader(http.StatusBadGateway)
return err return http.StatusBadGateway, err
} }
// Remove hop-by-hop headers listed in the "Connection" header of the response, Remove hop-by-hop headers. // Remove hop-by-hop headers listed in the "Connection" header of the response, Remove hop-by-hop headers.
removeHeaders(res.Header, rrr.NoCache) if !rrr.NoRemoveHopByHop {
removeHeaders(res.Header, rrr.NoCache)
}
//Remove the User-Agent header if exists //Remove the User-Agent header if exists
if _, ok := res.Header["User-Agent"]; ok { if _, ok := res.Header["User-Agent"]; ok {
@ -328,7 +346,7 @@ func (p *ReverseProxy) ProxyHTTP(rw http.ResponseWriter, req *http.Request, rrr
} }
//rw.WriteHeader(http.StatusBadGateway) //rw.WriteHeader(http.StatusBadGateway)
return err return http.StatusBadGateway, err
} }
} }
@ -375,7 +393,6 @@ func (p *ReverseProxy) ProxyHTTP(rw http.ResponseWriter, req *http.Request, rrr
copyHeader(rw.Header(), res.Header) copyHeader(rw.Header(), res.Header)
// inject permission policy headers // inject permission policy headers
//TODO: Load permission policy from rrr
permissionpolicy.InjectPermissionPolicyHeader(rw, nil) permissionpolicy.InjectPermissionPolicyHeader(rw, nil)
// The "Trailer" header isn't included in the Transport's response, Build it up from Trailer. // The "Trailer" header isn't included in the Transport's response, Build it up from Trailer.
@ -405,14 +422,14 @@ func (p *ReverseProxy) ProxyHTTP(rw http.ResponseWriter, req *http.Request, rrr
res.Body.Close() res.Body.Close()
copyHeader(rw.Header(), res.Trailer) copyHeader(rw.Header(), res.Trailer)
return nil return res.StatusCode, nil
} }
func (p *ReverseProxy) ProxyHTTPS(rw http.ResponseWriter, req *http.Request) error { func (p *ReverseProxy) ProxyHTTPS(rw http.ResponseWriter, req *http.Request) (int, error) {
hij, ok := rw.(http.Hijacker) hij, ok := rw.(http.Hijacker)
if !ok { if !ok {
p.logf("http server does not support hijacker") p.logf("http server does not support hijacker")
return errors.New("http server does not support hijacker") return http.StatusNotImplemented, errors.New("http server does not support hijacker")
} }
clientConn, _, err := hij.Hijack() clientConn, _, err := hij.Hijack()
@ -420,7 +437,7 @@ func (p *ReverseProxy) ProxyHTTPS(rw http.ResponseWriter, req *http.Request) err
if p.Verbal { if p.Verbal {
p.logf("http: proxy error: %v", err) p.logf("http: proxy error: %v", err)
} }
return err return http.StatusInternalServerError, err
} }
proxyConn, err := net.Dial("tcp", req.URL.Host) proxyConn, err := net.Dial("tcp", req.URL.Host)
@ -429,7 +446,7 @@ func (p *ReverseProxy) ProxyHTTPS(rw http.ResponseWriter, req *http.Request) err
p.logf("http: proxy error: %v", err) p.logf("http: proxy error: %v", err)
} }
return err return http.StatusInternalServerError, err
} }
// The returned net.Conn may have read or write deadlines // The returned net.Conn may have read or write deadlines
@ -448,7 +465,7 @@ func (p *ReverseProxy) ProxyHTTPS(rw http.ResponseWriter, req *http.Request) err
if p.Verbal { if p.Verbal {
p.logf("http: proxy error: %v", err) p.logf("http: proxy error: %v", err)
} }
return err return http.StatusGatewayTimeout, err
} }
err = proxyConn.SetDeadline(deadline) err = proxyConn.SetDeadline(deadline)
@ -457,7 +474,7 @@ func (p *ReverseProxy) ProxyHTTPS(rw http.ResponseWriter, req *http.Request) err
p.logf("http: proxy error: %v", err) p.logf("http: proxy error: %v", err)
} }
return err return http.StatusGatewayTimeout, err
} }
_, err = clientConn.Write([]byte("HTTP/1.0 200 OK\r\n\r\n")) _, err = clientConn.Write([]byte("HTTP/1.0 200 OK\r\n\r\n"))
@ -466,7 +483,7 @@ func (p *ReverseProxy) ProxyHTTPS(rw http.ResponseWriter, req *http.Request) err
p.logf("http: proxy error: %v", err) p.logf("http: proxy error: %v", err)
} }
return err return http.StatusInternalServerError, err
} }
go func() { go func() {
@ -479,15 +496,13 @@ func (p *ReverseProxy) ProxyHTTPS(rw http.ResponseWriter, req *http.Request) err
proxyConn.Close() proxyConn.Close()
clientConn.Close() clientConn.Close()
return nil return http.StatusOK, nil
} }
func (p *ReverseProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request, rrr *ResponseRewriteRuleSet) error { func (p *ReverseProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request, rrr *ResponseRewriteRuleSet) (int, error) {
if req.Method == "CONNECT" { if req.Method == "CONNECT" {
err := p.ProxyHTTPS(rw, req) return p.ProxyHTTPS(rw, req)
return err
} else { } else {
err := p.ProxyHTTP(rw, req, rrr) return p.ProxyHTTP(rw, req, rrr)
return err
} }
} }

View File

@ -1,7 +1,10 @@
package dpcore package dpcore
import ( import (
"bytes"
"io"
"net" "net"
"net/http"
"net/url" "net/url"
"strings" "strings"
) )
@ -92,3 +95,63 @@ func isExternalDomainName(hostname string) bool {
return true return true
} }
// DeepCopyRequest returns a deep copy of the given http.Request.
func DeepCopyRequest(req *http.Request) (*http.Request, error) {
// Copy the URL
urlCopy := *req.URL
// Copy the headers
headersCopy := make(http.Header, len(req.Header))
for k, vv := range req.Header {
vvCopy := make([]string, len(vv))
copy(vvCopy, vv)
headersCopy[k] = vvCopy
}
// Copy the cookies
cookiesCopy := make([]*http.Cookie, len(req.Cookies()))
for i, cookie := range req.Cookies() {
cookieCopy := *cookie
cookiesCopy[i] = &cookieCopy
}
// Copy the body, if present
var bodyCopy io.ReadCloser
if req.Body != nil {
var buf bytes.Buffer
if _, err := buf.ReadFrom(req.Body); err != nil {
return nil, err
}
// Reset the request body so it can be read again
if err := req.Body.Close(); err != nil {
return nil, err
}
req.Body = io.NopCloser(&buf)
bodyCopy = io.NopCloser(bytes.NewReader(buf.Bytes()))
}
// Create the new request
reqCopy := &http.Request{
Method: req.Method,
URL: &urlCopy,
Proto: req.Proto,
ProtoMajor: req.ProtoMajor,
ProtoMinor: req.ProtoMinor,
Header: headersCopy,
Body: bodyCopy,
ContentLength: req.ContentLength,
TransferEncoding: append([]string(nil), req.TransferEncoding...),
Close: req.Close,
Host: req.Host,
Form: req.Form,
PostForm: req.PostForm,
MultipartForm: req.MultipartForm,
Trailer: req.Trailer,
RemoteAddr: req.RemoteAddr,
TLS: req.TLS,
// Cancel and Context are not copied as it might cause issues
}
return reqCopy, nil
}

View File

@ -158,12 +158,13 @@ func (router *Router) StartProxyService() error {
router.logRequest(r, false, 404, "vdir-http", r.Host) router.logRequest(r, false, 404, "vdir-http", r.Host)
} }
selectedUpstream.ServeHTTP(w, r, &dpcore.ResponseRewriteRuleSet{ selectedUpstream.ServeHTTP(w, r, &dpcore.ResponseRewriteRuleSet{
ProxyDomain: selectedUpstream.OriginIpOrDomain, ProxyDomain: selectedUpstream.OriginIpOrDomain,
OriginalHost: originalHostHeader, OriginalHost: originalHostHeader,
UseTLS: selectedUpstream.RequireTLS, UseTLS: selectedUpstream.RequireTLS,
NoRemoveHopByHop: sep.DisableHopByHopHeaderRemoval, HostHeaderOverwrite: sep.RequestHostOverwrite,
PathPrefix: "", NoRemoveHopByHop: sep.DisableHopByHopHeaderRemoval,
Version: sep.parent.Option.HostVersion, PathPrefix: "",
Version: sep.parent.Option.HostVersion,
}) })
return return
} }

View File

@ -61,8 +61,8 @@ func (u *Upstream) Clone() *Upstream {
return &newUpstream return &newUpstream
} }
// ServeHTTP uses this upstream proxy router to route the current request // ServeHTTP uses this upstream proxy router to route the current request, return the status code and error if any
func (u *Upstream) ServeHTTP(w http.ResponseWriter, r *http.Request, rrr *dpcore.ResponseRewriteRuleSet) error { func (u *Upstream) ServeHTTP(w http.ResponseWriter, r *http.Request, rrr *dpcore.ResponseRewriteRuleSet) (int, error) {
//Auto rewrite to upstream origin if not set //Auto rewrite to upstream origin if not set
if rrr.ProxyDomain == "" { if rrr.ProxyDomain == "" {
rrr.ProxyDomain = u.OriginIpOrDomain rrr.ProxyDomain = u.OriginIpOrDomain

View File

@ -112,6 +112,8 @@ func (router *Router) rewriteURL(rooturl string, requestURL string) string {
func (h *ProxyHandler) hostRequest(w http.ResponseWriter, r *http.Request, target *ProxyEndpoint) { func (h *ProxyHandler) hostRequest(w http.ResponseWriter, r *http.Request, target *ProxyEndpoint) {
r.Header.Set("X-Forwarded-Host", r.Host) r.Header.Set("X-Forwarded-Host", r.Host)
r.Header.Set("X-Forwarded-Server", "zoraxy-"+h.Parent.Option.HostUUID) r.Header.Set("X-Forwarded-Server", "zoraxy-"+h.Parent.Option.HostUUID)
/* Load balancing */
selectedUpstream, err := h.Parent.loadBalancer.GetRequestUpstreamTarget(w, r, target.ActiveOrigins, target.UseStickySession) selectedUpstream, err := h.Parent.loadBalancer.GetRequestUpstreamTarget(w, r, target.ActiveOrigins, target.UseStickySession)
if err != nil { if err != nil {
http.ServeFile(w, r, "./web/rperror.html") http.ServeFile(w, r, "./web/rperror.html")
@ -119,6 +121,8 @@ func (h *ProxyHandler) hostRequest(w http.ResponseWriter, r *http.Request, targe
h.Parent.logRequest(r, false, 521, "subdomain-http", r.URL.Hostname()) h.Parent.logRequest(r, false, 521, "subdomain-http", r.URL.Hostname())
return return
} }
/* WebSocket automatic proxy */
requestURL := r.URL.String() requestURL := r.URL.String()
if r.Header["Upgrade"] != nil && strings.ToLower(r.Header["Upgrade"][0]) == "websocket" { if r.Header["Upgrade"] != nil && strings.ToLower(r.Header["Upgrade"][0]) == "websocket" {
//Handle WebSocket request. Forward the custom Upgrade header and rewrite origin //Handle WebSocket request. Forward the custom Upgrade header and rewrite origin
@ -156,16 +160,17 @@ func (h *ProxyHandler) hostRequest(w http.ResponseWriter, r *http.Request, targe
//Build downstream and upstream header rules //Build downstream and upstream header rules
upstreamHeaders, downstreamHeaders := target.SplitInboundOutboundHeaders() upstreamHeaders, downstreamHeaders := target.SplitInboundOutboundHeaders()
err = selectedUpstream.ServeHTTP(w, r, &dpcore.ResponseRewriteRuleSet{ statusCode, err := selectedUpstream.ServeHTTP(w, r, &dpcore.ResponseRewriteRuleSet{
ProxyDomain: selectedUpstream.OriginIpOrDomain, ProxyDomain: selectedUpstream.OriginIpOrDomain,
OriginalHost: originalHostHeader, OriginalHost: originalHostHeader,
UseTLS: selectedUpstream.RequireTLS, UseTLS: selectedUpstream.RequireTLS,
NoCache: h.Parent.Option.NoCache, NoCache: h.Parent.Option.NoCache,
PathPrefix: "", PathPrefix: "",
UpstreamHeaders: upstreamHeaders, UpstreamHeaders: upstreamHeaders,
DownstreamHeaders: downstreamHeaders, DownstreamHeaders: downstreamHeaders,
NoRemoveHopByHop: target.DisableHopByHopHeaderRemoval, HostHeaderOverwrite: target.RequestHostOverwrite,
Version: target.parent.Option.HostVersion, NoRemoveHopByHop: target.DisableHopByHopHeaderRemoval,
Version: target.parent.Option.HostVersion,
}) })
var dnsError *net.DNSError var dnsError *net.DNSError
@ -181,7 +186,7 @@ func (h *ProxyHandler) hostRequest(w http.ResponseWriter, r *http.Request, targe
} }
} }
h.Parent.logRequest(r, true, 200, "host-http", r.URL.Hostname()) h.Parent.logRequest(r, true, statusCode, "host-http", r.URL.Hostname())
} }
// Handle vdir type request // Handle vdir type request
@ -223,14 +228,15 @@ func (h *ProxyHandler) vdirRequest(w http.ResponseWriter, r *http.Request, targe
//Build downstream and upstream header rules //Build downstream and upstream header rules
upstreamHeaders, downstreamHeaders := target.parent.SplitInboundOutboundHeaders() upstreamHeaders, downstreamHeaders := target.parent.SplitInboundOutboundHeaders()
err := target.proxy.ServeHTTP(w, r, &dpcore.ResponseRewriteRuleSet{ statusCode, err := target.proxy.ServeHTTP(w, r, &dpcore.ResponseRewriteRuleSet{
ProxyDomain: target.Domain, ProxyDomain: target.Domain,
OriginalHost: originalHostHeader, OriginalHost: originalHostHeader,
UseTLS: target.RequireTLS, UseTLS: target.RequireTLS,
PathPrefix: target.MatchingPath, PathPrefix: target.MatchingPath,
UpstreamHeaders: upstreamHeaders, UpstreamHeaders: upstreamHeaders,
DownstreamHeaders: downstreamHeaders, DownstreamHeaders: downstreamHeaders,
Version: target.parent.parent.Option.HostVersion, HostHeaderOverwrite: target.parent.RequestHostOverwrite,
Version: target.parent.parent.Option.HostVersion,
}) })
var dnsError *net.DNSError var dnsError *net.DNSError
@ -245,7 +251,7 @@ func (h *ProxyHandler) vdirRequest(w http.ResponseWriter, r *http.Request, targe
h.Parent.logRequest(r, false, 521, "vdir-http", target.Domain) h.Parent.logRequest(r, false, 521, "vdir-http", target.Domain)
} }
} }
h.Parent.logRequest(r, true, 200, "vdir-http", target.Domain) h.Parent.logRequest(r, true, statusCode, "vdir-http", target.Domain)
} }

View File

@ -195,6 +195,6 @@ func (t *RuleTable) log(message string, err error) {
log.Println("[Redirect] " + message + ": " + err.Error()) log.Println("[Redirect] " + message + ": " + err.Error())
} }
} else { } else {
t.Logger.PrintAndLog("Redirect", message, err) t.Logger.PrintAndLog("redirect", message, err)
} }
} }

View File

@ -132,10 +132,11 @@ type ProxyEndpoint struct {
//Custom Headers //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
RequestHostOverwrite string //If not empty, this domain will be used to overwrite the Host field in request header
HSTSMaxAge int64 //HSTS max age, set to 0 for disable HSTS headers HSTSMaxAge int64 //HSTS max age, set to 0 for disable HSTS headers
EnablePermissionPolicyHeader bool //Enable injection of permission policy header EnablePermissionPolicyHeader bool //Enable injection of permission policy header
PermissionPolicy *permissionpolicy.PermissionsPolicy //Permission policy header PermissionPolicy *permissionpolicy.PermissionsPolicy //Permission policy header
DisableHopByHopHeaderRemoval bool //TODO: Do not remove hop-by-hop headers DisableHopByHopHeaderRemoval bool //Do not remove hop-by-hop headers
//Authentication //Authentication
RequireBasicAuth bool //Set to true to request basic auth before proxy RequireBasicAuth bool //Set to true to request basic auth before proxy

View File

@ -19,9 +19,8 @@ type Store struct {
geodbIpv6 [][]string //Parsed geodb list for ipv6 geodbIpv6 [][]string //Parsed geodb list for ipv6
geotrie *trie geotrie *trie
geotrieIpv6 *trie geotrieIpv6 *trie
//geoipCache sync.Map sysdb *database.Database
sysdb *database.Database option *StoreOptions
option *StoreOptions
} }
type StoreOptions struct { type StoreOptions struct {

View File

@ -43,7 +43,7 @@ func TestResolveCountryCodeFromIP(t *testing.T) {
// Create a new store // Create a new store
store, err := geodb.NewGeoDb(nil, &geodb.StoreOptions{ store, err := geodb.NewGeoDb(nil, &geodb.StoreOptions{
false, false,
false, true,
}) })
if err != nil { if err != nil {
t.Errorf("error creating store: %v", err) t.Errorf("error creating store: %v", err)
@ -56,6 +56,7 @@ func TestResolveCountryCodeFromIP(t *testing.T) {
{"176.113.115.113", "RU"}, {"176.113.115.113", "RU"},
{"65.21.233.213", "FI"}, {"65.21.233.213", "FI"},
{"94.23.207.193", "FR"}, {"94.23.207.193", "FR"},
{"77.131.21.232", "FR"},
} }
for _, testcase := range knownIpCountryMap { for _, testcase := range knownIpCountryMap {

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -16,13 +16,6 @@ func (s *Store) search(ip string) string {
ip = strings.Split(ip, ",")[0] ip = strings.Split(ip, ",")[0]
ip = strings.TrimSpace(ip) ip = strings.TrimSpace(ip)
} }
//See if there are cached country code for this ip
/*
ccc, ok := s.geoipCache.Load(ip)
if ok {
return ccc.(string)
}
*/
//Search in geotrie tree //Search in geotrie tree
cc := "" cc := ""

View File

@ -1,7 +1,6 @@
package geodb package geodb
import ( import (
"math"
"net" "net"
) )
@ -41,14 +40,10 @@ func (t *trie) insert(ipAddr string, cc string) {
ipBytes := ipToBytes(ipAddr) ipBytes := ipToBytes(ipAddr)
current := t.root current := t.root
for _, b := range ipBytes { for _, b := range ipBytes {
//For each byte in the ip address //For each byte in the ip address (4 / 16 bytes)
//each byte is 8 bit //each byte is 8 bit
for j := 0; j < 8; j++ { for j := 7; j >= 0; j-- {
bitwise := (b&uint8(math.Pow(float64(2), float64(j))) > 0) bit := int(b >> j & 1)
bit := 0b0000
if bitwise {
bit = 0b0001
}
if current.childrens[bit] == nil { if current.childrens[bit] == nil {
current.childrens[bit] = &trie_Node{ current.childrens[bit] = &trie_Node{
childrens: [2]*trie_Node{}, childrens: [2]*trie_Node{},
@ -58,21 +53,9 @@ func (t *trie) insert(ipAddr string, cc string) {
current = current.childrens[bit] current = current.childrens[bit]
} }
} }
/*
for i := 63; i >= 0; i-- {
bit := (ipInt64 >> uint(i)) & 1
if current.childrens[bit] == nil {
current.childrens[bit] = &trie_Node{
childrens: [2]*trie_Node{},
cc: cc,
}
}
current = current.childrens[bit]
}
*/
} }
// isReservedIP check if the given ip address is NOT a public ip address
func isReservedIP(ip string) bool { func isReservedIP(ip string) bool {
parsedIP := net.ParseIP(ip) parsedIP := net.ParseIP(ip)
if parsedIP == nil { if parsedIP == nil {
@ -86,12 +69,10 @@ func isReservedIP(ip string) bool {
if parsedIP.IsLinkLocalUnicast() || parsedIP.IsLinkLocalMulticast() { if parsedIP.IsLinkLocalUnicast() || parsedIP.IsLinkLocalMulticast() {
return true return true
} }
//Check if the IP is in the reserved private range
if parsedIP.IsPrivate() { if parsedIP.IsPrivate() {
return true return true
} }
// If the IP address is not a reserved address, return false
return false return false
} }
@ -106,27 +87,15 @@ func (t *trie) search(ipAddr string) string {
for _, b := range ipBytes { for _, b := range ipBytes {
//For each byte in the ip address //For each byte in the ip address
//each byte is 8 bit //each byte is 8 bit
for j := 0; j < 8; j++ { for j := 7; j >= 0; j-- {
bitwise := (b&uint8(math.Pow(float64(2), float64(j))) > 0) bit := int(b >> j & 1)
bit := 0b0000
if bitwise {
bit = 0b0001
}
if current.childrens[bit] == nil { if current.childrens[bit] == nil {
return current.cc return current.cc
} }
current = current.childrens[bit] current = current.childrens[bit]
} }
} }
/*
for i := 63; i >= 0; i-- {
bit := (ipInt64 >> uint(i)) & 1
if current.childrens[bit] == nil {
return current.cc
}
current = current.childrens[bit]
}
*/
if len(current.childrens) == 0 { if len(current.childrens) == 0 {
return current.cc return current.cc
} }

View File

@ -3,7 +3,6 @@ package logviewer
import ( import (
"encoding/json" "encoding/json"
"errors" "errors"
"fmt"
"io/fs" "io/fs"
"net/http" "net/http"
"os" "os"
@ -105,7 +104,6 @@ func (v *Viewer) LoadLogFile(filename string) (string, error) {
filename = filepath.ToSlash(filename) filename = filepath.ToSlash(filename)
filename = strings.ReplaceAll(filename, "../", "") filename = strings.ReplaceAll(filename, "../", "")
logFilepath := filepath.Join(v.option.RootFolder, filename) logFilepath := filepath.Join(v.option.RootFolder, filename)
fmt.Println(logFilepath)
if utils.FileExists(logFilepath) { if utils.FileExists(logFilepath) {
//Load it //Load it
content, err := os.ReadFile(logFilepath) content, err := os.ReadFile(logFilepath)

View File

@ -3,8 +3,6 @@ package netstat
import ( import (
"encoding/json" "encoding/json"
"errors" "errors"
"fmt"
"log"
"net/http" "net/http"
"os" "os"
"os/exec" "os/exec"
@ -14,6 +12,7 @@ import (
"strings" "strings"
"time" "time"
"imuslab.com/zoraxy/mod/info/logger"
"imuslab.com/zoraxy/mod/utils" "imuslab.com/zoraxy/mod/utils"
) )
@ -35,10 +34,11 @@ type NetStatBuffers struct {
Stats []*FlowStat //Statistic of the flow Stats []*FlowStat //Statistic of the flow
StopChan chan bool //Channel to stop the ticker StopChan chan bool //Channel to stop the ticker
EventTicker *time.Ticker //Ticker for event logging EventTicker *time.Ticker //Ticker for event logging
logger *logger.Logger
} }
// Get a new network statistic buffers // Get a new network statistic buffers
func NewNetStatBuffer(recordCount int) (*NetStatBuffers, error) { func NewNetStatBuffer(recordCount int, systemWideLogger *logger.Logger) (*NetStatBuffers, error) {
//Flood fill the stats with 0 //Flood fill the stats with 0
initialStats := []*FlowStat{} initialStats := []*FlowStat{}
for i := 0; i < recordCount; i++ { for i := 0; i < recordCount; i++ {
@ -65,21 +65,22 @@ func NewNetStatBuffer(recordCount int) (*NetStatBuffers, error) {
Stats: initialStats, Stats: initialStats,
StopChan: stopCh, StopChan: stopCh,
EventTicker: ticker, EventTicker: ticker,
logger: systemWideLogger,
} }
//Get the initial measurements of netstats //Get the initial measurements of netstats
rx, tx, err := GetNetworkInterfaceStats() rx, tx, err := thisNetBuffer.GetNetworkInterfaceStats()
if err != nil { if err != nil {
log.Println("Unable to get NIC stats: ", err.Error()) systemWideLogger.PrintAndLog("netstat", "Unable to get NIC stats: ", err)
} }
retryCount := 0 retryCount := 0
for rx == 0 && tx == 0 && retryCount < 10 { for rx == 0 && tx == 0 && retryCount < 10 {
//Strange. Retry //Strange. Retry
log.Println("NIC stats return all 0. Retrying...") systemWideLogger.PrintAndLog("netstat", "NIC stats return all 0. Retrying...", nil)
rx, tx, err = GetNetworkInterfaceStats() rx, tx, err = thisNetBuffer.GetNetworkInterfaceStats()
if err != nil { if err != nil {
log.Println("Unable to get NIC stats: ", err.Error()) systemWideLogger.PrintAndLog("netstat", "Unable to get NIC stats: ", err)
} }
retryCount++ retryCount++
} }
@ -94,20 +95,20 @@ func NewNetStatBuffer(recordCount int) (*NetStatBuffers, error) {
for { for {
select { select {
case <-n.StopChan: case <-n.StopChan:
fmt.Println("- Netstats listener stopped") systemWideLogger.PrintAndLog("netstat", "Netstats listener stopped", nil)
return return
case <-ticker.C: case <-ticker.C:
if n.PreviousStat.RX == 0 && n.PreviousStat.TX == 0 { if n.PreviousStat.RX == 0 && n.PreviousStat.TX == 0 {
//Initiation state is still not done. Ignore request //Initiation state is still not done. Ignore request
log.Println("No initial states. Waiting") systemWideLogger.PrintAndLog("netstat", "No initial states. Waiting", nil)
return return
} }
// Get the latest network interface stats // Get the latest network interface stats
rx, tx, err := GetNetworkInterfaceStats() rx, tx, err := thisNetBuffer.GetNetworkInterfaceStats()
if err != nil { if err != nil {
// Log the error, but don't stop the buffer // Log the error, but don't stop the buffer
log.Printf("Failed to get network interface stats: %v", err) systemWideLogger.PrintAndLog("netstat", "Failed to get network interface stats", err)
continue continue
} }
@ -173,8 +174,8 @@ func (n *NetStatBuffers) Close() {
n.EventTicker.Stop() n.EventTicker.Stop()
} }
func HandleGetNetworkInterfaceStats(w http.ResponseWriter, r *http.Request) { func (n *NetStatBuffers) HandleGetNetworkInterfaceStats(w http.ResponseWriter, r *http.Request) {
rx, tx, err := GetNetworkInterfaceStats() rx, tx, err := n.GetNetworkInterfaceStats()
if err != nil { if err != nil {
utils.SendErrorResponse(w, err.Error()) utils.SendErrorResponse(w, err.Error())
return return
@ -193,7 +194,7 @@ func HandleGetNetworkInterfaceStats(w http.ResponseWriter, r *http.Request) {
} }
// Get network interface stats, return accumulated rx bits, tx bits and error if any // Get network interface stats, return accumulated rx bits, tx bits and error if any
func GetNetworkInterfaceStats() (int64, int64, error) { func (n *NetStatBuffers) GetNetworkInterfaceStats() (int64, int64, error) {
if runtime.GOOS == "windows" { if runtime.GOOS == "windows" {
//Windows wmic sometime freeze and not respond. //Windows wmic sometime freeze and not respond.
//The safer way is to make a bypass mechanism //The safer way is to make a bypass mechanism
@ -262,7 +263,7 @@ func GetNetworkInterfaceStats() (int64, int64, error) {
result = <-callbackChan result = <-callbackChan
cmd = nil cmd = nil
if result.Err != nil { if result.Err != nil {
log.Println("Unable to extract NIC info from wmic: " + result.Err.Error()) n.logger.PrintAndLog("netstat", "Unable to extract NIC info from wmic", result.Err)
} }
return result.RX, result.TX, result.Err return result.RX, result.TX, result.Err
} else if runtime.GOOS == "linux" { } else if runtime.GOOS == "linux" {

View File

@ -11,6 +11,7 @@ import (
"path/filepath" "path/filepath"
"strings" "strings"
"imuslab.com/zoraxy/mod/info/logger"
"imuslab.com/zoraxy/mod/utils" "imuslab.com/zoraxy/mod/utils"
) )
@ -21,15 +22,16 @@ type CertCache struct {
} }
type Manager struct { type Manager struct {
CertStore string //Path where all the certs are stored CertStore string //Path where all the certs are stored
LoadedCerts []*CertCache //A list of loaded certs LoadedCerts []*CertCache //A list of loaded certs
Logger *logger.Logger //System wide logger for debug mesage
verbal bool verbal bool
} }
//go:embed localhost.pem localhost.key //go:embed localhost.pem localhost.key
var buildinCertStore embed.FS var buildinCertStore embed.FS
func NewManager(certStore string, verbal bool) (*Manager, error) { func NewManager(certStore string, verbal bool, logger *logger.Logger) (*Manager, error) {
if !utils.FileExists(certStore) { if !utils.FileExists(certStore) {
os.MkdirAll(certStore, 0775) os.MkdirAll(certStore, 0775)
} }
@ -52,6 +54,7 @@ func NewManager(certStore string, verbal bool) (*Manager, error) {
CertStore: certStore, CertStore: certStore,
LoadedCerts: []*CertCache{}, LoadedCerts: []*CertCache{},
verbal: verbal, verbal: verbal,
Logger: logger,
} }
err := thisManager.UpdateLoadedCertList() err := thisManager.UpdateLoadedCertList()
@ -78,7 +81,7 @@ func (m *Manager) UpdateLoadedCertList() error {
priKey := filepath.Join(m.CertStore, certname+".key") priKey := filepath.Join(m.CertStore, certname+".key")
certificate, err := tls.LoadX509KeyPair(pubKey, priKey) certificate, err := tls.LoadX509KeyPair(pubKey, priKey)
if err != nil { if err != nil {
log.Println("Certificate loaded failed: " + certname) m.Logger.PrintAndLog("tls-router", "Certificate load failed: "+certname, err)
continue continue
} }
@ -86,6 +89,7 @@ func (m *Manager) UpdateLoadedCertList() error {
loadedCert, err := x509.ParseCertificate(thisCert) loadedCert, err := x509.ParseCertificate(thisCert)
if err != nil { if err != nil {
//Error pasring cert, skip this byte segment //Error pasring cert, skip this byte segment
m.Logger.PrintAndLog("tls-router", "Certificate parse failed: "+certname, err)
continue continue
} }
@ -171,37 +175,10 @@ func (m *Manager) GetCert(helloInfo *tls.ClientHelloInfo) (*tls.Certificate, err
pubKey, priKey = m.GetCertByX509CNHostname(helloInfo.ServerName) pubKey, priKey = m.GetCertByX509CNHostname(helloInfo.ServerName)
} else { } else {
//Fallback to legacy method of matching certificates //Fallback to legacy method of matching certificates
/*
domainCerts, _ := m.ListCertDomains()
cloestDomainCert := matchClosestDomainCertificate(helloInfo.ServerName, domainCerts)
if cloestDomainCert != "" {
//There is a matching parent domain for this subdomain. Use this instead.
pubKey = filepath.Join(m.CertStore, cloestDomainCert+".pem")
priKey = filepath.Join(m.CertStore, cloestDomainCert+".key")
} else if m.DefaultCertExists() {
//Use default.pem and default.key
pubKey = filepath.Join(m.CertStore, "default.pem")
priKey = filepath.Join(m.CertStore, "default.key")
if m.verbal {
log.Println("No matching certificate found. Serving with default")
}
} else {
if m.verbal {
log.Println("Matching certificate not found. Serving with build-in certificate. Requesting server name: ", helloInfo.ServerName)
}
}*/
if m.DefaultCertExists() { if m.DefaultCertExists() {
//Use default.pem and default.key //Use default.pem and default.key
pubKey = filepath.Join(m.CertStore, "default.pem") pubKey = filepath.Join(m.CertStore, "default.pem")
priKey = filepath.Join(m.CertStore, "default.key") priKey = filepath.Join(m.CertStore, "default.key")
//if m.verbal {
// log.Println("No matching certificate found. Serving with default")
//}
} else {
//if m.verbal {
// log.Println("Matching certificate not found. Serving with build-in certificate. Requesting server name: ", helloInfo.ServerName)
//}
} }
} }

View File

@ -13,7 +13,6 @@ import (
"strconv" "strconv"
"strings" "strings"
v308 "imuslab.com/zoraxy/mod/update/v308"
"imuslab.com/zoraxy/mod/utils" "imuslab.com/zoraxy/mod/utils"
) )
@ -22,6 +21,13 @@ import (
// This function support cross versions updates (e.g. 307 -> 310) // This function support cross versions updates (e.g. 307 -> 310)
func RunConfigUpdate(fromVersion int, toVersion int) { func RunConfigUpdate(fromVersion int, toVersion int) {
versionFile := "./conf/version" versionFile := "./conf/version"
isFirstTimeInit, _ := isFirstTimeInitialize("./conf/proxy/")
if isFirstTimeInit {
//Create version file and exit
os.MkdirAll("./conf/", 0775)
os.WriteFile(versionFile, []byte(strconv.Itoa(toVersion)), 0775)
return
}
if fromVersion == 0 { if fromVersion == 0 {
//Run auto previous version detection //Run auto previous version detection
fromVersion = 307 fromVersion = 307
@ -49,8 +55,8 @@ func RunConfigUpdate(fromVersion int, toVersion int) {
//Do iterate update //Do iterate update
for i := fromVersion; i < toVersion; i++ { for i := fromVersion; i < toVersion; i++ {
oldVersion := fromVersion oldVersion := i
newVersion := fromVersion + 1 newVersion := i + 1
fmt.Println("Updating from v", oldVersion, " to v", newVersion) fmt.Println("Updating from v", oldVersion, " to v", newVersion)
runUpdateRoutineWithVersion(oldVersion, newVersion) runUpdateRoutineWithVersion(oldVersion, newVersion)
//Write the updated version to file //Write the updated version to file
@ -65,12 +71,37 @@ func GetVersionIntFromVersionNumber(version string) int {
return versionInt return versionInt
} }
func runUpdateRoutineWithVersion(fromVersion int, toVersion int) { // Check if the folder "./conf/proxy/" exists and contains files
if fromVersion == 307 && toVersion == 308 { func isFirstTimeInitialize(path string) (bool, error) {
//Updating from v3.0.7 to v3.0.8 // Check if the folder exists
err := v308.UpdateFrom307To308() info, err := os.Stat(path)
if err != nil { if os.IsNotExist(err) {
panic(err) // The folder does not exist
} return true, nil
} }
if err != nil {
// Some other error occurred
return false, err
}
// Check if it is a directory
if !info.IsDir() {
// The path is not a directory
return false, fmt.Errorf("%s is not a directory", path)
}
// Read the directory contents
files, err := os.ReadDir(path)
if err != nil {
return false, err
}
// Check if the directory is empty
if len(files) == 0 {
// The folder exists but is empty
return true, nil
}
// The folder exists and contains files
return false, nil
} }

View File

@ -0,0 +1,16 @@
package update
import v308 "imuslab.com/zoraxy/mod/update/v308"
// Updater Core logic
func runUpdateRoutineWithVersion(fromVersion int, toVersion int) {
if fromVersion == 307 && toVersion == 308 {
//Updating from v3.0.7 to v3.0.8
err := v308.UpdateFrom307To308()
if err != nil {
panic(err)
}
}
//ADD MORE VERSIONS HERE
}

View File

@ -1,6 +1,10 @@
package utils package utils
import ( import (
"archive/zip"
"io"
"os"
"path/filepath"
"strconv" "strconv"
"strings" "strings"
) )
@ -50,3 +54,52 @@ func ReplaceSpecialCharacters(filename string) string {
return filename return filename
} }
/* Zip File Handler */
// zipFiles compresses multiple files into a single zip archive file
func ZipFiles(filename string, files ...string) error {
newZipFile, err := os.Create(filename)
if err != nil {
return err
}
defer newZipFile.Close()
zipWriter := zip.NewWriter(newZipFile)
defer zipWriter.Close()
for _, file := range files {
if err := addFileToZip(zipWriter, file); err != nil {
return err
}
}
return nil
}
// addFileToZip adds an individual file to a zip archive
func addFileToZip(zipWriter *zip.Writer, filename string) error {
fileToZip, err := os.Open(filename)
if err != nil {
return err
}
defer fileToZip.Close()
info, err := fileToZip.Stat()
if err != nil {
return err
}
header, err := zip.FileInfoHeader(info)
if err != nil {
return err
}
header.Name = filepath.Base(filename)
header.Method = zip.Deflate
writer, err := zipWriter.CreateHeader(header)
if err != nil {
return err
}
_, err = io.Copy(writer, fileToZip)
return err
}

View File

@ -49,6 +49,24 @@ func GetPara(r *http.Request, key string) (string, error) {
} }
} }
// Get GET paramter as boolean, accept 1 or true
func GetBool(r *http.Request, key string) (bool, error) {
x, err := GetPara(r, key)
if err != nil {
return false, err
}
x = strings.TrimSpace(x)
if x == "1" || strings.ToLower(x) == "true" || strings.ToLower(x) == "on" {
return true, nil
} else if x == "0" || strings.ToLower(x) == "false" || strings.ToLower(x) == "off" {
return false, nil
}
return false, errors.New("invalid boolean given")
}
// Get POST paramter // Get POST paramter
func PostPara(r *http.Request, key string) (string, error) { func PostPara(r *http.Request, key string) (string, error) {
r.ParseForm() r.ParseForm()

View File

@ -173,7 +173,7 @@ func (fm *FileManager) HandleDownload(w http.ResponseWriter, r *http.Request) {
// HandleNewFolder creates a new folder in the specified directory // HandleNewFolder creates a new folder in the specified directory
func (fm *FileManager) HandleNewFolder(w http.ResponseWriter, r *http.Request) { func (fm *FileManager) HandleNewFolder(w http.ResponseWriter, r *http.Request) {
// Parse the directory name from the request // Parse the directory name from the request
dirName, err := utils.GetPara(r, "path") dirName, err := utils.PostPara(r, "path")
if err != nil { if err != nil {
utils.SendErrorResponse(w, "invalid directory name") utils.SendErrorResponse(w, "invalid directory name")
return return
@ -268,13 +268,13 @@ func (fm *FileManager) HandleFileCopy(w http.ResponseWriter, r *http.Request) {
func (fm *FileManager) HandleFileMove(w http.ResponseWriter, r *http.Request) { func (fm *FileManager) HandleFileMove(w http.ResponseWriter, r *http.Request) {
// Parse the source and destination paths from the request // Parse the source and destination paths from the request
srcPath, err := utils.GetPara(r, "srcpath") srcPath, err := utils.PostPara(r, "srcpath")
if err != nil { if err != nil {
utils.SendErrorResponse(w, "invalid source path") utils.SendErrorResponse(w, "invalid source path")
return return
} }
destPath, err := utils.GetPara(r, "destpath") destPath, err := utils.PostPara(r, "destpath")
if err != nil { if err != nil {
utils.SendErrorResponse(w, "invalid destination path") utils.SendErrorResponse(w, "invalid destination path")
return return

View File

@ -509,6 +509,9 @@ func ReverseProxyHandleEditEndpoint(w http.ResponseWriter, r *http.Request) {
//Save it to file //Save it to file
SaveReverseProxyConfig(newProxyEndpoint) SaveReverseProxyConfig(newProxyEndpoint)
//Update uptime monitor targets
UpdateUptimeMonitorTargets()
utils.SendOK(w) utils.SendOK(w)
} }
@ -569,7 +572,7 @@ func ReverseProxyHandleAlias(w http.ResponseWriter, r *http.Request) {
} }
func DeleteProxyEndpoint(w http.ResponseWriter, r *http.Request) { func DeleteProxyEndpoint(w http.ResponseWriter, r *http.Request) {
ep, err := utils.GetPara(r, "ep") ep, err := utils.PostPara(r, "ep")
if err != nil { if err != nil {
utils.SendErrorResponse(w, "Invalid ep given") utils.SendErrorResponse(w, "Invalid ep given")
return return
@ -589,12 +592,6 @@ func DeleteProxyEndpoint(w http.ResponseWriter, r *http.Request) {
return return
} }
//Update utm if exists
if uptimeMonitor != nil {
uptimeMonitor.Config.Targets = GetUptimeTargetsFromReverseProxyRules(dynamicProxyRouter)
uptimeMonitor.CleanRecords()
}
//Update uptime monitor //Update uptime monitor
UpdateUptimeMonitorTargets() UpdateUptimeMonitorTargets()
@ -944,18 +941,22 @@ func ReverseProxyList(w http.ResponseWriter, r *http.Request) {
// Handle port 80 incoming traffics // Handle port 80 incoming traffics
func HandleUpdatePort80Listener(w http.ResponseWriter, r *http.Request) { func HandleUpdatePort80Listener(w http.ResponseWriter, r *http.Request) {
enabled, err := utils.GetPara(r, "enable") if r.Method == http.MethodGet {
if err != nil {
//Load the current status //Load the current status
currentEnabled := false currentEnabled := false
err = sysdb.Read("settings", "listenP80", &currentEnabled) err := sysdb.Read("settings", "listenP80", &currentEnabled)
if err != nil { if err != nil {
utils.SendErrorResponse(w, err.Error()) utils.SendErrorResponse(w, err.Error())
return return
} }
js, _ := json.Marshal(currentEnabled) js, _ := json.Marshal(currentEnabled)
utils.SendJSONResponse(w, string(js)) utils.SendJSONResponse(w, string(js))
} else { } else if r.Method == http.MethodPost {
enabled, err := utils.PostPara(r, "enable")
if err != nil {
utils.SendErrorResponse(w, "enable state not set")
return
}
if enabled == "true" { if enabled == "true" {
sysdb.Write("settings", "listenP80", true) sysdb.Write("settings", "listenP80", true)
SystemWideLogger.Println("Enabling port 80 listener") SystemWideLogger.Println("Enabling port 80 listener")
@ -968,38 +969,48 @@ func HandleUpdatePort80Listener(w http.ResponseWriter, r *http.Request) {
utils.SendErrorResponse(w, "invalid mode given: "+enabled) utils.SendErrorResponse(w, "invalid mode given: "+enabled)
} }
utils.SendOK(w) utils.SendOK(w)
} else {
http.Error(w, "405 - Method not allowed", http.StatusMethodNotAllowed)
} }
} }
// Handle https redirect // Handle https redirect
func HandleUpdateHttpsRedirect(w http.ResponseWriter, r *http.Request) { func HandleUpdateHttpsRedirect(w http.ResponseWriter, r *http.Request) {
useRedirect, err := utils.GetPara(r, "set") if r.Method == http.MethodGet {
if err != nil {
currentRedirectToHttps := false currentRedirectToHttps := false
//Load the current status //Load the current status
err = sysdb.Read("settings", "redirect", &currentRedirectToHttps) err := sysdb.Read("settings", "redirect", &currentRedirectToHttps)
if err != nil { if err != nil {
utils.SendErrorResponse(w, err.Error()) utils.SendErrorResponse(w, err.Error())
return return
} }
js, _ := json.Marshal(currentRedirectToHttps) js, _ := json.Marshal(currentRedirectToHttps)
utils.SendJSONResponse(w, string(js)) utils.SendJSONResponse(w, string(js))
} else { } else if r.Method == http.MethodPost {
useRedirect, err := utils.PostBool(r, "set")
if err != nil {
utils.SendErrorResponse(w, "status not set")
return
}
if dynamicProxyRouter.Option.Port == 80 { if dynamicProxyRouter.Option.Port == 80 {
utils.SendErrorResponse(w, "This option is not available when listening on port 80") utils.SendErrorResponse(w, "This option is not available when listening on port 80")
return return
} }
if useRedirect == "true" { if useRedirect {
sysdb.Write("settings", "redirect", true) sysdb.Write("settings", "redirect", true)
SystemWideLogger.Println("Updating force HTTPS redirection to true") SystemWideLogger.Println("Updating force HTTPS redirection to true")
dynamicProxyRouter.UpdateHttpToHttpsRedirectSetting(true) dynamicProxyRouter.UpdateHttpToHttpsRedirectSetting(true)
} else if useRedirect == "false" { } else {
sysdb.Write("settings", "redirect", false) sysdb.Write("settings", "redirect", false)
SystemWideLogger.Println("Updating force HTTPS redirection to false") SystemWideLogger.Println("Updating force HTTPS redirection to false")
dynamicProxyRouter.UpdateHttpToHttpsRedirectSetting(false) dynamicProxyRouter.UpdateHttpToHttpsRedirectSetting(false)
} }
utils.SendOK(w) utils.SendOK(w)
} else {
http.Error(w, "405 - Method not allowed", http.StatusMethodNotAllowed)
} }
} }
@ -1089,13 +1100,13 @@ func HandleIncomingPortSet(w http.ResponseWriter, r *http.Request) {
//List all the custom header defined in this proxy rule //List all the custom header defined in this proxy rule
func HandleCustomHeaderList(w http.ResponseWriter, r *http.Request) { func HandleCustomHeaderList(w http.ResponseWriter, r *http.Request) {
epType, err := utils.PostPara(r, "type") epType, err := utils.GetPara(r, "type")
if err != nil { if err != nil {
utils.SendErrorResponse(w, "endpoint type not defined") utils.SendErrorResponse(w, "endpoint type not defined")
return return
} }
domain, err := utils.PostPara(r, "domain") domain, err := utils.GetPara(r, "domain")
if err != nil { if err != nil {
utils.SendErrorResponse(w, "domain or matching rule not defined") utils.SendErrorResponse(w, "domain or matching rule not defined")
return return
@ -1238,6 +1249,150 @@ func HandleCustomHeaderRemove(w http.ResponseWriter, r *http.Request) {
} }
func HandleHostOverwrite(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
}
}
//Get the proxy endpoint object dedicated to this domain
targetProxyEndpoint, err := dynamicProxyRouter.LoadProxy(domain)
if err != nil {
utils.SendErrorResponse(w, "target endpoint not exists")
return
}
if r.Method == http.MethodGet {
//Get the current host header
js, _ := json.Marshal(targetProxyEndpoint.RequestHostOverwrite)
utils.SendJSONResponse(w, string(js))
} else if r.Method == http.MethodPost {
//Set the new host header
newHostname, _ := utils.PostPara(r, "hostname")
//As this will require change in the proxy instance we are running
//we need to clone and respawn this proxy endpoint
newProxyEndpoint := targetProxyEndpoint.Clone()
newProxyEndpoint.RequestHostOverwrite = newHostname
//Save proxy endpoint
err = SaveReverseProxyConfig(newProxyEndpoint)
if err != nil {
utils.SendErrorResponse(w, err.Error())
return
}
//Spawn a new endpoint with updated dpcore
preparedEndpoint, err := dynamicProxyRouter.PrepareProxyRoute(newProxyEndpoint)
if err != nil {
utils.SendErrorResponse(w, err.Error())
return
}
//Remove the old endpoint
err = targetProxyEndpoint.Remove()
if err != nil {
utils.SendErrorResponse(w, err.Error())
return
}
//Add the newly prepared endpoint to runtime
err = dynamicProxyRouter.AddProxyRouteToRuntime(preparedEndpoint)
if err != nil {
utils.SendErrorResponse(w, err.Error())
return
}
//Print log message
if newHostname != "" {
SystemWideLogger.Println("Updated " + domain + " hostname overwrite to: " + newHostname)
} else {
SystemWideLogger.Println("Removed " + domain + " hostname overwrite")
}
utils.SendOK(w)
} else {
//Invalid method
http.Error(w, "405 - Method not allowed", http.StatusMethodNotAllowed)
}
}
// HandleHopByHop get and set the hop by hop remover state
// note that it shows the DISABLE STATE of hop-by-hop remover, not the enable state
func HandleHopByHop(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 {
//Get the current hop by hop header state
js, _ := json.Marshal(!targetProxyEndpoint.DisableHopByHopHeaderRemoval)
utils.SendJSONResponse(w, string(js))
} else if r.Method == http.MethodPost {
//Set the hop by hop header state
enableHopByHopRemover, _ := utils.PostBool(r, "removeHopByHop")
//As this will require change in the proxy instance we are running
//we need to clone and respawn this proxy endpoint
newProxyEndpoint := targetProxyEndpoint.Clone()
//Storage file use false as default, so disable removal = not enable remover
newProxyEndpoint.DisableHopByHopHeaderRemoval = !enableHopByHopRemover
//Save proxy endpoint
err = SaveReverseProxyConfig(newProxyEndpoint)
if err != nil {
utils.SendErrorResponse(w, err.Error())
return
}
//Spawn a new endpoint with updated dpcore
preparedEndpoint, err := dynamicProxyRouter.PrepareProxyRoute(newProxyEndpoint)
if err != nil {
utils.SendErrorResponse(w, err.Error())
return
}
//Remove the old endpoint
err = targetProxyEndpoint.Remove()
if err != nil {
utils.SendErrorResponse(w, err.Error())
return
}
//Add the newly prepared endpoint to runtime
err = dynamicProxyRouter.AddProxyRouteToRuntime(preparedEndpoint)
if err != nil {
utils.SendErrorResponse(w, err.Error())
return
}
//Print log message
if enableHopByHopRemover {
SystemWideLogger.Println("Enabled hop-by-hop headers removal on " + domain)
} else {
SystemWideLogger.Println("Disabled hop-by-hop headers removal on " + domain)
}
utils.SendOK(w)
} else {
http.Error(w, "405 - Method not allowed", http.StatusMethodNotAllowed)
}
}
// Handle view or edit HSTS states // Handle view or edit HSTS states
func HandleHSTSState(w http.ResponseWriter, r *http.Request) { func HandleHSTSState(w http.ResponseWriter, r *http.Request) {
domain, err := utils.PostPara(r, "domain") domain, err := utils.PostPara(r, "domain")

View File

@ -4,9 +4,11 @@ import (
"fmt" "fmt"
"net/http" "net/http"
"net/url" "net/url"
"os"
"path/filepath" "path/filepath"
"strings" "strings"
"github.com/gorilla/csrf"
"imuslab.com/zoraxy/mod/sshprox" "imuslab.com/zoraxy/mod/sshprox"
) )
@ -42,11 +44,15 @@ func FSHandler(handler http.Handler) http.Handler {
// Allow access to /script/*, /img/pubic/* and /login.html without authentication // Allow access to /script/*, /img/pubic/* and /login.html without authentication
if strings.HasPrefix(r.URL.Path, ppf("/script/")) || strings.HasPrefix(r.URL.Path, ppf("/img/public/")) || r.URL.Path == ppf("/login.html") || r.URL.Path == ppf("/reset.html") || r.URL.Path == ppf("/favicon.png") { if strings.HasPrefix(r.URL.Path, ppf("/script/")) || strings.HasPrefix(r.URL.Path, ppf("/img/public/")) || r.URL.Path == ppf("/login.html") || r.URL.Path == ppf("/reset.html") || r.URL.Path == ppf("/favicon.png") {
if isHTMLFilePath(r.URL.Path) {
handleInjectHTML(w, r, r.URL.Path)
return
}
handler.ServeHTTP(w, r) handler.ServeHTTP(w, r)
return return
} }
// check authentication // Check authentication
if !authAgent.CheckAuth(r) && requireAuth { if !authAgent.CheckAuth(r) && requireAuth {
http.Redirect(w, r, ppf("/login.html"), http.StatusTemporaryRedirect) http.Redirect(w, r, ppf("/login.html"), http.StatusTemporaryRedirect)
return return
@ -77,6 +83,10 @@ func FSHandler(handler http.Handler) http.Handler {
} }
//Authenticated //Authenticated
if isHTMLFilePath(r.URL.Path) {
handleInjectHTML(w, r, r.URL.Path)
return
}
handler.ServeHTTP(w, r) handler.ServeHTTP(w, r)
}) })
} }
@ -88,3 +98,53 @@ func ppf(relativeFilepath string) string {
} }
return relativeFilepath return relativeFilepath
} }
func isHTMLFilePath(requestURI string) bool {
return strings.HasSuffix(requestURI, ".html") || strings.HasSuffix(requestURI, "/")
}
// Serve the html file with template token injected
func handleInjectHTML(w http.ResponseWriter, r *http.Request, relativeFilepath string) {
// Read the HTML file
var content []byte
var err error
if len(relativeFilepath) > 0 && relativeFilepath[len(relativeFilepath)-1:] == "/" {
relativeFilepath = relativeFilepath + "index.html"
}
if development {
//Load from disk
targetFilePath := strings.ReplaceAll(filepath.Join("web/", relativeFilepath), "\\", "/")
content, err = os.ReadFile(targetFilePath)
if err != nil {
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
} else {
//Load from embedded fs, require trimming off the prefix slash for relative path
relativeFilepath = strings.TrimPrefix(relativeFilepath, "/")
content, err = webres.ReadFile(relativeFilepath)
if err != nil {
SystemWideLogger.Println("load embedded web file failed: ", err)
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
}
// Convert the file content to a string
htmlContent := string(content)
//Defeine the system template for this request
templateStrings := map[string]string{
".csrfToken": csrf.Token(r),
}
// Replace template tokens in the HTML content
for key, value := range templateStrings {
placeholder := "{{" + key + "}}"
htmlContent = strings.ReplaceAll(htmlContent, placeholder, value)
}
// Write the modified HTML content to the response
w.Header().Set("Content-Type", "text/html")
w.Write([]byte(htmlContent))
}

View File

@ -84,7 +84,7 @@ func startupSequence() {
}) })
//Create a TLS certificate manager //Create a TLS certificate manager
tlsCertManager, err = tlscert.NewManager("./conf/certs", development) tlsCertManager, err = tlscert.NewManager("./conf/certs", development, SystemWideLogger)
if err != nil { if err != nil {
panic(err) panic(err)
} }
@ -145,7 +145,7 @@ func startupSequence() {
staticWebServer.RestorePreviousState() staticWebServer.RestorePreviousState()
//Create a netstat buffer //Create a netstat buffer
netstatBuffers, err = netstat.NewNetStatBuffer(300) netstatBuffers, err = netstat.NewNetStatBuffer(300, SystemWideLogger)
if err != nil { if err != nil {
SystemWideLogger.PrintAndLog("Network", "Failed to load network statistic info", err) SystemWideLogger.PrintAndLog("Network", "Failed to load network statistic info", err)
panic(err) panic(err)
@ -279,14 +279,20 @@ func startupSequence() {
//Create a table just to store acme related preferences //Create a table just to store acme related preferences
sysdb.NewTable("acmepref") sysdb.NewTable("acmepref")
acmeHandler = initACME() acmeHandler = initACME()
acmeAutoRenewer, err = acme.NewAutoRenewer("./conf/acme_conf.json", "./conf/certs/", int64(*acmeAutoRenewInterval), acmeHandler) acmeAutoRenewer, err = acme.NewAutoRenewer(
"./conf/acme_conf.json",
"./conf/certs/",
int64(*acmeAutoRenewInterval),
*acmeCertAutoRenewDays,
acmeHandler,
)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
/* Docker UX Optimizer */ /* Docker UX Optimizer */
if runtime.GOOS == "windows" && *runningInDocker { if runtime.GOOS == "windows" && *runningInDocker {
SystemWideLogger.PrintAndLog("WARNING", "Invalid start flag combination: docker=true && runtime.GOOS == windows. Running in docker UX development mode.", nil) SystemWideLogger.PrintAndLog("warning", "Invalid start flag combination: docker=true && runtime.GOOS == windows. Running in docker UX development mode.", nil)
} }
DockerUXOptimizer = dockerux.NewDockerOptimizer(*runningInDocker, SystemWideLogger) DockerUXOptimizer = dockerux.NewDockerOptimizer(*runningInDocker, SystemWideLogger)

View File

@ -19,7 +19,7 @@ import (
// List upstreams from a endpoint // List upstreams from a endpoint
func ReverseProxyUpstreamList(w http.ResponseWriter, r *http.Request) { func ReverseProxyUpstreamList(w http.ResponseWriter, r *http.Request) {
endpoint, err := utils.PostPara(r, "ep") endpoint, err := utils.GetPara(r, "ep")
if err != nil { if err != nil {
utils.SendErrorResponse(w, "endpoint not defined") utils.SendErrorResponse(w, "endpoint not defined")
return return

View File

@ -197,6 +197,8 @@ func ReverseProxyDeleteVdir(w http.ResponseWriter, r *http.Request) {
return return
} }
UpdateUptimeMonitorTargets()
utils.SendOK(w) utils.SendOK(w)
} }

View File

@ -1000,7 +1000,7 @@
*/ */
function enableBlacklist() { function enableBlacklist() {
var isChecked = $('#enableBlacklist').is(':checked'); var isChecked = $('#enableBlacklist').is(':checked');
$.ajax({ $.cjax({
type: 'POST', type: 'POST',
url: '/api/blacklist/enable', url: '/api/blacklist/enable',
data: { enable: isChecked, id: currentEditingAccessRule}, data: { enable: isChecked, id: currentEditingAccessRule},
@ -1028,9 +1028,10 @@
let counter = 0; let counter = 0;
for(var i = 0; i < ccs.length; i++){ for(var i = 0; i < ccs.length; i++){
let thisCountryCode = ccs[i]; let thisCountryCode = ccs[i];
$.ajax({ $.cjax({
type: "POST", type: "POST",
url: "/api/blacklist/country/add", url: "/api/blacklist/country/add",
method: "POST",
data: { cc: thisCountryCode, id: currentEditingAccessRule}, data: { cc: thisCountryCode, id: currentEditingAccessRule},
success: function(response) { success: function(response) {
if (response.error != undefined){ if (response.error != undefined){
@ -1066,7 +1067,7 @@
function removeFromBannedList(countryCode){ function removeFromBannedList(countryCode){
countryCode = countryCode.toLowerCase(); countryCode = countryCode.toLowerCase();
let countryName = getCountryName(countryCode); let countryName = getCountryName(countryCode);
$.ajax({ $.cjax({
url: "/api/blacklist/country/remove", url: "/api/blacklist/country/remove",
method: "POST", method: "POST",
data: { cc: countryCode, id: currentEditingAccessRule}, data: { cc: countryCode, id: currentEditingAccessRule},
@ -1097,7 +1098,7 @@
} }
} }
$.ajax({ $.cjax({
url: "/api/blacklist/ip/add", url: "/api/blacklist/ip/add",
type: "POST", type: "POST",
data: {ip: targetIp.toLowerCase(), id: currentEditingAccessRule}, data: {ip: targetIp.toLowerCase(), id: currentEditingAccessRule},
@ -1119,7 +1120,7 @@
function removeIpBlacklist(ipaddr){ function removeIpBlacklist(ipaddr){
if (confirm("Confirm remove blacklist for " + ipaddr + " ?")){ if (confirm("Confirm remove blacklist for " + ipaddr + " ?")){
$.ajax({ $.cjax({
url: "/api/blacklist/ip/remove", url: "/api/blacklist/ip/remove",
type: "POST", type: "POST",
data: {ip: ipaddr.toLowerCase(), id: currentEditingAccessRule}, data: {ip: ipaddr.toLowerCase(), id: currentEditingAccessRule},
@ -1143,7 +1144,7 @@
*/ */
function enableWhitelist() { function enableWhitelist() {
var isChecked = $('#enableWhitelist').is(':checked'); var isChecked = $('#enableWhitelist').is(':checked');
$.ajax({ $.cjax({
type: 'POST', type: 'POST',
url: '/api/whitelist/enable', url: '/api/whitelist/enable',
data: { enable: isChecked , id: currentEditingAccessRule}, data: { enable: isChecked , id: currentEditingAccessRule},
@ -1165,7 +1166,7 @@
let counter = 0; let counter = 0;
for(var i = 0; i < ccs.length; i++){ for(var i = 0; i < ccs.length; i++){
let thisCountryCode = ccs[i]; let thisCountryCode = ccs[i];
$.ajax({ $.cjax({
type: "POST", type: "POST",
url: "/api/whitelist/country/add", url: "/api/whitelist/country/add",
data: { cc: thisCountryCode , id: currentEditingAccessRule}, data: { cc: thisCountryCode , id: currentEditingAccessRule},
@ -1199,7 +1200,7 @@
function removeFromWhiteList(countryCode){ function removeFromWhiteList(countryCode){
if (confirm("Confirm removing " + getCountryName(countryCode) + " from whitelist?")){ if (confirm("Confirm removing " + getCountryName(countryCode) + " from whitelist?")){
countryCode = countryCode.toLowerCase(); countryCode = countryCode.toLowerCase();
$.ajax({ $.cjax({
url: "/api/whitelist/country/remove", url: "/api/whitelist/country/remove",
method: "POST", method: "POST",
data: { cc: countryCode , id: currentEditingAccessRule}, data: { cc: countryCode , id: currentEditingAccessRule},
@ -1230,7 +1231,7 @@
} }
} }
$.ajax({ $.cjax({
url: "/api/whitelist/ip/add", url: "/api/whitelist/ip/add",
type: "POST", type: "POST",
data: {ip: targetIp.toLowerCase(), "comment": remarks, id: currentEditingAccessRule}, data: {ip: targetIp.toLowerCase(), "comment": remarks, id: currentEditingAccessRule},
@ -1253,7 +1254,7 @@
function removeIpWhitelist(ipaddr){ function removeIpWhitelist(ipaddr){
if (confirm("Confirm remove whitelist for " + ipaddr + " ?")){ if (confirm("Confirm remove whitelist for " + ipaddr + " ?")){
$.ajax({ $.cjax({
url: "/api/whitelist/ip/remove", url: "/api/whitelist/ip/remove",
type: "POST", type: "POST",
data: {ip: ipaddr.toLowerCase(), id: currentEditingAccessRule}, data: {ip: ipaddr.toLowerCase(), id: currentEditingAccessRule},

View File

@ -161,6 +161,7 @@
$(btn).addClass('disabled'); $(btn).addClass('disabled');
$(btn).html(`<i class="ui loading spinner icon"></i>`); $(btn).html(`<i class="ui loading spinner icon"></i>`);
} }
obtainCertificate(domain, dns, defaultCA.trim(), function(succ){ obtainCertificate(domain, dns, defaultCA.trim(), function(succ){
if (btn != undefined){ if (btn != undefined){
$(btn).removeClass('disabled'); $(btn).removeClass('disabled');
@ -256,7 +257,7 @@
//Delete the certificate by its domain //Delete the certificate by its domain
function deleteCertificate(domain){ function deleteCertificate(domain){
if (confirm("Confirm delete certificate for " + domain + " ?")){ if (confirm("Confirm delete certificate for " + domain + " ?")){
$.ajax({ $.cjax({
url: "/api/cert/delete", url: "/api/cert/delete",
method: "POST", method: "POST",
data: {domain: domain}, data: {domain: domain},
@ -315,7 +316,7 @@
return; return;
} }
$.ajax({ $.cjax({
url: "/api/acme/autoRenew/email", url: "/api/acme/autoRenew/email",
method: "POST", method: "POST",
data: {"set": newDefaultEmail}, data: {"set": newDefaultEmail},
@ -329,7 +330,7 @@
} }
}); });
$.ajax({ $.cjax({
url: "/api/acme/autoRenew/ca", url: "/api/acme/autoRenew/ca",
data: {"set": newDefaultCA}, data: {"set": newDefaultCA},
method: "POST", method: "POST",
@ -356,13 +357,16 @@
}); });
data.forEach(entry => { data.forEach(entry => {
let isExpired = entry.RemainingDays <= 0; let isExpired = entry.RemainingDays <= 0;
let entryDomainRenewKey = entry.Domain;
if (entryDomainRenewKey.includes("_.")){
entryDomainRenewKey = entryDomainRenewKey.replace("_.","*.");
}
$("#certifiedDomainList").append(`<tr> $("#certifiedDomainList").append(`<tr>
<td>${entry.Domain}</td> <td><a style="cursor: pointer;" title="Download certificate" onclick="handleCertDownload('${entry.Domain}');">${entry.Domain}</a></td>
<td>${entry.LastModifiedDate}</td> <td>${entry.LastModifiedDate}</td>
<td class="${isExpired?"expired":"valid"} certdate">${entry.ExpireDate} (${!isExpired?entry.RemainingDays+" days left":"Expired"})</td> <td class="${isExpired?"expired":"valid"} certdate">${entry.ExpireDate} (${!isExpired?entry.RemainingDays+" days left":"Expired"})</td>
<td><i class="${entry.UseDNS?"green check": "red times"} circle outline icon"></i></td> <td><i class="${entry.UseDNS?"green check": "red times"} icon"></i></td>
<td><button title="Renew Certificate" class="ui mini basic icon button renewButton" onclick="renewCertificate('${entry.Domain}', '${entry.UseDNS}', this);"><i class="ui green refresh icon"></i></button></td> <td><button title="Renew Certificate" class="ui mini basic icon button renewButton" onclick="renewCertificate('${entryDomainRenewKey}', '${entry.UseDNS}', this);"><i class="ui green refresh icon"></i></button></td>
<td><button title="Delete key-pair" class="ui mini basic red icon button" onclick="deleteCertificate('${entry.Domain}');"><i class="ui red trash icon"></i></button></td> <td><button title="Delete key-pair" class="ui mini basic red icon button" onclick="deleteCertificate('${entry.Domain}');"><i class="ui red trash icon"></i></button></td>
</tr>`); </tr>`);
}); });
@ -397,6 +401,19 @@
initManagedDomainCertificateList(); initManagedDomainCertificateList();
}); });
} }
function handleCertDownload(certName){
$.get("/api/cert/download?seek=true&certname=" + certName, function(data){
if (data.error != undefined){
//Error resolving certificate
msgbox(data.error, false);
}else{
//Continue to download
window.open("/api/cert/download?certname=" + certName);
}
});
}
//Handle domain keys upload //Handle domain keys upload
function handleDomainKeysUpload(callback=undefined){ function handleDomainKeysUpload(callback=undefined){
let domain = $("#certdomain").val(); let domain = $("#certdomain").val();
@ -406,6 +423,8 @@
} }
if (uploadPendingPublicKey && uploadPendingPrivateKey && typeof uploadPendingPublicKey === 'object' && typeof uploadPendingPrivateKey === 'object') { if (uploadPendingPublicKey && uploadPendingPrivateKey && typeof uploadPendingPublicKey === 'object' && typeof uploadPendingPrivateKey === 'object') {
const publicKeyForm = new FormData(); const publicKeyForm = new FormData();
const csrfToken = document.querySelector('meta[name="zoraxy.csrf.Token"]').getAttribute("content");
publicKeyForm.append('file', uploadPendingPublicKey, 'publicKey'); publicKeyForm.append('file', uploadPendingPublicKey, 'publicKey');
const privateKeyForm = new FormData(); const privateKeyForm = new FormData();
@ -413,6 +432,7 @@
const publicKeyRequest = new XMLHttpRequest(); const publicKeyRequest = new XMLHttpRequest();
publicKeyRequest.open('POST', '/api/cert/upload?ktype=pub&domain=' + domain); publicKeyRequest.open('POST', '/api/cert/upload?ktype=pub&domain=' + domain);
publicKeyRequest.setRequestHeader('X-CSRF-Token', csrfToken);
publicKeyRequest.onreadystatechange = function() { publicKeyRequest.onreadystatechange = function() {
if (publicKeyRequest.readyState === XMLHttpRequest.DONE) { if (publicKeyRequest.readyState === XMLHttpRequest.DONE) {
if (publicKeyRequest.status !== 200) { if (publicKeyRequest.status !== 200) {
@ -429,6 +449,7 @@
const privateKeyRequest = new XMLHttpRequest(); const privateKeyRequest = new XMLHttpRequest();
privateKeyRequest.open('POST', '/api/cert/upload?ktype=pri&domain=' + domain); privateKeyRequest.open('POST', '/api/cert/upload?ktype=pri&domain=' + domain);
privateKeyRequest.setRequestHeader('X-CSRF-Token', csrfToken);
privateKeyRequest.onreadystatechange = function() { privateKeyRequest.onreadystatechange = function() {
if (privateKeyRequest.readyState === XMLHttpRequest.DONE) { if (privateKeyRequest.readyState === XMLHttpRequest.DONE) {
if (privateKeyRequest.status !== 200) { if (privateKeyRequest.status !== 200) {
@ -449,15 +470,11 @@
//ktype = {"pub" / "pri"} //ktype = {"pub" / "pri"}
function handleFileSelect(event, ktype="pub") { function handleFileSelect(event, ktype="pub") {
const file = event.target.files[0]; const file = event.target.files[0];
//const fileNameInput = document.getElementById('selected-file-name');
if (ktype == "pub"){ if (ktype == "pub"){
uploadPendingPublicKey = file; uploadPendingPublicKey = file;
}else if (ktype == "pri"){ }else if (ktype == "pri"){
uploadPendingPrivateKey = file; uploadPendingPrivateKey = file;
} }
//fileNameInput.value = file.name;
} }
//Check if the default keypairs exists //Check if the default keypairs exists
@ -480,14 +497,18 @@
input.addEventListener('change', () => { input.addEventListener('change', () => {
// create form data object // create form data object
const formData = new FormData(); const formData = new FormData();
const csrfToken = document.querySelector('meta[name="zoraxy.csrf.Token"]').getAttribute("content");
// add selected file to form data // add selected file to form data
formData.append('file', input.files[0]); formData.append('file', input.files[0]);
// send form data to server // send form data to server
fetch('/api/cert/upload?ktype=pri', { fetch('/api/cert/upload?ktype=pri', {
method: 'POST', method: 'POST',
body: formData body: formData,
headers: {
'X-CSRF-Token': csrfToken
}
}) })
.then(response => { .then(response => {
initDefaultKeypairCheck(); initDefaultKeypairCheck();
@ -514,6 +535,7 @@
function uploadPublicKey() { function uploadPublicKey() {
// create file input element // create file input element
const input = document.createElement('input'); const input = document.createElement('input');
const csrfToken = document.querySelector('meta[name="zoraxy.csrf.Token"]').getAttribute("content");
input.type = 'file'; input.type = 'file';
// add change listener to file input // add change listener to file input
@ -527,7 +549,10 @@
// send form data to server // send form data to server
fetch('/api/cert/upload?ktype=pub', { fetch('/api/cert/upload?ktype=pub', {
method: 'POST', method: 'POST',
body: formData body: formData,
headers: {
'X-CSRF-Token': csrfToken
}
}) })
.then(response => { .then(response => {
if (response.ok) { if (response.ok) {

View File

@ -87,7 +87,7 @@
} }
function addGANet() { function addGANet() {
$.ajax({ $.cjax({
url: "/api/gan/network/add", url: "/api/gan/network/add",
type: "POST", type: "POST",
dataType: "json", dataType: "json",
@ -191,7 +191,7 @@
//Remove the given GANet //Remove the given GANet
function removeGANet(netid){ function removeGANet(netid){
if (confirm("Confirm remove Network " + netid + " PERMANENTLY ?")) if (confirm("Confirm remove Network " + netid + " PERMANENTLY ?"))
$.ajax({ $.cjax({
url: "/api/gan/network/remove", url: "/api/gan/network/remove",
type: "POST", type: "POST",
dataType: "json", dataType: "json",

View File

@ -214,7 +214,7 @@
//Get CIDR from selected range group //Get CIDR from selected range group
var cidr = $(".iprange.active").attr("cidr"); var cidr = $(".iprange.active").attr("cidr");
$.ajax({ $.cjax({
url: "/api/gan/network/setRange", url: "/api/gan/network/setRange",
metohd: "POST", metohd: "POST",
data:{ data:{
@ -240,7 +240,7 @@
if (object != undefined){ if (object != undefined){
$(object).addClass("loading"); $(object).addClass("loading");
} }
$.ajax({ $.cjax({
url: "/api/gan/network/name", url: "/api/gan/network/name",
method: "POST", method: "POST",
data: { data: {
@ -287,7 +287,7 @@
//Handle delete IP from memeber //Handle delete IP from memeber
function deleteIpFromMemeber(memberid, ip){ function deleteIpFromMemeber(memberid, ip){
$.ajax({ $.cjax({
url: "/api/gan/members/ip", url: "/api/gan/members/ip",
metohd: "POST", metohd: "POST",
data: { data: {
@ -334,7 +334,7 @@
return return
} }
$.ajax({ $.cjax({
url: "/api/gan/members/ip", url: "/api/gan/members/ip",
metohd: "POST", metohd: "POST",
data: { data: {
@ -461,7 +461,7 @@
$(".memberName").each(function(){ $(".memberName").each(function(){
let addr = $(this).attr("addr"); let addr = $(this).attr("addr");
let targetDOM = $(this); let targetDOM = $(this);
$.ajax({ $.cjax({
url: "/api/gan/members/name", url: "/api/gan/members/name",
method: "POST", method: "POST",
data: { data: {
@ -487,7 +487,7 @@
let newname = prompt("Enter a easy manageable name for " + targetMemberAddr, ""); let newname = prompt("Enter a easy manageable name for " + targetMemberAddr, "");
if (newname != null && newname.trim() != "") { if (newname != null && newname.trim() != "") {
$.ajax({ $.cjax({
url: "/api/gan/members/name", url: "/api/gan/members/name",
method: "POST", method: "POST",
data: { data: {
@ -553,7 +553,7 @@
function handleMemberAuth(object){ function handleMemberAuth(object){
let targetMemberAddr = $(object).attr("addr"); let targetMemberAddr = $(object).attr("addr");
let isAuthed = object.checked; let isAuthed = object.checked;
$.ajax({ $.cjax({
url: "/api/gan/members/authorize", url: "/api/gan/members/authorize",
method: "POST", method: "POST",
data: { data: {
@ -580,7 +580,7 @@
function handleMemberDelete(addr){ function handleMemberDelete(addr){
if (confirm("Confirm delete member " + addr + " ?")){ if (confirm("Confirm delete member " + addr + " ?")){
$.ajax({ $.cjax({
url: "/api/gan/members/delete", url: "/api/gan/members/delete",
method: "POST", method: "POST",
data: { data: {
@ -605,7 +605,7 @@
$(".addControllerToNetworkBtn").addClass("disabled"); $(".addControllerToNetworkBtn").addClass("disabled");
$(".addControllerToNetworkBtn").addClass("loading"); $(".addControllerToNetworkBtn").addClass("loading");
$.ajax({ $.cjax({
url: "/api/gan/network/join", url: "/api/gan/network/join",
method: "POST", method: "POST",
data: { data: {
@ -630,7 +630,7 @@
$(".removeControllerFromNetworkBtn").addClass("disabled"); $(".removeControllerFromNetworkBtn").addClass("disabled");
$(".removeControllerFromNetworkBtn").addClass("loading"); $(".removeControllerFromNetworkBtn").addClass("loading");
$.ajax({ $.cjax({
url: "/api/gan/network/leave", url: "/api/gan/network/leave",
method: "POST", method: "POST",
data: { data: {

View File

@ -400,7 +400,7 @@
let rateLimit = $(row).find(".RateLimit").val(); let rateLimit = $(row).find(".RateLimit").val();
let bypassGlobalTLS = $(row).find(".BypassGlobalTLS")[0].checked; let bypassGlobalTLS = $(row).find(".BypassGlobalTLS")[0].checked;
$.ajax({ $.cjax({
url: "/api/proxy/edit", url: "/api/proxy/edit",
method: "POST", method: "POST",
data: { data: {
@ -422,6 +422,28 @@
} }
}) })
} }
//Generic functions for delete rp endpoints
function deleteEndpoint(epoint){
epoint = decodeURIComponent(epoint).hexDecode();
if (confirm("Confirm remove proxy for :" + epoint + "?")){
$.cjax({
url: "/api/proxy/del",
method: "POST",
data: {ep: epoint},
success: function(data){
if (data.error == undefined){
listProxyEndpoints();
msgbox("Proxy Rule Deleted", true);
reloadUptimeList();
}else{
msgbox(data.error, false);
}
}
})
}
}
/* button events */ /* button events */
function editBasicAuthCredentials(uuid){ function editBasicAuthCredentials(uuid){
@ -474,7 +496,7 @@
function handleProxyRuleToggle(object){ function handleProxyRuleToggle(object){
let endpointUUID = $(object).attr("eptuuid"); let endpointUUID = $(object).attr("eptuuid");
let isChecked = object.checked; let isChecked = object.checked;
$.ajax({ $.cjax({
url: "/api/proxy/toggle", url: "/api/proxy/toggle",
data: { data: {
"ep": endpointUUID, "ep": endpointUUID,

View File

@ -339,7 +339,7 @@ function setWoLAddress() {
$("#wol_mac").parent().removeClass("error"); $("#wol_mac").parent().removeClass("error");
} }
$.ajax({ $.cjax({
url: wake_on_lan_API, url: wake_on_lan_API,
type: "POST", type: "POST",
data: { data: {
@ -363,7 +363,7 @@ function setWoLAddress() {
function delWoLAddr(mac, name) { function delWoLAddr(mac, name) {
if (confirm(`Confirm remove WoL record for ${name} (${mac}) ?`)){ if (confirm(`Confirm remove WoL record for ${name} (${mac}) ?`)){
$.ajax({ $.cjax({
url: wake_on_lan_API, url: wake_on_lan_API,
type: "POST", type: "POST",
data: { data: {
@ -385,7 +385,7 @@ function wakeWoL(mac, object=undefined) {
if (object != undefined){ if (object != undefined){
$(object).addClass("loading").addClass("disabled"); $(object).addClass("loading").addClass("disabled");
} }
$.ajax({ $.cjax({
url: wake_on_lan_API, url: wake_on_lan_API,
type: "POST", type: "POST",
data: { data: {
@ -594,7 +594,7 @@ function initForwardProxyInfo(){
initForwardProxyInfo(); initForwardProxyInfo();
function toggleForwadProxy(enabled){ function toggleForwadProxy(enabled){
$.ajax({ $.cjax({
url: "/api/tools/fwdproxy/enable", url: "/api/tools/fwdproxy/enable",
method: "POST", method: "POST",
data: { data: {
@ -620,7 +620,7 @@ function updateForwardProxyPort(){
$("#newPortNumber").parent().removeClass('error'); $("#newPortNumber").parent().removeClass('error');
} }
$.ajax({ $.cjax({
url: "/api/tools/fwdproxy/port", url: "/api/tools/fwdproxy/port",
method: "POST", method: "POST",
data: { data: {

View File

@ -116,7 +116,7 @@
let forwardChildpath = document.querySelector('input[name="forward-childpath"]').checked; let forwardChildpath = document.querySelector('input[name="forward-childpath"]').checked;
let redirectType = document.querySelector('input[name="redirect-type"]:checked').value; let redirectType = document.querySelector('input[name="redirect-type"]:checked').value;
$.ajax({ $.cjax({
url: "/api/redirect/add", url: "/api/redirect/add",
method: "POST", method: "POST",
data: { data: {
@ -141,7 +141,7 @@
let targetURL = $(obj).attr("rurl"); let targetURL = $(obj).attr("rurl");
targetURL = JSON.parse(decodeURIComponent(targetURL)); targetURL = JSON.parse(decodeURIComponent(targetURL));
if (confirm("Confirm remove redirection from " + targetURL + " ?")){ if (confirm("Confirm remove redirection from " + targetURL + " ?")){
$.ajax({ $.cjax({
url: "/api/redirect/delete", url: "/api/redirect/delete",
method: "POST", method: "POST",
data: { data: {
@ -191,8 +191,9 @@
//Bind event to the checkbox //Bind event to the checkbox
$("#redirectRegex").on("change", function(){ $("#redirectRegex").on("change", function(){
$.ajax({ $.cjax({
url: "/api/redirect/regex", url: "/api/redirect/regex",
method: "POST",
data: {"enable": $(this)[0].checked}, data: {"enable": $(this)[0].checked},
success: function(data){ success: function(data){
if (data.error != undefined){ if (data.error != undefined){

View File

@ -181,8 +181,9 @@
targetDomain = targetDomain.substring(8); targetDomain = targetDomain.substring(8);
$("#proxyRoot").val(targetDomain); $("#proxyRoot").val(targetDomain);
} }
$.ajax({ $.cjax({
url: "/api/proxy/tlscheck", url: "/api/proxy/tlscheck",
method: "POST",
data: {url: targetDomain}, data: {url: targetDomain},
success: function(data){ success: function(data){
if (data.error != undefined){ if (data.error != undefined){
@ -232,7 +233,7 @@
} }
//Create the endpoint by calling add //Create the endpoint by calling add
$.ajax({ $.cjax({
url: "/api/proxy/add", url: "/api/proxy/add",
data: { data: {
"type": "root", "type": "root",

View File

@ -212,8 +212,9 @@
} }
//Create the endpoint by calling add //Create the endpoint by calling add
$.ajax({ $.cjax({
url: "/api/proxy/add", url: "/api/proxy/add",
method: "POST",
data: { data: {
type: "host", type: "host",
rootname: rootname, rootname: rootname,
@ -270,22 +271,6 @@
} }
//Generic functions for delete rp endpoints
function deleteEndpoint(epoint){
epoint = decodeURIComponent(epoint).hexDecode();
if (confirm("Confirm remove proxy for :" + epoint + "?")){
$.ajax({
url: "/api/proxy/del",
data: {ep: epoint, },
success: function(){
listProxyEndpoints();
msgbox("Proxy Rule Deleted", true);
reloadUptimeList();
}
})
}
}
//Clearn the proxy target value, make sure user do not enter http:// or https:// //Clearn the proxy target value, make sure user do not enter http:// or https://
//and auto select TLS checkbox if https:// exists //and auto select TLS checkbox if https:// exists
function autoFillTargetTLS(input){ function autoFillTargetTLS(input){
@ -307,12 +292,12 @@
//Automatic check if the site require TLS and check the checkbox if needed //Automatic check if the site require TLS and check the checkbox if needed
function autoCheckTls(targetDomain){ function autoCheckTls(targetDomain){
$.ajax({ $.cjax({
url: "/api/proxy/tlscheck", url: "/api/proxy/tlscheck",
data: {url: targetDomain}, data: {url: targetDomain},
success: function(data){ success: function(data){
if (data.error != undefined){ if (data.error != undefined){
msgbox(data.error, false);
}else if (data == "https"){ }else if (data == "https"){
$("#reqTls").parent().checkbox("set checked"); $("#reqTls").parent().checkbox("set checked");
}else if (data == "http"){ }else if (data == "http"){

View File

@ -315,26 +315,39 @@
//Start and stop service button //Start and stop service button
function startService(){ function startService(){
$.post("/api/proxy/enable", {enable: true}, function(data){ $.cjax({
if (data.error != undefined){ url: "/api/proxy/enable",
msgbox(data.error, false, 5000); method: "POST",
data: {enable: true},
success: function(data){
if (data.error != undefined){
msgbox(data.error, false, 5000);
}
initRPStaste();
} }
initRPStaste();
}); });
} }
function stopService(){ function stopService(){
$.post("/api/proxy/enable", {enable: false}, function(data){ $.cjax({
if (data.error != undefined){ url: "/api/proxy/enable",
msgbox(data.error, false, 5000); method: "POST",
data: {enable: false},
success: function(data){
if (data.error != undefined){
msgbox(data.error, false, 5000);
}
initRPStaste();
} }
initRPStaste();
}); });
} }
function handleP80ListenerStateChange(enabled){ function handleP80ListenerStateChange(enabled){
$.ajax({ $.cjax({
url: "/api/proxy/listenPort80", url: "/api/proxy/listenPort80",
method: "POST",
data: {"enable": enabled}, data: {"enable": enabled},
success: function(data){ success: function(data){
if (data.error != undefined){ if (data.error != undefined){
@ -361,16 +374,21 @@
return; return;
} }
$.post("/api/proxy/setIncoming", {incoming: newPortValue}, function(data){ $.cjax({
if (data.error != undefined){ url: "/api/proxy/setIncoming",
msgbox(data.error, false, 5000); method: "POST",
return; data: {incoming: newPortValue},
} success: function(data){
msgbox("Listening Port Updated"); if (data.error != undefined){
initRPStaste(); msgbox(data.error, false, 5000);
return;
}
msgbox("Listening Port Updated");
initRPStaste();
//Hide the reminder text //Hide the reminder text
$("#applyButtonReminder").hide(); $("#applyButtonReminder").hide();
}
}); });
} }
@ -402,8 +420,9 @@
//Initiate the input listener on the checkbox //Initiate the input listener on the checkbox
$("#redirect").find("input").on("change", function(){ $("#redirect").find("input").on("change", function(){
let thisValue = $("#redirect").checkbox("is checked"); let thisValue = $("#redirect").checkbox("is checked");
$.ajax({ $.cjax({
url: "/api/proxy/useHttpsRedirect", url: "/api/proxy/useHttpsRedirect",
method: "POST",
data: {set: thisValue}, data: {set: thisValue},
success: function(data){ success: function(data){
if (data.error != undefined){ if (data.error != undefined){
@ -440,9 +459,10 @@
//Bind events to the checkbox //Bind events to the checkbox
$("#tlsMinVer").find("input").on("change", function(){ $("#tlsMinVer").find("input").on("change", function(){
let thisValue = $("#tlsMinVer").checkbox("is checked"); let thisValue = $("#tlsMinVer").checkbox("is checked");
$.ajax({ $.cjax({
url: "/api/cert/tlsRequireLatest", url: "/api/cert/tlsRequireLatest",
data: {"set": thisValue}, data: {"set": thisValue},
method: "POST",
success: function(data){ success: function(data){
if (data.error != undefined){ if (data.error != undefined){
msgbox(data.error, false, 5000); msgbox(data.error, false, 5000);
@ -498,15 +518,15 @@
}else{ }else{
$(".tlsEnabledOnly").addClass('disabled'); $(".tlsEnabledOnly").addClass('disabled');
} }
$.ajax({ $.cjax({
url: "/api/cert/tls", url: "/api/cert/tls",
method: "POST",
data: {set: thisValue}, data: {set: thisValue},
success: function(data){ success: function(data){
if (data.error != undefined){ if (data.error != undefined){
alert(data.error); msgbox(data.error, false);
}else{ }else{
//Updated //Updated
//Check for case if the port is invalid default ports //Check for case if the port is invalid default ports
if ($("#incomingPort").val() == "80" && thisValue == true){ if ($("#incomingPort").val() == "80" && thisValue == true){
confirmBox("Change listen port to :443?", function(choice){ confirmBox("Change listen port to :443?", function(choice){

View File

@ -100,7 +100,7 @@
} }
// Send the AJAX POST request // Send the AJAX POST request
$.ajax({ $.cjax({
type: 'POST', type: 'POST',
url: '/api/streamprox/config/add', url: '/api/streamprox/config/add',
data: form.serialize(), data: form.serialize(),
@ -285,7 +285,7 @@
} }
// Send the AJAX POST request // Send the AJAX POST request
$.ajax({ $.cjax({
type: 'POST', type: 'POST',
url: '/api/streamprox/config/edit', url: '/api/streamprox/config/edit',
method: "POST", method: "POST",
@ -316,7 +316,7 @@
} }
function deleteTCPProxyConfig(configUUID){ function deleteTCPProxyConfig(configUUID){
$.ajax({ $.cjax({
url: "/api/streamprox/config/delete", url: "/api/streamprox/config/delete",
method: "POST", method: "POST",
data: {uuid: configUUID}, data: {uuid: configUUID},
@ -333,7 +333,7 @@
//Start a TCP proxy by their config UUID //Start a TCP proxy by their config UUID
function startStreamProx(configUUID){ function startStreamProx(configUUID){
$.ajax({ $.cjax({
url: "/api/streamprox/config/start", url: "/api/streamprox/config/start",
method: "POST", method: "POST",
data: {uuid: configUUID}, data: {uuid: configUUID},
@ -351,7 +351,7 @@
//Stop a TCP proxy by their config UUID //Stop a TCP proxy by their config UUID
function stopStreamProx(configUUID){ function stopStreamProx(configUUID){
$.ajax({ $.cjax({
url: "/api/streamprox/config/stop", url: "/api/streamprox/config/stop",
method: "POST", method: "POST",
data: {uuid: configUUID}, data: {uuid: configUUID},

View File

@ -233,7 +233,7 @@
const newPassword = document.getElementsByName('newPassword')[0].value; const newPassword = document.getElementsByName('newPassword')[0].value;
const confirmNewPassword = document.getElementsByName('confirmNewPassword')[0].value; const confirmNewPassword = document.getElementsByName('confirmNewPassword')[0].value;
$.ajax({ $.cjax({
type: "POST", type: "POST",
url: "/api/auth/changePassword", url: "/api/auth/changePassword",
data: { data: {
@ -279,7 +279,7 @@
return; return;
} }
$.ajax({ $.cjax({
type: "POST", type: "POST",
url: "/api/tools/smtp/set", url: "/api/tools/smtp/set",
data: data, data: data,

View File

@ -190,7 +190,7 @@
function updateVDTargetTLSState(){ function updateVDTargetTLSState(){
var targetDomain = $("#virtualDirectoryDomain").val().trim(); var targetDomain = $("#virtualDirectoryDomain").val().trim();
if (targetDomain != ""){ if (targetDomain != ""){
$.ajax({ $.cjax({
url: "/api/proxy/tlscheck", url: "/api/proxy/tlscheck",
data: {url: targetDomain}, data: {url: targetDomain},
success: function(data){ success: function(data){
@ -252,7 +252,7 @@
} }
//Create a virtual directory endpoint //Create a virtual directory endpoint
$.ajax({ $.cjax({
url: "/api/proxy/vdir/add", url: "/api/proxy/vdir/add",
method: "POST", method: "POST",
data: { data: {
@ -295,7 +295,7 @@
epType = "root"; epType = "root";
path = ""; path = "";
} }
$.ajax({ $.cjax({
url: "/api/proxy/vdir/del", url: "/api/proxy/vdir/del",
method: "POST", method: "POST",
data: { data: {
@ -384,7 +384,7 @@
//console.log(mathingPath, newDomain, requireTLS, skipValidation); //console.log(mathingPath, newDomain, requireTLS, skipValidation);
$.ajax({ $.cjax({
url: "/api/proxy/vdir/edit", url: "/api/proxy/vdir/edit",
method: "POST", method: "POST",
data: { data: {

View File

@ -164,7 +164,7 @@
$("#webserv_enableDirList").off("change").on("change", function(){ $("#webserv_enableDirList").off("change").on("change", function(){
let enable = $(this)[0].checked; let enable = $(this)[0].checked;
$.ajax({ $.cjax({
url: "/api/webserv/setDirList", url: "/api/webserv/setDirList",
method: "POST", method: "POST",
data: {"enable": enable}, data: {"enable": enable},
@ -186,7 +186,7 @@
confirmBox("This setting might cause port conflict. Continue Anyway?", function(choice){ confirmBox("This setting might cause port conflict. Continue Anyway?", function(choice){
if (choice == true){ if (choice == true){
//Continue anyway //Continue anyway
$.ajax({ $.cjax({
url: "/api/webserv/setPort", url: "/api/webserv/setPort",
method: "POST", method: "POST",
data: {"port": newPort}, data: {"port": newPort},
@ -206,7 +206,7 @@
} }
}); });
}else{ }else{
$.ajax({ $.cjax({
url: "/api/webserv/setPort", url: "/api/webserv/setPort",
method: "POST", method: "POST",
data: {"port": newPort}, data: {"port": newPort},

View File

@ -5,6 +5,7 @@
<meta name="viewport" content="user-scalable=no, width=device-width, initial-scale=1, maximum-scale=1"/> <meta name="viewport" content="user-scalable=no, width=device-width, initial-scale=1, maximum-scale=1"/>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="theme-color" content="#4b75ff"> <meta name="theme-color" content="#4b75ff">
<meta name="zoraxy.csrf.Token" content="{{.csrfToken}}">
<link rel="icon" type="image/png" href="./favicon.png" /> <link rel="icon" type="image/png" href="./favicon.png" />
<title>Control Panel | Zoraxy</title> <title>Control Panel | Zoraxy</title>
<link rel="stylesheet" href="script/semantic/semantic.min.css"> <link rel="stylesheet" href="script/semantic/semantic.min.css">

View File

@ -4,6 +4,7 @@
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="robots" content="noindex" /> <meta name="robots" content="noindex" />
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="zoraxy.csrf.Token" content="{{.csrfToken}}">
<link rel="icon" type="image/png" href="./favicon.png" /> <link rel="icon" type="image/png" href="./favicon.png" />
<title>Login | Zoraxy</title> <title>Login | Zoraxy</title>
<link rel="stylesheet" href="script/semantic/semantic.min.css"> <link rel="stylesheet" href="script/semantic/semantic.min.css">
@ -250,10 +251,10 @@
}); });
$("#regsiterbtn").on("click", function(event){ $("#regsiterbtn").on("click", function(event){
var username = $("#username").val(); let username = $("#username").val();
var magic = $("#magic").val(); let magic = $("#magic").val();
var repeatMagic = $("#repeatMagic").val(); let repeatMagic = $("#repeatMagic").val();
let csrfToken = document.getElementsByTagName("meta")["zoraxy.csrf.Token"].getAttribute("content");
if (magic !== repeatMagic) { if (magic !== repeatMagic) {
alert("Password does not match"); alert("Password does not match");
return; return;
@ -262,6 +263,9 @@
$.ajax({ $.ajax({
url: "/api/auth/register", url: "/api/auth/register",
method: "POST", method: "POST",
beforeSend: function(request) {
request.setRequestHeader("X-CSRF-Token",csrfToken);
},
data: { data: {
username: username, username: username,
password: magic password: magic
@ -297,29 +301,45 @@
//Login system with the given username and password //Login system with the given username and password
function login(){ function login(){
var username = $("#username").val(); let username = $("#username").val();
var magic = $("#magic").val(); let magic = $("#magic").val();
var rmbme = document.getElementById("rmbme").checked; let rmbme = document.getElementById("rmbme").checked;
let csrfToken = document.getElementsByTagName("meta")["zoraxy.csrf.Token"].getAttribute("content");
$("#errmsg").stop().finish().slideUp("fast"); $("#errmsg").stop().finish().slideUp("fast");
$("input").addClass('disabled'); $("input").addClass('disabled');
$.post(loginAddress, {"username": username, "password": magic, "rmbme": rmbme}).done(function(data){ $.ajax({
if (data.error !== undefined){ url: loginAddress,
//Something went wrong during the login type: "POST",
$("#errmsg").html(`<i class="red remove icon"></i> ${data.error}`); beforeSend: function(request) {
$("#errmsg").stop().finish().slideDown('fast'); request.setRequestHeader("X-CSRF-Token",csrfToken);
}else if(data.redirect !== undefined){ },
//LDAP Related Code data: {
window.location.href = data.redirect; "username": username,
}else{ "password": magic,
//Login succeed "rmbme": rmbme,
if (redirectionAddress == ""){ },
//Redirect back to index success: function(data){
window.location.href = "./"; if (data.error !== undefined){
//Something went wrong during the login
$("#errmsg").html(`<i class="red remove icon"></i> ${data.error}`);
$("#errmsg").stop().finish().slideDown('fast');
}else if(data.redirect !== undefined){
//LDAP Related Code
window.location.href = data.redirect;
}else{ }else{
window.location.href = redirectionAddress; //Login succeed
if (redirectionAddress == ""){
//Redirect back to index
window.location.href = "./";
}else{
window.location.href = redirectionAddress;
}
} }
$("input").removeClass('disabled');
},
error: function(){
alert("Something went wrong.")
} }
$("input").removeClass('disabled');
}); });
} }

View File

@ -2,6 +2,7 @@
<html> <html>
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="zoraxy.csrf.Token" content="{{.csrfToken}}">
<meta name="robots" content="noindex" /> <meta name="robots" content="noindex" />
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/png" href="./favicon.png" /> <link rel="icon" type="image/png" href="./favicon.png" />
@ -255,25 +256,36 @@
} }
// Send POST request with input values as data // Send POST request with input values as data
$.post('/api/account/new', { username: username, token: token, newpw: newPassword }) let csrfToken = document.getElementsByTagName("meta")["zoraxy.csrf.Token"].getAttribute("content");
.done(function(data) { $.ajax({
// Handle successful response url: "/api/account/new",
if (data.error != undefined){ method: "POST",
$("#errmsg").html(`<i class="red circle times icon"></i> ` + data.error); data: {
$("#errmsg").show(); username: username,
}else{ token: token,
$("#errmsg").hide(); newpw: newPassword
$("#countdown").hide(); },
$("#succmsg").show(); headers: {
setTimeout(function(){ "X-CSRF-Token": csrfToken,
window.location.href = "/"; },
}, 3000); success: function(data){
// Handle successful response
if (data.error != undefined){
$("#errmsg").html(`<i class="red circle times icon"></i> ` + data.error);
$("#errmsg").show();
}else{
$("#errmsg").hide();
$("#countdown").hide();
$("#succmsg").show();
setTimeout(function(){
window.location.href = "/";
}, 3000);
}
},
error: function(){
console.error(error);
} }
}) })
.fail(function(error) {
// Handle error response
console.error(error);
});
}); });

View File

@ -26,4 +26,18 @@ Object.defineProperty(String.prototype, 'capitalize', {
return this.charAt(0).toUpperCase() + this.slice(1); return this.charAt(0).toUpperCase() + this.slice(1);
}, },
enumerable: false enumerable: false
}); });
//Add a new function to jquery for ajax override with csrf token injected
$.cjax = function(payload){
let requireTokenMethod = ["POST", "PUT", "DELETE"];;
if (requireTokenMethod.includes(payload.method) || requireTokenMethod.includes(payload.type)){
//csrf token is required
let csrfToken = document.getElementsByTagName("meta")["zoraxy.csrf.Token"].getAttribute("content");
payload.headers = {
"X-CSRF-Token": csrfToken,
}
}
$.ajax(payload);
}

View File

@ -3,9 +3,11 @@
<head> <head>
<!-- Notes: This should be open in its original path--> <!-- Notes: This should be open in its original path-->
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="zoraxy.csrf.Token" content="{{.csrfToken}}">
<link rel="stylesheet" href="../script/semantic/semantic.min.css"> <link rel="stylesheet" href="../script/semantic/semantic.min.css">
<script src="../script/jquery-3.6.0.min.js"></script> <script src="../script/jquery-3.6.0.min.js"></script>
<script src="../script/semantic/semantic.min.js"></script> <script src="../script/semantic/semantic.min.js"></script>
<script src="../script/utils.js"></script>
<style> <style>
#refreshAccessRuleListBtn{ #refreshAccessRuleListBtn{
position: absolute; position: absolute;
@ -94,7 +96,7 @@
$("#accessRuleForm input[name='accessRuleName']").val(""); $("#accessRuleForm input[name='accessRuleName']").val("");
$("#accessRuleForm textarea[name='description']").val(""); $("#accessRuleForm textarea[name='description']").val("");
$.ajax({ $.cjax({
url: "/api/access/create", url: "/api/access/create",
method: "POST", method: "POST",
data: { data: {
@ -162,7 +164,7 @@
console.log('Access Rule Name:', accessRuleName); console.log('Access Rule Name:', accessRuleName);
console.log('Description:', description); console.log('Description:', description);
$.ajax({ $.cjax({
url: "/api/access/update", url: "/api/access/update",
method: "POST", method: "POST",
data: { data: {
@ -238,7 +240,7 @@
} }
let accessRuleName = $("#modifyRuleInfo input[name='accessRuleName']").val(); let accessRuleName = $("#modifyRuleInfo input[name='accessRuleName']").val();
if (confirm("Confirm removing access rule " + accessRuleName + "?")){ if (confirm("Confirm removing access rule " + accessRuleName + "?")){
$.ajax({ $.cjax({
url: "/api/access/remove", url: "/api/access/remove",
data: { data: {
"id": accessRuleUUID "id": accessRuleUUID

View File

@ -3,9 +3,11 @@
<head> <head>
<!-- Notes: This should be open in its original path--> <!-- Notes: This should be open in its original path-->
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="zoraxy.csrf.Token" content="{{.csrfToken}}">
<link rel="stylesheet" href="../script/semantic/semantic.min.css"> <link rel="stylesheet" href="../script/semantic/semantic.min.css">
<script src="../script/jquery-3.6.0.min.js"></script> <script src="../script/jquery-3.6.0.min.js"></script>
<script src="../script/semantic/semantic.min.js"></script> <script src="../script/semantic/semantic.min.js"></script>
<script src="../script/utils.js"></script>
<style> <style>
.disabled.table{ .disabled.table{
opacity: 0.5; opacity: 0.5;
@ -234,8 +236,9 @@
initRenewerConfigFromFile(); initRenewerConfigFromFile();
function saveEmailToConfig(btn){ function saveEmailToConfig(btn){
$.ajax({ $.cjax({
url: "/api/acme/autoRenew/email", url: "/api/acme/autoRenew/email",
method: "POST",
data: {set: $("#caRegisterEmail").val()}, data: {set: $("#caRegisterEmail").val()},
success: function(data){ success: function(data){
if (data.error != undefined){ if (data.error != undefined){
@ -256,27 +259,29 @@
function toggleAutoRenew(){ function toggleAutoRenew(){
var enabled = $("#enableCertAutoRenew").parent().checkbox("is checked"); var enabled = $("#enableCertAutoRenew").parent().checkbox("is checked");
$.post("/api/acme/autoRenew/enable?enable=" + enabled, function(data){ $.cjax({
if (data.error){ url: "/api/acme/autoRenew/enable",
parent.msgbox(data.error, false, 5000); method: "POST",
if (enabled){ data: {"enable": enabled},
enableTrigerOnChangeEvent = false; success: function(data){
$("#enableCertAutoRenew").parent().checkbox("set unchecked"); if (data.error){
enableTrigerOnChangeEvent = true; parent.msgbox(data.error, false, 5000);
} if (enabled){
if (parent && parent.setACMEEnableStates){ enableTrigerOnChangeEvent = false;
parent.setACMEEnableStates(!enabled); $("#enableCertAutoRenew").parent().checkbox("set unchecked");
} enableTrigerOnChangeEvent = true;
}else{ }
$("#enableToggleSucc").stop().finish().fadeIn("fast").delay(3000).fadeOut("fast"); if (parent && parent.setACMEEnableStates){
if (parent && parent.setACMEEnableStates){ parent.setACMEEnableStates(!enabled);
parent.setACMEEnableStates(enabled); }
}else{
$("#enableToggleSucc").stop().finish().fadeIn("fast").delay(3000).fadeOut("fast");
if (parent && parent.setACMEEnableStates){
parent.setACMEEnableStates(enabled);
}
} }
} }
}); });
} }
//Render the domains table that exists in this zoraxy host //Render the domains table that exists in this zoraxy host
@ -630,7 +635,7 @@
return; return;
} }
$.ajax({ $.cjax({
url: "/api/acme/autoRenew/setDNS", url: "/api/acme/autoRenew/setDNS",
method: "POST", method: "POST",
data: { data: {
@ -843,8 +848,9 @@
function saveAutoRenewPolicy(){ function saveAutoRenewPolicy(){
let autoRenewAll = $("#renewAllSupported").parent().checkbox("is checked"); let autoRenewAll = $("#renewAllSupported").parent().checkbox("is checked");
if (autoRenewAll == true){ if (autoRenewAll == true){
$.ajax({ $.cjax({
url: "/api/acme/autoRenew/setDomains", url: "/api/acme/autoRenew/setDomains",
method: "POST",
data: {opr: "setAuto"}, data: {opr: "setAuto"},
success: function(data){ success: function(data){
parent.msgbox("Renew policy rule updated") parent.msgbox("Renew policy rule updated")
@ -856,8 +862,9 @@
checkedNames.push($(this).attr('name')); checkedNames.push($(this).attr('name'));
}); });
$.ajax({ $.cjax({
url: "/api/acme/autoRenew/setDomains", url: "/api/acme/autoRenew/setDomains",
method: "POST",
data: {opr: "setSelected", domains: JSON.stringify(checkedNames)}, data: {opr: "setSelected", domains: JSON.stringify(checkedNames)},
success: function(data){ success: function(data){
parent.msgbox("Renew policy rule updated") parent.msgbox("Renew policy rule updated")

View File

@ -3,9 +3,11 @@
<head> <head>
<!-- Notes: This should be open in its original path--> <!-- Notes: This should be open in its original path-->
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="zoraxy.csrf.Token" content="{{.csrfToken}}">
<link rel="stylesheet" href="../script/semantic/semantic.min.css"> <link rel="stylesheet" href="../script/semantic/semantic.min.css">
<script src="../script/jquery-3.6.0.min.js"></script> <script src="../script/jquery-3.6.0.min.js"></script>
<script src="../script/semantic/semantic.min.js"></script> <script src="../script/semantic/semantic.min.js"></script>
<script src="../script/utils.js"></script>
</head> </head>
<body> <body>
<br> <br>
@ -46,7 +48,7 @@
function handleResetStats(){ function handleResetStats(){
if (confirm("Confirm remove statistics from " + startDate + " to " + endDate +"?")){ if (confirm("Confirm remove statistics from " + startDate + " to " + endDate +"?")){
$.ajax({ $.cjax({
url: "/api/analytic/resetRange?start=" + startDate + "&end=" + endDate, url: "/api/analytic/resetRange?start=" + startDate + "&end=" + endDate,
method: "DELETE", method: "DELETE",
success: function(data){ success: function(data){

View File

@ -3,9 +3,11 @@
<head> <head>
<!-- Notes: This should be open in its original path--> <!-- Notes: This should be open in its original path-->
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="zoraxy.csrf.Token" content="{{.csrfToken}}">
<link rel="stylesheet" href="../script/semantic/semantic.min.css"> <link rel="stylesheet" href="../script/semantic/semantic.min.css">
<script src="../script/jquery-3.6.0.min.js"></script> <script src="../script/jquery-3.6.0.min.js"></script>
<script src="../script/semantic/semantic.min.js"></script> <script src="../script/semantic/semantic.min.js"></script>
<script src="../script/utils.js"></script>
</head> </head>
<body> <body>
<br> <br>
@ -71,7 +73,7 @@
} }
function initAliasNames(){ function initAliasNames(){
$.ajax({ $.cjax({
url: "/api/proxy/detail", url: "/api/proxy/detail",
method: "POST", method: "POST",
data: { data: {
@ -130,7 +132,7 @@
} }
function saveCurrentAliasList(callback=undefined){ function saveCurrentAliasList(callback=undefined){
$.ajax({ $.cjax({
url: "/api/proxy/setAlias", url: "/api/proxy/setAlias",
method: "POST", method: "POST",
data:{ data:{

View File

@ -3,9 +3,11 @@
<head> <head>
<!-- Notes: This should be open in its original path--> <!-- Notes: This should be open in its original path-->
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="zoraxy.csrf.Token" content="{{.csrfToken}}">
<link rel="stylesheet" href="../script/semantic/semantic.min.css"> <link rel="stylesheet" href="../script/semantic/semantic.min.css">
<script src="../script/jquery-3.6.0.min.js"></script> <script src="../script/jquery-3.6.0.min.js"></script>
<script src="../script/semantic/semantic.min.js"></script> <script src="../script/semantic/semantic.min.js"></script>
<script src="../script/utils.js"></script>
</head> </head>
<body> <body>
<br> <br>
@ -174,7 +176,7 @@
parent.msgbox("Matching prefix cannot be empty!", false, 5000); parent.msgbox("Matching prefix cannot be empty!", false, 5000);
return; return;
} }
$.ajax({ $.cjax({
url: "/api/proxy/auth/exceptions/add", url: "/api/proxy/auth/exceptions/add",
data:{ data:{
ep: editingEndpoint.ep, ep: editingEndpoint.ep,
@ -195,7 +197,7 @@
function removeExceptionPath(object){ function removeExceptionPath(object){
let matchingPrefix = $(object).attr("prefix"); let matchingPrefix = $(object).attr("prefix");
$.ajax({ $.cjax({
url: "/api/proxy/auth/exceptions/delete", url: "/api/proxy/auth/exceptions/delete",
data:{ data:{
ep: editingEndpoint.ep, ep: editingEndpoint.ep,
@ -290,7 +292,7 @@
} }
function saveCredentials(){ function saveCredentials(){
$.ajax({ $.cjax({
url: "/api/proxy/updateCredentials", url: "/api/proxy/updateCredentials",
method: "POST", method: "POST",
data: { data: {

View File

@ -3,9 +3,11 @@
<head> <head>
<!-- Notes: This should be open in its original path--> <!-- Notes: This should be open in its original path-->
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="zoraxy.csrf.Token" content="{{.csrfToken}}">
<link rel="stylesheet" href="../script/semantic/semantic.min.css"> <link rel="stylesheet" href="../script/semantic/semantic.min.css">
<script src="../script/jquery-3.6.0.min.js"></script> <script src="../script/jquery-3.6.0.min.js"></script>
<script src="../script/semantic/semantic.min.js"></script> <script src="../script/semantic/semantic.min.js"></script>
<script src="../script/utils.js"></script>
</head> </head>
<body> <body>
<br> <br>
@ -70,10 +72,10 @@
} }
} }
document.getElementById("uploadForm").addEventListener("submit", function(event) { $("#uploadForm").submit(function(event) {
event.preventDefault(); // Prevent the form from submitting normally event.preventDefault(); // Prevent the form from submitting normally
var fileInput = document.getElementById("fileInput"); var fileInput = $("#fileInput")[0];
var file = fileInput.files[0]; var file = fileInput.files[0];
if (!file) { if (!file) {
alert("Missing file."); alert("Missing file.");
@ -83,18 +85,19 @@
var formData = new FormData(); var formData = new FormData();
formData.append("file", file); formData.append("file", file);
var xhr = new XMLHttpRequest(); $.cjax({
xhr.open("POST", "/api/conf/import", true); url: "/api/conf/import",
xhr.onreadystatechange = function() { type: "POST",
if (xhr.readyState === XMLHttpRequest.DONE) { data: formData,
if (xhr.status === 200) { processData: false, // Not to process the data
parent.msgbox("Config restore succeed. Restart Zoraxy to apply changes.") contentType: false, // Not to set contentType
} else { success: function(response) {
parent.msgbox("Restore failed: " + xhr.responseText, false, 5000); parent.msgbox("Config restore succeed. Restart Zoraxy to apply changes.");
} },
error: function(xhr) {
parent.msgbox("Restore failed: " + xhr.responseText, false, 5000);
} }
}; });
xhr.send(formData);
}); });
</script> </script>
</body> </body>

View File

@ -3,9 +3,11 @@
<head> <head>
<!-- Notes: This should be open in its original path--> <!-- Notes: This should be open in its original path-->
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="zoraxy.csrf.Token" content="{{.csrfToken}}">
<link rel="stylesheet" href="../script/semantic/semantic.min.css"> <link rel="stylesheet" href="../script/semantic/semantic.min.css">
<script src="../script/jquery-3.6.0.min.js"></script> <script src="../script/jquery-3.6.0.min.js"></script>
<script src="../script/semantic/semantic.min.js"></script> <script src="../script/semantic/semantic.min.js"></script>
<script src="../script/utils.js"></script>
<style> <style>
.ui.tabular.menu .item.narrowpadding{ .ui.tabular.menu .item.narrowpadding{
padding: 0.6em !important; padding: 0.6em !important;
@ -83,6 +85,40 @@
<div class="ui divider"></div> <div class="ui divider"></div>
</div> </div>
</div> </div>
<div class="ui divider"></div>
<div class="ui basic segment" style="background-color: #f7f7f7; border-radius: 1em;">
<div class="ui fluid accordion">
<div class="title">
<i class="dropdown icon" tabindex="0"><div class="menu" tabindex="-1"></div></i>
Advance Settings
</div>
<div class="content">
<br>
<div class="ui container">
<h4>Overwrite Host Header</h4>
<p>Manual override the automatic "Host" header rewrite logic. Leave empty for automatic.</p>
<div class="ui fluid action input">
<input type="text" id="manualHostOverwrite" placeholder="Overwrite Host name">
<button onclick="updateManualHostOverwrite();" class="ui basic icon button" title="Update"><i class="ui green save icon"></i></button>
<button onclick="clearManualHostOverwrite();" class="ui basic icon button" title="Clear"><i class="ui grey remove icon"></i></button>
</div>
<div class="ui divider"></div>
<h4>Remove Hop-by-hop Headers</h4>
<p>Remove headers like "Connection" and "Keep-Alive" from both upstream and downstream requests. Set to ON by default.</p>
<div class="ui toggle checkbox">
<input type="checkbox" id="removeHopByHop" name="">
<label>Remove Hop-by-hop Header<br>
<small>This should be ON by default</small></label>
</div>
<div class="ui yellow message">
<p><i class="exclamation triangle icon"></i>Settings in this section are for advanced users. Invalid settings might cause werid, unexpected behavior.</p>
</div>
</div>
</div>
</div>
</div>
</div> </div>
<div class="ui tab basic segment" data-tab="security"> <div class="ui tab basic segment" data-tab="security">
<h4>HTTP Strict Transport Security</h4> <h4>HTTP Strict Transport Security</h4>
@ -129,6 +165,7 @@
<script> <script>
$('.menu .item').tab(); $('.menu .item').tab();
$(".accordion").accordion();
let permissionPolicyKeys = []; let permissionPolicyKeys = [];
let editingEndpoint = {}; let editingEndpoint = {};
@ -211,8 +248,9 @@
} }
} }
$.ajax({ $.cjax({
url: "/api/proxy/header/add", url: "/api/proxy/header/add",
method: "POST",
data: { data: {
"type": getHeaderEditMode(), "type": getHeaderEditMode(),
"domain": editingEndpoint.ep, "domain": editingEndpoint.ep,
@ -243,10 +281,10 @@
} }
function deleteCustomHeader(name){ function deleteCustomHeader(name){
$.ajax({ $.cjax({
url: "/api/proxy/header/remove", url: "/api/proxy/header/remove",
method: "POST",
data: { data: {
//"type": editingEndpoint.ept,
"domain": editingEndpoint.ep, "domain": editingEndpoint.ep,
"name": name, "name": name,
}, },
@ -263,6 +301,7 @@
$("#headerTable").html(`<tr><td colspan="3"><i class="ui loading spinner icon"></i> Loading</td></tr>`); $("#headerTable").html(`<tr><td colspan="3"><i class="ui loading spinner icon"></i> Loading</td></tr>`);
$.ajax({ $.ajax({
url: "/api/proxy/header/list", url: "/api/proxy/header/list",
method: "GET",
data: { data: {
"type": editingEndpoint.ept, "type": editingEndpoint.ept,
"domain": editingEndpoint.ep, "domain": editingEndpoint.ep,
@ -271,7 +310,6 @@
if (data.error != undefined){ if (data.error != undefined){
alert(data.error); alert(data.error);
}else{ }else{
$("#headerTable").html(""); $("#headerTable").html("");
data.forEach(header => { data.forEach(header => {
let editModeIcon = header.IsRemove?`<i class="ui red times circle icon"></i>`:`<i class="ui green add circle icon"></i>`; let editModeIcon = header.IsRemove?`<i class="ui red times circle icon"></i>`:`<i class="ui green add circle icon"></i>`;
@ -315,7 +353,7 @@
/* Bind events to toggles */ /* Bind events to toggles */
$("#enableHSTS").on("change", function(){ $("#enableHSTS").on("change", function(){
let HSTSEnabled = $("#enableHSTS")[0].checked; let HSTSEnabled = $("#enableHSTS")[0].checked;
$.ajax({ $.cjax({
url: "/api/proxy/header/handleHSTS", url: "/api/proxy/header/handleHSTS",
method: "POST", method: "POST",
data: { data: {
@ -390,7 +428,7 @@
$("#permissionPolicyEditor").addClass("disabled"); $("#permissionPolicyEditor").addClass("disabled");
} }
$.ajax({ $.cjax({
url: "/api/proxy/header/handlePermissionPolicy", url: "/api/proxy/header/handlePermissionPolicy",
method: "POST", method: "POST",
data: { data: {
@ -496,7 +534,7 @@
let permissionPolicy = generatePermissionPolicyObject(); let permissionPolicy = generatePermissionPolicyObject();
let domain = editingEndpoint.ep; let domain = editingEndpoint.ep;
$.ajax({ $.cjax({
url: "/api/proxy/header/handlePermissionPolicy", url: "/api/proxy/header/handlePermissionPolicy",
method: "PUT", method: "PUT",
data: { data: {
@ -512,6 +550,93 @@
} }
}) })
} }
/* Manual HOST header overwrite */
function updateManualHostOverwrite(){
updateManualHostOverwriteVal(function(data){
if (data.error != undefined){
parent.msgbox(data.error, false);
}else{
parent.msgbox("Host field Overwrite Updated");
initManualHostOverwriteValue();
}
});
}
function clearManualHostOverwrite(){
$('#manualHostOverwrite').val('');
updateManualHostOverwriteVal(function(data){
if (data.error != undefined){
parent.msgbox(data.error, false);
}else{
parent.msgbox("Host field Overwrite Cleared");
initManualHostOverwriteValue();
}
})
}
function updateManualHostOverwriteVal(callback=undefined){
let newHostname = $("#manualHostOverwrite").val().trim();
$.cjax({
url: "/api/proxy/header/handleHostOverwrite",
method: "POST",
data: {
"domain": editingEndpoint.ep,
"hostname": newHostname,
},
success: function(data){
callback(data);
}
})
}
function initManualHostOverwriteValue(){
$.get("/api/proxy/header/handleHostOverwrite?domain=" + editingEndpoint.ep, function(data){
if (data.error != undefined){
parent.msgbox(data.error, false);
}else{
$("#manualHostOverwrite").val(data);
}
});
}
initManualHostOverwriteValue();
/* Hop-by-hop headers */
function initHopByHopRemoverState(){
$.get("/api/proxy/header/handleHopByHop?domain=" + editingEndpoint.ep, function(data){
if (data.error != undefined){
parent.msgbox(data.error);
}else{
if (data == true){
$("#removeHopByHop").parent().checkbox("set checked");
}else{
$("#removeHopByHop").parent().checkbox("set unchecked");
}
//Bind event to the checkbox
$("#removeHopByHop").on("change", function(evt){
let isChecked = $(this)[0].checked;
$.cjax({
url: "/api/proxy/header/handleHopByHop",
method: "POST",
data: {
"domain": editingEndpoint.ep,
"removeHopByHop": isChecked,
},
success: function(data){
if (data.error != undefined){
parent.msgbox(data.error, false);
}else{
parent.msgbox("Hop-by-Hop header rule updated");
}
}
})
})
}
})
}
initHopByHopRemoverState();
</script> </script>
</body> </body>
</html> </html>

View File

@ -3,9 +3,11 @@
<head> <head>
<!-- Notes: This should be open in its original path--> <!-- Notes: This should be open in its original path-->
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="zoraxy.csrf.Token" content="{{.csrfToken}}">
<link rel="stylesheet" href="../script/semantic/semantic.min.css"> <link rel="stylesheet" href="../script/semantic/semantic.min.css">
<script src="../script/jquery-3.6.0.min.js"></script> <script src="../script/jquery-3.6.0.min.js"></script>
<script src="../script/semantic/semantic.min.js"></script> <script src="../script/semantic/semantic.min.js"></script>
<script src="../script/utils.js"></script>
<style> <style>
.accessRule{ .accessRule{
cursor: pointer; cursor: pointer;
@ -124,12 +126,10 @@
} }
} }
}); });
} }
initAccessRuleList(function(){ initAccessRuleList(function(){
$.ajax({ $.cjax({
url: "/api/proxy/detail", url: "/api/proxy/detail",
method: "POST", method: "POST",
data: {"type":"host", "epname": editingEndpoint.ep }, data: {"type":"host", "epname": editingEndpoint.ep },
@ -160,7 +160,7 @@
function applyChangeAndClose(){ function applyChangeAndClose(){
let newAccessRuleID = $(".accessRule.active").attr("ruleid"); let newAccessRuleID = $(".accessRule.active").attr("ruleid");
let targetEndpoint = editingEndpoint.ep; let targetEndpoint = editingEndpoint.ep;
$.ajax({ $.cjax({
url: "/api/access/attach", url: "/api/access/attach",
method: "POST", method: "POST",
data: { data: {

View File

@ -72,6 +72,18 @@
<div class="ui divider"></div> <div class="ui divider"></div>
<div id="logList" class="ui accordion"> <div id="logList" class="ui accordion">
</div>
<div class="ui divider"></div>
<h5>Filters</h5>
<button style="margin-top: 0.4em;" filter="system" class="ui fluid basic small button filterbtn"><i class="ui blue info circle icon"></i> System</button>
<button style="margin-top: 0.4em;" filter="request" class="ui fluid basic small button filterbtn"><i class="green exchange icon"></i> Request</button>
<button style="margin-top: 0.4em;" filter="error" class="ui fluid basic small button filterbtn"><i class="red exclamation triangle icon"></i> Error</button>
<button style="margin-top: 0.4em;" filter="all" class="ui fluid basic active small button filterbtn">All</button>
<div class="ui divider"></div>
<div class="ui toggle checkbox">
<input type="checkbox" id="enableAutoScroll" onchange="handleAutoScrollTicker(event, this.checked);">
<label>Auto Refresh<br>
<small>Refresh the viewing log every 10 seconds</small></label>
</div> </div>
<div class="ui divider"></div> <div class="ui divider"></div>
<small>Notes: Some log files might be huge. Make sure you have checked the log file size before opening</small> <small>Notes: Some log files might be huge. Make sure you have checked the log file size before opening</small>
@ -89,6 +101,10 @@
</body> </body>
<script> <script>
var currentOpenedLogURL = ""; var currentOpenedLogURL = "";
var currentFilter = "all";
var autoscroll = false;
$(".checkbox").checkbox();
function openLogInNewTab(){ function openLogInNewTab(){
if (currentOpenedLogURL != ""){ if (currentOpenedLogURL != ""){
@ -105,7 +121,7 @@
alert(data.error); alert(data.error);
return; return;
} }
$("#logrender").val(data); renderLogWithCurrentFilter(data);
}); });
} }
@ -151,5 +167,67 @@
} }
return(n.toFixed(n < 10 && l > 0 ? 1 : 0) + ' ' + units[l]); return(n.toFixed(n < 10 && l > 0 ? 1 : 0) + ' ' + units[l]);
} }
//Filter the log and render it to text area based on current filter choice
function renderLogWithCurrentFilter(data){
if (currentFilter == "all"){
$("#logrender").val(data);
}else{
let filterLines = data.split("\n");
let filteredLogFile = "";
for (var i = 0; i < filterLines.length; i++){
const thisLine = filterLines[i];
if (currentFilter == "system" && thisLine.indexOf("[system:") >= 0){
filteredLogFile += thisLine + "\n";
}else if (currentFilter == "request" && thisLine.indexOf("[router:") >= 0){
filteredLogFile += thisLine + "\n";
}else if (currentFilter == "error" && thisLine.indexOf(":error]") >= 0){
filteredLogFile += thisLine + "\n";
}
}
$("#logrender").val(filteredLogFile);
}
var textarea = document.getElementById('logrender');
textarea.scrollTop = textarea.scrollHeight;
}
/* Filter related functions */
$(".filterbtn").on("click", function(evt){
//Set filter type
let filterType = $(this).attr("filter");
currentFilter = (filterType);
$(".filterbtn.active").removeClass("active");
$(this).addClass('active');
//Reload the log with filter
if (currentOpenedLogURL != ""){
$.get(currentOpenedLogURL, function(data){
if (data.error !== undefined){
alert(data.error);
return;
}
renderLogWithCurrentFilter(data);
});
}
});
/* Auto scroll function */
setInterval(function(){
if (autoscroll){
//Update the log and scroll to bottom
if (currentOpenedLogURL != ""){
$.get(currentOpenedLogURL, function(data){
if (data.error !== undefined){
console.log(data.error);
return;
}
renderLogWithCurrentFilter(data);
});
}
}
}, 10000);
function handleAutoScrollTicker(event, checked){
autoscroll = checked;
}
</script> </script>
</html> </html>

View File

@ -2,9 +2,11 @@
<html> <html>
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="zoraxy.csrf.Token" content="{{.csrfToken}}">
<link rel="stylesheet" href="../script/semantic/semantic.min.css"> <link rel="stylesheet" href="../script/semantic/semantic.min.css">
<script src="../script/jquery-3.6.0.min.js"></script> <script src="../script/jquery-3.6.0.min.js"></script>
<script src="../script/semantic/semantic.min.js"></script> <script src="../script/semantic/semantic.min.js"></script>
<script src="../script/utils.js"></script>
<style> <style>
body{ body{
height: 100%; height: 100%;

View File

@ -3,9 +3,11 @@
<head> <head>
<!-- Notes: This should be open in its original path--> <!-- Notes: This should be open in its original path-->
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="zoraxy.csrf.Token" content="{{.csrfToken}}">
<link rel="stylesheet" href="../script/semantic/semantic.min.css"> <link rel="stylesheet" href="../script/semantic/semantic.min.css">
<script src="../script/jquery-3.6.0.min.js"></script> <script src="../script/jquery-3.6.0.min.js"></script>
<script src="../script/semantic/semantic.min.js"></script> <script src="../script/semantic/semantic.min.js"></script>
<script src="../script/utils.js"></script>
<style> <style>
.upstreamActions{ .upstreamActions{
position: absolute; position: absolute;
@ -133,7 +135,7 @@
function initOriginList(){ function initOriginList(){
$.ajax({ $.ajax({
url: "/api/proxy/upstream/list", url: "/api/proxy/upstream/list",
method: "POST", method: "GET",
data: { data: {
"type":"host", "type":"host",
"ep": editingEndpoint.ep "ep": editingEndpoint.ep
@ -284,8 +286,9 @@
}else{ }else{
//URL does not contains https or http protocol tag //URL does not contains https or http protocol tag
//sniff header //sniff header
$.ajax({ $.cjax({
url: "/api/proxy/tlscheck", url: "/api/proxy/tlscheck",
method: "POST",
data: {url: targetDomain}, data: {url: targetDomain},
success: function(data){ success: function(data){
if (data.error != undefined){ if (data.error != undefined){
@ -313,7 +316,7 @@
return; return;
} }
$.ajax({ $.cjax({
url: "/api/proxy/upstream/add", url: "/api/proxy/upstream/add",
method: "POST", method: "POST",
data:{ data:{
@ -365,7 +368,7 @@
let newConfig = getUpstreamSettingFromDOM(targetDOM); let newConfig = getUpstreamSettingFromDOM(targetDOM);
let isActive = $(targetDOM).find(".enableState")[0].checked; let isActive = $(targetDOM).find(".enableState")[0].checked;
console.log(newConfig); console.log(newConfig);
$.ajax({ $.cjax({
url: "/api/proxy/upstream/update", url: "/api/proxy/upstream/update",
method: "POST", method: "POST",
data: { data: {
@ -418,8 +421,9 @@
}else{ }else{
//URL does not contains https or http protocol tag //URL does not contains https or http protocol tag
//sniff header //sniff header
$.ajax({ $.cjax({
url: "/api/proxy/tlscheck", url: "/api/proxy/tlscheck",
method: "POST",
data: {url: targetDomain}, data: {url: targetDomain},
success: function(data){ success: function(data){
if (data.error != undefined){ if (data.error != undefined){
@ -460,7 +464,7 @@
//Set a weight of a upstream //Set a weight of a upstream
function setUpstreamWeight(originIP, newWeight){ function setUpstreamWeight(originIP, newWeight){
$.ajax({ $.cjax({
url: "/api/proxy/upstream/setPriority", url: "/api/proxy/upstream/setPriority",
method: "POST", method: "POST",
data: { data: {
@ -489,7 +493,7 @@
return; return;
} }
//Remove the upstream //Remove the upstream
$.ajax({ $.cjax({
url: "/api/proxy/upstream/remove", url: "/api/proxy/upstream/remove",
method: "POST", method: "POST",
data: { data: {

View File

@ -2,12 +2,14 @@
<head> <head>
<title>File Manager</title> <title>File Manager</title>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="zoraxy.csrf.Token" content="{{.csrfToken}}">
<meta name="viewport" content="width=device-width, initial-scale=1.0 user-scalable=no"> <meta name="viewport" content="width=device-width, initial-scale=1.0 user-scalable=no">
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script> <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.4.1/semantic.min.css" /> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.4.1/semantic.min.css" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.4.1/semantic.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.4.1/semantic.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jszip/3.6.0/jszip.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/jszip/3.6.0/jszip.min.js"></script>
<link rel="stylesheet" href="fs.css"> <link rel="stylesheet" href="fs.css">
<script src="../script/utils.js"></script>
<script> <script>
</script> </script>
@ -199,7 +201,7 @@
let counter = $(".fileObject.selected").length; let counter = $(".fileObject.selected").length;
$(".fileObject.selected").each(function(){ $(".fileObject.selected").each(function(){
let thisFilepath = $(this).attr("filepath"); let thisFilepath = $(this).attr("filepath");
$.ajax({ $.cjax({
url: "/api/fs/del?target=" + thisFilepath, url: "/api/fs/del?target=" + thisFilepath,
method: "POST", method: "POST",
success: function(data){ success: function(data){
@ -241,22 +243,9 @@
let filename = $(this).attr("filename"); let filename = $(this).attr("filename");
if (ftype != "folder"){ if (ftype != "folder"){
let ext = filepath.split(".").pop(); let ext = filepath.split(".").pop();
if (isCodeFiles(ext)){ openthis($(this), evt);
editableCodeFiles.push({
"filename": filename,
"filepath": filepath
});
}else{
openthis($(this), evt);
}
} }
}); });
if (editableCodeFiles.length > 0){
let hash = encodeURIComponent(JSON.stringify(editableCodeFiles))
window.open("notepad/index.html#" + hash);
}
} }
function refresh(){ function refresh(){
@ -571,12 +560,19 @@
return; return;
} }
$.post("/api/fs/newFolder?path=" + currentPath + folderName, function(data){ $.cjax({
if (data.error != undefined){ url: "/api/fs/newFolder",
msgbox(data.error, false); method: "POST",
}else{ data: {
msgbox("Folder Created"); "path": currentPath + folderName,
refresh(); },
success: function(data){
if (data.error != undefined){
msgbox(data.error, false);
}else{
msgbox("Folder Created");
refresh();
}
} }
}); });
} }
@ -597,8 +593,12 @@
if (newName && newName != oldName) { if (newName && newName != oldName) {
// User entered a new name, perform renaming logic here // User entered a new name, perform renaming logic here
console.log(oldPath, currentPath + newName); console.log(oldPath, currentPath + newName);
$.ajax({ $.cjax({
url: "/api/fs/move?srcpath=" + oldPath + "&destpath=" + currentPath + newName, url: "/api/fs/move",
data: {
"srcpath": oldPath,
"destpath": currentPath + newName
},
method: "POST", method: "POST",
success: function(data){ success: function(data){
if (data.error != undefined){ if (data.error != undefined){
@ -826,6 +826,7 @@
ajax.addEventListener("error", errorHandler, false); ajax.addEventListener("error", errorHandler, false);
ajax.addEventListener("abort", abortHandler, false); ajax.addEventListener("abort", abortHandler, false);
ajax.open("POST", "/api/fs/upload?dir=" + dir); ajax.open("POST", "/api/fs/upload?dir=" + dir);
ajax.setRequestHeader("X-CSRF-Token", document.getElementsByTagName("meta")["zoraxy.csrf.Token"].getAttribute("content"));
ajax.send(formdata); ajax.send(formdata);
} }
@ -914,8 +915,12 @@
let filename = fileToPaste.filename; let filename = fileToPaste.filename;
let filepath = fileToPaste.filepath; let filepath = fileToPaste.filepath;
$.ajax({ $.cjax({
url: "/api/fs/move?srcpath=" + filepath + "&destpath=" + currentPath + filename, url: "/api/fs/move",
data:{
"srcpath": filepath,
"destpath": currentPath + filename,
},
method: "POST", method: "POST",
success: function(data){ success: function(data){
if (data.error != undefined){ if (data.error != undefined){
@ -939,7 +944,7 @@
function copyFirstItemInQueueUntilAllCopied(){ function copyFirstItemInQueueUntilAllCopied(){
let file = copyPendingFiles.shift(); let file = copyPendingFiles.shift();
let startingDir = currentPath; let startingDir = currentPath;
$.ajax({ $.cjax({
url: "/api/fs/copy", url: "/api/fs/copy",
method: "POST", method: "POST",
data: { data: {

View File

@ -1,6 +1,7 @@
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>
<meta name="zoraxy.csrf.Token" content="{{.csrfToken}}">
<meta name="apple-mobile-web-app-capable" content="yes" /> <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 name="viewport" content="user-scalable=no, width=device-width, initial-scale=1, maximum-scale=1"/>
<meta charset="UTF-8"> <meta charset="UTF-8">
@ -13,6 +14,7 @@
<script src="../script/semantic/semantic.min.js"></script> <script src="../script/semantic/semantic.min.js"></script>
<script src="../script/tablesort.js"></script> <script src="../script/tablesort.js"></script>
<link rel="stylesheet" href="../main.css"> <link rel="stylesheet" href="../main.css">
<script src="../script/utils.js"></script>
<style> <style>
.offlinehost{ .offlinehost{
display: none; display: none;
@ -86,9 +88,14 @@
<div class="ui basic segment" align="center"> <div class="ui basic segment" align="center">
<i class="loading spinner icon"></i> Scanning <i class="loading spinner icon"></i> Scanning
</div>`); </div>`);
$.post("/api/tools/ipscan", {start: start, end: end}, function(data) { $.cjax({
displayResults(data); url: "/api/tools/ipscan",
$(".scanbtn").removeClass("disabled"); data: {start: start, end: end},
method: "POST",
success: function(data){
displayResults(data);
$(".scanbtn").removeClass("disabled");
}
}); });
}); });
@ -109,9 +116,14 @@
<div class="ui basic segment" align="center"> <div class="ui basic segment" align="center">
<i class="loading spinner icon"></i> Scanning <i class="loading spinner icon"></i> Scanning
</div>`); </div>`);
$.post("/api/tools/ipscan", {cidr: cidr}, function(data) { $.cjax({
displayResults(data); url: "/api/tools/ipscan",
$(".scanbtn").removeClass("disabled"); method: "POST",
data: {cidr: cidr},
success: function(data) {
displayResults(data);
$(".scanbtn").removeClass("disabled");
}
}); });
}); });

View File

@ -1,6 +1,7 @@
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>
<meta name="zoraxy.csrf.Token" content="{{.csrfToken}}">
<meta name="apple-mobile-web-app-capable" content="yes" /> <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 name="viewport" content="user-scalable=no, width=device-width, initial-scale=1, maximum-scale=1"/>
<meta charset="UTF-8"> <meta charset="UTF-8">
@ -13,6 +14,7 @@
<script src="../script/semantic/semantic.min.js"></script> <script src="../script/semantic/semantic.min.js"></script>
<script src="../script/tablesort.js"></script> <script src="../script/tablesort.js"></script>
<link rel="stylesheet" href="../main.css"> <link rel="stylesheet" href="../main.css">
<script src="../script/utils.js"></script>
<style> <style>
body{ body{
overflow-x: auto; overflow-x: auto;
@ -54,7 +56,7 @@
var domain = $("#domain").val(); var domain = $("#domain").val();
$("#discover").addClass("loading").addClass('disabled'); $("#discover").addClass("loading").addClass('disabled');
setCountdown(); setCountdown();
$.ajax({ $.cjax({
type: "POST", type: "POST",
url: "/api/mdns/discover", url: "/api/mdns/discover",
data: { domain: domain }, data: { domain: domain },

View File

@ -1,6 +1,7 @@
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>
<meta name="zoraxy.csrf.Token" content="{{.csrfToken}}">
<meta name="apple-mobile-web-app-capable" content="yes" /> <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 name="viewport" content="user-scalable=no, width=device-width, initial-scale=1, maximum-scale=1"/>
<meta charset="UTF-8"> <meta charset="UTF-8">
@ -13,6 +14,7 @@
<script src="../script/semantic/semantic.min.js"></script> <script src="../script/semantic/semantic.min.js"></script>
<script src="../script/tablesort.js"></script> <script src="../script/tablesort.js"></script>
<link rel="stylesheet" href="../main.css"> <link rel="stylesheet" href="../main.css">
<script src="../script/utils.js"></script>
<style> <style>
#loadingUI{ #loadingUI{
width: 100%; width: 100%;
@ -153,7 +155,7 @@
//Try to ask the server side to create a ssh proxy object //Try to ask the server side to create a ssh proxy object
function createSSHProxy(remoteAddr, remotePort, username){ function createSSHProxy(remoteAddr, remotePort, username){
//Request to create a ssh session instance //Request to create a ssh session instance
$.ajax({ $.cjax({
url: "/api/tools/webssh", url: "/api/tools/webssh",
data: {ipaddr: remoteAddr, port: remotePort, username:username}, data: {ipaddr: remoteAddr, port: remotePort, username:username},
method: "POST", method: "POST",

View File

@ -111,6 +111,7 @@ func HandleCountryDistrSummary(w http.ResponseWriter, r *http.Request) {
func UpdateUptimeMonitorTargets() { func UpdateUptimeMonitorTargets() {
if uptimeMonitor != nil { if uptimeMonitor != nil {
uptimeMonitor.Config.Targets = GetUptimeTargetsFromReverseProxyRules(dynamicProxyRouter) uptimeMonitor.Config.Targets = GetUptimeTargetsFromReverseProxyRules(dynamicProxyRouter)
uptimeMonitor.CleanRecords()
go func() { go func() {
uptimeMonitor.ExecuteUptimeCheck() uptimeMonitor.ExecuteUptimeCheck()
}() }()

View File

@ -28,6 +28,7 @@ var defTemplate string = `package acmedns
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"time"
"github.com/go-acme/lego/v4/challenge" "github.com/go-acme/lego/v4/challenge"
{{imports}} {{imports}}
@ -78,27 +79,16 @@ func getExcludedDNSProviders() []string {
"selectelv2", //Not sure why not working with our code generator "selectelv2", //Not sure why not working with our code generator
"designate", //OpenStack, if you are using this you shd not be using zoraxy "designate", //OpenStack, if you are using this you shd not be using zoraxy
"mythicbeasts", //Module require url.URL, which cannot be automatically parsed "mythicbeasts", //Module require url.URL, which cannot be automatically parsed
"directadmin", //Reserve for next dependency update
} }
} }
// Exclude list for Windows build, due to limitations for lego versions // Exclude list for Windows build, due to limitations for lego versions
func getExcludedDNSProvidersNT61() []string { func getExcludedDNSProvidersNT61() []string {
return []string{ return append(getExcludedDNSProviders(), []string{"cpanel",
"edgedns", //Too complex data structure
"exec", //Not a DNS provider
"httpreq", //Not a DNS provider
"hurricane", //Multi-credentials arch
"oraclecloud", //Evil company
"acmedns", //Not a DNS provider
"selectelv2", //Not sure why not working with our code generator
"designate", //OpenStack, if you are using this you shd not be using zoraxy
"mythicbeasts", //Module require url.URL, which cannot be automatically parsed
//The following suppliers was not in lego v1.15 (Windows 7 last supported version of lego)
"cpanel",
"mailinabox", "mailinabox",
"shellrent", "shellrent",
} }...)
} }
func isInSlice(str string, slice []string) bool { func isInSlice(str string, slice []string) bool {
@ -226,6 +216,11 @@ func main() {
case "*url.URL": case "*url.URL":
fallthrough fallthrough
case "string": case "string":
//Add exception rule for gandi baseURL
if (providerName == "gandi" || providerName == "gandiv5") && fields[0] == "BaseURL" {
//Not useful stuff. Ignore this field
continue
}
configKeys = append(configKeys, &Field{ configKeys = append(configKeys, &Field{
Title: fields[0], Title: fields[0],
Datatype: fields[1], Datatype: fields[1],
@ -280,8 +275,21 @@ func main() {
if err != nil { if err != nil {
return nil, err return nil, err
} }
cfg.PropagationTimeout = 5*time.Minute
return ` + providerName + `.NewDNSProviderConfig(cfg)` return ` + providerName + `.NewDNSProviderConfig(cfg)`
//Add fixed for Netcup timeout
if strings.ToLower(providerName) == "netcup" {
codeSegment = `
case "` + providerName + `":
cfg := ` + providerName + `.NewDefaultConfig()
err := json.Unmarshal([]byte(js), &cfg)
if err != nil {
return nil, err
}
cfg.PropagationTimeout = 20*time.Minute
return ` + providerName + `.NewDNSProviderConfig(cfg)`
}
generatedConvertcode += codeSegment generatedConvertcode += codeSegment
importList += ` "github.com/go-acme/lego/v4/providers/dns/` + providerName + "\"\n" importList += ` "github.com/go-acme/lego/v4/providers/dns/` + providerName + "\"\n"
} }

View File

@ -21,6 +21,7 @@ go run ./extract.go
go run ./extract.go -- "win7" go run ./extract.go -- "win7"
echo "Cleaning up lego" echo "Cleaning up lego"
sleep 2
# Comment the line below if you dont want to pull everytime update # Comment the line below if you dont want to pull everytime update
# This is to help go compiler to not load all the lego source file when compile # This is to help go compiler to not load all the lego source file when compile
#rm -rf ./lego/ #rm -rf ./lego/

View File

@ -0,0 +1,33 @@
module imuslab.com/zoraxy
go 1.20
require (
github.com/boltdb/bolt v1.3.1
github.com/go-acme/lego/v4 v4.15.0
github.com/go-ping/ping v1.1.0
github.com/google/uuid v1.6.0
github.com/gorilla/sessions v1.2.2
github.com/gorilla/websocket v1.5.1
github.com/grandcat/zeroconf v1.0.0
github.com/likexian/whois v1.15.2
github.com/microcosm-cc/bluemonday v1.0.26
golang.org/x/net v0.24.0
golang.org/x/sys v0.19.0
golang.org/x/text v0.14.0
)
require (
github.com/aymerick/douceur v0.2.0 // indirect
github.com/cenkalti/backoff v2.2.1+incompatible // indirect
github.com/cenkalti/backoff/v4 v4.2.1 // indirect
github.com/go-jose/go-jose/v3 v3.0.1 // indirect
github.com/google/go-cmp v0.5.9 // indirect
github.com/gorilla/css v1.0.0 // indirect
github.com/gorilla/securecookie v1.1.2 // indirect
github.com/miekg/dns v1.1.58 // indirect
golang.org/x/crypto v0.22.0 // indirect
golang.org/x/mod v0.14.0 // indirect
golang.org/x/sync v0.6.0 // indirect
golang.org/x/tools v0.17.0 // indirect
)

View File

@ -0,0 +1,187 @@
module imuslab.com/zoraxy
go 1.20
require (
github.com/boltdb/bolt v1.3.1
github.com/docker/docker v27.0.0+incompatible
github.com/go-acme/lego/v4 v4.15.0
github.com/go-ping/ping v1.1.0
github.com/google/uuid v1.6.0
github.com/gorilla/sessions v1.2.2
github.com/gorilla/websocket v1.5.1
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.25.0
golang.org/x/sys v0.20.0
golang.org/x/text v0.15.0
)
require (
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
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.6.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.3.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/dns/armdns v1.1.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/privatedns/armprivatedns v1.1.0 // indirect
github.com/Azure/go-autorest v14.2.0+incompatible // indirect
github.com/Azure/go-autorest/autorest v0.11.29 // indirect
github.com/Azure/go-autorest/autorest/adal v0.9.22 // indirect
github.com/Azure/go-autorest/autorest/azure/auth v0.5.12 // indirect
github.com/Azure/go-autorest/autorest/azure/cli v0.4.5 // indirect
github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect
github.com/Azure/go-autorest/autorest/to v0.4.0 // indirect
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/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
github.com/aws/aws-sdk-go-v2/config v1.26.6 // indirect
github.com/aws/aws-sdk-go-v2/credentials v1.16.16 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.11 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.10 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.10 // indirect
github.com/aws/aws-sdk-go-v2/internal/ini v1.7.3 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.10.4 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.10.10 // indirect
github.com/aws/aws-sdk-go-v2/service/lightsail v1.34.0 // indirect
github.com/aws/aws-sdk-go-v2/service/route53 v1.37.0 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.18.7 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.21.7 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.26.7 // indirect
github.com/aws/smithy-go v1.19.0 // indirect
github.com/aymerick/douceur v0.2.0 // indirect
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc // indirect
github.com/cenkalti/backoff v2.2.1+incompatible // indirect
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/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/v3 v3.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/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.4 // indirect
github.com/google/go-querystring v1.1.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/gorilla/css v1.0.1 // indirect
github.com/gorilla/securecookie v1.1.2 // indirect
github.com/hashicorp/errwrap v1.0.0 // indirect
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/hashicorp/go-retryablehttp v0.7.5 // indirect
github.com/iij/doapi v0.0.0-20190504054126-0bbf12d6d7df // indirect
github.com/infobloxopen/infoblox-go-client v1.1.1 // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213 // indirect
github.com/kolo/xmlrpc v0.0.0-20220921171641-a4b6fa1dd06b // indirect
github.com/kylelemons/godebug v1.1.0 // indirect
github.com/labbsr0x/bindman-dns-webhook v1.0.2 // indirect
github.com/labbsr0x/goh v1.0.1 // indirect
github.com/linode/linodego v1.28.0 // indirect
github.com/liquidweb/liquidweb-cli v0.6.9 // indirect
github.com/liquidweb/liquidweb-go v1.6.4 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/miekg/dns v1.1.58 // indirect
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
github.com/nrdcg/desec v0.7.0 // indirect
github.com/nrdcg/dnspod-go v0.4.0 // indirect
github.com/nrdcg/freemyip v0.2.0 // indirect
github.com/nrdcg/goinwx v0.10.0 // indirect
github.com/nrdcg/namesilo v0.2.1 // indirect
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/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/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
github.com/pquerna/otp v1.4.0 // indirect
github.com/sacloud/api-client-go v0.2.8 // indirect
github.com/sacloud/go-http v0.1.6 // indirect
github.com/sacloud/iaas-api-go v1.11.1 // indirect
github.com/sacloud/packages-go v0.0.9 // indirect
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.22 // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
github.com/smartystreets/go-aws-auth v0.0.0-20180515143844-0c1422d1fdb9 // indirect
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.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
github.com/ultradns/ultradns-go-sdk v1.6.1-20231103022937-8589b6a // indirect
github.com/vinyldns/go-vinyldns v0.9.16 // indirect
github.com/vultr/govultr/v2 v2.17.2 // indirect
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.49.0 // indirect
go.opentelemetry.io/otel v1.24.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.24.0 // indirect
go.opentelemetry.io/otel/metric v1.24.0 // indirect
go.opentelemetry.io/otel/sdk v1.24.0 // indirect
go.opentelemetry.io/otel/trace v1.24.0 // indirect
go.uber.org/ratelimit v0.2.0 // indirect
golang.org/x/crypto v0.23.0 // indirect
golang.org/x/mod 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.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-20240318140521-94a12d6c2237 // 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

@ -0,0 +1,193 @@
module imuslab.com/zoraxy
go 1.20
require (
github.com/boltdb/bolt v1.3.1
github.com/docker/docker v27.0.0+incompatible
github.com/go-acme/lego/v4 v4.15.0
github.com/go-ping/ping v1.1.0
github.com/google/uuid v1.6.0
github.com/gorilla/csrf v1.7.0
github.com/gorilla/sessions v1.2.2
github.com/gorilla/websocket v1.5.1
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.25.0
golang.org/x/sys v0.20.0
golang.org/x/text v0.15.0
)
require (
cloud.google.com/go/compute/metadata v0.3.0 // indirect
github.com/AdamSLevy/jsonrpc2/v14 v14.1.0 // indirect
github.com/Azure/azure-sdk-for-go v68.0.0+incompatible // indirect
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.11.1 // indirect
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.6.0-beta.4 // indirect
github.com/Azure/azure-sdk-for-go/sdk/internal v1.9.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/dns/armdns v1.2.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/privatedns/armprivatedns v1.2.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resourcegraph/armresourcegraph v0.9.0 // indirect
github.com/Azure/go-autorest v14.2.0+incompatible // indirect
github.com/Azure/go-autorest/autorest v0.11.29 // indirect
github.com/Azure/go-autorest/autorest/adal v0.9.22 // indirect
github.com/Azure/go-autorest/autorest/azure/auth v0.5.13 // indirect
github.com/Azure/go-autorest/autorest/azure/cli v0.4.6 // indirect
github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect
github.com/Azure/go-autorest/autorest/to v0.4.0 // indirect
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.2.2 // indirect
github.com/Microsoft/go-winio v0.4.14 // indirect
github.com/OpenDNS/vegadns2client v0.0.0-20180418235048-a3fa4a771d87 // indirect
github.com/aliyun/alibaba-cloud-sdk-go v1.62.712 // indirect
github.com/aws/aws-sdk-go-v2 v1.27.2 // indirect
github.com/aws/aws-sdk-go-v2/config v1.27.18 // indirect
github.com/aws/aws-sdk-go-v2/credentials v1.17.18 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.5 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.9 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.9 // indirect
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.2 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.11 // indirect
github.com/aws/aws-sdk-go-v2/service/lightsail v1.38.3 // indirect
github.com/aws/aws-sdk-go-v2/service/route53 v1.40.10 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.20.11 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.24.5 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.28.12 // indirect
github.com/aws/smithy-go v1.20.2 // indirect
github.com/aymerick/douceur v0.2.0 // indirect
github.com/benbjohnson/clock v1.3.0 // indirect
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc // indirect
github.com/cenkalti/backoff v2.2.1+incompatible // indirect
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
github.com/civo/civogo v0.3.11 // indirect
github.com/cloudflare/cloudflare-go v0.97.0 // 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.7.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/v3 v3.0.1 // indirect
github.com/go-jose/go-jose/v4 v4.0.2 // 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 // indirect
github.com/goccy/go-json v0.10.3 // 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-jwt/jwt/v5 v5.2.1 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // 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.7 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect
github.com/googleapis/gax-go/v2 v2.12.3 // indirect
github.com/gophercloud/gophercloud v1.12.0 // 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
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/hashicorp/go-retryablehttp v0.7.7 // indirect
github.com/iij/doapi v0.0.0-20190504054126-0bbf12d6d7df // indirect
github.com/infobloxopen/infoblox-go-client v1.1.1 // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213 // indirect
github.com/kolo/xmlrpc v0.0.0-20220921171641-a4b6fa1dd06b // indirect
github.com/kylelemons/godebug v1.1.0 // indirect
github.com/labbsr0x/bindman-dns-webhook v1.0.2 // indirect
github.com/labbsr0x/goh v1.0.1 // indirect
github.com/linode/linodego v1.28.0 // indirect
github.com/liquidweb/liquidweb-cli v0.6.9 // indirect
github.com/liquidweb/liquidweb-go v1.6.4 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/miekg/dns v1.1.59 // indirect
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-20240207213615-dde5bf4577a3 // indirect
github.com/nrdcg/desec v0.8.0 // indirect
github.com/nrdcg/dnspod-go v0.4.0 // indirect
github.com/nrdcg/freemyip v0.2.0 // indirect
github.com/nrdcg/goinwx v0.10.0 // indirect
github.com/nrdcg/mailinabox v0.2.0 // indirect
github.com/nrdcg/namesilo v0.2.1 // indirect
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/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.1.0 // indirect
github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b // indirect
github.com/ovh/go-ovh v1.5.1 // indirect
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/pquerna/otp v1.4.0 // indirect
github.com/sacloud/api-client-go v0.2.10 // indirect
github.com/sacloud/go-http v0.1.8 // indirect
github.com/sacloud/iaas-api-go v1.12.0 // indirect
github.com/sacloud/packages-go v0.0.10 // indirect
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.27 // indirect
github.com/shopspring/decimal v1.3.1 // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
github.com/smartystreets/go-aws-auth v0.0.0-20180515143844-0c1422d1fdb9 // indirect
github.com/softlayer/softlayer-go v1.1.5 // 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.2 // indirect
github.com/stretchr/testify v1.9.0 // indirect
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.898 // indirect
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.898 // indirect
github.com/transip/gotransip/v6 v6.23.0 // indirect
github.com/ultradns/ultradns-go-sdk v1.6.1-20231103022937-8589b6a // indirect
github.com/vinyldns/go-vinyldns v0.9.16 // indirect
github.com/vultr/govultr/v2 v2.17.2 // indirect
github.com/yandex-cloud/go-genproto v0.0.0-20240318083951-4fe6125f286e // indirect
github.com/yandex-cloud/go-sdk v0.0.0-20240318084659-dfa50323a0b4 // indirect
go.opencensus.io v0.24.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 // indirect
go.opentelemetry.io/otel v1.24.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.24.0 // indirect
go.opentelemetry.io/otel/metric v1.24.0 // indirect
go.opentelemetry.io/otel/sdk v1.24.0 // indirect
go.opentelemetry.io/otel/trace v1.24.0 // indirect
go.uber.org/ratelimit v0.3.0 // indirect
golang.org/x/crypto v0.23.0 // indirect
golang.org/x/mod v0.18.0 // indirect
golang.org/x/oauth2 v0.21.0 // indirect
golang.org/x/sync v0.7.0 // indirect
golang.org/x/time v0.5.0 // indirect
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect
google.golang.org/api v0.172.0 // indirect
google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20240318140521-94a12d6c2237 // 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
)