Compare commits
65 Commits
Author | SHA1 | Date | |
---|---|---|---|
d5315e5b8e | |||
31cc1a69a1 | |||
d348cbf48b | |||
f6339868ac | |||
af10f2a644 | |||
3b247c31da | |||
d74e8badb9 | |||
b40131d212 | |||
563a12c860 | |||
8b2c3b7e03 | |||
608cc0c523 | |||
b558bcbfcf | |||
9ea3fa2542 | |||
01f68c5ef5 | |||
a7f89086d4 | |||
a5ef6456c6 | |||
87659b43bd | |||
ddbecf7b68 | |||
1b3a9de378 | |||
6dd62f509d | |||
d5cc6a6859 | |||
1d965da7d0 | |||
3567c70bab | |||
0a734e0bd3 | |||
f4fa92635c | |||
7d5151bb00 | |||
54475e4b99 | |||
6ac16caf37 | |||
97502db607 | |||
0747cf4b0f | |||
94483acc92 | |||
7626857c02 | |||
0f772a715b | |||
fd1439f746 | |||
ca37bfbfa6 | |||
c1e16d55ab | |||
f595da92a1 | |||
8a8ec1cb0b | |||
e53c3cf3c4 | |||
d17de5c200 | |||
97ff48ee70 | |||
d64b1174af | |||
bec363abab | |||
0dddd1f9e3 | |||
6bfcb2e1f5 | |||
02ff288280 | |||
b1c5bc2963 | |||
d3dbbf9052 | |||
f4a5c905e7 | |||
245379e91f | |||
955a2232df | |||
7eb7ae7ced | |||
3aa0f2d914 | |||
39b0c8c674 | |||
bddeae8365 | |||
8e0e9531e7 | |||
6ff22865e0 | |||
0828fd1958 | |||
82f84470f7 | |||
cf9a05f130 | |||
301072db90 | |||
cfcd10d64f | |||
c85760c73a | |||
b7bb918aa3 | |||
962f3e0566 |
36
CHANGELOG.md
@ -1,3 +1,39 @@
|
|||||||
|
# v3.1.0 31 Jul 2024
|
||||||
|
|
||||||
|
+ Updated log viewer with filter and auto refresh [#243](https://github.com/tobychui/zoraxy/issues/243)
|
||||||
|
+ Fixed csrf vulnerability [#267](https://github.com/tobychui/zoraxy/issues/267)
|
||||||
|
+ Fixed promox issue
|
||||||
|
+ Fixed status code bug in upstream log [#254](https://github.com/tobychui/zoraxy/issues/254)
|
||||||
|
+ Added host overwrite and hop-by-hop header remover
|
||||||
|
+ Added early renew days settings [#256](https://github.com/tobychui/zoraxy/issues/256)
|
||||||
|
+ Updated make file to force no CGO in cicd process
|
||||||
|
+ Fixed bug in updater
|
||||||
|
+ Fixed wildcard certificate renew bug [#249](https://github.com/tobychui/zoraxy/issues/249)
|
||||||
|
+ Added certificate download function [#227](https://github.com/tobychui/zoraxy/issues/227)
|
||||||
|
|
||||||
|
# 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)
|
||||||
|
22
README.md
@ -4,7 +4,6 @@
|
|||||||
|
|
||||||
A general purpose HTTP reverse proxy and forwarding tool. Now written in Go!
|
A general purpose HTTP reverse proxy and forwarding tool. Now written in Go!
|
||||||
|
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
|
||||||
- Simple to use interface with detail in-system instructions
|
- Simple to use interface with detail in-system instructions
|
||||||
@ -21,12 +20,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
|
||||||
@ -39,12 +40,13 @@ A general purpose HTTP reverse proxy and forwarding tool. Now written in Go!
|
|||||||
/ [Linux (amd64)](https://github.com/tobychui/zoraxy/releases/latest/download/zoraxy_linux_amd64)
|
/ [Linux (amd64)](https://github.com/tobychui/zoraxy/releases/latest/download/zoraxy_linux_amd64)
|
||||||
/ [Linux (arm64)](https://github.com/tobychui/zoraxy/releases/latest/download/zoraxy_linux_arm64)
|
/ [Linux (arm64)](https://github.com/tobychui/zoraxy/releases/latest/download/zoraxy_linux_arm64)
|
||||||
|
|
||||||
For other systems or architectures, please see [Release](https://github.com/tobychui/zoraxy/releases/latest/)
|
For other systems or architectures, please see [Releases](https://github.com/tobychui/zoraxy/releases/latest/)
|
||||||
|
|
||||||
## Getting Started
|
## Getting Started
|
||||||
|
|
||||||
[Installing Zoraxy Reverse Proxy: Your Gateway to Efficient Web Routing](https://geekscircuit.com/installing-zoraxy-reverse-proxy-your-gateway-to-efficient-web-routing/)
|
[Installing Zoraxy Reverse Proxy: Your Gateway to Efficient Web Routing](https://geekscircuit.com/installing-zoraxy-reverse-proxy-your-gateway-to-efficient-web-routing/)
|
||||||
|
|
||||||
Thank you for the well written and easy to follow tutorial by Reddit users [itsvmn](https://www.reddit.com/user/itsvmn/)!
|
Thank you for the well written and easy to follow tutorial by Reddit user [itsvmn](https://www.reddit.com/user/itsvmn/)!
|
||||||
If you have no background in setting up reverse proxy or web routing, you should check this out before you start setting up your Zoraxy.
|
If you have no background in setting up reverse proxy or web routing, you should check this out before you start setting up your Zoraxy.
|
||||||
|
|
||||||
## Build from Source
|
## Build from Source
|
||||||
@ -62,7 +64,7 @@ sudo ./zoraxy -port=:8000
|
|||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
Zoraxy provides basic authentication system for standalone mode. To use it in standalone mode, follow the instructionss below for your desired deployment platform.
|
Zoraxy provides basic authentication system for standalone mode. To use it in standalone mode, follow the instructions below for your desired deployment platform.
|
||||||
|
|
||||||
### Standalone Mode
|
### Standalone Mode
|
||||||
|
|
||||||
@ -90,18 +92,18 @@ The installation method is same as Linux. For other ARM SBCs, please refer to yo
|
|||||||
|
|
||||||
See the [/docker](https://github.com/tobychui/zoraxy/tree/main/docker) folder for more details.
|
See the [/docker](https://github.com/tobychui/zoraxy/tree/main/docker) folder for more details.
|
||||||
|
|
||||||
### Start Paramters
|
### Start Parameters
|
||||||
|
|
||||||
```
|
```
|
||||||
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
|
||||||
@ -117,7 +119,7 @@ Usage of zoraxy:
|
|||||||
-webfm
|
-webfm
|
||||||
Enable web file manager for static web server root folder (default true)
|
Enable web file manager for static web server root folder (default true)
|
||||||
-webroot string
|
-webroot string
|
||||||
Static web server root folder. Only allow chnage in start paramters (default "./www")
|
Static web server root folder. Only allow change in start parameters (default "./www")
|
||||||
-ztauth string
|
-ztauth string
|
||||||
ZeroTier authtoken for the local node
|
ZeroTier authtoken for the local node
|
||||||
-ztport int
|
-ztport int
|
||||||
@ -132,7 +134,7 @@ If you already have an upstream reverse proxy server in place with permission ma
|
|||||||
./zoraxy -noauth=true
|
./zoraxy -noauth=true
|
||||||
```
|
```
|
||||||
|
|
||||||
*Note: For security reaons, you should only enable no-auth if you are running Zoraxy in a trusted environment or with another authentication management proxy in front.*
|
*Note: For security reasons, you should only enable no-auth if you are running Zoraxy in a trusted environment or with another authentication management proxy in front.*
|
||||||
|
|
||||||
## Screenshots
|
## Screenshots
|
||||||
|
|
||||||
|
@ -1,11 +1,8 @@
|
|||||||
FROM docker.io/golang:alpine AS build
|
FROM docker.io/golang:alpine AS build
|
||||||
|
|
||||||
RUN mkdir -p /opt/zoraxy/source/ &&\
|
RUN mkdir -p /opt/zoraxy/source/ &&\
|
||||||
mkdir -p /opt/zoraxy/config/ &&\
|
|
||||||
mkdir -p /usr/local/bin/
|
mkdir -p /usr/local/bin/
|
||||||
|
|
||||||
RUN chmod -R 770 /opt/zoraxy/
|
|
||||||
|
|
||||||
# If you build it yourself, you will need to add the src directory into the docker directory.
|
# If you build it yourself, you will need to add the src directory into the docker directory.
|
||||||
COPY ./src/ /opt/zoraxy/source/
|
COPY ./src/ /opt/zoraxy/source/
|
||||||
|
|
||||||
@ -13,25 +10,29 @@ WORKDIR /opt/zoraxy/source/
|
|||||||
|
|
||||||
RUN go mod tidy &&\
|
RUN go mod tidy &&\
|
||||||
go build -o /usr/local/bin/zoraxy &&\
|
go build -o /usr/local/bin/zoraxy &&\
|
||||||
|
chmod 755 /usr/local/bin/zoraxy
|
||||||
|
|
||||||
|
FROM docker.io/alpine:latest
|
||||||
|
|
||||||
|
WORKDIR /opt/zoraxy/source/
|
||||||
|
|
||||||
|
RUN apk add --no-cache bash netcat-openbsd sudo &&\
|
||||||
|
wget https://dl-cdn.alpinelinux.org/alpine/v3.17/community/x86_64/zerotier-one-1.10.2-r0.apk &&\
|
||||||
|
apk add --no-cache zerotier-one-1.10.2-r0.apk &&\
|
||||||
rm -r /opt/zoraxy/source/
|
rm -r /opt/zoraxy/source/
|
||||||
|
|
||||||
RUN chmod 755 /usr/local/bin/zoraxy &&\
|
|
||||||
chmod +x /usr/local/bin/zoraxy
|
|
||||||
|
|
||||||
FROM docker.io/alpine:3.20
|
|
||||||
|
|
||||||
RUN apk add --no-cache bash netcat-openbsd sudo
|
|
||||||
|
|
||||||
COPY --from=build /usr/local/bin/zoraxy /usr/local/bin/zoraxy
|
COPY --from=build /usr/local/bin/zoraxy /usr/local/bin/zoraxy
|
||||||
COPY --from=build /opt/zoraxy/config/ /opt/zoraxy/config
|
COPY --chmod=700 ./entrypoint.sh /opt/zoraxy/
|
||||||
|
|
||||||
VOLUME [ "/opt/zoraxy/config/" ]
|
|
||||||
|
|
||||||
WORKDIR /opt/zoraxy/config/
|
WORKDIR /opt/zoraxy/config/
|
||||||
|
|
||||||
|
ENV ZEROTIER="false"
|
||||||
|
|
||||||
ENV AUTORENEW="86400"
|
ENV AUTORENEW="86400"
|
||||||
|
ENV CFGUPGRADE="true"
|
||||||
|
ENV DOCKER="true"
|
||||||
|
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"
|
||||||
@ -40,9 +41,12 @@ ENV SSHLB="false"
|
|||||||
ENV VERSION="false"
|
ENV VERSION="false"
|
||||||
ENV WEBFM="true"
|
ENV WEBFM="true"
|
||||||
ENV WEBROOT="./www"
|
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}"
|
VOLUME [ "/opt/zoraxy/config/", "/var/lib/zerotier-one/" ]
|
||||||
|
|
||||||
|
ENTRYPOINT [ "/opt/zoraxy/entrypoint.sh" ]
|
||||||
|
|
||||||
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
|
||||||
|
|
||||||
|
121
docker/README.md
@ -1,71 +1,98 @@
|
|||||||
# [zoraxy](https://github.com/tobychui/zoraxy/) </br>
|
# Zoraxy Docker
|
||||||
|
|
||||||
[](https://hub.docker.com/r/zoraxydocker/zoraxy)
|
[](https://hub.docker.com/r/zoraxydocker/zoraxy)
|
||||||
[](https://hub.docker.com/r/zoraxydocker/zoraxy)
|
[](https://hub.docker.com/r/zoraxydocker/zoraxy)
|
||||||
[](https://hub.docker.com/r/zoraxydocker/zoraxy)
|
[](https://hub.docker.com/r/zoraxydocker/zoraxy)
|
||||||
[](https://hub.docker.com/r/zoraxydocker/zoraxy)
|
[](https://hub.docker.com/r/zoraxydocker/zoraxy)
|
||||||
|
|
||||||
## Setup: </br>
|
## Usage
|
||||||
Although not required, it is recommended to give Zoraxy a dedicated location on the host to mount the container. That way, the host/user can access them whenever needed. A volume will be created automatically within Docker if a location is not specified. </br>
|
|
||||||
|
|
||||||
You may also need to portforward your 80/443 to allow http and https traffic. If you are accessing the interface from outside of the local network, you may also need to forward your management port. If you know how to do this, great! If not, find the manufacturer of your router and search on how to do that. There are too many to be listed here. </br>
|
If you are attempting to access your service from outside your network, make sure to forward ports 80 and 443 to the Zoraxy host to allow web traffic. If you know how to do this, great! If not, find the manufacturer of your router and search on how to do that. There are too many to be listed here. Read more about it from [whatismyip](https://www.whatismyip.com/port-forwarding/).
|
||||||
|
|
||||||
The examples below are not exactly how it should be set up, rather they give a general idea of usage.
|
In the examples below, make sure to update `/path/to/zoraxy/config/` with your actual path. If a path is not provided, a Docker volume will be created at the location but it is recommended to store the data at a defined host location.
|
||||||
|
|
||||||
|
Once setup, access the webui at `http://<host-ip>:8000` to configure Zoraxy. Change the port in the URL if you changed the management port.
|
||||||
|
|
||||||
|
### Docker Run
|
||||||
|
|
||||||
### Using Docker run </br>
|
|
||||||
```
|
```
|
||||||
docker run -d --name (container name) -p 80:80 -p 443:443 -p (management external):(management internal) -v (path to storage directory):/opt/zoraxy/data/ -e (flag)="(value)" zoraxydocker/zoraxy:latest
|
docker run -d \
|
||||||
|
--name zoraxy \
|
||||||
|
--restart unless-stopped \
|
||||||
|
-p 80:80 \
|
||||||
|
-p 443:443 \
|
||||||
|
-p 8000:8000 \
|
||||||
|
-v /path/to/zoraxy/config/:/opt/zoraxy/config/ \
|
||||||
|
-v /path/to/zerotier/config/:/var/lib/zerotier-one/ \
|
||||||
|
-v /var/run/docker.sock:/var/run/docker.sock \
|
||||||
|
-v /etc/localtime:/etc/localtime \
|
||||||
|
-e FASTGEOIP="true" \
|
||||||
|
-e ZEROTIER="true" \
|
||||||
|
zoraxydocker/zoraxy:latest
|
||||||
```
|
```
|
||||||
|
|
||||||
### Using Docker Compose </br>
|
### Docker Compose
|
||||||
|
|
||||||
```yml
|
```yml
|
||||||
services:
|
services:
|
||||||
zoraxy-docker:
|
zoraxy:
|
||||||
image: zoraxydocker/zoraxy:latest
|
|
||||||
container_name: (container name)
|
|
||||||
ports:
|
|
||||||
- 80:80
|
|
||||||
- 443:443
|
|
||||||
- (management external):(management internal)
|
|
||||||
volumes:
|
|
||||||
- (path to storage directory):/opt/zoraxy/config/
|
|
||||||
environment:
|
|
||||||
(flag): "(value)"
|
|
||||||
```
|
|
||||||
|
|
||||||
| Operator | Need | Details |
|
|
||||||
|:-|:-|:-|
|
|
||||||
| `-d` | Yes | will run the container in the background. |
|
|
||||||
| `--name (container name)` | No | Sets the name of the container to the following word. You can change this to whatever you want. |
|
|
||||||
| `-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 /var/run/docker.sock:/var/run/docker.sock` | No | Used for autodiscovery. |
|
|
||||||
| `-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. |
|
|
||||||
|
|
||||||
> [!IMPORTANT]
|
|
||||||
> Docker usage of the port flag should not include the colon. Ex: PORT="8000"
|
|
||||||
|
|
||||||
## Examples: </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 Compose </br>
|
|
||||||
```yml
|
|
||||||
services:
|
|
||||||
zoraxy-docker:
|
|
||||||
image: zoraxydocker/zoraxy:latest
|
image: zoraxydocker/zoraxy:latest
|
||||||
container_name: zoraxy
|
container_name: zoraxy
|
||||||
|
restart: unless-stopped
|
||||||
ports:
|
ports:
|
||||||
- 80:80
|
- 80:80
|
||||||
- 443:443
|
- 443:443
|
||||||
- 8005:8005
|
- 8000:8000
|
||||||
volumes:
|
volumes:
|
||||||
- /home/docker/Containers/Zoraxy:/opt/zoraxy/config/
|
- /path/to/zoraxy/config/:/opt/zoraxy/config/
|
||||||
|
- /path/to/zerotier/config/:/var/lib/zerotier-one/
|
||||||
- /var/run/docker.sock:/var/run/docker.sock
|
- /var/run/docker.sock:/var/run/docker.sock
|
||||||
|
- /etc/localtime:/etc/localtime
|
||||||
environment:
|
environment:
|
||||||
PORT: "8005"
|
|
||||||
FASTGEOIP: "true"
|
FASTGEOIP: "true"
|
||||||
|
ZEROTIER: "true"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Ports
|
||||||
|
|
||||||
|
| Port | Details |
|
||||||
|
|:-|:-|
|
||||||
|
| `80` | HTTP traffic. |
|
||||||
|
| `443` | HTTPS traffic. |
|
||||||
|
| `8000` | Management interface. Can be changed with the `PORT` env. |
|
||||||
|
|
||||||
|
### Volumes
|
||||||
|
|
||||||
|
| Volume | Details |
|
||||||
|
|:-|:-|
|
||||||
|
| `/opt/zoraxy/config/` | Zoraxy configuration. |
|
||||||
|
| `/var/lib/zerotier-one/` | ZeroTier configuration. Only required if you wish to use ZeroTier. |
|
||||||
|
| `/var/run/docker.sock` | Docker socket. Used for additional functionality with Zoraxy. |
|
||||||
|
| `/etc/localtime` | Localtime. Set to ensure the host and container are synchronized. |
|
||||||
|
|
||||||
|
### Environment
|
||||||
|
|
||||||
|
Variables are the same as those in [Start Parameters](https://github.com/tobychui/zoraxy?tab=readme-ov-file#start-paramters).
|
||||||
|
|
||||||
|
| Variable | Default | Details |
|
||||||
|
|:-|:-|:-|
|
||||||
|
| `AUTORENEW` | `86400` (Integer) | ACME auto TLS/SSL certificate renew check interval. |
|
||||||
|
| `CFGUPGRADE` | `true` (Boolean) | Enable auto config upgrade if breaking change is detected. |
|
||||||
|
| `DOCKER` | `true` (Boolean) | Run Zoraxy in docker compatibility mode. |
|
||||||
|
| `EARLYRENEW` | `30` (Integer) | Number of days to early renew a soon expiring certificate. |
|
||||||
|
| `FASTGEOIP` | `false` (Boolean) | Enable high speed geoip lookup, require 1GB extra memory (Not recommend for low end devices). |
|
||||||
|
| `MDNS` | `true` (Boolean) | Enable mDNS scanner and transponder. |
|
||||||
|
| `MDNSNAME` | `''` (String) | mDNS name, leave empty to use default (zoraxy_{node-uuid}.local). |
|
||||||
|
| `NOAUTH` | `false` (Boolean) | Disable authentication for management interface. |
|
||||||
|
| `PORT` | `8000` (Integer) | Management web interface listening port |
|
||||||
|
| `SSHLB` | `false` (Boolean) | Allow loopback web ssh connection (DANGER). |
|
||||||
|
| `VERSION` | `false` (Boolean) | Show version of this server. |
|
||||||
|
| `WEBFM` | `true` (Boolean) | Enable web file manager for static web server root folder. |
|
||||||
|
| `WEBROOT` | `./www` (String) | Static web server root folder. Only allow change in start parameters. |
|
||||||
|
| `ZEROTIER` | `false` (Boolean) | Enable ZeroTier functionality for GAN. |
|
||||||
|
| `ZTAUTH` | `""` (String) | ZeroTier authtoken for the local node. |
|
||||||
|
| `ZTPORT` | `9993` (Integer) | ZeroTier controller API port. |
|
||||||
|
|
||||||
|
> [!IMPORTANT]
|
||||||
|
> Contrary to the Zoraxy README, Docker usage of the port flag should NOT include the colon. Ex: `-e PORT="8000"` for Docker run and `PORT: "8000"` for Docker compose.
|
||||||
|
|
||||||
|
25
docker/entrypoint.sh
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
if [ "$ZEROTIER" = "true" ]; then
|
||||||
|
echo "Starting ZeroTier daemon..."
|
||||||
|
zerotier-one -d
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Starting Zoraxy..."
|
||||||
|
exec zoraxy \
|
||||||
|
-autorenew="$AUTORENEW" \
|
||||||
|
-cfgupgrade="$CFGUPGRADE" \
|
||||||
|
-docker="$DOCKER" \
|
||||||
|
-earlyrenew="$EARLYRENEW" \
|
||||||
|
-fastgeoip="$FASTGEOIP" \
|
||||||
|
-mdns="$MDNS" \
|
||||||
|
-mdnsname="$MDNSNAME" \
|
||||||
|
-noauth="$NOAUTH" \
|
||||||
|
-port=:"$PORT" \
|
||||||
|
-sshlb="$SSHLB" \
|
||||||
|
-version="$VERSION" \
|
||||||
|
-webfm="$WEBFM" \
|
||||||
|
-webroot="$WEBROOT" \
|
||||||
|
-ztauth="$ZTAUTH" \
|
||||||
|
-ztport="$ZTPORT"
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
||||||
|
@ -38,7 +38,7 @@ func initACME() *acme.ACMEHandler {
|
|||||||
port = getRandomPort(30000)
|
port = getRandomPort(30000)
|
||||||
}
|
}
|
||||||
|
|
||||||
return acme.NewACME("https://acme-v02.api.letsencrypt.org/directory", strconv.Itoa(port), sysdb)
|
return acme.NewACME("https://acme-v02.api.letsencrypt.org/directory", strconv.Itoa(port), sysdb, SystemWideLogger)
|
||||||
}
|
}
|
||||||
|
|
||||||
// create the special routing rule for ACME
|
// create the special routing rule for ACME
|
||||||
|
35
src/api.go
@ -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)
|
||||||
},
|
},
|
||||||
@ -39,10 +39,10 @@ func initAPIs() {
|
|||||||
}
|
}
|
||||||
//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)
|
||||||
|
68
src/cert.go
@ -182,27 +182,28 @@ func handleToggleTLSProxy(w http.ResponseWriter, r *http.Request) {
|
|||||||
sysdb.Read("settings", "usetls", ¤tTlsSetting)
|
sysdb.Read("settings", "usetls", ¤tTlsSetting)
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
||||||
|
@ -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
|
||||||
|
@ -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=
|
||||||
|
24
src/main.go
@ -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.1"
|
||||||
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()
|
||||||
@ -75,6 +77,8 @@ var (
|
|||||||
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
|
||||||
@ -113,8 +117,8 @@ func SetupCloseHandler() {
|
|||||||
|
|
||||||
func ShutdownSeq() {
|
func ShutdownSeq() {
|
||||||
SystemWideLogger.Println("Shutting down " + name)
|
SystemWideLogger.Println("Shutting down " + name)
|
||||||
SystemWideLogger.Println("Closing GeoDB ")
|
//SystemWideLogger.Println("Closing GeoDB")
|
||||||
geodbStore.Close()
|
//geodbStore.Close()
|
||||||
SystemWideLogger.Println("Closing Netstats Listener")
|
SystemWideLogger.Println("Closing Netstats Listener")
|
||||||
netstatBuffers.Close()
|
netstatBuffers.Close()
|
||||||
SystemWideLogger.Println("Closing Statistic Collector")
|
SystemWideLogger.Println("Closing Statistic Collector")
|
||||||
@ -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)
|
||||||
|
@ -11,7 +11,6 @@ import (
|
|||||||
"encoding/pem"
|
"encoding/pem"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
@ -26,6 +25,7 @@ import (
|
|||||||
"github.com/go-acme/lego/v4/lego"
|
"github.com/go-acme/lego/v4/lego"
|
||||||
"github.com/go-acme/lego/v4/registration"
|
"github.com/go-acme/lego/v4/registration"
|
||||||
"imuslab.com/zoraxy/mod/database"
|
"imuslab.com/zoraxy/mod/database"
|
||||||
|
"imuslab.com/zoraxy/mod/info/logger"
|
||||||
"imuslab.com/zoraxy/mod/utils"
|
"imuslab.com/zoraxy/mod/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -68,25 +68,31 @@ type ACMEHandler struct {
|
|||||||
DefaultAcmeServer string
|
DefaultAcmeServer string
|
||||||
Port string
|
Port string
|
||||||
Database *database.Database
|
Database *database.Database
|
||||||
|
Logger *logger.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewACME creates a new ACMEHandler instance.
|
// NewACME creates a new ACMEHandler instance.
|
||||||
func NewACME(acmeServer string, port string, database *database.Database) *ACMEHandler {
|
func NewACME(defaultAcmeServer string, port string, database *database.Database, logger *logger.Logger) *ACMEHandler {
|
||||||
return &ACMEHandler{
|
return &ACMEHandler{
|
||||||
DefaultAcmeServer: acmeServer,
|
DefaultAcmeServer: defaultAcmeServer,
|
||||||
Port: port,
|
Port: port,
|
||||||
Database: database,
|
Database: database,
|
||||||
|
Logger: logger,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *ACMEHandler) Logf(message string, err error) {
|
||||||
|
a.Logger.PrintAndLog("ACME", message, err)
|
||||||
|
}
|
||||||
|
|
||||||
// ObtainCert obtains a certificate for the specified domains.
|
// ObtainCert obtains a certificate for the specified domains.
|
||||||
func (a *ACMEHandler) ObtainCert(domains []string, certificateName string, email string, caName string, caUrl string, skipTLS bool, useDNS bool) (bool, error) {
|
func (a *ACMEHandler) ObtainCert(domains []string, certificateName string, email string, caName string, caUrl string, skipTLS bool, useDNS bool) (bool, error) {
|
||||||
log.Println("[ACME] Obtaining certificate...")
|
a.Logf("Obtaining certificate for: "+strings.Join(domains, ", "), nil)
|
||||||
|
|
||||||
// generate private key
|
// generate private key
|
||||||
privateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
privateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(err)
|
a.Logf("Private key generation failed", err)
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -102,7 +108,7 @@ func (a *ACMEHandler) ObtainCert(domains []string, certificateName string, email
|
|||||||
// skip TLS verify if need
|
// skip TLS verify if need
|
||||||
// Ref: https://github.com/go-acme/lego/blob/6af2c756ac73a9cb401621afca722d0f4112b1b8/lego/client_config.go#L74
|
// Ref: https://github.com/go-acme/lego/blob/6af2c756ac73a9cb401621afca722d0f4112b1b8/lego/client_config.go#L74
|
||||||
if skipTLS {
|
if skipTLS {
|
||||||
log.Println("[INFO] Ignore TLS/SSL Verification Error for ACME Server")
|
a.Logf("Ignoring TLS/SSL Verification Error for ACME Server", nil)
|
||||||
config.HTTPClient.Transport = &http.Transport{
|
config.HTTPClient.Transport = &http.Transport{
|
||||||
Proxy: http.ProxyFromEnvironment,
|
Proxy: http.ProxyFromEnvironment,
|
||||||
DialContext: (&net.Dialer{
|
DialContext: (&net.Dialer{
|
||||||
@ -129,16 +135,16 @@ func (a *ACMEHandler) ObtainCert(domains []string, certificateName string, email
|
|||||||
|
|
||||||
// if not custom ACME url, load it from ca.json
|
// if not custom ACME url, load it from ca.json
|
||||||
if caName == "custom" {
|
if caName == "custom" {
|
||||||
log.Println("[INFO] Using Custom ACME " + caUrl + " for CA Directory URL")
|
a.Logf("Using Custom ACME "+caUrl+" for CA Directory URL", nil)
|
||||||
} else {
|
} else {
|
||||||
caLinkOverwrite, err := loadCAApiServerFromName(caName)
|
caLinkOverwrite, err := loadCAApiServerFromName(caName)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
config.CADirURL = caLinkOverwrite
|
config.CADirURL = caLinkOverwrite
|
||||||
log.Println("[INFO] Using " + caLinkOverwrite + " for CA Directory URL")
|
a.Logf("Using "+caLinkOverwrite+" for CA Directory URL", nil)
|
||||||
} else {
|
} else {
|
||||||
// (caName == "" || caUrl == "") will use default acme
|
// (caName == "" || caUrl == "") will use default acme
|
||||||
config.CADirURL = a.DefaultAcmeServer
|
config.CADirURL = a.DefaultAcmeServer
|
||||||
log.Println("[INFO] Using Default ACME " + a.DefaultAcmeServer + " for CA Directory URL")
|
a.Logf("Using Default ACME "+a.DefaultAcmeServer+" for CA Directory URL", nil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -146,7 +152,7 @@ func (a *ACMEHandler) ObtainCert(domains []string, certificateName string, email
|
|||||||
|
|
||||||
client, err := lego.NewClient(config)
|
client, err := lego.NewClient(config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(err)
|
a.Logf("Failed to spawn new ACME client from current config", err)
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -164,32 +170,32 @@ func (a *ACMEHandler) ObtainCert(domains []string, certificateName string, email
|
|||||||
var dnsCredentials string
|
var dnsCredentials string
|
||||||
err := a.Database.Read("acme", certificateName+"_dns_credentials", &dnsCredentials)
|
err := a.Database.Read("acme", certificateName+"_dns_credentials", &dnsCredentials)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(err)
|
a.Logf("Read DNS credential failed", err)
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var dnsProvider string
|
var dnsProvider string
|
||||||
err = a.Database.Read("acme", certificateName+"_dns_provider", &dnsProvider)
|
err = a.Database.Read("acme", certificateName+"_dns_provider", &dnsProvider)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(err)
|
a.Logf("Read DNS Provider failed", err)
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
provider, err := GetDnsChallengeProviderByName(dnsProvider, dnsCredentials)
|
provider, err := GetDnsChallengeProviderByName(dnsProvider, dnsCredentials)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(err)
|
a.Logf("Unable to resolve DNS challenge provider", err)
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = client.Challenge.SetDNS01Provider(provider)
|
err = client.Challenge.SetDNS01Provider(provider)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(err)
|
a.Logf("Failed to resolve DNS01 Provider", err)
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
err = client.Challenge.SetHTTP01Provider(http01.NewProviderServer("", a.Port))
|
err = client.Challenge.SetHTTP01Provider(http01.NewProviderServer("", a.Port))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(err)
|
a.Logf("Failed to resolve HTTP01 Provider", err)
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -205,7 +211,7 @@ func (a *ACMEHandler) ObtainCert(domains []string, certificateName string, email
|
|||||||
var reg *registration.Resource
|
var reg *registration.Resource
|
||||||
// New users will need to register
|
// New users will need to register
|
||||||
if client.GetExternalAccountRequired() {
|
if client.GetExternalAccountRequired() {
|
||||||
log.Println("External Account Required for this ACME Provider.")
|
a.Logf("External Account Required for this ACME Provider", nil)
|
||||||
// IF KID and HmacEncoded is overidden
|
// IF KID and HmacEncoded is overidden
|
||||||
|
|
||||||
if !a.Database.TableExists("acme") {
|
if !a.Database.TableExists("acme") {
|
||||||
@ -220,20 +226,18 @@ func (a *ACMEHandler) ObtainCert(domains []string, certificateName string, email
|
|||||||
var kid string
|
var kid string
|
||||||
var hmacEncoded string
|
var hmacEncoded string
|
||||||
err := a.Database.Read("acme", config.CADirURL+"_kid", &kid)
|
err := a.Database.Read("acme", config.CADirURL+"_kid", &kid)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(err)
|
a.Logf("Failed to read kid from database", err)
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = a.Database.Read("acme", config.CADirURL+"_hmacEncoded", &hmacEncoded)
|
err = a.Database.Read("acme", config.CADirURL+"_hmacEncoded", &hmacEncoded)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(err)
|
a.Logf("Failed to read HMAC from database", err)
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Println("EAB Credential retrieved.", kid, hmacEncoded)
|
a.Logf("EAB Credential retrieved: "+kid+" / "+hmacEncoded, nil)
|
||||||
if kid != "" && hmacEncoded != "" {
|
if kid != "" && hmacEncoded != "" {
|
||||||
reg, err = client.Registration.RegisterWithExternalAccountBinding(registration.RegisterEABOptions{
|
reg, err = client.Registration.RegisterWithExternalAccountBinding(registration.RegisterEABOptions{
|
||||||
TermsOfServiceAgreed: true,
|
TermsOfServiceAgreed: true,
|
||||||
@ -242,14 +246,14 @@ func (a *ACMEHandler) ObtainCert(domains []string, certificateName string, email
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(err)
|
a.Logf("Register with external account binder failed", err)
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
//return false, errors.New("External Account Required for this ACME Provider.")
|
//return false, errors.New("External Account Required for this ACME Provider.")
|
||||||
} else {
|
} else {
|
||||||
reg, err = client.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: true})
|
reg, err = client.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: true})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(err)
|
a.Logf("Unable to register client", err)
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -262,7 +266,7 @@ func (a *ACMEHandler) ObtainCert(domains []string, certificateName string, email
|
|||||||
}
|
}
|
||||||
certificates, err := client.Certificate.Obtain(request)
|
certificates, err := client.Certificate.Obtain(request)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(err)
|
a.Logf("Obtain certificate failed", err)
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -270,12 +274,12 @@ func (a *ACMEHandler) ObtainCert(domains []string, certificateName string, email
|
|||||||
// private key, and a certificate URL.
|
// private key, and a certificate URL.
|
||||||
err = os.WriteFile("./conf/certs/"+certificateName+".pem", certificates.Certificate, 0777)
|
err = os.WriteFile("./conf/certs/"+certificateName+".pem", certificates.Certificate, 0777)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(err)
|
a.Logf("Failed to write public key to disk", err)
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
err = os.WriteFile("./conf/certs/"+certificateName+".key", certificates.PrivateKey, 0777)
|
err = os.WriteFile("./conf/certs/"+certificateName+".key", certificates.PrivateKey, 0777)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(err)
|
a.Logf("Failed to write private key to disk", err)
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -289,13 +293,13 @@ func (a *ACMEHandler) ObtainCert(domains []string, certificateName string, email
|
|||||||
|
|
||||||
certInfoBytes, err := json.Marshal(certInfo)
|
certInfoBytes, err := json.Marshal(certInfo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(err)
|
a.Logf("Marshal certificate renew config failed", err)
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = os.WriteFile("./conf/certs/"+certificateName+".json", certInfoBytes, 0777)
|
err = os.WriteFile("./conf/certs/"+certificateName+".json", certInfoBytes, 0777)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(err)
|
a.Logf("Failed to write certificate renew config to file", err)
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -313,7 +317,7 @@ func (a *ACMEHandler) CheckCertificate() []string {
|
|||||||
expiredCerts := []string{}
|
expiredCerts := []string{}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(err)
|
a.Logf("Failed to load certificate folder", err)
|
||||||
return []string{}
|
return []string{}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -410,14 +414,14 @@ func (a *ACMEHandler) HandleRenewCertificate(w http.ResponseWriter, r *http.Requ
|
|||||||
|
|
||||||
ca, err := utils.PostPara(r, "ca")
|
ca, err := utils.PostPara(r, "ca")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println("[INFO] CA not set. Using default")
|
a.Logf("CA not set. Using default", nil)
|
||||||
ca, caUrl = "", ""
|
ca, caUrl = "", ""
|
||||||
}
|
}
|
||||||
|
|
||||||
if ca == "custom" {
|
if ca == "custom" {
|
||||||
caUrl, err = utils.PostPara(r, "caURL")
|
caUrl, err = utils.PostPara(r, "caURL")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println("[INFO] Custom CA set but no URL provide, Using default")
|
a.Logf("Custom CA set but no URL provide, Using default", nil)
|
||||||
ca, caUrl = "", ""
|
ca, caUrl = "", ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -465,7 +469,7 @@ func (a *ACMEHandler) HandleRenewCertificate(w http.ResponseWriter, r *http.Requ
|
|||||||
func jsonEscape(i string) string {
|
func jsonEscape(i string) string {
|
||||||
b, err := json.Marshal(i)
|
b, err := json.Marshal(i)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println("Unable to escape json data: " + err.Error())
|
//log.Println("Unable to escape json data: " + err.Error())
|
||||||
return i
|
return i
|
||||||
}
|
}
|
||||||
s := string(b)
|
s := string(b)
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
*/
|
||||||
|
@ -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,6 +727,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 nearlyfreespeech.NewDNSProviderConfig(cfg)
|
return nearlyfreespeech.NewDNSProviderConfig(cfg)
|
||||||
case "netcup":
|
case "netcup":
|
||||||
cfg := netcup.NewDefaultConfig()
|
cfg := netcup.NewDefaultConfig()
|
||||||
@ -660,6 +735,7 @@ func GetDNSProviderByJsonConfig(name string, js string)(challenge.Provider, erro
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
cfg.PropagationTimeout = 20*time.Minute
|
||||||
return netcup.NewDNSProviderConfig(cfg)
|
return netcup.NewDNSProviderConfig(cfg)
|
||||||
case "netlify":
|
case "netlify":
|
||||||
cfg := netlify.NewDefaultConfig()
|
cfg := netlify.NewDefaultConfig()
|
||||||
@ -667,6 +743,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 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)
|
||||||
|
@ -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"
|
||||||
|
@ -75,6 +75,15 @@ func HandleGuidedStepCheck(w http.ResponseWriter, r *http.Request) {
|
|||||||
httpServerReachable := isHTTPServerAvailable(domain)
|
httpServerReachable := isHTTPServerAvailable(domain)
|
||||||
js, _ := json.Marshal(httpServerReachable)
|
js, _ := json.Marshal(httpServerReachable)
|
||||||
utils.SendJSONResponse(w, string(js))
|
utils.SendJSONResponse(w, string(js))
|
||||||
|
} else if stepNo == 10 {
|
||||||
|
//Resolve public Ip address for tour
|
||||||
|
publicIp, err := getPublicIPAddress()
|
||||||
|
if err != nil {
|
||||||
|
utils.SendErrorResponse(w, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
js, _ := json.Marshal(publicIp)
|
||||||
|
utils.SendJSONResponse(w, string(js))
|
||||||
} else {
|
} else {
|
||||||
utils.SendErrorResponse(w, "invalid step number")
|
utils.SendErrorResponse(w, "invalid step number")
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,6 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/mail"
|
"net/mail"
|
||||||
"os"
|
"os"
|
||||||
@ -12,6 +11,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"imuslab.com/zoraxy/mod/info/logger"
|
||||||
"imuslab.com/zoraxy/mod/utils"
|
"imuslab.com/zoraxy/mod/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -34,7 +34,9 @@ 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
|
||||||
|
Logger *logger.Logger //System wide logger
|
||||||
}
|
}
|
||||||
|
|
||||||
type ExpiredCerts struct {
|
type ExpiredCerts struct {
|
||||||
@ -44,11 +46,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, logger *logger.Logger) (*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
|
||||||
@ -82,6 +88,7 @@ func NewAutoRenewer(config string, certFolder string, renewCheckInterval int64,
|
|||||||
AcmeHandler: AcmeHandler,
|
AcmeHandler: AcmeHandler,
|
||||||
RenewerConfig: &renewerConfig,
|
RenewerConfig: &renewerConfig,
|
||||||
RenewTickInterval: renewCheckInterval,
|
RenewTickInterval: renewCheckInterval,
|
||||||
|
Logger: logger,
|
||||||
}
|
}
|
||||||
|
|
||||||
if thisRenewer.RenewerConfig.Enabled {
|
if thisRenewer.RenewerConfig.Enabled {
|
||||||
@ -95,6 +102,10 @@ func NewAutoRenewer(config string, certFolder string, renewCheckInterval int64,
|
|||||||
return &thisRenewer, nil
|
return &thisRenewer, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *AutoRenewer) Logf(message string, err error) {
|
||||||
|
a.Logger.PrintAndLog("CertRenew", message, err)
|
||||||
|
}
|
||||||
|
|
||||||
func (a *AutoRenewer) StartAutoRenewTicker() {
|
func (a *AutoRenewer) StartAutoRenewTicker() {
|
||||||
//Stop the previous ticker if still running
|
//Stop the previous ticker if still running
|
||||||
if a.TickerstopChan != nil {
|
if a.TickerstopChan != nil {
|
||||||
@ -113,7 +124,7 @@ func (a *AutoRenewer) StartAutoRenewTicker() {
|
|||||||
case <-done:
|
case <-done:
|
||||||
return
|
return
|
||||||
case <-ticker.C:
|
case <-ticker.C:
|
||||||
log.Println("Check and renew certificates in progress")
|
a.Logf("Check and renew certificates in progress", nil)
|
||||||
a.CheckAndRenewCertificates()
|
a.CheckAndRenewCertificates()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -135,7 +146,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 +176,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,42 +221,52 @@ 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")
|
a.Logf("ACME auto renew enabled", nil)
|
||||||
a.StartAutoRenewTicker()
|
a.StartAutoRenewTicker()
|
||||||
} else {
|
} else {
|
||||||
a.RenewerConfig.Enabled = false
|
a.RenewerConfig.Enabled = false
|
||||||
a.saveRenewConfigToFile()
|
a.saveRenewConfigToFile()
|
||||||
log.Println("[ACME] ACME auto renew disabled")
|
a.Logf("ACME auto renew disabled", nil)
|
||||||
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 +275,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
|
||||||
@ -263,7 +289,7 @@ func (a *AutoRenewer) CheckAndRenewCertificates() ([]string, error) {
|
|||||||
certFolder := a.CertFolder
|
certFolder := a.CertFolder
|
||||||
files, err := os.ReadDir(certFolder)
|
files, err := os.ReadDir(certFolder)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println("Unable to renew certificates: " + err.Error())
|
a.Logf("Read certificate store failed", err)
|
||||||
return []string{}, err
|
return []string{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -277,13 +303,13 @@ 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)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
//Maybe self signed. Ignore this
|
//Maybe self signed. Ignore this
|
||||||
log.Println("Encounted error when trying to resolve DNS name for cert " + file.Name())
|
a.Logf("Encounted error when trying to resolve DNS name for cert "+file.Name(), err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -305,13 +331,12 @@ 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)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
//Maybe self signed. Ignore this
|
//Maybe self signed. Ignore this
|
||||||
log.Println("Encounted error when trying to resolve DNS name for cert " + file.Name())
|
a.Logf("Encounted error when trying to resolve DNS name for cert "+file.Name(), err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -338,7 +363,7 @@ func (a *AutoRenewer) Close() {
|
|||||||
func (a *AutoRenewer) renewExpiredDomains(certs []*ExpiredCerts) ([]string, error) {
|
func (a *AutoRenewer) renewExpiredDomains(certs []*ExpiredCerts) ([]string, error) {
|
||||||
renewedCertFiles := []string{}
|
renewedCertFiles := []string{}
|
||||||
for _, expiredCert := range certs {
|
for _, expiredCert := range certs {
|
||||||
log.Println("Renewing " + expiredCert.Filepath + " (Might take a few minutes)")
|
a.Logf("Renewing "+expiredCert.Filepath+" (Might take a few minutes)", nil)
|
||||||
fileName := filepath.Base(expiredCert.Filepath)
|
fileName := filepath.Base(expiredCert.Filepath)
|
||||||
certName := fileName[:len(fileName)-len(filepath.Ext(fileName))]
|
certName := fileName[:len(fileName)-len(filepath.Ext(fileName))]
|
||||||
|
|
||||||
@ -346,10 +371,10 @@ func (a *AutoRenewer) renewExpiredDomains(certs []*ExpiredCerts) ([]string, erro
|
|||||||
certInfoFilename := fmt.Sprintf("%s/%s.json", filepath.Dir(expiredCert.Filepath), certName)
|
certInfoFilename := fmt.Sprintf("%s/%s.json", filepath.Dir(expiredCert.Filepath), certName)
|
||||||
certInfo, err := LoadCertInfoJSON(certInfoFilename)
|
certInfo, err := LoadCertInfoJSON(certInfoFilename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Renew %s certificate error, can't get the ACME detail for cert: %v, trying org section as ca", certName, err)
|
a.Logf("Renew "+certName+"certificate error, can't get the ACME detail for certificate, trying org section as ca", err)
|
||||||
|
|
||||||
if CAName, extractErr := ExtractIssuerNameFromPEM(expiredCert.Filepath); extractErr != nil {
|
if CAName, extractErr := ExtractIssuerNameFromPEM(expiredCert.Filepath); extractErr != nil {
|
||||||
log.Printf("extract issuer name for cert error: %v, using default ca", extractErr)
|
a.Logf("Extract issuer name for cert error, using default ca", err)
|
||||||
certInfo = &CertificateInfoJSON{}
|
certInfo = &CertificateInfoJSON{}
|
||||||
} else {
|
} else {
|
||||||
certInfo = &CertificateInfoJSON{AcmeName: CAName}
|
certInfo = &CertificateInfoJSON{AcmeName: CAName}
|
||||||
@ -358,9 +383,9 @@ func (a *AutoRenewer) renewExpiredDomains(certs []*ExpiredCerts) ([]string, erro
|
|||||||
|
|
||||||
_, err = a.AcmeHandler.ObtainCert(expiredCert.Domains, certName, a.RenewerConfig.Email, certInfo.AcmeName, certInfo.AcmeUrl, certInfo.SkipTLS, certInfo.UseDNS)
|
_, err = a.AcmeHandler.ObtainCert(expiredCert.Domains, certName, a.RenewerConfig.Email, certInfo.AcmeName, certInfo.AcmeUrl, certInfo.SkipTLS, certInfo.UseDNS)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println("Renew " + fileName + "(" + strings.Join(expiredCert.Domains, ",") + ") failed: " + err.Error())
|
a.Logf("Renew "+fileName+"("+strings.Join(expiredCert.Domains, ",")+") failed", err)
|
||||||
} else {
|
} else {
|
||||||
log.Println("Successfully renewed " + filepath.Base(expiredCert.Filepath))
|
a.Logf("Successfully renewed "+filepath.Base(expiredCert.Filepath), nil)
|
||||||
renewedCertFiles = append(renewedCertFiles, filepath.Base(expiredCert.Filepath))
|
renewedCertFiles = append(renewedCertFiles, filepath.Base(expiredCert.Filepath))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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 {
|
||||||
|
@ -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,6 +35,7 @@ func (router *RouterDef) HandleFunc(endpoint string, handler func(http.ResponseW
|
|||||||
authAgent := router.option.AuthAgent
|
authAgent := router.option.AuthAgent
|
||||||
|
|
||||||
//OK. Register handler
|
//OK. Register handler
|
||||||
|
if router.option.TargetMux == nil {
|
||||||
http.HandleFunc(endpoint, func(w http.ResponseWriter, r *http.Request) {
|
http.HandleFunc(endpoint, func(w http.ResponseWriter, r *http.Request) {
|
||||||
//Check authentication of the user
|
//Check authentication of the user
|
||||||
if router.option.RequireAuth {
|
if router.option.RequireAuth {
|
||||||
@ -46,6 +47,19 @@ func (router *RouterDef) HandleFunc(endpoint string, handler func(http.ResponseW
|
|||||||
}
|
}
|
||||||
|
|
||||||
})
|
})
|
||||||
|
} 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
|
||||||
|
|
||||||
|
@ -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))
|
||||||
|
21
src/mod/dynamicproxy/domainsniff/proxmox.go
Normal 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
|
||||||
|
}
|
@ -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,16 @@ 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
|
||||||
|
|
||||||
|
//Appended by Zoraxy project
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type ResponseRewriteRuleSet struct {
|
type ResponseRewriteRuleSet struct {
|
||||||
|
/* Basic Rewrite Rulesets */
|
||||||
ProxyDomain string
|
ProxyDomain string
|
||||||
OriginalHost string
|
OriginalHost string
|
||||||
UseTLS bool
|
UseTLS bool
|
||||||
@ -64,7 +68,12 @@ 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
|
|
||||||
|
/* 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
|
Version string //Version number of Zoraxy, use for X-Proxy-By
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -73,8 +82,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 +261,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 +290,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 +303,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.
|
||||||
|
if !rrr.NoRemoveHopByHop {
|
||||||
removeHeaders(outreq.Header, rrr.NoCache)
|
removeHeaders(outreq.Header, rrr.NoCache)
|
||||||
|
}
|
||||||
|
|
||||||
// Add X-Forwarded-For Header.
|
// Add X-Forwarded-For Header.
|
||||||
addXForwardedForHeader(outreq)
|
addXForwardedForHeader(outreq)
|
||||||
@ -302,6 +316,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 +328,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.
|
||||||
|
if !rrr.NoRemoveHopByHop {
|
||||||
removeHeaders(res.Header, rrr.NoCache)
|
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,17 +349,10 @@ 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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO: Figure out a way to proxy for proxmox
|
|
||||||
//if res.StatusCode == 501 || res.StatusCode == 500 {
|
|
||||||
// fmt.Println(outreq.Proto, outreq.RemoteAddr, outreq.RequestURI)
|
|
||||||
// fmt.Println(">>>", outreq.Method, res.Header, res.ContentLength, res.StatusCode)
|
|
||||||
// fmt.Println(outreq.Header, req.Host)
|
|
||||||
//}
|
|
||||||
|
|
||||||
//Add debug X-Proxy-By tracker
|
//Add debug X-Proxy-By tracker
|
||||||
res.Header.Set("x-proxy-by", "zoraxy/"+rrr.Version)
|
res.Header.Set("x-proxy-by", "zoraxy/"+rrr.Version)
|
||||||
|
|
||||||
@ -375,7 +389,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 +418,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 +433,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 +442,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 +461,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 +470,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 +479,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 +492,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
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
}
|
||||||
|
@ -161,6 +161,7 @@ func (router *Router) StartProxyService() error {
|
|||||||
ProxyDomain: selectedUpstream.OriginIpOrDomain,
|
ProxyDomain: selectedUpstream.OriginIpOrDomain,
|
||||||
OriginalHost: originalHostHeader,
|
OriginalHost: originalHostHeader,
|
||||||
UseTLS: selectedUpstream.RequireTLS,
|
UseTLS: selectedUpstream.RequireTLS,
|
||||||
|
HostHeaderOverwrite: sep.RequestHostOverwrite,
|
||||||
NoRemoveHopByHop: sep.DisableHopByHopHeaderRemoval,
|
NoRemoveHopByHop: sep.DisableHopByHopHeaderRemoval,
|
||||||
PathPrefix: "",
|
PathPrefix: "",
|
||||||
Version: sep.parent.Option.HostVersion,
|
Version: sep.parent.Option.HostVersion,
|
||||||
|
@ -83,6 +83,10 @@ func GetUpstreamsAsString(upstreams []*Upstream) string {
|
|||||||
for _, upstream := range upstreams {
|
for _, upstream := range upstreams {
|
||||||
targets = append(targets, upstream.String())
|
targets = append(targets, upstream.String())
|
||||||
}
|
}
|
||||||
|
if len(targets) == 0 {
|
||||||
|
//No upstream
|
||||||
|
return "(no upstream config)"
|
||||||
|
}
|
||||||
return strings.Join(targets, ", ")
|
return strings.Join(targets, ", ")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -93,7 +97,7 @@ func (m *RouteManager) Close() {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Print debug message
|
// Log Println, replace all log.Println or fmt.Println with this
|
||||||
func (m *RouteManager) debugPrint(message string, err error) {
|
func (m *RouteManager) println(message string, err error) {
|
||||||
m.Options.Logger.PrintAndLog("LoadBalancer", message, err)
|
m.Options.Logger.PrintAndLog("LoadBalancer", message, err)
|
||||||
}
|
}
|
||||||
|
@ -2,8 +2,6 @@ package loadbalance
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
@ -29,7 +27,7 @@ func (m *RouteManager) GetRequestUpstreamTarget(w http.ResponseWriter, r *http.R
|
|||||||
//No valid session found. Assign a new upstream
|
//No valid session found. Assign a new upstream
|
||||||
targetOrigin, index, err := getRandomUpstreamByWeight(origins)
|
targetOrigin, index, err := getRandomUpstreamByWeight(origins)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("Oops. Unable to get random upstream")
|
m.println("Unable to get random upstream", err)
|
||||||
targetOrigin = origins[0]
|
targetOrigin = origins[0]
|
||||||
index = 0
|
index = 0
|
||||||
}
|
}
|
||||||
@ -44,7 +42,7 @@ func (m *RouteManager) GetRequestUpstreamTarget(w http.ResponseWriter, r *http.R
|
|||||||
var err error
|
var err error
|
||||||
targetOrigin, _, err = getRandomUpstreamByWeight(origins)
|
targetOrigin, _, err = getRandomUpstreamByWeight(origins)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(err)
|
m.println("Failed to get next origin", err)
|
||||||
targetOrigin = origins[0]
|
targetOrigin = origins[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -102,42 +100,66 @@ func (m *RouteManager) getSessionHandler(r *http.Request, upstreams []*Upstream)
|
|||||||
/* Functions related to random upstream picking */
|
/* Functions related to random upstream picking */
|
||||||
// Get a random upstream by the weights defined in Upstream struct, return the upstream, index value and any error
|
// Get a random upstream by the weights defined in Upstream struct, return the upstream, index value and any error
|
||||||
func getRandomUpstreamByWeight(upstreams []*Upstream) (*Upstream, int, error) {
|
func getRandomUpstreamByWeight(upstreams []*Upstream) (*Upstream, int, error) {
|
||||||
var ret *Upstream
|
// If there is only one upstream, return it
|
||||||
sum := 0
|
if len(upstreams) == 1 {
|
||||||
for _, c := range upstreams {
|
return upstreams[0], 0, nil
|
||||||
sum += c.Weight
|
|
||||||
}
|
|
||||||
r, err := intRange(0, sum)
|
|
||||||
if err != nil {
|
|
||||||
return ret, -1, err
|
|
||||||
}
|
|
||||||
counter := 0
|
|
||||||
for _, c := range upstreams {
|
|
||||||
r -= c.Weight
|
|
||||||
if r < 0 {
|
|
||||||
return c, counter, nil
|
|
||||||
}
|
|
||||||
counter++
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ret == nil {
|
// Preserve the index with upstreams
|
||||||
//All fallback
|
type upstreamWithIndex struct {
|
||||||
//use the first one that is with weight = 0
|
Upstream *Upstream
|
||||||
fallbackUpstreams := []*Upstream{}
|
Index int
|
||||||
fallbackUpstreamsOriginalID := []int{}
|
}
|
||||||
for ix, upstream := range upstreams {
|
|
||||||
if upstream.Weight == 0 {
|
// Calculate total weight for upstreams with weight > 0
|
||||||
fallbackUpstreams = append(fallbackUpstreams, upstream)
|
totalWeight := 0
|
||||||
fallbackUpstreamsOriginalID = append(fallbackUpstreamsOriginalID, ix)
|
fallbackUpstreams := make([]upstreamWithIndex, 0, len(upstreams))
|
||||||
|
|
||||||
|
for index, upstream := range upstreams {
|
||||||
|
if upstream.Weight > 0 {
|
||||||
|
totalWeight += upstream.Weight
|
||||||
|
} else {
|
||||||
|
// Collect fallback upstreams
|
||||||
|
fallbackUpstreams = append(fallbackUpstreams, upstreamWithIndex{upstream, index})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
upstreamID := rand.Intn(len(fallbackUpstreams))
|
|
||||||
return fallbackUpstreams[upstreamID], fallbackUpstreamsOriginalID[upstreamID], nil
|
// If there are no upstreams with weight > 0, return a fallback upstream if available
|
||||||
|
if totalWeight == 0 {
|
||||||
|
if len(fallbackUpstreams) > 0 {
|
||||||
|
// Randomly select one of the fallback upstreams
|
||||||
|
randIndex := rand.Intn(len(fallbackUpstreams))
|
||||||
|
return fallbackUpstreams[randIndex].Upstream, fallbackUpstreams[randIndex].Index, nil
|
||||||
}
|
}
|
||||||
return ret, -1, errors.New("failed to pick an upstream origin server")
|
// No upstreams available at all
|
||||||
|
return nil, -1, errors.New("no valid upstream servers available")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Random weight between 0 and total weight
|
||||||
|
randomWeight := rand.Intn(totalWeight)
|
||||||
|
|
||||||
|
// Select an upstream based on the random weight
|
||||||
|
for index, upstream := range upstreams {
|
||||||
|
if upstream.Weight > 0 { // Only consider upstreams with weight > 0
|
||||||
|
if randomWeight < upstream.Weight {
|
||||||
|
// Return the selected upstream and its index
|
||||||
|
return upstream, index, nil
|
||||||
|
}
|
||||||
|
randomWeight -= upstream.Weight
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we reach here, it means we should return a fallback upstream if available
|
||||||
|
if len(fallbackUpstreams) > 0 {
|
||||||
|
randIndex := rand.Intn(len(fallbackUpstreams))
|
||||||
|
return fallbackUpstreams[randIndex].Upstream, fallbackUpstreams[randIndex].Index, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, -1, errors.New("failed to pick an upstream origin server")
|
||||||
}
|
}
|
||||||
|
|
||||||
// IntRange returns a random integer in the range from min to max.
|
// IntRange returns a random integer in the range from min to max.
|
||||||
|
/*
|
||||||
func intRange(min, max int) (int, error) {
|
func intRange(min, max int) (int, error) {
|
||||||
var result int
|
var result int
|
||||||
switch {
|
switch {
|
||||||
@ -152,3 +174,4 @@ func intRange(min, max int) (int, error) {
|
|||||||
}
|
}
|
||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
@ -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
|
||||||
|
@ -112,13 +112,17 @@ 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")
|
||||||
log.Println(err.Error())
|
h.Parent.Option.Logger.PrintAndLog("proxy", "Failed to assign an upstream for this request", err)
|
||||||
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
|
||||||
@ -140,6 +144,7 @@ func (h *ProxyHandler) hostRequest(w http.ResponseWriter, r *http.Request, targe
|
|||||||
wspHandler := websocketproxy.NewProxy(u, websocketproxy.Options{
|
wspHandler := websocketproxy.NewProxy(u, websocketproxy.Options{
|
||||||
SkipTLSValidation: selectedUpstream.SkipCertValidations,
|
SkipTLSValidation: selectedUpstream.SkipCertValidations,
|
||||||
SkipOriginCheck: selectedUpstream.SkipWebSocketOriginCheck,
|
SkipOriginCheck: selectedUpstream.SkipWebSocketOriginCheck,
|
||||||
|
Logger: h.Parent.Option.Logger,
|
||||||
})
|
})
|
||||||
wspHandler.ServeHTTP(w, r)
|
wspHandler.ServeHTTP(w, r)
|
||||||
return
|
return
|
||||||
@ -156,7 +161,7 @@ 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,
|
||||||
@ -164,6 +169,7 @@ func (h *ProxyHandler) hostRequest(w http.ResponseWriter, r *http.Request, targe
|
|||||||
PathPrefix: "",
|
PathPrefix: "",
|
||||||
UpstreamHeaders: upstreamHeaders,
|
UpstreamHeaders: upstreamHeaders,
|
||||||
DownstreamHeaders: downstreamHeaders,
|
DownstreamHeaders: downstreamHeaders,
|
||||||
|
HostHeaderOverwrite: target.RequestHostOverwrite,
|
||||||
NoRemoveHopByHop: target.DisableHopByHopHeaderRemoval,
|
NoRemoveHopByHop: target.DisableHopByHopHeaderRemoval,
|
||||||
Version: target.parent.Option.HostVersion,
|
Version: target.parent.Option.HostVersion,
|
||||||
})
|
})
|
||||||
@ -172,16 +178,15 @@ func (h *ProxyHandler) hostRequest(w http.ResponseWriter, r *http.Request, targe
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.As(err, &dnsError) {
|
if errors.As(err, &dnsError) {
|
||||||
http.ServeFile(w, r, "./web/hosterror.html")
|
http.ServeFile(w, r, "./web/hosterror.html")
|
||||||
log.Println(err.Error())
|
|
||||||
h.Parent.logRequest(r, false, 404, "host-http", r.URL.Hostname())
|
h.Parent.logRequest(r, false, 404, "host-http", r.URL.Hostname())
|
||||||
} else {
|
} else {
|
||||||
http.ServeFile(w, r, "./web/rperror.html")
|
http.ServeFile(w, r, "./web/rperror.html")
|
||||||
log.Println(err.Error())
|
//TODO: Take this upstream offline automatically
|
||||||
h.Parent.logRequest(r, false, 521, "host-http", r.URL.Hostname())
|
h.Parent.logRequest(r, false, 521, "host-http", r.URL.Hostname())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
||||||
@ -207,6 +212,7 @@ func (h *ProxyHandler) vdirRequest(w http.ResponseWriter, r *http.Request, targe
|
|||||||
wspHandler := websocketproxy.NewProxy(u, websocketproxy.Options{
|
wspHandler := websocketproxy.NewProxy(u, websocketproxy.Options{
|
||||||
SkipTLSValidation: target.SkipCertValidations,
|
SkipTLSValidation: target.SkipCertValidations,
|
||||||
SkipOriginCheck: true, //You should not use websocket via virtual directory. But keep this to true for compatibility
|
SkipOriginCheck: true, //You should not use websocket via virtual directory. But keep this to true for compatibility
|
||||||
|
Logger: h.Parent.Option.Logger,
|
||||||
})
|
})
|
||||||
wspHandler.ServeHTTP(w, r)
|
wspHandler.ServeHTTP(w, r)
|
||||||
return
|
return
|
||||||
@ -223,13 +229,14 @@ 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,
|
||||||
|
HostHeaderOverwrite: target.parent.RequestHostOverwrite,
|
||||||
Version: target.parent.parent.Option.HostVersion,
|
Version: target.parent.parent.Option.HostVersion,
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -245,7 +252,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)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -70,6 +70,11 @@ func (router *Router) PrepareProxyRoute(endpoint *ProxyEndpoint) (*ProxyEndpoint
|
|||||||
|
|
||||||
// Add Proxy Route to current runtime. Call to PrepareProxyRoute before adding to runtime
|
// Add Proxy Route to current runtime. Call to PrepareProxyRoute before adding to runtime
|
||||||
func (router *Router) AddProxyRouteToRuntime(endpoint *ProxyEndpoint) error {
|
func (router *Router) AddProxyRouteToRuntime(endpoint *ProxyEndpoint) error {
|
||||||
|
if len(endpoint.ActiveOrigins) == 0 {
|
||||||
|
//There are no active origins. No need to check for ready
|
||||||
|
router.ProxyEndpoints.Store(endpoint.RootOrMatchingDomain, endpoint)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
if !router.loadBalancer.UpstreamsReady(endpoint.ActiveOrigins) {
|
if !router.loadBalancer.UpstreamsReady(endpoint.ActiveOrigins) {
|
||||||
//This endpoint is not prepared
|
//This endpoint is not prepared
|
||||||
return errors.New("proxy endpoint not ready. Use PrepareProxyRoute before adding to runtime")
|
return errors.New("proxy endpoint not ready. Use PrepareProxyRoute before adding to runtime")
|
||||||
|
@ -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
|
||||||
|
@ -18,7 +18,7 @@ func (this *defaultDialer) Dial(address string) Socket {
|
|||||||
if socket, err := net.DialTimeout("tcp", address, this.timeout); err == nil {
|
if socket, err := net.DialTimeout("tcp", address, this.timeout); err == nil {
|
||||||
return socket
|
return socket
|
||||||
} else {
|
} else {
|
||||||
this.logger.Printf("[INFO] Unable to establish connection to [%s]: %s", address, err)
|
this.logger.Printf("Unable to establish connection to [%s]: %s", address, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -17,7 +17,7 @@ func (this *loggingInitializer) Initialize(client, server Socket) bool {
|
|||||||
result := this.inner.Initialize(client, server)
|
result := this.inner.Initialize(client, server)
|
||||||
|
|
||||||
if !result {
|
if !result {
|
||||||
this.logger.Printf("[INFO] Connection failed [%s] -> [%s]", client.RemoteAddr(), server.RemoteAddr())
|
this.logger.Printf("Connection failed [%s] -> [%s]", client.RemoteAddr(), server.RemoteAddr())
|
||||||
}
|
}
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
@ -19,7 +19,6 @@ 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
|
||||||
}
|
}
|
||||||
|
@ -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 {
|
||||||
|
@ -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 := ""
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
@ -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" {
|
||||||
|
@ -88,6 +88,7 @@ func (m *Manager) HandleHttpByInstanceId(instanceId string, w http.ResponseWrite
|
|||||||
wspHandler := websocketproxy.NewProxy(u, websocketproxy.Options{
|
wspHandler := websocketproxy.NewProxy(u, websocketproxy.Options{
|
||||||
SkipTLSValidation: false,
|
SkipTLSValidation: false,
|
||||||
SkipOriginCheck: false,
|
SkipOriginCheck: false,
|
||||||
|
Logger: nil,
|
||||||
})
|
})
|
||||||
wspHandler.ServeHTTP(w, r)
|
wspHandler.ServeHTTP(w, r)
|
||||||
return
|
return
|
||||||
|
@ -6,11 +6,11 @@ import (
|
|||||||
"embed"
|
"embed"
|
||||||
"encoding/pem"
|
"encoding/pem"
|
||||||
"io"
|
"io"
|
||||||
"log"
|
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"imuslab.com/zoraxy/mod/info/logger"
|
||||||
"imuslab.com/zoraxy/mod/utils"
|
"imuslab.com/zoraxy/mod/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -23,13 +23,14 @@ 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 +53,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 +80,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 +88,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,44 +174,16 @@ 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)
|
|
||||||
//}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//Load the cert and serve it
|
//Load the cert and serve it
|
||||||
cer, err := tls.LoadX509KeyPair(pubKey, priKey)
|
cer, err := tls.LoadX509KeyPair(pubKey, priKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(err)
|
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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 os.IsNotExist(err) {
|
||||||
|
// The folder does not exist
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
// 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
|
||||||
}
|
}
|
||||||
|
16
src/mod/update/updatelogic.go
Normal 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
|
||||||
|
}
|
@ -3,7 +3,6 @@ package uptime
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"log"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/cookiejar"
|
"net/http/cookiejar"
|
||||||
"strconv"
|
"strconv"
|
||||||
@ -242,7 +241,7 @@ func getWebsiteStatus(url string) (int, error) {
|
|||||||
// Create a one-time use cookie jar to store cookies
|
// Create a one-time use cookie jar to store cookies
|
||||||
jar, err := cookiejar.New(&cookiejar.Options{PublicSuffixList: publicsuffix.List})
|
jar, err := cookiejar.New(&cookiejar.Options{PublicSuffixList: publicsuffix.List})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
return 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
client := http.Client{
|
client := http.Client{
|
||||||
|
@ -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
|
||||||
|
}
|
||||||
|
@ -41,23 +41,44 @@ func SendOK(w http.ResponseWriter) {
|
|||||||
|
|
||||||
// Get GET parameter
|
// Get GET parameter
|
||||||
func GetPara(r *http.Request, key string) (string, error) {
|
func GetPara(r *http.Request, key string) (string, error) {
|
||||||
keys, ok := r.URL.Query()[key]
|
// Get first value from the URL query
|
||||||
if !ok || len(keys[0]) < 1 {
|
value := r.URL.Query().Get(key)
|
||||||
|
if len(value) == 0 {
|
||||||
return "", errors.New("invalid " + key + " given")
|
return "", errors.New("invalid " + key + " given")
|
||||||
} else {
|
|
||||||
return keys[0], nil
|
|
||||||
}
|
}
|
||||||
|
return value, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get POST paramter
|
// Get GET paramter as boolean, accept 1 or true
|
||||||
func PostPara(r *http.Request, key string) (string, error) {
|
func GetBool(r *http.Request, key string) (bool, error) {
|
||||||
r.ParseForm()
|
x, err := GetPara(r, key)
|
||||||
x := r.Form.Get(key)
|
if err != nil {
|
||||||
if x == "" {
|
return false, err
|
||||||
return "", errors.New("invalid " + key + " given")
|
|
||||||
} else {
|
|
||||||
return x, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Convert to lowercase and trim spaces just once to compare
|
||||||
|
switch strings.ToLower(strings.TrimSpace(x)) {
|
||||||
|
case "1", "true", "on":
|
||||||
|
return true, nil
|
||||||
|
case "0", "false", "off":
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return false, errors.New("invalid boolean given")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get POST parameter
|
||||||
|
func PostPara(r *http.Request, key string) (string, error) {
|
||||||
|
// Try to parse the form
|
||||||
|
if err := r.ParseForm(); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
// Get first value from the form
|
||||||
|
x := r.Form.Get(key)
|
||||||
|
if len(x) == 0 {
|
||||||
|
return "", errors.New("invalid " + key + " given")
|
||||||
|
}
|
||||||
|
return x, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get POST paramter as boolean, accept 1 or true
|
// Get POST paramter as boolean, accept 1 or true
|
||||||
@ -67,11 +88,11 @@ func PostBool(r *http.Request, key string) (bool, error) {
|
|||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
x = strings.TrimSpace(x)
|
// Convert to lowercase and trim spaces just once to compare
|
||||||
|
switch strings.ToLower(strings.TrimSpace(x)) {
|
||||||
if x == "1" || strings.ToLower(x) == "true" || strings.ToLower(x) == "on" {
|
case "1", "true", "on":
|
||||||
return true, nil
|
return true, nil
|
||||||
} else if x == "0" || strings.ToLower(x) == "false" || strings.ToLower(x) == "off" {
|
case "0", "false", "off":
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -96,14 +117,19 @@ func PostInt(r *http.Request, key string) (int, error) {
|
|||||||
|
|
||||||
func FileExists(filename string) bool {
|
func FileExists(filename string) bool {
|
||||||
_, err := os.Stat(filename)
|
_, err := os.Stat(filename)
|
||||||
if os.IsNotExist(err) {
|
if err == nil {
|
||||||
|
// File exists
|
||||||
|
return true
|
||||||
|
} else if errors.Is(err, os.ErrNotExist) {
|
||||||
|
// File does not exist
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return true
|
// Some other error
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func IsDir(path string) bool {
|
func IsDir(path string) bool {
|
||||||
if FileExists(path) == false {
|
if !FileExists(path) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
fi, err := os.Stat(path)
|
fi, err := os.Stat(path)
|
||||||
|
@ -42,6 +42,12 @@ func (fm *FileManager) HandleList(w http.ResponseWriter, r *http.Request) {
|
|||||||
// Construct the absolute path to the target directory
|
// Construct the absolute path to the target directory
|
||||||
targetDir := filepath.Join(fm.Directory, directory)
|
targetDir := filepath.Join(fm.Directory, directory)
|
||||||
|
|
||||||
|
// Clean path to prevent path escape #274
|
||||||
|
targetDir = filepath.ToSlash(filepath.Clean(targetDir))
|
||||||
|
for strings.Contains(targetDir, "../") {
|
||||||
|
targetDir = strings.ReplaceAll(targetDir, "../", "")
|
||||||
|
}
|
||||||
|
|
||||||
// Open the target directory
|
// Open the target directory
|
||||||
dirEntries, err := os.ReadDir(targetDir)
|
dirEntries, err := os.ReadDir(targetDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -173,7 +179,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 +274,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
|
||||||
|
@ -3,6 +3,7 @@ package websocketproxy
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
@ -12,6 +13,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/gorilla/websocket"
|
"github.com/gorilla/websocket"
|
||||||
|
"imuslab.com/zoraxy/mod/info/logger"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -56,6 +58,7 @@ type WebsocketProxy struct {
|
|||||||
type Options struct {
|
type Options struct {
|
||||||
SkipTLSValidation bool //Skip backend TLS validation
|
SkipTLSValidation bool //Skip backend TLS validation
|
||||||
SkipOriginCheck bool //Skip origin check
|
SkipOriginCheck bool //Skip origin check
|
||||||
|
Logger *logger.Logger //Logger, can be nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ProxyHandler returns a new http.Handler interface that reverse proxies the
|
// ProxyHandler returns a new http.Handler interface that reverse proxies the
|
||||||
@ -78,17 +81,26 @@ func NewProxy(target *url.URL, options Options) *WebsocketProxy {
|
|||||||
return &WebsocketProxy{Backend: backend, Verbal: false, Options: options}
|
return &WebsocketProxy{Backend: backend, Verbal: false, Options: options}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Utilities function for log printing
|
||||||
|
func (w *WebsocketProxy) Println(messsage string, err error) {
|
||||||
|
if w.Options.Logger != nil {
|
||||||
|
w.Options.Logger.PrintAndLog("websocket", messsage, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Println("[websocketproxy] [system:info]"+messsage, err)
|
||||||
|
}
|
||||||
|
|
||||||
// ServeHTTP implements the http.Handler that proxies WebSocket connections.
|
// ServeHTTP implements the http.Handler that proxies WebSocket connections.
|
||||||
func (w *WebsocketProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
func (w *WebsocketProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
||||||
if w.Backend == nil {
|
if w.Backend == nil {
|
||||||
log.Println("websocketproxy: backend function is not defined")
|
w.Println("Invalid websocket backend configuration", errors.New("backend function not found"))
|
||||||
http.Error(rw, "internal server error (code: 1)", http.StatusInternalServerError)
|
http.Error(rw, "internal server error (code: 1)", http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
backendURL := w.Backend(req)
|
backendURL := w.Backend(req)
|
||||||
if backendURL == nil {
|
if backendURL == nil {
|
||||||
log.Println("websocketproxy: backend URL is nil")
|
w.Println("Invalid websocket backend configuration", errors.New("backend URL is nil"))
|
||||||
http.Error(rw, "internal server error (code: 2)", http.StatusInternalServerError)
|
http.Error(rw, "internal server error (code: 2)", http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -158,13 +170,13 @@ func (w *WebsocketProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
|||||||
// http://tools.ietf.org/html/draft-ietf-hybi-websocket-multiplexing-01
|
// http://tools.ietf.org/html/draft-ietf-hybi-websocket-multiplexing-01
|
||||||
connBackend, resp, err := dialer.Dial(backendURL.String(), requestHeader)
|
connBackend, resp, err := dialer.Dial(backendURL.String(), requestHeader)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("websocketproxy: couldn't dial to remote backend url %s", err)
|
w.Println("Couldn't dial to remote backend url "+backendURL.String(), err)
|
||||||
if resp != nil {
|
if resp != nil {
|
||||||
// If the WebSocket handshake fails, ErrBadHandshake is returned
|
// If the WebSocket handshake fails, ErrBadHandshake is returned
|
||||||
// along with a non-nil *http.Response so that callers can handle
|
// along with a non-nil *http.Response so that callers can handle
|
||||||
// redirects, authentication, etcetera.
|
// redirects, authentication, etcetera.
|
||||||
if err := copyResponse(rw, resp); err != nil {
|
if err := copyResponse(rw, resp); err != nil {
|
||||||
log.Printf("websocketproxy: couldn't write response after failed remote backend handshake: %s", err)
|
w.Println("Couldn't write response after failed remote backend handshake to "+backendURL.String(), err)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
http.Error(rw, http.StatusText(http.StatusServiceUnavailable), http.StatusServiceUnavailable)
|
http.Error(rw, http.StatusText(http.StatusServiceUnavailable), http.StatusServiceUnavailable)
|
||||||
@ -198,7 +210,7 @@ func (w *WebsocketProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
|||||||
// Also pass the header that we gathered from the Dial handshake.
|
// Also pass the header that we gathered from the Dial handshake.
|
||||||
connPub, err := upgrader.Upgrade(rw, req, upgradeHeader)
|
connPub, err := upgrader.Upgrade(rw, req, upgradeHeader)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("websocketproxy: couldn't upgrade %s", err)
|
w.Println("Couldn't upgrade incoming request", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer connPub.Close()
|
defer connPub.Close()
|
||||||
|
@ -31,6 +31,7 @@ func TestProxy(t *testing.T) {
|
|||||||
proxy := NewProxy(u, Options{
|
proxy := NewProxy(u, Options{
|
||||||
SkipTLSValidation: false,
|
SkipTLSValidation: false,
|
||||||
SkipOriginCheck: false,
|
SkipOriginCheck: false,
|
||||||
|
Logger: nil,
|
||||||
})
|
})
|
||||||
proxy.Upgrader = upgrader
|
proxy.Upgrader = upgrader
|
||||||
|
|
||||||
|
@ -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()
|
||||||
|
|
||||||
@ -913,7 +910,6 @@ func ReverseProxyList(w http.ResponseWriter, r *http.Request) {
|
|||||||
results := []*dynamicproxy.ProxyEndpoint{}
|
results := []*dynamicproxy.ProxyEndpoint{}
|
||||||
dynamicProxyRouter.ProxyEndpoints.Range(func(key, value interface{}) bool {
|
dynamicProxyRouter.ProxyEndpoints.Range(func(key, value interface{}) bool {
|
||||||
thisEndpoint := dynamicproxy.CopyEndpoint(value.(*dynamicproxy.ProxyEndpoint))
|
thisEndpoint := dynamicproxy.CopyEndpoint(value.(*dynamicproxy.ProxyEndpoint))
|
||||||
|
|
||||||
//Clear the auth passwords before showing to front-end
|
//Clear the auth passwords before showing to front-end
|
||||||
cleanedCredentials := []*dynamicproxy.BasicAuthCredentials{}
|
cleanedCredentials := []*dynamicproxy.BasicAuthCredentials{}
|
||||||
for _, user := range thisEndpoint.BasicAuthCredentials {
|
for _, user := range thisEndpoint.BasicAuthCredentials {
|
||||||
@ -922,7 +918,6 @@ func ReverseProxyList(w http.ResponseWriter, r *http.Request) {
|
|||||||
PasswordHash: "",
|
PasswordHash: "",
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
thisEndpoint.BasicAuthCredentials = cleanedCredentials
|
thisEndpoint.BasicAuthCredentials = cleanedCredentials
|
||||||
results = append(results, thisEndpoint)
|
results = append(results, thisEndpoint)
|
||||||
return true
|
return true
|
||||||
@ -944,18 +939,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", ¤tEnabled)
|
err := sysdb.Read("settings", "listenP80", ¤tEnabled)
|
||||||
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 +967,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", ¤tRedirectToHttps)
|
err := sysdb.Read("settings", "redirect", ¤tRedirectToHttps)
|
||||||
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 +1098,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 +1247,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")
|
||||||
|
@ -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))
|
||||||
|
}
|
||||||
|
15
src/start.go
@ -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,21 @@ 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,
|
||||||
|
SystemWideLogger,
|
||||||
|
)
|
||||||
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)
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -197,6 +197,8 @@ func ReverseProxyDeleteVdir(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
UpdateUptimeMonitorTargets()
|
||||||
|
|
||||||
utils.SendOK(w)
|
utils.SendOK(w)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -197,7 +197,7 @@
|
|||||||
<div class="item" data-value="lt"><i class="lt flag"></i>Lithuania</div>
|
<div class="item" data-value="lt"><i class="lt flag"></i>Lithuania</div>
|
||||||
<div class="item" data-value="lu"><i class="lu flag"></i>Luxembourg</div>
|
<div class="item" data-value="lu"><i class="lu flag"></i>Luxembourg</div>
|
||||||
<div class="item" data-value="mo"><i class="mo flag"></i>Macau</div>
|
<div class="item" data-value="mo"><i class="mo flag"></i>Macau</div>
|
||||||
<div class="item" data-value="mk"><i class="mk flag"></i>Macedonia</div>
|
<div class="item" data-value="mk"><i class="mk flag"></i>North Macedonia</div>
|
||||||
<div class="item" data-value="mg"><i class="mg flag"></i>Madagascar</div>
|
<div class="item" data-value="mg"><i class="mg flag"></i>Madagascar</div>
|
||||||
<div class="item" data-value="mw"><i class="mw flag"></i>Malawi</div>
|
<div class="item" data-value="mw"><i class="mw flag"></i>Malawi</div>
|
||||||
<div class="item" data-value="my"><i class="my flag"></i>Malaysia</div>
|
<div class="item" data-value="my"><i class="my flag"></i>Malaysia</div>
|
||||||
@ -514,7 +514,7 @@
|
|||||||
<div class="item" data-value="lt"><i class="lt flag"></i>Lithuania</div>
|
<div class="item" data-value="lt"><i class="lt flag"></i>Lithuania</div>
|
||||||
<div class="item" data-value="lu"><i class="lu flag"></i>Luxembourg</div>
|
<div class="item" data-value="lu"><i class="lu flag"></i>Luxembourg</div>
|
||||||
<div class="item" data-value="mo"><i class="mo flag"></i>Macau</div>
|
<div class="item" data-value="mo"><i class="mo flag"></i>Macau</div>
|
||||||
<div class="item" data-value="mk"><i class="mk flag"></i>Macedonia</div>
|
<div class="item" data-value="mk"><i class="mk flag"></i>North Macedonia</div>
|
||||||
<div class="item" data-value="mg"><i class="mg flag"></i>Madagascar</div>
|
<div class="item" data-value="mg"><i class="mg flag"></i>Madagascar</div>
|
||||||
<div class="item" data-value="mw"><i class="mw flag"></i>Malawi</div>
|
<div class="item" data-value="mw"><i class="mw flag"></i>Malawi</div>
|
||||||
<div class="item" data-value="my"><i class="my flag"></i>Malaysia</div>
|
<div class="item" data-value="my"><i class="my flag"></i>Malaysia</div>
|
||||||
@ -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},
|
||||||
@ -1109,7 +1110,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
$("#ipAddressInput").val("");
|
$("#ipAddressInput").val("");
|
||||||
$("#ipAddressInput").parent().remvoeClass("error");
|
$("#ipAddressInput").parent().removeClass("error");
|
||||||
},
|
},
|
||||||
error: function() {
|
error: function() {
|
||||||
alert("Failed to add IP address to blacklist");
|
alert("Failed to add IP address to blacklist");
|
||||||
@ -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},
|
||||||
@ -1243,7 +1244,7 @@
|
|||||||
|
|
||||||
$("#ipAddressInputWhitelist").val("");
|
$("#ipAddressInputWhitelist").val("");
|
||||||
$("#ipAddressCommentsWhitelist").val("");
|
$("#ipAddressCommentsWhitelist").val("");
|
||||||
$("#ipAddressInputWhitelist").parent().remvoeClass("error");
|
$("#ipAddressInputWhitelist").parent().removeClass("error");
|
||||||
},
|
},
|
||||||
error: function() {
|
error: function() {
|
||||||
alert("Failed to add IP address to whitelist");
|
alert("Failed to add IP address to whitelist");
|
||||||
@ -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},
|
||||||
|
@ -59,7 +59,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p>Current list of loaded certificates</p>
|
<p>Current list of loaded certificates</p>
|
||||||
<div>
|
<div tourstep="certTable">
|
||||||
<div style="width: 100%; overflow-x: auto; margin-bottom: 1em;">
|
<div style="width: 100%; overflow-x: auto; margin-bottom: 1em;">
|
||||||
<table class="ui sortable unstackable basic celled table">
|
<table class="ui sortable unstackable basic celled table">
|
||||||
<thead>
|
<thead>
|
||||||
@ -79,6 +79,7 @@
|
|||||||
<button class="ui basic button" onclick="initManagedDomainCertificateList();"><i class="green refresh icon"></i> Refresh List</button>
|
<button class="ui basic button" onclick="initManagedDomainCertificateList();"><i class="green refresh icon"></i> Refresh List</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="ui divider"></div>
|
<div class="ui divider"></div>
|
||||||
|
<div tourstep="defaultCertificate">
|
||||||
<h3>Fallback Certificate</h3>
|
<h3>Fallback Certificate</h3>
|
||||||
<p>When there are no matching certificate for the requested server name, reverse proxy router will always fallback to this one.<br>Note that you need both of them uploaded for it to fallback properly</p>
|
<p>When there are no matching certificate for the requested server name, reverse proxy router will always fallback to this one.<br>Note that you need both of them uploaded for it to fallback properly</p>
|
||||||
<table class="ui very basic unstackable celled table">
|
<table class="ui very basic unstackable celled table">
|
||||||
@ -102,7 +103,9 @@
|
|||||||
<button class="ui basic grey button" onclick="uploadPublicKey();"><i class="globe icon"></i> Public Key</button>
|
<button class="ui basic grey button" onclick="uploadPublicKey();"><i class="globe icon"></i> Public Key</button>
|
||||||
<button class="ui basic black button" onclick="uploadPrivateKey();"><i class="black lock icon"></i> Private Key</button>
|
<button class="ui basic black button" onclick="uploadPrivateKey();"><i class="black lock icon"></i> Private Key</button>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
<div class="ui divider"></div>
|
<div class="ui divider"></div>
|
||||||
|
<div tourstep="acmeSettings">
|
||||||
<h3>Certificate Authority (CA) and Auto Renew (ACME)</h3>
|
<h3>Certificate Authority (CA) and Auto Renew (ACME)</h3>
|
||||||
<p>Management features regarding CA and ACME</p>
|
<p>Management features regarding CA and ACME</p>
|
||||||
<h4>Prefered Certificate Authority</h4>
|
<h4>Prefered Certificate Authority</h4>
|
||||||
@ -138,7 +141,8 @@
|
|||||||
</h4>
|
</h4>
|
||||||
</div>
|
</div>
|
||||||
<p>This tool provide you a graphical interface to setup auto certificate renew on your (sub)domains. You can also manually generate a certificate if one of your domain do not have certificate.</p>
|
<p>This tool provide you a graphical interface to setup auto certificate renew on your (sub)domains. You can also manually generate a certificate if one of your domain do not have certificate.</p>
|
||||||
<button class="ui basic button" onclick="openACMEManager();"><i class="yellow external icon"></i> Open ACME Tool</button>
|
<button class="ui basic button" tourstep="openACMEManager" onclick="openACMEManager();"><i class="yellow external icon"></i> Open ACME Tool</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<script>
|
<script>
|
||||||
var uploadPendingPublicKey = undefined;
|
var uploadPendingPublicKey = undefined;
|
||||||
@ -161,6 +165,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 +261,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 +320,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 +334,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 +361,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 +405,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 +427,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 +436,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 +453,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 +474,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,6 +501,7 @@
|
|||||||
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]);
|
||||||
@ -487,7 +509,10 @@
|
|||||||
// 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 +539,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 +553,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) {
|
||||||
|
@ -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",
|
||||||
|
@ -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: {
|
||||||
|
@ -348,6 +348,20 @@
|
|||||||
`);
|
`);
|
||||||
}else if (datatype == "inbound"){
|
}else if (datatype == "inbound"){
|
||||||
let originalContent = $(column).html();
|
let originalContent = $(column).html();
|
||||||
|
|
||||||
|
//Check if this host is covered within one of the certificates. If not, show the icon
|
||||||
|
let domainIsCovered = true;
|
||||||
|
let domains = [payload.RootOrMatchingDomain]; //Domain for getting certificate if needed
|
||||||
|
for (var i = 0; i < payload.MatchingDomainAlias.length; i++){
|
||||||
|
let thisAliasName = payload.MatchingDomainAlias[i];
|
||||||
|
domains.push(thisAliasName);
|
||||||
|
}
|
||||||
|
if (true){
|
||||||
|
domainIsCovered = false;
|
||||||
|
}
|
||||||
|
//encode the domain to DOM
|
||||||
|
let certificateDomains = encodeURIComponent(JSON.stringify(domains));
|
||||||
|
|
||||||
column.empty().append(`${originalContent}
|
column.empty().append(`${originalContent}
|
||||||
<div class="ui divider"></div>
|
<div class="ui divider"></div>
|
||||||
<div class="ui checkbox" style="margin-top: 0.4em;">
|
<div class="ui checkbox" style="margin-top: 0.4em;">
|
||||||
@ -357,9 +371,10 @@
|
|||||||
</div><br>
|
</div><br>
|
||||||
<button class="ui basic compact tiny button" style="margin-left: 0.4em; margin-top: 0.4em;" onclick="editAliasHostnames('${uuid}');"><i class=" blue at icon"></i> Alias</button>
|
<button class="ui basic compact tiny button" style="margin-left: 0.4em; margin-top: 0.4em;" onclick="editAliasHostnames('${uuid}');"><i class=" blue at icon"></i> Alias</button>
|
||||||
<button class="ui basic compact tiny button" style="margin-left: 0.4em; margin-top: 0.4em;" onclick="editAccessRule('${uuid}');"><i class="ui filter icon"></i> Access Rule</button>
|
<button class="ui basic compact tiny button" style="margin-left: 0.4em; margin-top: 0.4em;" onclick="editAccessRule('${uuid}');"><i class="ui filter icon"></i> Access Rule</button>
|
||||||
|
<button class="ui basic compact tiny ${domainIsCovered?"disabled":""} button" style="margin-left: 0.4em; margin-top: 0.4em;" onclick="requestCertificateForExistingHost('${uuid}', '${certificateDomains}');"><i class="green lock icon"></i> Get Certificate</button>
|
||||||
`);
|
`);
|
||||||
|
|
||||||
|
|
||||||
$(".hostAccessRuleSelector").dropdown();
|
$(".hostAccessRuleSelector").dropdown();
|
||||||
}else{
|
}else{
|
||||||
//Unknown field. Leave it untouched
|
//Unknown field. Leave it untouched
|
||||||
@ -400,7 +415,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: {
|
||||||
@ -423,6 +438,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){
|
||||||
let payload = encodeURIComponent(JSON.stringify({
|
let payload = encodeURIComponent(JSON.stringify({
|
||||||
@ -474,7 +511,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,
|
||||||
@ -495,6 +532,15 @@
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Certificate Shortcut
|
||||||
|
*/
|
||||||
|
|
||||||
|
function requestCertificateForExistingHost(hostUUID, RootAndAliasDomains){
|
||||||
|
RootAndAliasDomains = JSON.parse(decodeURIComponent(RootAndAliasDomains))
|
||||||
|
alert(RootAndAliasDomains.join(", "))
|
||||||
|
}
|
||||||
|
|
||||||
//Bind on tab switch events
|
//Bind on tab switch events
|
||||||
tabSwitchEventBind["httprp"] = function(){
|
tabSwitchEventBind["httprp"] = function(){
|
||||||
listProxyEndpoints();
|
listProxyEndpoints();
|
||||||
|
@ -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: {
|
||||||
|
77
src/web/components/quickstart.html
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
|
||||||
|
<div id="quickstart" class="standardContainer">
|
||||||
|
<div class="ui container">
|
||||||
|
<h1 class="ui header">
|
||||||
|
<img src="img/res/1F44B.png">
|
||||||
|
<div class="content" style="font-weight: lighter;">
|
||||||
|
Welcome to Zoraxy!
|
||||||
|
<div class="sub header">What services are you planning to setup today?</div>
|
||||||
|
</div>
|
||||||
|
</h1>
|
||||||
|
<br>
|
||||||
|
<div class="ui stackable equal width grid">
|
||||||
|
<div class="column">
|
||||||
|
<div class="serviceOption homepage" name="homepage">
|
||||||
|
<div class="titleWrapper">
|
||||||
|
<p>Basic Homepage</p>
|
||||||
|
</div>
|
||||||
|
<div class="ui divider"></div>
|
||||||
|
<p>Host a static homepage with Zoraxy and point your domain name to your web server.</p>
|
||||||
|
<img class="themebackground ui small image" src="img/res/1F310.png">
|
||||||
|
<div class="activeOption">
|
||||||
|
<i class="ui white huge circle check icon"></i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="column">
|
||||||
|
<div class="serviceOption subdomain" name="subdomain">
|
||||||
|
<div class="titleWrapper">
|
||||||
|
<p>Sub-domains Routing</p>
|
||||||
|
</div>
|
||||||
|
<div class="ui divider"></div>
|
||||||
|
<p>Add and handle traffic from your subdomains and point them to a dedicated web services somewhere else.</p>
|
||||||
|
<img class="themebackground ui small image" src="img/res/1F500.png">
|
||||||
|
<div class="activeOption">
|
||||||
|
<i class="ui white huge circle check icon"></i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="column">
|
||||||
|
<div class="serviceOption tls" name="tls">
|
||||||
|
<div class="titleWrapper">
|
||||||
|
<p>HTTPS Green Lock(s)</p>
|
||||||
|
</div>
|
||||||
|
<div class="ui divider"></div>
|
||||||
|
<p>Turn your unsafe HTTP website into HTTPS using free certificate from public certificate authorities organizations.</p>
|
||||||
|
<img class="themebackground ui small image" src="img/res/1F512.png">
|
||||||
|
<div class="activeOption">
|
||||||
|
<i class="ui white huge circle check icon"></i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="ui divider"></div>
|
||||||
|
<div style="width: 100%;" align="center">
|
||||||
|
<button onclick="startQuickStartTour();" class="ui finished button quickstartControlButton">
|
||||||
|
Start Walkthrough
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<script>
|
||||||
|
var currentQuickSetupClass = "";
|
||||||
|
var currentQuickSetupTourStep = 0;
|
||||||
|
//For tour logic, see quicksetup.js
|
||||||
|
|
||||||
|
|
||||||
|
//Bind selecting events to serviceOption
|
||||||
|
$("#quickstart .serviceOption").on("click", function(data){
|
||||||
|
$(".serviceOption.active").removeClass("active");
|
||||||
|
$(this).addClass("active");
|
||||||
|
let tourType = $(this).attr("name");
|
||||||
|
currentQuickSetupClass = tourType;
|
||||||
|
});
|
||||||
|
|
||||||
|
</script>
|
||||||
|
<script src="script/quicksetup.js"></script>
|
@ -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){
|
||||||
|
@ -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",
|
||||||
|
@ -30,12 +30,12 @@
|
|||||||
<h2>New Proxy Rule</h2>
|
<h2>New Proxy Rule</h2>
|
||||||
<p>You can add more proxy rules to support more site via domain / subdomains</p>
|
<p>You can add more proxy rules to support more site via domain / subdomains</p>
|
||||||
<div class="ui form">
|
<div class="ui form">
|
||||||
<div class="field">
|
<div class="field" tourstep="matchingkeyword">
|
||||||
<label>Matching Keyword / Domain</label>
|
<label>Matching Keyword / Domain</label>
|
||||||
<input type="text" id="rootname" placeholder="mydomain.com">
|
<input type="text" id="rootname" placeholder="mydomain.com">
|
||||||
<small>Support subdomain and wildcard, e.g. s1.mydomain.com or *.test.mydomain.com. Use comma (,) for alias hostnames. </small>
|
<small>Support subdomain and wildcard, e.g. s1.mydomain.com or *.test.mydomain.com. Use comma (,) for alias hostnames. </small>
|
||||||
</div>
|
</div>
|
||||||
<div class="field">
|
<div class="field" tourstep="targetdomain">
|
||||||
<label>Target IP Address or Domain Name with port</label>
|
<label>Target IP Address or Domain Name with port</label>
|
||||||
<input type="text" id="proxyDomain" onchange="autoFillTargetTLS(this);">
|
<input type="text" id="proxyDomain" onchange="autoFillTargetTLS(this);">
|
||||||
<small>e.g. 192.168.0.101:8000 or example.com</small>
|
<small>e.g. 192.168.0.101:8000 or example.com</small>
|
||||||
@ -43,7 +43,7 @@
|
|||||||
<div class="field dockerOptimizations" style="display:none;">
|
<div class="field dockerOptimizations" style="display:none;">
|
||||||
<button style="margin-top: -2em;" class="ui basic small button" onclick="openDockerContainersList();"><i class="blue docker icon"></i> Pick from Docker Containers</button>
|
<button style="margin-top: -2em;" class="ui basic small button" onclick="openDockerContainersList();"><i class="blue docker icon"></i> Pick from Docker Containers</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="field">
|
<div class="field" tourstep="requireTLS">
|
||||||
<div class="ui checkbox">
|
<div class="ui checkbox">
|
||||||
<input type="checkbox" id="reqTls">
|
<input type="checkbox" id="reqTls">
|
||||||
<label>Proxy Target require TLS Connection <br><small>(i.e. Your proxy target starts with https://)</small></label>
|
<label>Proxy Target require TLS Connection <br><small>(i.e. Your proxy target starts with https://)</small></label>
|
||||||
@ -67,7 +67,7 @@
|
|||||||
<i class="ui green lock icon"></i>
|
<i class="ui green lock icon"></i>
|
||||||
Security
|
Security
|
||||||
</div>
|
</div>
|
||||||
<div class="field">
|
<div class="field" tourstep="skipTLSValidation">
|
||||||
<div class="ui checkbox">
|
<div class="ui checkbox">
|
||||||
<input type="checkbox" id="skipTLSValidation">
|
<input type="checkbox" id="skipTLSValidation">
|
||||||
<label>Ignore TLS/SSL Verification Error<br><small>For targets that is using self-signed, expired certificate (Not Recommended)</small></label>
|
<label>Ignore TLS/SSL Verification Error<br><small>For targets that is using self-signed, expired certificate (Not Recommended)</small></label>
|
||||||
@ -154,7 +154,9 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<br>
|
<br>
|
||||||
|
<div tourstep="newProxyRule" style="display: inline-block;">
|
||||||
<button class="ui basic button" onclick="newProxyEndpoint();"><i class="green add icon"></i> Create Endpoint</button>
|
<button class="ui basic button" onclick="newProxyEndpoint();"><i class="green add icon"></i> Create Endpoint</button>
|
||||||
|
</div>
|
||||||
<br><br>
|
<br><br>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -212,8 +214,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 +273,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 +294,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"){
|
||||||
|
@ -53,8 +53,10 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="standardContainer" style="padding-bottom: 0 !important;">
|
<div class="standardContainer" style="padding-bottom: 0 !important;">
|
||||||
<!-- Power Buttons-->
|
<!-- Power Buttons-->
|
||||||
|
<div class="poweroptions" style="display:inline-block;">
|
||||||
<button id="startbtn" class="ui basic button" onclick="startService();"><i class="ui green arrow alternate circle up icon"></i> Start Service</button>
|
<button id="startbtn" class="ui basic button" onclick="startService();"><i class="ui green arrow alternate circle up icon"></i> Start Service</button>
|
||||||
<button id="stopbtn" class="ui basic notloopbackOnly disabled button" onclick="stopService();"><i class="ui red minus circle icon"></i> Stop Service</button>
|
<button id="stopbtn" class="ui basic notloopbackOnly disabled button" onclick="stopService();"><i class="ui red minus circle icon"></i> Stop Service</button>
|
||||||
|
</div>
|
||||||
<div class="ui divider"></div>
|
<div class="ui divider"></div>
|
||||||
<h4>Network Status</h4>
|
<h4>Network Status</h4>
|
||||||
<p>Overall Network I/O in Current Host Server</p>
|
<p>Overall Network I/O in Current Host Server</p>
|
||||||
@ -69,7 +71,7 @@
|
|||||||
<div class="ui divider"></div>
|
<div class="ui divider"></div>
|
||||||
<h4>Global Settings</h4>
|
<h4>Global Settings</h4>
|
||||||
<p>Inbound Port (Reverse Proxy Listening Port)</p>
|
<p>Inbound Port (Reverse Proxy Listening Port)</p>
|
||||||
<div class="ui action fluid notloopbackOnly input">
|
<div class="ui action fluid notloopbackOnly input" tourstep="incomingPort">
|
||||||
<small id="applyButtonReminder">Click "Apply" button to confirm listening port changes</small>
|
<small id="applyButtonReminder">Click "Apply" button to confirm listening port changes</small>
|
||||||
<input type="text" id="incomingPort" placeholder="Incoming Port" value="80">
|
<input type="text" id="incomingPort" placeholder="Incoming Port" value="80">
|
||||||
<button class="ui green notloopbackOnly button" style="background: linear-gradient(60deg, #27e7ff, #00ca52);" onclick="handlePortChange();"><i class="ui checkmark icon"></i> Apply</button>
|
<button class="ui green notloopbackOnly button" style="background: linear-gradient(60deg, #27e7ff, #00ca52);" onclick="handlePortChange();"><i class="ui checkmark icon"></i> Apply</button>
|
||||||
@ -86,10 +88,12 @@
|
|||||||
<small>(Only apply when TLS enabled and not using port 80)</small></label>
|
<small>(Only apply when TLS enabled and not using port 80)</small></label>
|
||||||
</div>
|
</div>
|
||||||
<br>
|
<br>
|
||||||
|
<div tourstep="forceHttpsRedirect" style="display: inline-block;">
|
||||||
<div id="redirect" class="ui toggle notloopbackOnly tlsEnabledOnly checkbox" style="margin-top: 0.6em; padding-left: 2em;">
|
<div id="redirect" class="ui toggle notloopbackOnly tlsEnabledOnly checkbox" style="margin-top: 0.6em; padding-left: 2em;">
|
||||||
<input type="checkbox">
|
<input type="checkbox">
|
||||||
<label>Force redirect HTTP request to HTTPS</label>
|
<label>Force redirect HTTP request to HTTPS</label>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
<div class="ui basic segment" style="background-color: #f7f7f7; border-radius: 1em;">
|
<div class="ui basic segment" style="background-color: #f7f7f7; border-radius: 1em;">
|
||||||
<div class="ui accordion advanceSettings">
|
<div class="ui accordion advanceSettings">
|
||||||
<div class="title">
|
<div class="title">
|
||||||
@ -315,26 +319,39 @@
|
|||||||
|
|
||||||
//Start and stop service button
|
//Start and stop service button
|
||||||
function startService(){
|
function startService(){
|
||||||
$.post("/api/proxy/enable", {enable: true}, function(data){
|
$.cjax({
|
||||||
|
url: "/api/proxy/enable",
|
||||||
|
method: "POST",
|
||||||
|
data: {enable: true},
|
||||||
|
success: function(data){
|
||||||
if (data.error != undefined){
|
if (data.error != undefined){
|
||||||
msgbox(data.error, false, 5000);
|
msgbox(data.error, false, 5000);
|
||||||
}
|
}
|
||||||
initRPStaste();
|
initRPStaste();
|
||||||
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function stopService(){
|
function stopService(){
|
||||||
$.post("/api/proxy/enable", {enable: false}, function(data){
|
$.cjax({
|
||||||
|
url: "/api/proxy/enable",
|
||||||
|
method: "POST",
|
||||||
|
data: {enable: false},
|
||||||
|
success: function(data){
|
||||||
if (data.error != undefined){
|
if (data.error != undefined){
|
||||||
msgbox(data.error, false, 5000);
|
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,7 +378,11 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$.post("/api/proxy/setIncoming", {incoming: newPortValue}, function(data){
|
$.cjax({
|
||||||
|
url: "/api/proxy/setIncoming",
|
||||||
|
method: "POST",
|
||||||
|
data: {incoming: newPortValue},
|
||||||
|
success: function(data){
|
||||||
if (data.error != undefined){
|
if (data.error != undefined){
|
||||||
msgbox(data.error, false, 5000);
|
msgbox(data.error, false, 5000);
|
||||||
return;
|
return;
|
||||||
@ -371,6 +392,7 @@
|
|||||||
|
|
||||||
//Hide the reminder text
|
//Hide the reminder text
|
||||||
$("#applyButtonReminder").hide();
|
$("#applyButtonReminder").hide();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -402,8 +424,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 +463,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 +522,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){
|
||||||
@ -563,14 +587,14 @@
|
|||||||
url: '/api/stats/netstatgraph?array=true',
|
url: '/api/stats/netstatgraph?array=true',
|
||||||
success: function(data){
|
success: function(data){
|
||||||
if (rxValues.length == 0){
|
if (rxValues.length == 0){
|
||||||
rxValues = JSON.parse(JSON.stringify(data.Rx));
|
rxValues.push(...data.Rx);
|
||||||
}else{
|
}else{
|
||||||
rxValues.push(data.Rx[dataCount-1]);
|
rxValues.push(data.Rx[dataCount-1]);
|
||||||
rxValues.shift();
|
rxValues.shift();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (txValues.length == 0){
|
if (txValues.length == 0){
|
||||||
txValues = JSON.parse(JSON.stringify(data.Tx));
|
txValues.push(...data.Tx);
|
||||||
}else{
|
}else{
|
||||||
txValues.push(data.Tx[dataCount-1]);
|
txValues.push(data.Tx[dataCount-1]);
|
||||||
txValues.shift();
|
txValues.shift();
|
||||||
|
@ -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},
|
||||||
|
@ -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,
|
||||||
|
@ -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: {
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</h4>
|
</h4>
|
||||||
</div>
|
</div>
|
||||||
|
<div>
|
||||||
<h3>Web Server Settings</h3>
|
<h3>Web Server Settings</h3>
|
||||||
<div class="ui form">
|
<div class="ui form">
|
||||||
<div class="inline field">
|
<div class="inline field">
|
||||||
@ -43,6 +43,7 @@
|
|||||||
<small>Use <code>http://127.0.0.1:<span class="webserv_port">8081</span></code> in proxy rules to access the web server</small>
|
<small>Use <code>http://127.0.0.1:<span class="webserv_port">8081</span></code> in proxy rules to access the web server</small>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
<small><i class="ui blue save icon"></i> Changes are saved automatically</small>
|
<small><i class="ui blue save icon"></i> Changes are saved automatically</small>
|
||||||
<br>
|
<br>
|
||||||
<div class="ui message">
|
<div class="ui message">
|
||||||
@ -164,7 +165,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 +187,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 +207,7 @@
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}else{
|
}else{
|
||||||
$.ajax({
|
$.cjax({
|
||||||
url: "/api/webserv/setPort",
|
url: "/api/webserv/setPort",
|
||||||
method: "POST",
|
method: "POST",
|
||||||
data: {"port": newPort},
|
data: {"port": newPort},
|
||||||
|
BIN
src/web/img/res/1F310.png
Normal file
After Width: | Height: | Size: 14 KiB |
BIN
src/web/img/res/1F387.png
Normal file
After Width: | Height: | Size: 10 KiB |
BIN
src/web/img/res/1F38A.png
Normal file
After Width: | Height: | Size: 11 KiB |
BIN
src/web/img/res/1F44B.png
Normal file
After Width: | Height: | Size: 14 KiB |
BIN
src/web/img/res/1F500.png
Normal file
After Width: | Height: | Size: 5.9 KiB |
BIN
src/web/img/res/1F512.png
Normal file
After Width: | Height: | Size: 15 KiB |
BIN
src/web/img/res/1F914.png
Normal file
After Width: | Height: | Size: 12 KiB |
BIN
src/web/img/res/2728.png
Normal file
After Width: | Height: | Size: 12 KiB |
BIN
src/web/img/res/2753.png
Normal file
After Width: | Height: | Size: 8.1 KiB |
BIN
src/web/img/res/E25E.png
Normal file
After Width: | Height: | Size: 4.4 KiB |
@ -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">
|
||||||
@ -35,6 +36,9 @@
|
|||||||
<div class="wrapper">
|
<div class="wrapper">
|
||||||
<div class="toolbar">
|
<div class="toolbar">
|
||||||
<div id="mainmenu" class="ui secondary vertical menu">
|
<div id="mainmenu" class="ui secondary vertical menu">
|
||||||
|
<a class="item" tag="qstart">
|
||||||
|
<i class="simplistic magic icon"></i>Quick Start
|
||||||
|
</a>
|
||||||
<a class="item active" tag="status">
|
<a class="item active" tag="status">
|
||||||
<i class="simplistic info circle icon"></i>Status
|
<i class="simplistic info circle icon"></i>Status
|
||||||
</a>
|
</a>
|
||||||
@ -91,6 +95,9 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="contentWindow">
|
<div class="contentWindow">
|
||||||
|
<!-- Quick Start -->
|
||||||
|
<div id="qstart" class="functiontab" target="quickstart.html"></div>
|
||||||
|
|
||||||
<!-- Status Tab -->
|
<!-- Status Tab -->
|
||||||
<div id="status" class="functiontab" target="status.html" style="display: block ;">
|
<div id="status" class="functiontab" target="status.html" style="display: block ;">
|
||||||
<br><br><div class="ui active centered inline loader"></div>
|
<br><br><div class="ui active centered inline loader"></div>
|
||||||
@ -170,6 +177,22 @@
|
|||||||
<div class="questionToConfirm">Confirm Exit?</div>
|
<div class="questionToConfirm">Confirm Exit?</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div id="tourModal" class="nofocus" position="center">
|
||||||
|
<h4 class="tourStepTitle">Welcome to Zoraxy Tour</h4>
|
||||||
|
<p class="tourStepContent">This is a simplified tour to show some of what it can do.
|
||||||
|
Use your keyboard or click the next button to get going.</p>
|
||||||
|
<div class="ui divider"></div>
|
||||||
|
<div class="ui equal width grid" align="center">
|
||||||
|
<div class="column"><button onclick="previousTourStep();" class="ui basic small disabled button tourStepButtonBack">Back</button></div>
|
||||||
|
<div class="column"><p style="margin-top: 0.4em">Steps <span class="tourStepCounter">1 / 9</span></p></div>
|
||||||
|
<div class="column nextStepAvaible"><button onclick="nextTourStep();" class="ui basic right floated small button tourStepButtonNext">Next</button></div>
|
||||||
|
<div class="column nextStepFinish"><button onclick="endTourFocus();" class="ui right floated small button tourStepButtonFinish">Finish</button></div>
|
||||||
|
</div>
|
||||||
|
<button onclick="endTourFocus();" class="ui circular small icon button tourCloseButton"><i class="ui times icon"></i></button>
|
||||||
|
</div>
|
||||||
|
<div id="tourModalOverlay" style="display:none;"></div>
|
||||||
|
|
||||||
<br><br>
|
<br><br>
|
||||||
<script>
|
<script>
|
||||||
$(".year").text(new Date().getFullYear());
|
$(".year").text(new Date().getFullYear());
|
||||||
|
@ -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,12 +301,24 @@
|
|||||||
|
|
||||||
//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({
|
||||||
|
url: loginAddress,
|
||||||
|
type: "POST",
|
||||||
|
beforeSend: function(request) {
|
||||||
|
request.setRequestHeader("X-CSRF-Token",csrfToken);
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
"username": username,
|
||||||
|
"password": magic,
|
||||||
|
"rmbme": rmbme,
|
||||||
|
},
|
||||||
|
success: function(data){
|
||||||
if (data.error !== undefined){
|
if (data.error !== undefined){
|
||||||
//Something went wrong during the login
|
//Something went wrong during the login
|
||||||
$("#errmsg").html(`<i class="red remove icon"></i> ${data.error}`);
|
$("#errmsg").html(`<i class="red remove icon"></i> ${data.error}`);
|
||||||
@ -320,6 +336,10 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
$("input").removeClass('disabled');
|
$("input").removeClass('disabled');
|
||||||
|
},
|
||||||
|
error: function(){
|
||||||
|
alert("Something went wrong.")
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
162
src/web/main.css
@ -46,7 +46,7 @@ body.darkTheme{
|
|||||||
--button_border_color: #646464;
|
--button_border_color: #646464;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Theme Toggle Css */
|
/* Theme Toggle CSS */
|
||||||
#themeColorButton{
|
#themeColorButton{
|
||||||
background-color: black;
|
background-color: black;
|
||||||
color: var(--text_color_inverted);
|
color: var(--text_color_inverted);
|
||||||
@ -85,7 +85,6 @@ body{
|
|||||||
top: 0;
|
top: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
z-index: 10;
|
z-index: 10;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.menubar .logo{
|
.menubar .logo{
|
||||||
@ -154,7 +153,7 @@ body{
|
|||||||
right: 1em;
|
right: 1em;
|
||||||
display:none;
|
display:none;
|
||||||
max-width: 300px;
|
max-width: 300px;
|
||||||
z-index: 999;
|
z-index: 1000;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Confirm Box */
|
/* Confirm Box */
|
||||||
@ -519,6 +518,14 @@ body{
|
|||||||
display:none;
|
display:none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Default Site
|
||||||
|
*/
|
||||||
|
|
||||||
|
#setroot{
|
||||||
|
border-radius: 0.6em;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
HTTP Proxy & Virtual Directory
|
HTTP Proxy & Virtual Directory
|
||||||
*/
|
*/
|
||||||
@ -711,3 +718,152 @@ body{
|
|||||||
#traceroute_results::selection {
|
#traceroute_results::selection {
|
||||||
background: #a9d1f3;
|
background: #a9d1f3;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Quick Start Overview
|
||||||
|
*/
|
||||||
|
|
||||||
|
#quickstart .serviceOption{
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
padding: 1em;
|
||||||
|
background-color: rgb(240, 240, 240);
|
||||||
|
border-radius: 0.6em;
|
||||||
|
cursor: pointer;
|
||||||
|
min-height: 250px;
|
||||||
|
transition: opacity 0.1s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#quickstart .serviceOption .activeOption{
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0.2em;
|
||||||
|
left: 0.2em;
|
||||||
|
display:none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#quickstart .serviceOption.active .activeOption{
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#quickstart .serviceOption .titleWrapper{
|
||||||
|
text-align: center;
|
||||||
|
width: 100%;
|
||||||
|
font-weight: bolder;
|
||||||
|
font-size: 1.3em;
|
||||||
|
}
|
||||||
|
|
||||||
|
#quickstart .serviceOption :not(.titleWrapper){
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
#quickstart .serviceOption .themebackground{
|
||||||
|
opacity: 0.2;
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
margin-right: -1em;
|
||||||
|
margin-bottom: -2em;
|
||||||
|
pointer-events: none;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#quickstart .serviceOption:not(.active):hover{
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
#quickstart .serviceOption.tls{
|
||||||
|
background: var(--theme_green);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
#quickstart .serviceOption.subdomain{
|
||||||
|
background: var(--theme_background);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
#quickstart .serviceOption.homepage{
|
||||||
|
background: var(--theme_background_inverted);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#quickstart .finished.ui.button{
|
||||||
|
background: var(--theme_green);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
#tourModal{
|
||||||
|
background-color: white;
|
||||||
|
border-radius: 0.6em;
|
||||||
|
padding: 1.4em;
|
||||||
|
position: fixed;
|
||||||
|
z-index: 999;
|
||||||
|
width: 380px;
|
||||||
|
display:none;
|
||||||
|
border: 1px solid rgb(230, 230, 230);
|
||||||
|
box-shadow: 3px 3px 11px -3px rgba(0,0,0,0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Locations of tourModal */
|
||||||
|
#tourModal[position="center"]{
|
||||||
|
top: 200px;
|
||||||
|
left: calc(50% - 190px);
|
||||||
|
}
|
||||||
|
|
||||||
|
#tourModal[position="topleft"]{
|
||||||
|
top: 4em;
|
||||||
|
left: 4em;
|
||||||
|
}
|
||||||
|
|
||||||
|
#tourModal[position="topright"]{
|
||||||
|
top: 4em;
|
||||||
|
right: 4em;
|
||||||
|
}
|
||||||
|
|
||||||
|
#tourModal[position="bottomleft"]{
|
||||||
|
bottom: 4em;
|
||||||
|
left: 4em;
|
||||||
|
}
|
||||||
|
|
||||||
|
#tourModal[position="bottomright"]{
|
||||||
|
bottom: 4em;
|
||||||
|
right: 4em;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#tourModal .tourStepButtonFinish{
|
||||||
|
background: var(--theme_green) !important;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#tourModal .tourCloseButton{
|
||||||
|
position: absolute;
|
||||||
|
top: 0em;
|
||||||
|
right: 0em;
|
||||||
|
margin-top: -0.6em;
|
||||||
|
margin-right: -0.6em;
|
||||||
|
}
|
||||||
|
|
||||||
|
#tourModal .nextStepFinish{
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#tourModal.nofocus{
|
||||||
|
box-shadow: 0 0 0 max(100vh, 100vw) rgba(0, 0, 0, .3);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#tourModalOverlay{
|
||||||
|
position: fixed;
|
||||||
|
top: 10em;
|
||||||
|
left: 10em;
|
||||||
|
width: 300px;
|
||||||
|
height: 200px;
|
||||||
|
pointer-events: none;
|
||||||
|
z-index: 199;
|
||||||
|
border-radius: 0.6em;
|
||||||
|
box-shadow: 0 0 0 max(100vh, 100vw) rgba(0, 0, 0, .3);
|
||||||
|
}
|
@ -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,8 +256,19 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 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({
|
||||||
|
url: "/api/account/new",
|
||||||
|
method: "POST",
|
||||||
|
data: {
|
||||||
|
username: username,
|
||||||
|
token: token,
|
||||||
|
newpw: newPassword
|
||||||
|
},
|
||||||
|
headers: {
|
||||||
|
"X-CSRF-Token": csrfToken,
|
||||||
|
},
|
||||||
|
success: function(data){
|
||||||
// Handle successful response
|
// Handle successful response
|
||||||
if (data.error != undefined){
|
if (data.error != undefined){
|
||||||
$("#errmsg").html(`<i class="red circle times icon"></i> ` + data.error);
|
$("#errmsg").html(`<i class="red circle times icon"></i> ` + data.error);
|
||||||
@ -269,11 +281,11 @@
|
|||||||
window.location.href = "/";
|
window.location.href = "/";
|
||||||
}, 3000);
|
}, 3000);
|
||||||
}
|
}
|
||||||
})
|
},
|
||||||
.fail(function(error) {
|
error: function(){
|
||||||
// Handle error response
|
|
||||||
console.error(error);
|
console.error(error);
|
||||||
});
|
}
|
||||||
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
506
src/web/script/quicksetup.js
Normal file
@ -0,0 +1,506 @@
|
|||||||
|
/*
|
||||||
|
Quick Setup Tour
|
||||||
|
|
||||||
|
This script file contains all the required script
|
||||||
|
for quick setup tour and walkthrough
|
||||||
|
*/
|
||||||
|
|
||||||
|
//tourStepFactory generate a function that renders the steps in tourModal
|
||||||
|
//Keys: {element, title, desc, tab, pos, scrollto, callback}
|
||||||
|
// elements -> Element (selector) to focus on
|
||||||
|
// tab -> Tab ID to switch pages
|
||||||
|
// pos -> Where to display the tour modal, {topleft, topright, bottomleft, bottomright, center}
|
||||||
|
// scrollto -> Element (selector) to scroll to, can be different from elements
|
||||||
|
// ignoreVisiableCheck -> Force highlight even if element is currently not visable
|
||||||
|
function adjustTourModalOverlayToElement(element){;
|
||||||
|
if ($(element) == undefined || $(element).offset() == undefined){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let padding = 12;
|
||||||
|
$("#tourModalOverlay").css({
|
||||||
|
"top": $(element).offset().top - padding - $(document).scrollTop(),
|
||||||
|
"left": $(element).offset().left - padding,
|
||||||
|
"width": $(element).width() + 2 * padding,
|
||||||
|
"height": $(element).height() + 2 * padding,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
var tourOverlayUpdateTicker;
|
||||||
|
|
||||||
|
function tourStepFactory(config){
|
||||||
|
return function(){
|
||||||
|
//Check if this step require tab swap
|
||||||
|
if (config.tab != undefined && config.tab != ""){
|
||||||
|
//This tour require tab swap. call to openTabById
|
||||||
|
openTabById(config.tab);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.ignoreVisiableCheck == undefined){
|
||||||
|
config.ignoreVisiableCheck = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.element == undefined || (!$(config.element).is(":visible") && !config.ignoreVisiableCheck)){
|
||||||
|
//No focused element in this step.
|
||||||
|
$(".tourFocusObject").removeClass("tourFocusObject");
|
||||||
|
$("#tourModal").addClass("nofocus");
|
||||||
|
$("#tourModalOverlay").hide();
|
||||||
|
|
||||||
|
//If there is a target element to scroll to
|
||||||
|
if (config.scrollto != undefined){
|
||||||
|
$('html, body').animate({
|
||||||
|
scrollTop: $(config.scrollto).offset().top - 100
|
||||||
|
}, 500);
|
||||||
|
}
|
||||||
|
|
||||||
|
}else{
|
||||||
|
|
||||||
|
let elementHighligher = function(){
|
||||||
|
//Match the overlay to element position and size
|
||||||
|
$(window).off("resize").on("resize", function(){
|
||||||
|
adjustTourModalOverlayToElement(config.element);
|
||||||
|
});
|
||||||
|
if (tourOverlayUpdateTicker != undefined){
|
||||||
|
clearInterval(tourOverlayUpdateTicker);
|
||||||
|
}
|
||||||
|
tourOverlayUpdateTicker = setInterval(function(){
|
||||||
|
adjustTourModalOverlayToElement(config.element);
|
||||||
|
}, 500);
|
||||||
|
adjustTourModalOverlayToElement(config.element);
|
||||||
|
$("#tourModalOverlay").fadeIn();
|
||||||
|
}
|
||||||
|
|
||||||
|
//Consists of focus element in this step
|
||||||
|
$(".tourFocusObject").removeClass("tourFocusObject");
|
||||||
|
$(config.element).addClass("tourFocusObject");
|
||||||
|
$("#tourModal").removeClass("nofocus");
|
||||||
|
$("#tourModalOverlay").hide();
|
||||||
|
//If there is a target element to scroll to
|
||||||
|
if (config.scrollto != undefined){
|
||||||
|
$('html, body').animate({
|
||||||
|
scrollTop: $(config.scrollto).offset().top - 100
|
||||||
|
}, 300, function(){
|
||||||
|
setTimeout(elementHighligher, 300);
|
||||||
|
});
|
||||||
|
}else{
|
||||||
|
setTimeout(elementHighligher, 300);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Get the modal location of this step
|
||||||
|
let showupZone = "center";
|
||||||
|
if (config.pos != undefined){
|
||||||
|
showupZone = config.pos
|
||||||
|
}
|
||||||
|
|
||||||
|
$("#tourModal").attr("position", showupZone);
|
||||||
|
|
||||||
|
$("#tourModal .tourStepTitle").html(config.title);
|
||||||
|
$("#tourModal .tourStepContent").html(config.desc);
|
||||||
|
if (config.callback != undefined){
|
||||||
|
config.callback();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Hide the side warpper in tour mode and prevent body from restoring to
|
||||||
|
//overflow scroll mode
|
||||||
|
function hideSideWrapperInTourMode(){
|
||||||
|
hideSideWrapper(); //Call to index.html hide side wrapper function
|
||||||
|
$("body").css("overflow", "hidden"); //Restore overflow state
|
||||||
|
}
|
||||||
|
|
||||||
|
function startQuickStartTour(){
|
||||||
|
if (currentQuickSetupClass == ""){
|
||||||
|
msgbox("No selected setup service tour", false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
//Show the tour modal
|
||||||
|
$("#tourModal").show();
|
||||||
|
//Load the tour steps
|
||||||
|
if (tourSteps[currentQuickSetupClass] == undefined || tourSteps[currentQuickSetupClass].length == 0){
|
||||||
|
//This tour is not defined or empty
|
||||||
|
let notFound = tourStepFactory({
|
||||||
|
title: "😭 Tour not found",
|
||||||
|
desc: "Seems you are requesting a tour that has not been developed yet. Check back on later!"
|
||||||
|
});
|
||||||
|
notFound();
|
||||||
|
|
||||||
|
//Enable the finish button
|
||||||
|
$("#tourModal .nextStepAvaible").hide();
|
||||||
|
$("#tourModal .nextStepFinish").show();
|
||||||
|
|
||||||
|
//Set step counter to 1
|
||||||
|
$("#tourModal .tourStepCounter").text("0 / 0");
|
||||||
|
return;
|
||||||
|
}else{
|
||||||
|
tourSteps[currentQuickSetupClass][0]();
|
||||||
|
}
|
||||||
|
|
||||||
|
updateTourStepCount();
|
||||||
|
|
||||||
|
//Disable the previous button
|
||||||
|
if (tourSteps[currentQuickSetupClass].length == 1){
|
||||||
|
//There are only 1 step in this tour
|
||||||
|
$("#tourModal .nextStepAvaible").hide();
|
||||||
|
$("#tourModal .nextStepFinish").show();
|
||||||
|
}else{
|
||||||
|
$("#tourModal .nextStepAvaible").show();
|
||||||
|
$("#tourModal .nextStepFinish").hide();
|
||||||
|
}
|
||||||
|
$("#tourModal .tourStepButtonBack").addClass("disabled");
|
||||||
|
|
||||||
|
//Disable body scroll and let tour steps to handle scrolling
|
||||||
|
$("body").css("overflow-y","hidden");
|
||||||
|
$("#mainmenu").css("pointer-events", "none");
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateTourStepCount(){
|
||||||
|
let tourlistLength = tourSteps[currentQuickSetupClass]==undefined?1:tourSteps[currentQuickSetupClass].length;
|
||||||
|
$("#tourModal .tourStepCounter").text((currentQuickSetupTourStep + 1) + " / " + tourlistLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
function nextTourStep(){
|
||||||
|
//Add one to the tour steps
|
||||||
|
currentQuickSetupTourStep++;
|
||||||
|
if (currentQuickSetupTourStep == tourSteps[currentQuickSetupClass].length - 1){
|
||||||
|
//Already the last step
|
||||||
|
$("#tourModal .nextStepAvaible").hide();
|
||||||
|
$("#tourModal .nextStepFinish").show();
|
||||||
|
}
|
||||||
|
updateTourStepCount();
|
||||||
|
tourSteps[currentQuickSetupClass][currentQuickSetupTourStep]();
|
||||||
|
if (currentQuickSetupTourStep > 0){
|
||||||
|
$("#tourModal .tourStepButtonBack").removeClass("disabled");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function previousTourStep(){
|
||||||
|
if (currentQuickSetupTourStep > 0){
|
||||||
|
currentQuickSetupTourStep--;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentQuickSetupTourStep != tourSteps[currentQuickSetupClass].length - 1){
|
||||||
|
//Not at the last step
|
||||||
|
$("#tourModal .nextStepAvaible").show();
|
||||||
|
$("#tourModal .nextStepFinish").hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentQuickSetupTourStep == 0){
|
||||||
|
//Cant go back anymore
|
||||||
|
$("#tourModal .tourStepButtonBack").addClass("disabled");
|
||||||
|
}
|
||||||
|
updateTourStepCount();
|
||||||
|
tourSteps[currentQuickSetupClass][currentQuickSetupTourStep]();
|
||||||
|
}
|
||||||
|
|
||||||
|
//End tour and reset everything
|
||||||
|
function endTourFocus(){
|
||||||
|
$(".tourFocusObject").removeClass("tourFocusObject");
|
||||||
|
$(".serviceOption.active").removeClass("active");
|
||||||
|
currentQuickSetupClass = "";
|
||||||
|
currentQuickSetupTourStep = 0;
|
||||||
|
$("#tourModal").hide();
|
||||||
|
$("#tourModal .nextStepAvaible").show();
|
||||||
|
$("#tourModal .nextStepFinish").hide();
|
||||||
|
$("#tourModalOverlay").hide();
|
||||||
|
if (tourOverlayUpdateTicker != undefined){
|
||||||
|
clearInterval(tourOverlayUpdateTicker);
|
||||||
|
}
|
||||||
|
$("body").css("overflow-y","auto");
|
||||||
|
$("#mainmenu").css("pointer-events", "auto");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
var tourSteps = {
|
||||||
|
//Homepage steps
|
||||||
|
"homepage": [
|
||||||
|
tourStepFactory({
|
||||||
|
title: "🎉 Congratulation on your first site!",
|
||||||
|
desc: "In this tour, you will be guided through the steps required to setup a basic static website using your own domain name with Zoraxy."
|
||||||
|
}),
|
||||||
|
tourStepFactory({
|
||||||
|
title: "👉 Pointing domain DNS to Zoraxy's IP",
|
||||||
|
desc: `Setup a DNS A Record that points your domain name to this Zoraxy instances public IP address. <br>
|
||||||
|
Assume your public IP is 93.184.215.14, you should have an A record like this.
|
||||||
|
<table class="ui celled collapsing basic striped table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Name</th>
|
||||||
|
<th>Type</th>
|
||||||
|
<th>Value</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>yourdomain.com</td>
|
||||||
|
<td>A</td>
|
||||||
|
<td>93.184.215.14</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<br>If the IP of Zoraxy start from 192.168, you might want to use your router's public IP address and setup port forward for both port 80 and 443 as well.`,
|
||||||
|
callback: function(){
|
||||||
|
$.get("/api/acme/wizard?step=10", function(data){
|
||||||
|
if (data.error == undefined){
|
||||||
|
//Should return the public IP address from acme wizard
|
||||||
|
//Overwrite the sample IP address
|
||||||
|
let originalText = $("#tourModal .tourStepContent").html();
|
||||||
|
originalText = originalText.split("93.184.215.14").join(data);
|
||||||
|
$("#tourModal .tourStepContent").html(originalText);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
tourStepFactory({
|
||||||
|
title: "🏠 Setup Default Site",
|
||||||
|
desc: `If you already have an apache or nginx web server running, use "Reverse Proxy Target" and enter your current web server IP address. <br>Otherwise, pick "Internal Static Web Server" and click "Apply Change"`,
|
||||||
|
tab: "setroot",
|
||||||
|
element: "#setroot",
|
||||||
|
pos: "bottomright"
|
||||||
|
}),
|
||||||
|
tourStepFactory({
|
||||||
|
title: "🌐 Enable Static Web Server",
|
||||||
|
desc: `Enable the static web server if it is not already enabled. Skip this step if you are using external web servers like Apache or Nginx.`,
|
||||||
|
tab: "webserv",
|
||||||
|
element: "#webserv",
|
||||||
|
pos: "bottomright"
|
||||||
|
}),
|
||||||
|
tourStepFactory({
|
||||||
|
title: "📤 Upload Static Website",
|
||||||
|
desc: `Upload your static website files (e.g. HTML files) to the web directory. If remote access is not avaible, you can also upload it with the web server file manager here.`,
|
||||||
|
tab: "webserv",
|
||||||
|
element: "#webserv_dirManager",
|
||||||
|
pos: "bottomright",
|
||||||
|
scrollto: "#webserv_dirManager"
|
||||||
|
}),
|
||||||
|
tourStepFactory({
|
||||||
|
title: "💡 Start Zoraxy HTTP listener",
|
||||||
|
desc: `Start Zoraxy (if it is not already running) by pressing the "Start Service" button.<br>You should now be able to visit your domain and see the static web server contents show up in your browser.`,
|
||||||
|
tab: "status",
|
||||||
|
element: "#status .poweroptions",
|
||||||
|
pos: "bottomright",
|
||||||
|
})
|
||||||
|
],
|
||||||
|
|
||||||
|
//Subdomains tour steps
|
||||||
|
"subdomain":[
|
||||||
|
tourStepFactory({
|
||||||
|
title: "🎉 Creating your first subdomain",
|
||||||
|
desc: "Seems you are now ready to expand your site with more services! To do so, you can create a new subdomain for your new web services. <br><br>In this tour, you will be guided through the steps to setup a new subdomain reverse proxy.",
|
||||||
|
pos: "center"
|
||||||
|
}),
|
||||||
|
tourStepFactory({
|
||||||
|
title: "👉 Pointing subdomain DNS to Zoraxy's IP",
|
||||||
|
desc: `Setup a DNS CNAME Record that points your subdomain to your root domain. <br>
|
||||||
|
Assume your public IP is 93.184.215.14, you should have an CNAME record like this.
|
||||||
|
<table class="ui celled collapsing basic striped table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Name</th>
|
||||||
|
<th>Type</th>
|
||||||
|
<th>Value</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>example.com</td>
|
||||||
|
<td>A</td>
|
||||||
|
<td>93.184.215.14</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>sub.example.com</td>
|
||||||
|
<td>CNAME</td>
|
||||||
|
<td>example.com</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>`,
|
||||||
|
callback: function(){
|
||||||
|
$.get("/api/acme/wizard?step=10", function(data){
|
||||||
|
if (data.error == undefined){
|
||||||
|
//Should return the public IP address from acme wizard
|
||||||
|
//Overwrite the sample IP address
|
||||||
|
let originalText = $("#tourModal .tourStepContent").html();
|
||||||
|
originalText = originalText.split("93.184.215.14").join(data);
|
||||||
|
$("#tourModal .tourStepContent").html(originalText);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
tourStepFactory({
|
||||||
|
title: "➕ Create New Proxy Rule",
|
||||||
|
desc: `Next, you can now move on to create a proxy rule that reverse proxy your new subdomain in Zoraxy. You can easily add new rules using the "New Proxy Rule" web form.`,
|
||||||
|
tab: "rules",
|
||||||
|
pos: "topright"
|
||||||
|
}),
|
||||||
|
tourStepFactory({
|
||||||
|
title: "🌐 Matching Keyword / Domain",
|
||||||
|
desc: `Fill in your new subdomain in the "Matching Keyword / Domain" field.<br> e.g. sub.example.com`,
|
||||||
|
element: "#rules .field[tourstep='matchingkeyword']",
|
||||||
|
pos: "bottomright"
|
||||||
|
}),
|
||||||
|
tourStepFactory({
|
||||||
|
title: "🖥️ Target IP Address or Domain Name with port",
|
||||||
|
desc: `Fill in the Reverse Proxy Destination. e.g. localhost:8080 or 192.168.1.100:9096. <br><br>Please make sure your web services is accessible by Zoraxy.`,
|
||||||
|
element: "#rules .field[tourstep='targetdomain']",
|
||||||
|
pos: "bottomright"
|
||||||
|
}),
|
||||||
|
tourStepFactory({
|
||||||
|
title: "🔐 Proxy Target require TLS Connection",
|
||||||
|
desc: `If your upstream service only accept https connection, select this option.`,
|
||||||
|
element: "#rules .field[tourstep='requireTLS']",
|
||||||
|
pos: "bottomright",
|
||||||
|
|
||||||
|
}),
|
||||||
|
tourStepFactory({
|
||||||
|
title: "🔓 Ignore TLS Validation Error",
|
||||||
|
desc: `Some open source projects like Proxmox or NextCloud use self-signed certificate to serve its web UI. If you are proxying services like that, enable this option. `,
|
||||||
|
element: "#rules #advanceProxyRules .field[tourstep='skipTLSValidation']",
|
||||||
|
scrollto: "#rules #advanceProxyRules",
|
||||||
|
pos: "bottomright",
|
||||||
|
ignoreVisiableCheck: true,
|
||||||
|
callback: function(){
|
||||||
|
$("#advanceProxyRules").accordion();
|
||||||
|
if (!$("#rules #advanceProxyRules .content").is(":visible")){
|
||||||
|
//Open up the advance config menu
|
||||||
|
$("#rules #advanceProxyRules .title")[0].click()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
tourStepFactory({
|
||||||
|
title: "💾 Save New Proxy Rule",
|
||||||
|
desc: `Now, click "Create Endpoint" to add this reverse proxy rule to runtime.`,
|
||||||
|
element: "#rules div[tourstep='newProxyRule']",
|
||||||
|
scrollto: "#rules div[tourstep='newProxyRule']",
|
||||||
|
pos: "topright",
|
||||||
|
}),
|
||||||
|
tourStepFactory({
|
||||||
|
title: "🎉 New Proxy Rule Setup Completed!",
|
||||||
|
desc: `You can continue to add more subdomains or alias domain using this web form. To view the created reverse proxy rules, you can navigate to the HTTP Proxy tab.`,
|
||||||
|
element: "#rules",
|
||||||
|
tab: "rules",
|
||||||
|
pos: "bottomright",
|
||||||
|
}),
|
||||||
|
tourStepFactory({
|
||||||
|
title: "🌲 HTTP Proxy List",
|
||||||
|
desc: `In this tab, you will see all the created HTTP proxy rules and edit them if needed. You should see your newly created HTTP proxy rule in the above list. <Br><Br>
|
||||||
|
This is the end of this tour. If you want further documentation on how to setup access control filters or load balancer, check out our Github Wiki page.`,
|
||||||
|
element: "#httprp",
|
||||||
|
tab: "httprp",
|
||||||
|
pos: "bottomright",
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
|
||||||
|
//TLS and ACME tour steps
|
||||||
|
"tls":[
|
||||||
|
tourStepFactory({
|
||||||
|
title: "🔐 Enable HTTPS (TLS) for your site",
|
||||||
|
desc: `Some technologies only work with HTTPS for security reasons. In this tour, you will be guided through the steps to enable HTTPS in Zoraxy.`,
|
||||||
|
pos: "center",
|
||||||
|
}),
|
||||||
|
tourStepFactory({
|
||||||
|
title: "➡️ Change Listening Port",
|
||||||
|
desc: `HTTPS listen on port 443 instead of 80. If your Zoraxy is currently listening to ports other than 443, change it to 443 in incoming port option and click "Apply"`,
|
||||||
|
tab: "status",
|
||||||
|
element: "#status div[tourstep='incomingPort']",
|
||||||
|
scrollto: "#status div[tourstep='incomingPort']",
|
||||||
|
pos: "bottomright",
|
||||||
|
}),
|
||||||
|
tourStepFactory({
|
||||||
|
title: "🔑 Enable TLS Serving",
|
||||||
|
desc: `Next, you can enable TLS by checking the "Use TLS to serve proxy request"`,
|
||||||
|
element: "#tls",
|
||||||
|
scrollto: "#tls",
|
||||||
|
pos: "bottomright",
|
||||||
|
}),
|
||||||
|
tourStepFactory({
|
||||||
|
title: "💻 Enable HTTP Server on Port 80",
|
||||||
|
desc: `As we might want some proxy rules to be accessible by HTTP, turn on the HTTP server listener on port 80 as well.`,
|
||||||
|
element: "#listenP80",
|
||||||
|
scrollto: "#tls",
|
||||||
|
pos: "bottomright",
|
||||||
|
}),
|
||||||
|
tourStepFactory({
|
||||||
|
title: "↩️ Force redirect HTTP request to HTTPS",
|
||||||
|
desc: `By default, if a HTTP host-name is not found, 404 error page will be returned. However, in common scenerio for self-hosting, you might want to redirect that request to your HTTPS server instead. <br><br>Enabling this option allows such redirection to be done automatically.`,
|
||||||
|
element: "#status div[tourstep='forceHttpsRedirect']",
|
||||||
|
scrollto: "#tls",
|
||||||
|
pos: "bottomright",
|
||||||
|
}),
|
||||||
|
tourStepFactory({
|
||||||
|
title: "🎉 HTTPS Enabled!",
|
||||||
|
desc: `Now, your Zoraxy instance is ready to serve HTTPS requests.
|
||||||
|
<br><br>By default, Zoraxy serve all your host-names by its internal self-signed certificate which is not a proper setup. That is why you will need to request a proper certificate for your site from your ISP or CA. `,
|
||||||
|
tab: "status",
|
||||||
|
pos: "center",
|
||||||
|
}),
|
||||||
|
tourStepFactory({
|
||||||
|
title: "🔐 TLS / SSL Certificates",
|
||||||
|
desc: `Zoraxy come with a simple and handy TLS management interface, where you can upload or request your certificates with a web form. You can click "TLS / SSL Certificate" from the side menu to open this page.`,
|
||||||
|
tab: "cert",
|
||||||
|
element: "#mainmenu",
|
||||||
|
pos: "center",
|
||||||
|
}),
|
||||||
|
tourStepFactory({
|
||||||
|
title: "📤 Uploading Fallback (Default) Certificate",
|
||||||
|
desc: `If you are using Cloudflare, you can upload the Cloudflare (full) strict mode certificate in the "Fallback Certificate" section and let Cloudflare handle all the remaining certificate dispatch. <br><br>
|
||||||
|
Public key usually use a file extension of .pub or .pem, and private key usually ends with .key.`,
|
||||||
|
element: "#cert div[tourstep='defaultCertificate']",
|
||||||
|
scrollto: "#cert div[tourstep='defaultCertificate']",
|
||||||
|
pos: "bottomright",
|
||||||
|
}),
|
||||||
|
tourStepFactory({
|
||||||
|
title: "⚙️ Setup ACME",
|
||||||
|
desc: `If you didn't want to pay for a certificate, there are free CA where you can use to obtain a certificate. By default, Let's Encrypt is used and in order to use their service, you will need to fill in your webmin contact email in the "ACME EMAIL" field.
|
||||||
|
<br><br> After you are done, click "Save Settings" and continue.`,
|
||||||
|
element: "#cert div[tourstep='acmeSettings']",
|
||||||
|
scrollto: "#cert div[tourstep='acmeSettings']",
|
||||||
|
pos: "bottomright",
|
||||||
|
}),
|
||||||
|
tourStepFactory({
|
||||||
|
title: "👉 Open ACME Tool",
|
||||||
|
desc: `Open the ACME Tool by pressing the button below the ACME settings. You will see a tool window popup from the side.`,
|
||||||
|
element: ".sideWrapper",
|
||||||
|
pos: "center",
|
||||||
|
callback: function(){
|
||||||
|
//Call to function in cert.html
|
||||||
|
openACMEManager();
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
tourStepFactory({
|
||||||
|
title: "📃 Obtain Certificate with ACME",
|
||||||
|
desc: `Now, we can finally start requesting a free certificate from the selected CA. Fill in the "Generate New Certificate" web-form and click <b>"Get Certificate"</b>.
|
||||||
|
This usually will takes a few minutes. Wait until the spinning icon disappear before moving on the next step.
|
||||||
|
<br><br>Tips: You can check the "Use DNS Challenge" if you are trying to request a certificate containing wildcard character (*).`,
|
||||||
|
element: ".sideWrapper",
|
||||||
|
pos: "topleft",
|
||||||
|
}),
|
||||||
|
tourStepFactory({
|
||||||
|
title: "🔄 Enable Auto Renew",
|
||||||
|
desc:`Free certificate only last for a few months. If you want Zoraxy to help you automate the certificate renew process, enable "Auto Renew" by clicking the <b>"Enable Certificate Auto Renew"</b> toggle switch.
|
||||||
|
<br><br>You can fine tune which certificate to renew in the "Advance Renew Policy" dropdown.`,
|
||||||
|
element: ".sideWrapper",
|
||||||
|
pos: "bottomleft",
|
||||||
|
callback: function(){
|
||||||
|
//If the user arrive this step from "Back"
|
||||||
|
if (!$(".sideWrapper").is(":visible")){
|
||||||
|
openACMEManager();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
tourStepFactory({
|
||||||
|
title: "🎉 Certificate Installed!",
|
||||||
|
desc:`Now, your certificate is loaded into the database and it is ready to use! In Zoraxy, you do not need to manually assign the certificate to a domain. Zoraxy will do that automatically for you.
|
||||||
|
<br><br>Now, you can try to visit your website with https:// and see your green lock shows up next to your domain name!`,
|
||||||
|
element: "#cert div[tourstep='certTable']",
|
||||||
|
scrollto: "#cert div[tourstep='certTable']",
|
||||||
|
pos: "bottomright",
|
||||||
|
callback: function(){
|
||||||
|
hideSideWrapperInTourMode();
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
|
||||||
|
],
|
||||||
|
}
|
@ -27,3 +27,17 @@ Object.defineProperty(String.prototype, 'capitalize', {
|
|||||||
},
|
},
|
||||||
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);
|
||||||
|
}
|
@ -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
|
||||||
|
@ -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,7 +259,11 @@
|
|||||||
|
|
||||||
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({
|
||||||
|
url: "/api/acme/autoRenew/enable",
|
||||||
|
method: "POST",
|
||||||
|
data: {"enable": enabled},
|
||||||
|
success: function(data){
|
||||||
if (data.error){
|
if (data.error){
|
||||||
parent.msgbox(data.error, false, 5000);
|
parent.msgbox(data.error, false, 5000);
|
||||||
if (enabled){
|
if (enabled){
|
||||||
@ -273,10 +280,8 @@
|
|||||||
parent.setACMEEnableStates(enabled);
|
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")
|
||||||
|
@ -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){
|
||||||
|
@ -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:{
|
||||||
|
@ -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: {
|
||||||
|
@ -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("Config restore succeed. Restart Zoraxy to apply changes.");
|
||||||
|
},
|
||||||
|
error: function(xhr) {
|
||||||
parent.msgbox("Restore failed: " + xhr.responseText, false, 5000);
|
parent.msgbox("Restore failed: " + xhr.responseText, false, 5000);
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
};
|
|
||||||
xhr.send(formData);
|
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
|
@ -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>
|
@ -34,7 +34,7 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
const lines = {};
|
const lines = {};
|
||||||
const linesAded = [];
|
const linesAded = {};
|
||||||
|
|
||||||
function getDockerContainers() {
|
function getDockerContainers() {
|
||||||
const hostRequest = $.get("/api/proxy/list?type=host");
|
const hostRequest = $.get("/api/proxy/list?type=host");
|
||||||
@ -54,7 +54,9 @@
|
|||||||
Config: [{ Gateway: gateway }],
|
Config: [{ Gateway: gateway }],
|
||||||
},
|
},
|
||||||
} = bridge;
|
} = bridge;
|
||||||
const existedDomains = hostData.map(({ Domain }) => Domain);
|
const existedDomains = hostData.reduce((acc, { ActiveOrigins }) => {
|
||||||
|
return acc.concat(ActiveOrigins.map(({ OriginIpOrDomain }) => OriginIpOrDomain));
|
||||||
|
}, []);
|
||||||
|
|
||||||
for (const container of containers) {
|
for (const container of containers) {
|
||||||
const {
|
const {
|
||||||
@ -63,19 +65,20 @@
|
|||||||
} = container;
|
} = container;
|
||||||
|
|
||||||
for (const portObject of Ports.filter(
|
for (const portObject of Ports.filter(
|
||||||
({ IP: ip }) => ip === "::"
|
({ IP: ip }) => ip === "::" || ip === '0.0.0.0'
|
||||||
)) {
|
)) {
|
||||||
const { IP: ip, PublicPort: port } = portObject;
|
const { IP: ip, PublicPort: port } = portObject;
|
||||||
const key = `${name}-${port}`;
|
const key = `${name}-${port}`;
|
||||||
|
|
||||||
if (
|
if (
|
||||||
existedDomains.some((item) => item === `${gateway}:${port}`)
|
existedDomains.some((item) => item === `${gateway}:${port}`) &&
|
||||||
|
!linesAded[key]
|
||||||
) {
|
) {
|
||||||
linesAded.push({
|
linesAded[key] = {
|
||||||
name: name.replace(/^\//, ""),
|
name: name.replace(/^\//, ""),
|
||||||
ip: gateway,
|
ip: gateway,
|
||||||
port,
|
port,
|
||||||
});
|
};
|
||||||
} else if (!lines[key]) {
|
} else if (!lines[key]) {
|
||||||
lines[key] = {
|
lines[key] = {
|
||||||
name: name.replace(/^\//, ""),
|
name: name.replace(/^\//, ""),
|
||||||
@ -100,7 +103,7 @@
|
|||||||
</div>`
|
</div>`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
for (const line of linesAded) {
|
for (const [key, line] of Object.entries(linesAded)) {
|
||||||
$("#containersAddedList").append(
|
$("#containersAddedList").append(
|
||||||
`<div class="item">
|
`<div class="item">
|
||||||
<div class="content">
|
<div class="content">
|
||||||
@ -111,7 +114,7 @@
|
|||||||
</div>`
|
</div>`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
linesAded.length &&
|
Object.entries(linesAded).length &&
|
||||||
$("#containersAddedListHeader").removeAttr("hidden");
|
$("#containersAddedListHeader").removeAttr("hidden");
|
||||||
$("#containersList .loader").removeClass("active");
|
$("#containersList .loader").removeClass("active");
|
||||||
} else {
|
} else {
|
||||||
|
@ -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: {
|
||||||
|
@ -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>
|
@ -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%;
|
||||||
|
@ -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: {
|
||||||
|