mirror of
https://github.com/tobychui/zoraxy.git
synced 2025-07-01 20:01:45 +02:00
Compare commits
30 Commits
Author | SHA1 | Date | |
---|---|---|---|
36e461795a | |||
d6e7641364 | |||
15cebd6e06 | |||
e9a074d4d1 | |||
4b7fd39e57 | |||
fa005f1327 | |||
c7a9f40baa | |||
d5b9726158 | |||
f659e66cf7 | |||
801bdbf298 | |||
09da93cfb3 | |||
70ace02e80 | |||
1f758e953d | |||
ffad2cab81 | |||
dbb10644de | |||
4848392185 | |||
956f4ac30f | |||
c09ff28fd5 | |||
20cf290d37 | |||
4ca0fcc6d1 | |||
ce4ce72820 | |||
e363d55899 | |||
172479e4fb | |||
156fa5dace | |||
4d40e0aa38 | |||
045e66b631 | |||
62e60d78de | |||
23bdaa1517 | |||
50f222cced | |||
640e1adf96 |
21
.github/workflows/main.yml
vendored
21
.github/workflows/main.yml
vendored
@ -9,18 +9,20 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.event.release.tag_name }}
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v2
|
||||
uses: docker/setup-qemu-action@v3
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Login to Dockerhub
|
||||
run: echo "${{ secrets.DOCKER_PASSWORD }}" | docker login -u "${{ secrets.DOCKER_USERNAME }}" --password-stdin
|
||||
- name: Login to Docker & GHCR
|
||||
run: |
|
||||
echo "${{ secrets.DOCKER_PASSWORD }}" | docker login -u "${{ secrets.DOCKER_USERNAME }}" --password-stdin
|
||||
#echo "${{ secrets.GHCR_PASSWORD }}" | docker login ghcr.io -u "${{ secrets.GHCR_USERNAME }}" --password-stdin
|
||||
|
||||
- name: Setup building file structure
|
||||
run: |
|
||||
@ -36,11 +38,8 @@ jobs:
|
||||
--provenance=false \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--tag zoraxydocker/zoraxy:${{ github.event.release.tag_name }} \
|
||||
.
|
||||
|
||||
docker buildx build --push \
|
||||
--build-arg VERSION=${{ github.event.release.tag_name }} \
|
||||
--provenance=false \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--tag zoraxydocker/zoraxy:latest \
|
||||
# Since this is still undetermined, I will leave it commented
|
||||
#--tag ghcr.io/zoraxydocker/zoraxy:${{ steps.get_latest_release_tag.outputs.latest_tag }} \
|
||||
#--tag ghcr.io/zoraxydocker/zoraxy:latest \
|
||||
.
|
||||
|
35
CHANGELOG.md
35
CHANGELOG.md
@ -1,3 +1,22 @@
|
||||
# v2.6.8 Nov 25 2023
|
||||
|
||||
+ Added opt-out for subdomains for global TLS settings: See [release notes](https://github.com/tobychui/zoraxy/releases/tag/2.6.8)
|
||||
+ Optimized subdomain / vdir editing interface
|
||||
+ Added system-wide logger (Work in progress)
|
||||
+ Fixed issue for uptime monitor bug [#77](https://github.com/tobychui/zoraxy/issues/77)
|
||||
+ Changed default static web port to 5487 (prevent already in use)
|
||||
+ Added automatic HTTP/2 to TLS mode
|
||||
+ Bug fix for webserver autostart [67](https://github.com/tobychui/zoraxy/issues/67)
|
||||
|
||||
# v2.6.7 Sep 26 2023
|
||||
|
||||
+ Added Static Web Server function [#56](https://github.com/tobychui/zoraxy/issues/56)
|
||||
+ Web Directory Manager (see static webserver tab)
|
||||
+ Added static web server and black / whitelist template [#38](https://github.com/tobychui/zoraxy/issues/38)
|
||||
+ Added default / preferred CA features for ACME [#47](https://github.com/tobychui/zoraxy/issues/47)
|
||||
+ Optimized TLS/SSL page and added dedicated section for ACME related features
|
||||
+ Bugfixes [#61](https://github.com/tobychui/zoraxy/issues/61) [#58](https://github.com/tobychui/zoraxy/issues/58)
|
||||
|
||||
# v2.6.6 Aug 30 2023
|
||||
|
||||
+ Added basic auth editor custom exception rules
|
||||
@ -5,20 +24,20 @@
|
||||
+ Optimized memory usage (from 1.2GB to 61MB for low speed geoip lookup) [#52](https://github.com/tobychui/zoraxy/issues/52)
|
||||
+ Added unset subdomain custom redirection feature [#46](https://github.com/tobychui/zoraxy/issues/46)
|
||||
+ Fixed potential security issue in satori/go.uuid [#55](https://github.com/tobychui/zoraxy/issues/55)
|
||||
+ Added custom acme feature in back-end, thx [@daluntw](https://github.com/daluntw)
|
||||
+ Added custom ACME feature in backend, thx [@daluntw](https://github.com/daluntw)
|
||||
+ Added bypass TLS check for custom acme server, thx [@daluntw](https://github.com/daluntw)
|
||||
+ Introduce new startparameter `-fastgeoip=true`, see [Releases](https://github.com/tobychui/zoraxy/releases/tag/2.6.6)
|
||||
+ Introduce new start parameter `-fastgeoip=true`: see [release notes](https://github.com/tobychui/zoraxy/releases/tag/2.6.6)
|
||||
|
||||
# v2.6.5.1 Jul 26 2023
|
||||
|
||||
+ Patch on memory leaking for Windows netstat module (do not effect any of the previous non Windows builds)
|
||||
+ Fixed potential memory leak in acme handler logic
|
||||
+ Added "Do you want to get a TLS certificate for this subdomain?" dialog when a new subdomain proxy rule is created
|
||||
+ Fixed potential memory leak in ACME handler logic
|
||||
+ Added "Do you want to get a TLS certificate for this subdomain?" dialogue when a new subdomain proxy rule is created
|
||||
|
||||
# v2.6.5 Jul 19 2023
|
||||
|
||||
+ Added Import / Export-Feature
|
||||
+ Moved configurationfiles to a separate folder [#26](https://github.com/tobychui/zoraxy/issues/26)
|
||||
+ Moved configuration files to a separate folder [#26](https://github.com/tobychui/zoraxy/issues/26)
|
||||
+ Added auto-renew with ACME [#6](https://github.com/tobychui/zoraxy/issues/6)
|
||||
+ Fixed Whitelistbug [#18](https://github.com/tobychui/zoraxy/issues/18)
|
||||
+ Added Whois
|
||||
@ -28,7 +47,7 @@
|
||||
+ Added force TLS v1.2 above toggle
|
||||
+ Added trace route
|
||||
+ Added ICMP ping
|
||||
+ Added special routing rules module for up-coming acme integration
|
||||
+ Added special routing rules module for up-coming ACME integration
|
||||
+ Fixed IPv6 check bug in black/whitelist
|
||||
+ Optimized UI for TCP Proxy
|
||||
|
||||
@ -38,7 +57,7 @@
|
||||
+ Split blacklist and whitelist from geodb script file
|
||||
+ Optimized compile binary size
|
||||
+ Added access control to TCP proxy
|
||||
+ Added "invalid config detect" in up time monitor for isse [#7](https://github.com/tobychui/zoraxy/issues/7)
|
||||
+ Added "invalid config detect" in up time monitor for issue [#7](https://github.com/tobychui/zoraxy/issues/7)
|
||||
+ Fixed minor bugs in advance stats panel
|
||||
+ Reduced file size of embedded materials
|
||||
|
||||
@ -65,6 +84,6 @@
|
||||
+ Basic auth
|
||||
+ Support TLS verification skip (for self signed certs)
|
||||
+ Added trend analysis
|
||||
+ Added referer and file type analysis
|
||||
+ Added referrer and file type analysis
|
||||
+ Added cert expire day display
|
||||
+ Moved subdomain proxy logic to dpcore
|
||||
|
85
README.md
85
README.md
@ -8,9 +8,7 @@ General purpose request (reverse) proxy and forwarding tool for low power device
|
||||
|
||||
- Simple to use interface with detail in-system instructions
|
||||
- Reverse Proxy
|
||||
|
||||
- Subdomain Reverse Proxy
|
||||
|
||||
- Virtual Directory Reverse Proxy
|
||||
- Redirection Rules
|
||||
- TLS / SSL setup and deploy
|
||||
@ -19,7 +17,6 @@ General purpose request (reverse) proxy and forwarding tool for low power device
|
||||
- Integrated Up-time Monitor
|
||||
- Web-SSH Terminal
|
||||
- Utilities
|
||||
|
||||
- CIDR IP converters
|
||||
- mDNS Scanner
|
||||
- IP Scanner
|
||||
@ -29,9 +26,9 @@ General purpose request (reverse) proxy and forwarding tool for low power device
|
||||
- SMTP config for password reset
|
||||
|
||||
## Build from Source
|
||||
Require Go 1.20 or above
|
||||
Requires Go 1.20 or higher
|
||||
|
||||
```
|
||||
```bash
|
||||
git clone https://github.com/tobychui/zoraxy
|
||||
cd ./zoraxy/src/
|
||||
go mod tidy
|
||||
@ -42,11 +39,11 @@ sudo ./zoraxy -port=:8000
|
||||
|
||||
## Usage
|
||||
|
||||
Zoraxy provide basic authentication system for standalone mode. To use it in standalone mode, follow the instruction below for your desired deployment platform.
|
||||
Zoraxy provides basic authentication system for standalone mode. To use it in standalone mode, follow the instructionss below for your desired deployment platform.
|
||||
|
||||
### Standalone Mode
|
||||
|
||||
Standalone mode is the default mode for Zoraxy. This allow single account to manage your reverse proxy server just like a home router. This mode is suitable for new owners for homelab or makers start growing their web services into multiple servers.
|
||||
Standalone mode is the default mode for Zoraxy. This allows a single account to manage your reverse proxy server, just like a home router. This mode is suitable for new owners to homelabs or makers starting growing their web services into multiple servers.
|
||||
|
||||
#### Linux
|
||||
|
||||
@ -60,18 +57,51 @@ Download the binary executable and double click the binary file to start it.
|
||||
|
||||
#### Raspberry Pi
|
||||
|
||||
The installation method is same as Linux. If you are using Raspberry Pi 4 or newer models, pick the arm64 release. For older version of Pis, use the arm (armv6) version instead.
|
||||
The installation method is same as Linux. If you are using a Raspberry Pi 4 or newer models, pick the arm64 release. For older version of Pis, use the arm (armv6) version instead.
|
||||
|
||||
#### Other ARM SBCs or Android phone with Termux
|
||||
|
||||
The installation method is same as Linux. For other ARM SBCs, please refer to your SBC's CPU architecture and pick the one that is suitable for your device.
|
||||
|
||||
#### Docker
|
||||
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
|
||||
```
|
||||
Usage of zoraxy:
|
||||
-autorenew int
|
||||
ACME auto TLS/SSL certificate renew check interval (seconds) (default 86400)
|
||||
-fastgeoip
|
||||
Enable high speed geoip lookup, require 1GB extra memory (Not recommend for low end devices)
|
||||
-info
|
||||
Show information about this program in JSON
|
||||
-log
|
||||
Log terminal output to file (default true)
|
||||
-mdns
|
||||
Enable mDNS scanner and transponder (default true)
|
||||
-noauth
|
||||
Disable authentication for management interface
|
||||
-port string
|
||||
Management web interface listening port (default ":8000")
|
||||
-rpt string
|
||||
Reserved
|
||||
-sshlb
|
||||
Allow loopback web ssh connection (DANGER)
|
||||
-version
|
||||
Show version of this server
|
||||
-webfm
|
||||
Enable web file manager for static web server root folder (default true)
|
||||
-webroot string
|
||||
Static web server root folder. Only allow chnage in start paramters (default "./www")
|
||||
-ztauth string
|
||||
ZeroTier authtoken for the local node
|
||||
-ztport int
|
||||
ZeroTier controller API port (default 9993)
|
||||
```
|
||||
|
||||
### External Permission Management Mode
|
||||
|
||||
If you already have a up-stream reverse proxy server in place with permission management, you can use Zoraxy in noauth mode. To enable noauth mode, start Zoraxy with the following flag
|
||||
If you already have an upstream reverse proxy server in place with permission management, you can use Zoraxy in noauth mode. To enable noauth mode, start Zoraxy with the following flag:
|
||||
|
||||
```bash
|
||||
./zoraxy -noauth=true
|
||||
@ -81,32 +111,32 @@ If you already have a up-stream reverse proxy server in place with permission ma
|
||||
|
||||
#### Use with ArozOS
|
||||
|
||||
[ArozOS ](https://arozos.com)subservice is a build in permission managed reverse proxy server. To use zoraxy with arozos, connect to your arozos host via ssh and use the following command to install zoraxy
|
||||
The [ArozOS](https://arozos.com) subservice is a built-in, permission-managed, reverse proxy server. To use Zoraxy with ArozOS, connect to your ArozOS host via SSH and use the following command to install Zoraxy:
|
||||
|
||||
```bash
|
||||
# cd into your arozos subservice folder. Sometime it is under ~/arozos/src/subservice
|
||||
# cd into your ArozOS subservice folder. Sometimes it is under ~/arozos/src/subservice.
|
||||
cd ~/arozos/subservices
|
||||
mkdir zoraxy
|
||||
cd ./zoraxy
|
||||
|
||||
# Download the release binary from Github release
|
||||
# Download the release binary from Github release.
|
||||
wget {binary executable link from release page}
|
||||
|
||||
# Set permission. Change this if required
|
||||
# Set permission. Change this if required.
|
||||
sudo chmod 775 -R ./
|
||||
|
||||
# Start zoraxy to see if the downloaded arch is correct.
|
||||
./zoraxy
|
||||
|
||||
# After the unzip done, press Ctrl + C to kill it
|
||||
# Rename it to valid arozos subservice binary format
|
||||
# After unzipping, press Ctrl + C to kill it.
|
||||
# Rename it to validate the ArozOS subservice binary format.
|
||||
mv ./zoraxy zoraxy_linux_amd64
|
||||
|
||||
# If you are using SBCs with different CPU arch, use the following names
|
||||
# If you are using SBCs with a different CPU arch, use the following names:
|
||||
# mv ./zoraxy zoraxy_linux_arm
|
||||
# mv ./zoraxy zoraxy_linux_arm64
|
||||
|
||||
# Restart arozos
|
||||
# Restart ArozOS
|
||||
sudo systemctl restart arozos
|
||||
```
|
||||
|
||||
@ -128,22 +158,21 @@ There is a wikipage with [Frequently-Asked-Questions](https://github.com/tobychu
|
||||
|
||||
This project also compatible with [ZeroTier](https://www.zerotier.com/). However, due to licensing issues, ZeroTier is not included in the binary.
|
||||
|
||||
Assuming you already have a valid license, to use Zoraxy with ZeroTier, install ZeroTier on your host and then run Zoraxy in sudo mode (or Run As Administrator if you are on Windows). The program will automatically grab the authtoken at correct location in your host.
|
||||
To use Zoraxy with ZeroTier, assuming you already have a valid license, install ZeroTier on your host and then run Zoraxy in sudo mode (or Run As Administrator if you are on Windows). The program will automatically grab the authtoken in the correct location on your host.
|
||||
|
||||
If you prefer not to run Zoraxy in sudo mode or you have some weird installation profile, you can also pass in the ZeroTier auth token using the following flags
|
||||
If you prefer not to run Zoraxy in sudo mode or you have some weird installation profile, you can also pass in the ZeroTier auth token using the following flags::
|
||||
|
||||
```
|
||||
```bash
|
||||
./zoraxy -ztauth="your_zerotier_authtoken" -ztport=9993
|
||||
```
|
||||
|
||||
The ZeroTier auth token can usually be found at ```/var/lib/zerotier-one/authtoken.secret``` or ```C:\ProgramData\ZeroTier\One\authtoken.secret```.
|
||||
|
||||
This allows you to have infinite number of network members in your Global Area Network controller. For more technical details, see [here](https://docs.zerotier.com/self-hosting/network-controllers/).
|
||||
This allows you to have an infinite number of network members in your Global Area Network controller. For more technical details, see [here](https://docs.zerotier.com/self-hosting/network-controllers/).
|
||||
|
||||
## Web.SSH
|
||||
|
||||
Web SSH currently only support Linux based OS. The following platforms are supported
|
||||
## Web SSH
|
||||
|
||||
Web SSH currently only supports Linux based OSes. The following platforms are supported:
|
||||
- linux/amd64
|
||||
- linux/arm64
|
||||
- linux/armv6 (experimental)
|
||||
@ -151,9 +180,9 @@ Web SSH currently only support Linux based OS. The following platforms are suppo
|
||||
|
||||
### Loopback Connection
|
||||
|
||||
Loopback web ssh connection, by default, is disabled. This means that if you are trying to connect to address like 127.0.0.1 or localhost, the system will reject your connection due to security issues. To enable loopback for testing or development purpose, use the following flags to override the loopback checking.
|
||||
Loopback web SSH connection, by default, is disabled. This means that if you are trying to connect to an address like 127.0.0.1 or localhost, the system will reject your connection for security reasons. To enable loopback for testing or development purpose, use the following flags to override the loopback checking:
|
||||
|
||||
```
|
||||
```bash
|
||||
./zoraxy -sshlb=true
|
||||
```
|
||||
|
||||
@ -165,5 +194,5 @@ If you like the project and want to support us, please consider a donation. You
|
||||
|
||||
## License
|
||||
|
||||
This project is open source under AGPL. I open source this project so everyone can check for security issues and benefit all users. **If your plans to use this project in commercial environment which violate the AGPL terms, please contact toby@imuslab.com for an alternative commercial license.**
|
||||
This project is open-sourced under AGPL. I open-sourced this project so everyone can check for security issues and benefit all users. **If you plan to use this project in a commercial environment (which violate the AGPL terms), please contact toby@imuslab.com for an alternative commercial license.**
|
||||
|
||||
|
@ -3,6 +3,8 @@ FROM docker.io/golang:alpine
|
||||
ARG VERSION
|
||||
|
||||
RUN apk add --no-cache bash netcat-openbsd sudo
|
||||
# Alternatives for security
|
||||
RUN apk add --no-cache openssl=3.1.4-r1
|
||||
|
||||
RUN mkdir -p /opt/zoraxy/source/ &&\
|
||||
mkdir -p /opt/zoraxy/config/ &&\
|
||||
|
@ -39,7 +39,7 @@ services:
|
||||
| `-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. |
|
||||
| `-e ARGS='(your arguments)'` | No | Sets the arguments to run Zoraxy with. Enter them as you would normally. By default, it is ran with `-noauth=false` but <b>you cannot change the management port.</b> This is required for the healthcheck to work. |
|
||||
| `zoraxydocker/zoraxy:latest` | Yes | The repository on Docker hub. By default, it is the latest version that I have published. |
|
||||
| `zoraxydocker/zoraxy:latest` | Yes | The repository on Docker hub. By default, it is the latest version that is published. |
|
||||
|
||||
## Examples: </br>
|
||||
### Docker Run </br>
|
||||
|
@ -148,7 +148,7 @@
|
||||
Reverse Proxy
|
||||
</div>
|
||||
</h3>
|
||||
<p>Simple to use, noobs friendly reverse proxy server that can be easily set-up using a web form and a few toggle switches.</p>
|
||||
<p>Simple to use noob-friendly reverse proxy server that can be easily set up using a web form and a few toggle switches.</p>
|
||||
</div>
|
||||
|
||||
<div class="four wide column featureItem">
|
||||
@ -158,7 +158,7 @@
|
||||
Redirection
|
||||
</div>
|
||||
</h3>
|
||||
<p>Direct and intuitive redirection rules with basic rewrite options. Suitable for most of the simple use cases.</p>
|
||||
<p>Direct and intuitive redirection rules with basic rewrite options. Suitable for most simple use cases.</p>
|
||||
</div>
|
||||
|
||||
<div class="four wide column featureItem">
|
||||
@ -168,7 +168,7 @@
|
||||
Geo-IP & Blacklist
|
||||
</div>
|
||||
</h3>
|
||||
<p>Blacklist with GeoIP support. Allow easy setup for regional services.</p>
|
||||
<p>Blacklist with GeoIP support. Allows easy setup for regional services.</p>
|
||||
</div>
|
||||
|
||||
<div class="four wide column featureItem">
|
||||
@ -189,7 +189,7 @@
|
||||
Web SSH
|
||||
</div>
|
||||
</h3>
|
||||
<p>Integrated with Gotty Web SSH terminal, allow one-stop management of your nodes inside private LAN via gateway nodes.</p>
|
||||
<p>Integration with Gotty Web SSH terminal allows one-stop management of your nodes inside private LAN via gateway nodes.</p>
|
||||
</div>
|
||||
|
||||
<div class="four wide column featureItem">
|
||||
@ -199,7 +199,7 @@
|
||||
Real Time Statistics
|
||||
</div>
|
||||
</h3>
|
||||
<p>Traffic data collection and real time analytic tools, provide you the best insights of visitors data without cookies.</p>
|
||||
<p>Traffic data collection and real-time analytic tools provide you the best insight of visitors data without cookies.</p>
|
||||
</div>
|
||||
|
||||
<div class="four wide column featureItem">
|
||||
@ -209,7 +209,7 @@
|
||||
Scanner & Utilities
|
||||
</div>
|
||||
</h3>
|
||||
<p>Build in IP scanner and mDNS discovering service, enable automatic service discovery within LAN.</p>
|
||||
<p>Build in IP scanner and mDNS discovery service to enable automatic service discovery within LAN.</p>
|
||||
</div>
|
||||
|
||||
<div class="four wide column featureItem">
|
||||
@ -219,7 +219,7 @@
|
||||
Open Source
|
||||
</div>
|
||||
</h3>
|
||||
<p>Project is open source under AGPL on Github. Feel free to contribute on missing functions you need! </p>
|
||||
<p>Project is open-source under AGPL on Github. Feel free to contribute on missing functions you need! </p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
13
src/acme.go
13
src/acme.go
@ -4,7 +4,6 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"math/rand"
|
||||
"net/http"
|
||||
"regexp"
|
||||
@ -29,7 +28,7 @@ func getRandomPort(minPort int) int {
|
||||
|
||||
// init the new ACME instance
|
||||
func initACME() *acme.ACMEHandler {
|
||||
log.Println("Starting ACME handler")
|
||||
SystemWideLogger.Println("Starting ACME handler")
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
// Generate a random port above 30000
|
||||
port := getRandomPort(30000)
|
||||
@ -44,7 +43,7 @@ func initACME() *acme.ACMEHandler {
|
||||
|
||||
// create the special routing rule for ACME
|
||||
func acmeRegisterSpecialRoutingRule() {
|
||||
log.Println("Assigned temporary port:" + acmeHandler.Getport())
|
||||
SystemWideLogger.Println("Assigned temporary port:" + acmeHandler.Getport())
|
||||
|
||||
err := dynamicProxyRouter.AddRoutingRules(&dynamicproxy.RoutingRule{
|
||||
ID: "acme-autorenew",
|
||||
@ -79,7 +78,7 @@ func acmeRegisterSpecialRoutingRule() {
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
log.Println("[Err] " + err.Error())
|
||||
SystemWideLogger.PrintAndLog("ACME", "Unable register temp port for DNS resolver", err)
|
||||
}
|
||||
}
|
||||
|
||||
@ -89,7 +88,7 @@ func AcmeCheckAndHandleRenewCertificate(w http.ResponseWriter, r *http.Request)
|
||||
if dynamicProxyRouter.Option.Port == 443 {
|
||||
//Enable port 80 to 443 redirect
|
||||
if !dynamicProxyRouter.Option.ForceHttpsRedirect {
|
||||
log.Println("Temporary enabling HTTP to HTTPS redirect for ACME certificate renew requests")
|
||||
SystemWideLogger.Println("Temporary enabling HTTP to HTTPS redirect for ACME certificate renew requests")
|
||||
dynamicProxyRouter.UpdateHttpToHttpsRedirectSetting(true)
|
||||
} else {
|
||||
//Set this to true, so after renew, do not turn it off
|
||||
@ -110,7 +109,7 @@ func AcmeCheckAndHandleRenewCertificate(w http.ResponseWriter, r *http.Request)
|
||||
if dynamicProxyRouter.Option.Port == 443 {
|
||||
if !isForceHttpsRedirectEnabledOriginally {
|
||||
//Default is off. Turn the redirection off
|
||||
log.Println("Restoring HTTP to HTTPS redirect settings")
|
||||
SystemWideLogger.PrintAndLog("ACME", "Restoring HTTP to HTTPS redirect settings", nil)
|
||||
dynamicProxyRouter.UpdateHttpToHttpsRedirectSetting(false)
|
||||
}
|
||||
}
|
||||
@ -130,7 +129,7 @@ func HandleACMEPreferredCA(w http.ResponseWriter, r *http.Request) {
|
||||
acme.IsSupportedCA(ca)
|
||||
//Set the new config
|
||||
sysdb.Write("acmepref", "prefca", ca)
|
||||
log.Println("Updating prefered ACME CA to " + ca)
|
||||
SystemWideLogger.Println("Updating prefered ACME CA to " + ca)
|
||||
utils.SendOK(w)
|
||||
}
|
||||
|
||||
|
@ -54,6 +54,7 @@ func initAPIs() {
|
||||
authRouter.HandleFunc("/api/proxy/tlscheck", HandleCheckSiteSupportTLS)
|
||||
authRouter.HandleFunc("/api/proxy/setIncoming", HandleIncomingPortSet)
|
||||
authRouter.HandleFunc("/api/proxy/useHttpsRedirect", HandleUpdateHttpsRedirect)
|
||||
authRouter.HandleFunc("/api/proxy/listenPort80", HandleUpdatePort80Listener)
|
||||
authRouter.HandleFunc("/api/proxy/requestIsProxied", HandleManagementProxyCheck)
|
||||
//Reverse proxy root related APIs
|
||||
authRouter.HandleFunc("/api/proxy/root/listOptions", HandleRootRouteOptionList)
|
||||
|
11
src/cert.go
11
src/cert.go
@ -6,7 +6,6 @@ import (
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@ -128,7 +127,7 @@ func handleListDomains(w http.ResponseWriter, r *http.Request) {
|
||||
certBtyes, err := os.ReadFile(certFilepath)
|
||||
if err != nil {
|
||||
// Unable to load this file
|
||||
log.Println("Unable to load certificate: " + certFilepath)
|
||||
SystemWideLogger.PrintAndLog("TLS", "Unable to load certificate: "+certFilepath, err)
|
||||
continue
|
||||
} else {
|
||||
// Cert loaded. Check its expiry time
|
||||
@ -182,11 +181,11 @@ func handleToggleTLSProxy(w http.ResponseWriter, r *http.Request) {
|
||||
} else {
|
||||
if newState == "true" {
|
||||
sysdb.Write("settings", "usetls", true)
|
||||
log.Println("Enabling TLS mode on reverse proxy")
|
||||
SystemWideLogger.Println("Enabling TLS mode on reverse proxy")
|
||||
dynamicProxyRouter.UpdateTLSSetting(true)
|
||||
} else if newState == "false" {
|
||||
sysdb.Write("settings", "usetls", false)
|
||||
log.Println("Disabling TLS mode on reverse proxy")
|
||||
SystemWideLogger.Println("Disabling TLS mode on reverse proxy")
|
||||
dynamicProxyRouter.UpdateTLSSetting(false)
|
||||
} else {
|
||||
utils.SendErrorResponse(w, "invalid state given. Only support true or false")
|
||||
@ -213,11 +212,11 @@ func handleSetTlsRequireLatest(w http.ResponseWriter, r *http.Request) {
|
||||
} else {
|
||||
if newState == "true" {
|
||||
sysdb.Write("settings", "forceLatestTLS", true)
|
||||
log.Println("Updating minimum TLS version to v1.2 or above")
|
||||
SystemWideLogger.Println("Updating minimum TLS version to v1.2 or above")
|
||||
dynamicProxyRouter.UpdateTLSVersion(true)
|
||||
} else if newState == "false" {
|
||||
sysdb.Write("settings", "forceLatestTLS", false)
|
||||
log.Println("Updating minimum TLS version to v1.0 or above")
|
||||
SystemWideLogger.Println("Updating minimum TLS version to v1.0 or above")
|
||||
dynamicProxyRouter.UpdateTLSVersion(false)
|
||||
} else {
|
||||
utils.SendErrorResponse(w, "invalid state given")
|
||||
|
@ -5,7 +5,6 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@ -29,6 +28,7 @@ type Record struct {
|
||||
Rootname string
|
||||
ProxyTarget string
|
||||
UseTLS bool
|
||||
BypassGlobalTLS bool
|
||||
SkipTlsValidation bool
|
||||
RequireBasicAuth bool
|
||||
BasicAuthCredentials []*dynamicproxy.BasicAuthCredentials
|
||||
@ -61,11 +61,11 @@ func SaveReverseProxyEndpointToFile(proxyEndpoint *dynamicproxy.ProxyEndpoint) e
|
||||
func RemoveReverseProxyConfigFile(rootname string) error {
|
||||
filename := getFilenameFromRootName(rootname)
|
||||
removePendingFile := strings.ReplaceAll(filepath.Join("./conf/proxy/", filename), "\\", "/")
|
||||
log.Println("Config Removed: ", removePendingFile)
|
||||
SystemWideLogger.Println("Config Removed: ", removePendingFile)
|
||||
if utils.FileExists(removePendingFile) {
|
||||
err := os.Remove(removePendingFile)
|
||||
if err != nil {
|
||||
log.Println(err.Error())
|
||||
SystemWideLogger.PrintAndLog("Proxy", "Unabel to remove config file", err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
@ -81,6 +81,7 @@ func LoadReverseProxyConfig(filename string) (*Record, error) {
|
||||
Rootname: "",
|
||||
ProxyTarget: "",
|
||||
UseTLS: false,
|
||||
BypassGlobalTLS: false,
|
||||
SkipTlsValidation: false,
|
||||
RequireBasicAuth: false,
|
||||
BasicAuthCredentials: []*dynamicproxy.BasicAuthCredentials{},
|
||||
@ -109,6 +110,7 @@ func ConvertProxyEndpointToRecord(targetProxyEndpoint *dynamicproxy.ProxyEndpoin
|
||||
Rootname: targetProxyEndpoint.RootOrMatchingDomain,
|
||||
ProxyTarget: targetProxyEndpoint.Domain,
|
||||
UseTLS: targetProxyEndpoint.RequireTLS,
|
||||
BypassGlobalTLS: targetProxyEndpoint.BypassGlobalTLS,
|
||||
SkipTlsValidation: targetProxyEndpoint.SkipCertValidations,
|
||||
RequireBasicAuth: targetProxyEndpoint.RequireBasicAuth,
|
||||
BasicAuthCredentials: targetProxyEndpoint.BasicAuthCredentials,
|
||||
@ -191,14 +193,14 @@ func ExportConfigAsZip(w http.ResponseWriter, r *http.Request) {
|
||||
//Also zip in the sysdb
|
||||
zipFile, err := zipWriter.Create("sys.db")
|
||||
if err != nil {
|
||||
log.Println("[Backup] Unable to zip sysdb: " + err.Error())
|
||||
SystemWideLogger.PrintAndLog("Backup", "Unable to zip sysdb", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Open the file on disk
|
||||
file, err := os.Open("sys.db")
|
||||
if err != nil {
|
||||
log.Println("[Backup] Unable to open sysdb: " + err.Error())
|
||||
SystemWideLogger.PrintAndLog("Backup", "Unable to open sysdb", err)
|
||||
return
|
||||
}
|
||||
defer file.Close()
|
||||
@ -206,7 +208,7 @@ func ExportConfigAsZip(w http.ResponseWriter, r *http.Request) {
|
||||
// Copy the file contents to the zip file
|
||||
_, err = io.Copy(zipFile, file)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
SystemWideLogger.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
@ -311,12 +313,12 @@ func ImportConfigFromZip(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
// Send a success response
|
||||
w.WriteHeader(http.StatusOK)
|
||||
log.Println("Configuration restored")
|
||||
SystemWideLogger.Println("Configuration restored")
|
||||
fmt.Fprintln(w, "Configuration restored")
|
||||
|
||||
if restoreDatabase {
|
||||
go func() {
|
||||
log.Println("Database altered. Restarting in 3 seconds...")
|
||||
SystemWideLogger.Println("Database altered. Restarting in 3 seconds...")
|
||||
time.Sleep(3 * time.Second)
|
||||
os.Exit(0)
|
||||
}()
|
||||
|
12
src/main.go
12
src/main.go
@ -20,6 +20,7 @@ import (
|
||||
"imuslab.com/zoraxy/mod/email"
|
||||
"imuslab.com/zoraxy/mod/ganserv"
|
||||
"imuslab.com/zoraxy/mod/geodb"
|
||||
"imuslab.com/zoraxy/mod/info/logger"
|
||||
"imuslab.com/zoraxy/mod/mdns"
|
||||
"imuslab.com/zoraxy/mod/netstat"
|
||||
"imuslab.com/zoraxy/mod/pathrule"
|
||||
@ -44,10 +45,11 @@ var acmeAutoRenewInterval = flag.Int("autorenew", 86400, "ACME auto TLS/SSL cert
|
||||
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 allowWebFileManager = flag.Bool("webfm", true, "Enable web file manager for static web server root folder")
|
||||
var logOutputToFile = flag.Bool("log", true, "Log terminal output to file")
|
||||
|
||||
var (
|
||||
name = "Zoraxy"
|
||||
version = "2.6.7"
|
||||
version = "2.6.8"
|
||||
nodeUUID = "generic"
|
||||
development = false //Set this to false to use embedded web fs
|
||||
bootTime = time.Now().Unix()
|
||||
@ -82,6 +84,7 @@ var (
|
||||
//Helper modules
|
||||
EmailSender *email.Sender //Email sender that handle email sending
|
||||
AnalyticLoader *analytic.DataLoader //Data loader for Zoraxy Analytic
|
||||
SystemWideLogger *logger.Logger //Logger for Zoraxy
|
||||
)
|
||||
|
||||
// Kill signal handler. Do something before the system the core terminate.
|
||||
@ -116,6 +119,9 @@ func ShutdownSeq() {
|
||||
fmt.Println("- Cleaning up tmp files")
|
||||
os.RemoveAll("./tmp")
|
||||
|
||||
fmt.Println("- Closing system wide logger")
|
||||
SystemWideLogger.Close()
|
||||
|
||||
//Close database, final
|
||||
fmt.Println("- Stopping system database")
|
||||
sysdb.Close()
|
||||
@ -151,7 +157,7 @@ func main() {
|
||||
}
|
||||
uuidBytes, err := os.ReadFile(uuidRecord)
|
||||
if err != nil {
|
||||
log.Println("Unable to read system uuid from file system")
|
||||
SystemWideLogger.PrintAndLog("ZeroTier", "Unable to read system uuid from file system", nil)
|
||||
panic(err)
|
||||
}
|
||||
nodeUUID = string(uuidBytes)
|
||||
@ -173,7 +179,7 @@ func main() {
|
||||
//Start the finalize sequences
|
||||
finalSequence()
|
||||
|
||||
log.Println("Zoraxy started. Visit control panel at http://localhost" + handler.Port)
|
||||
SystemWideLogger.Println("Zoraxy started. Visit control panel at http://localhost" + handler.Port)
|
||||
err = http.ListenAndServe(handler.Port, nil)
|
||||
|
||||
if err != nil {
|
||||
|
@ -60,6 +60,12 @@ func (router *Router) UpdateTLSVersion(requireLatest bool) {
|
||||
router.Restart()
|
||||
}
|
||||
|
||||
// Update port 80 listener state
|
||||
func (router *Router) UpdatePort80ListenerState(useRedirect bool) {
|
||||
router.Option.ListenOnPort80 = useRedirect
|
||||
router.Restart()
|
||||
}
|
||||
|
||||
// Update https redirect, which will require updates
|
||||
func (router *Router) UpdateHttpToHttpsRedirectSetting(useRedirect bool) {
|
||||
router.Option.ForceHttpsRedirect = useRedirect
|
||||
@ -95,6 +101,7 @@ func (router *Router) StartProxyService() error {
|
||||
}
|
||||
|
||||
if router.Option.UseTls {
|
||||
/*
|
||||
//Serve with TLS mode
|
||||
ln, err := tls.Listen("tcp", ":"+strconv.Itoa(router.Option.Port), config)
|
||||
if err != nil {
|
||||
@ -103,20 +110,65 @@ func (router *Router) StartProxyService() error {
|
||||
return err
|
||||
}
|
||||
router.tlsListener = ln
|
||||
router.server = &http.Server{Addr: ":" + strconv.Itoa(router.Option.Port), Handler: router.mux}
|
||||
*/
|
||||
router.server = &http.Server{
|
||||
Addr: ":" + strconv.Itoa(router.Option.Port),
|
||||
Handler: router.mux,
|
||||
TLSConfig: config,
|
||||
}
|
||||
router.Running = true
|
||||
|
||||
if router.Option.Port != 80 && router.Option.ForceHttpsRedirect {
|
||||
if router.Option.Port != 80 && router.Option.ListenOnPort80 {
|
||||
//Add a 80 to 443 redirector
|
||||
httpServer := &http.Server{
|
||||
Addr: ":80",
|
||||
Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
//Check if the domain requesting allow non TLS mode
|
||||
domainOnly := r.Host
|
||||
if strings.Contains(r.Host, ":") {
|
||||
hostPath := strings.Split(r.Host, ":")
|
||||
domainOnly = hostPath[0]
|
||||
}
|
||||
sep := router.getSubdomainProxyEndpointFromHostname(domainOnly)
|
||||
if sep != nil && sep.BypassGlobalTLS {
|
||||
//Allow routing via non-TLS handler
|
||||
originalHostHeader := r.Host
|
||||
if r.URL != nil {
|
||||
r.Host = r.URL.Host
|
||||
} else {
|
||||
//Fallback when the upstream proxy screw something up in the header
|
||||
r.URL, _ = url.Parse(originalHostHeader)
|
||||
}
|
||||
|
||||
sep.Proxy.ServeHTTP(w, r, &dpcore.ResponseRewriteRuleSet{
|
||||
ProxyDomain: sep.Domain,
|
||||
OriginalHost: originalHostHeader,
|
||||
UseTLS: sep.RequireTLS,
|
||||
PathPrefix: "",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if router.Option.ForceHttpsRedirect {
|
||||
//Redirect to https is enabled
|
||||
protocol := "https://"
|
||||
if router.Option.Port == 443 {
|
||||
http.Redirect(w, r, protocol+r.Host+r.RequestURI, http.StatusTemporaryRedirect)
|
||||
} else {
|
||||
http.Redirect(w, r, protocol+r.Host+":"+strconv.Itoa(router.Option.Port)+r.RequestURI, http.StatusTemporaryRedirect)
|
||||
}
|
||||
} else {
|
||||
//Do not do redirection
|
||||
if sep != nil {
|
||||
//Sub-domain exists but not allow non-TLS access
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
w.Write([]byte("400 - Bad Request"))
|
||||
} else {
|
||||
//No defined sub-domain
|
||||
http.NotFound(w, r)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}),
|
||||
ReadTimeout: 3 * time.Second,
|
||||
@ -143,7 +195,7 @@ func (router *Router) StartProxyService() error {
|
||||
if err := httpServer.ListenAndServe(); err != nil && err != http.ErrServerClosed {
|
||||
//Unable to startup port 80 listener. Handle shutdown process gracefully
|
||||
stopChan <- true
|
||||
log.Fatalf("Could not start server: %v\n", err)
|
||||
log.Fatalf("Could not start redirection server: %v\n", err)
|
||||
}
|
||||
}()
|
||||
router.tlsRedirectStop = stopChan
|
||||
@ -152,8 +204,8 @@ func (router *Router) StartProxyService() error {
|
||||
//Start the TLS server
|
||||
log.Println("Reverse proxy service started in the background (TLS mode)")
|
||||
go func() {
|
||||
if err := router.server.Serve(ln); err != nil && err != http.ErrServerClosed {
|
||||
log.Fatalf("Could not start server: %v\n", err)
|
||||
if err := router.server.ListenAndServeTLS("", ""); err != nil && err != http.ErrServerClosed {
|
||||
log.Fatalf("Could not start proxy server: %v\n", err)
|
||||
}
|
||||
}()
|
||||
} else {
|
||||
|
@ -38,6 +38,7 @@ func (router *Router) AddSubdomainRoutingService(options *SubdOptions) error {
|
||||
Domain: domain,
|
||||
RequireTLS: options.RequireTLS,
|
||||
Proxy: proxy,
|
||||
BypassGlobalTLS: options.BypassGlobalTLS,
|
||||
SkipCertValidations: options.SkipCertValidations,
|
||||
RequireBasicAuth: options.RequireBasicAuth,
|
||||
BasicAuthCredentials: options.BasicAuthCredentials,
|
||||
|
@ -27,6 +27,7 @@ type RouterOption struct {
|
||||
Port int //Incoming port
|
||||
UseTls bool //Use TLS to serve incoming requsts
|
||||
ForceTLSLatest bool //Force TLS1.2 or above
|
||||
ListenOnPort80 bool //Enable port 80 http listener
|
||||
ForceHttpsRedirect bool //Force redirection of http to https endpoint
|
||||
TlsManager *tlscert.Manager
|
||||
RedirectRuleTable *redirection.RuleTable
|
||||
|
103
src/mod/info/logger/logger.go
Normal file
103
src/mod/info/logger/logger.go
Normal file
@ -0,0 +1,103 @@
|
||||
package logger
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
/*
|
||||
Zoraxy Logger
|
||||
|
||||
This script is designed to make a managed log for the Zoraxy
|
||||
and replace the ton of log.Println in the system core
|
||||
*/
|
||||
|
||||
type Logger struct {
|
||||
LogToFile bool //Set enable write to file
|
||||
Prefix string //Prefix for log files
|
||||
LogFolder string //Folder to store the log file
|
||||
CurrentLogFile string //Current writing filename
|
||||
file *os.File
|
||||
}
|
||||
|
||||
func NewLogger(logFilePrefix string, logFolder string, logToFile bool) (*Logger, error) {
|
||||
err := os.MkdirAll(logFolder, 0775)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
thisLogger := Logger{
|
||||
LogToFile: logToFile,
|
||||
Prefix: logFilePrefix,
|
||||
LogFolder: logFolder,
|
||||
}
|
||||
|
||||
logFilePath := thisLogger.getLogFilepath()
|
||||
f, err := os.OpenFile(logFilePath, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0755)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
thisLogger.CurrentLogFile = logFilePath
|
||||
thisLogger.file = f
|
||||
return &thisLogger, nil
|
||||
}
|
||||
|
||||
func (l *Logger) getLogFilepath() string {
|
||||
year, month, _ := time.Now().Date()
|
||||
return filepath.Join(l.LogFolder, l.Prefix+"_"+strconv.Itoa(year)+"-"+strconv.Itoa(int(month))+".log")
|
||||
}
|
||||
|
||||
// PrintAndLog will log the message to file and print the log to STDOUT
|
||||
func (l *Logger) PrintAndLog(title string, message string, originalError error) {
|
||||
go func() {
|
||||
l.Log(title, message, originalError)
|
||||
}()
|
||||
log.Println("[" + title + "] " + message)
|
||||
}
|
||||
|
||||
// Println is a fast snap-in replacement for log.Println
|
||||
func (l *Logger) Println(v ...interface{}) {
|
||||
//Convert the array of interfaces into string
|
||||
message := fmt.Sprint(v...)
|
||||
go func() {
|
||||
l.Log("info", string(message), nil)
|
||||
}()
|
||||
log.Println("[INFO] " + string(message))
|
||||
}
|
||||
|
||||
func (l *Logger) Log(title string, errorMessage string, originalError error) {
|
||||
l.ValidateAndUpdateLogFilepath()
|
||||
if l.LogToFile {
|
||||
if originalError == nil {
|
||||
l.file.WriteString(time.Now().Format("2006-01-02 15:04:05.000000") + "|" + fmt.Sprintf("%-16s", title) + " [INFO]" + errorMessage + "\n")
|
||||
} else {
|
||||
l.file.WriteString(time.Now().Format("2006-01-02 15:04:05.000000") + "|" + fmt.Sprintf("%-16s", title) + " [ERROR]" + errorMessage + " " + originalError.Error() + "\n")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Validate if the logging target is still valid (detect any months change)
|
||||
func (l *Logger) ValidateAndUpdateLogFilepath() {
|
||||
expectedCurrentLogFilepath := l.getLogFilepath()
|
||||
if l.CurrentLogFile != expectedCurrentLogFilepath {
|
||||
//Change of month. Update to a new log file
|
||||
l.file.Close()
|
||||
f, err := os.OpenFile(expectedCurrentLogFilepath, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0755)
|
||||
if err != nil {
|
||||
log.Println("[Logger] Unable to create new log. Logging to file disabled.")
|
||||
l.LogToFile = false
|
||||
return
|
||||
}
|
||||
l.CurrentLogFile = expectedCurrentLogFilepath
|
||||
l.file = f
|
||||
}
|
||||
}
|
||||
|
||||
func (l *Logger) Close() {
|
||||
l.file.Close()
|
||||
}
|
122
src/mod/info/logviewer/logviewer.go
Normal file
122
src/mod/info/logviewer/logviewer.go
Normal file
@ -0,0 +1,122 @@
|
||||
package logviewer
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"io/fs"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"imuslab.com/zoraxy/mod/utils"
|
||||
)
|
||||
|
||||
type ViewerOption struct {
|
||||
RootFolder string //The root folder to scan for log
|
||||
Extension string //The extension the root files use, include the . in your ext (e.g. .log)
|
||||
}
|
||||
|
||||
type Viewer struct {
|
||||
option *ViewerOption
|
||||
}
|
||||
|
||||
type LogFile struct {
|
||||
Title string
|
||||
Filename string
|
||||
Fullpath string
|
||||
Filesize int64
|
||||
}
|
||||
|
||||
func NewLogViewer(option *ViewerOption) *Viewer {
|
||||
return &Viewer{option: option}
|
||||
}
|
||||
|
||||
/*
|
||||
Log Request Handlers
|
||||
*/
|
||||
//List all the log files in the log folder. Return in map[string]LogFile format
|
||||
func (v *Viewer) HandleListLog(w http.ResponseWriter, r *http.Request) {
|
||||
logFiles := v.ListLogFiles(false)
|
||||
js, _ := json.Marshal(logFiles)
|
||||
utils.SendJSONResponse(w, string(js))
|
||||
}
|
||||
|
||||
// Read log of a given catergory and filename
|
||||
// Require GET varaible: file and catergory
|
||||
func (v *Viewer) HandleReadLog(w http.ResponseWriter, r *http.Request) {
|
||||
filename, err := utils.GetPara(r, "file")
|
||||
if err != nil {
|
||||
utils.SendErrorResponse(w, "invalid filename given")
|
||||
return
|
||||
}
|
||||
|
||||
catergory, err := utils.GetPara(r, "catergory")
|
||||
if err != nil {
|
||||
utils.SendErrorResponse(w, "invalid catergory given")
|
||||
return
|
||||
}
|
||||
|
||||
content, err := v.LoadLogFile(strings.TrimSpace(filepath.Base(catergory)), strings.TrimSpace(filepath.Base(filename)))
|
||||
if err != nil {
|
||||
utils.SendErrorResponse(w, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
utils.SendTextResponse(w, content)
|
||||
}
|
||||
|
||||
/*
|
||||
Log Access Functions
|
||||
*/
|
||||
|
||||
func (v *Viewer) ListLogFiles(showFullpath bool) map[string][]*LogFile {
|
||||
result := map[string][]*LogFile{}
|
||||
filepath.WalkDir(v.option.RootFolder, func(path string, di fs.DirEntry, err error) error {
|
||||
if filepath.Ext(path) == v.option.Extension {
|
||||
catergory := filepath.Base(filepath.Dir(path))
|
||||
logList, ok := result[catergory]
|
||||
if !ok {
|
||||
//this catergory hasn't been scanned before.
|
||||
logList = []*LogFile{}
|
||||
}
|
||||
|
||||
fullpath := filepath.ToSlash(path)
|
||||
if !showFullpath {
|
||||
fullpath = ""
|
||||
}
|
||||
|
||||
st, err := os.Stat(path)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
logList = append(logList, &LogFile{
|
||||
Title: strings.TrimSuffix(filepath.Base(path), filepath.Ext(path)),
|
||||
Filename: filepath.Base(path),
|
||||
Fullpath: fullpath,
|
||||
Filesize: st.Size(),
|
||||
})
|
||||
|
||||
result[catergory] = logList
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
return result
|
||||
}
|
||||
|
||||
func (v *Viewer) LoadLogFile(catergory string, filename string) (string, error) {
|
||||
logFilepath := filepath.Join(v.option.RootFolder, catergory, filename)
|
||||
if utils.FileExists(logFilepath) {
|
||||
//Load it
|
||||
content, err := os.ReadFile(logFilepath)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return string(content), nil
|
||||
} else {
|
||||
return "", errors.New("log file not found")
|
||||
}
|
||||
}
|
@ -2,7 +2,6 @@ package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"log"
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
@ -25,33 +24,43 @@ func ReverseProxtInit() {
|
||||
inboundPort := 80
|
||||
if sysdb.KeyExists("settings", "inbound") {
|
||||
sysdb.Read("settings", "inbound", &inboundPort)
|
||||
log.Println("Serving inbound port ", inboundPort)
|
||||
SystemWideLogger.Println("Serving inbound port ", inboundPort)
|
||||
} else {
|
||||
log.Println("Inbound port not set. Using default (80)")
|
||||
SystemWideLogger.Println("Inbound port not set. Using default (80)")
|
||||
}
|
||||
|
||||
useTls := false
|
||||
sysdb.Read("settings", "usetls", &useTls)
|
||||
if useTls {
|
||||
log.Println("TLS mode enabled. Serving proxxy request with TLS")
|
||||
SystemWideLogger.Println("TLS mode enabled. Serving proxxy request with TLS")
|
||||
} else {
|
||||
log.Println("TLS mode disabled. Serving proxy request with plain http")
|
||||
SystemWideLogger.Println("TLS mode disabled. Serving proxy request with plain http")
|
||||
}
|
||||
|
||||
forceLatestTLSVersion := false
|
||||
sysdb.Read("settings", "forceLatestTLS", &forceLatestTLSVersion)
|
||||
if forceLatestTLSVersion {
|
||||
log.Println("Force latest TLS mode enabled. Minimum TLS LS version is set to v1.2")
|
||||
SystemWideLogger.Println("Force latest TLS mode enabled. Minimum TLS LS version is set to v1.2")
|
||||
} else {
|
||||
log.Println("Force latest TLS mode disabled. Minimum TLS version is set to v1.0")
|
||||
SystemWideLogger.Println("Force latest TLS mode disabled. Minimum TLS version is set to v1.0")
|
||||
}
|
||||
|
||||
listenOnPort80 := false
|
||||
sysdb.Read("settings", "listenP80", &listenOnPort80)
|
||||
if listenOnPort80 {
|
||||
SystemWideLogger.Println("Port 80 listener enabled")
|
||||
} else {
|
||||
SystemWideLogger.Println("Port 80 listener disabled")
|
||||
}
|
||||
|
||||
forceHttpsRedirect := false
|
||||
sysdb.Read("settings", "redirect", &forceHttpsRedirect)
|
||||
if forceHttpsRedirect {
|
||||
log.Println("Force HTTPS mode enabled")
|
||||
SystemWideLogger.Println("Force HTTPS mode enabled")
|
||||
//Port 80 listener must be enabled to perform http -> https redirect
|
||||
listenOnPort80 = true
|
||||
} else {
|
||||
log.Println("Force HTTPS mode disabled")
|
||||
SystemWideLogger.Println("Force HTTPS mode disabled")
|
||||
}
|
||||
|
||||
dprouter, err := dynamicproxy.NewDynamicProxy(dynamicproxy.RouterOption{
|
||||
@ -59,6 +68,7 @@ func ReverseProxtInit() {
|
||||
Port: inboundPort,
|
||||
UseTls: useTls,
|
||||
ForceTLSLatest: forceLatestTLSVersion,
|
||||
ListenOnPort80: listenOnPort80,
|
||||
ForceHttpsRedirect: forceHttpsRedirect,
|
||||
TlsManager: tlsCertManager,
|
||||
RedirectRuleTable: redirectTable,
|
||||
@ -67,7 +77,7 @@ func ReverseProxtInit() {
|
||||
WebDirectory: *staticWebServerRoot,
|
||||
})
|
||||
if err != nil {
|
||||
log.Println(err.Error())
|
||||
SystemWideLogger.PrintAndLog("Proxy", "Unable to create dynamic proxy router", err)
|
||||
return
|
||||
}
|
||||
|
||||
@ -78,7 +88,7 @@ func ReverseProxtInit() {
|
||||
for _, conf := range confs {
|
||||
record, err := LoadReverseProxyConfig(conf)
|
||||
if err != nil {
|
||||
log.Println("Failed to load "+filepath.Base(conf), err.Error())
|
||||
SystemWideLogger.PrintAndLog("Proxy", "Failed to load config file: "+filepath.Base(conf), err)
|
||||
return
|
||||
}
|
||||
|
||||
@ -92,6 +102,7 @@ func ReverseProxtInit() {
|
||||
MatchingDomain: record.Rootname,
|
||||
Domain: record.ProxyTarget,
|
||||
RequireTLS: record.UseTLS,
|
||||
BypassGlobalTLS: record.BypassGlobalTLS,
|
||||
SkipCertValidations: record.SkipTlsValidation,
|
||||
RequireBasicAuth: record.RequireBasicAuth,
|
||||
BasicAuthCredentials: record.BasicAuthCredentials,
|
||||
@ -102,13 +113,14 @@ func ReverseProxtInit() {
|
||||
RootName: record.Rootname,
|
||||
Domain: record.ProxyTarget,
|
||||
RequireTLS: record.UseTLS,
|
||||
BypassGlobalTLS: record.BypassGlobalTLS,
|
||||
SkipCertValidations: record.SkipTlsValidation,
|
||||
RequireBasicAuth: record.RequireBasicAuth,
|
||||
BasicAuthCredentials: record.BasicAuthCredentials,
|
||||
BasicAuthExceptionRules: record.BasicAuthExceptionRules,
|
||||
})
|
||||
} else {
|
||||
log.Println("Unsupported endpoint type: " + record.ProxyType + ". Skipping " + filepath.Base(conf))
|
||||
SystemWideLogger.PrintAndLog("Proxy", "Unsupported endpoint type: "+record.ProxyType+". Skipping "+filepath.Base(conf), nil)
|
||||
}
|
||||
}
|
||||
|
||||
@ -117,7 +129,7 @@ func ReverseProxtInit() {
|
||||
//reverse proxy server in front of this service
|
||||
time.Sleep(300 * time.Millisecond)
|
||||
dynamicProxyRouter.StartProxyService()
|
||||
log.Println("Dynamic Reverse Proxy service started")
|
||||
SystemWideLogger.Println("Dynamic Reverse Proxy service started")
|
||||
|
||||
//Add all proxy services to uptime monitor
|
||||
//Create a uptime monitor service
|
||||
@ -128,7 +140,7 @@ func ReverseProxtInit() {
|
||||
Interval: 300, //5 minutes
|
||||
MaxRecordsStore: 288, //1 day
|
||||
})
|
||||
log.Println("Uptime Monitor background service started")
|
||||
SystemWideLogger.Println("Uptime Monitor background service started")
|
||||
}()
|
||||
|
||||
}
|
||||
@ -180,6 +192,13 @@ func ReverseProxyHandleAddEndpoint(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
useTLS := (tls == "true")
|
||||
|
||||
bypassGlobalTLS, _ := utils.PostPara(r, "bypassGlobalTLS")
|
||||
if bypassGlobalTLS == "" {
|
||||
bypassGlobalTLS = "false"
|
||||
}
|
||||
|
||||
useBypassGlobalTLS := bypassGlobalTLS == "true"
|
||||
|
||||
stv, _ := utils.PostPara(r, "tlsval")
|
||||
if stv == "" {
|
||||
stv = "false"
|
||||
@ -240,6 +259,7 @@ func ReverseProxyHandleAddEndpoint(w http.ResponseWriter, r *http.Request) {
|
||||
RootName: vdir,
|
||||
Domain: endpoint,
|
||||
RequireTLS: useTLS,
|
||||
BypassGlobalTLS: useBypassGlobalTLS,
|
||||
SkipCertValidations: skipTlsValidation,
|
||||
RequireBasicAuth: requireBasicAuth,
|
||||
BasicAuthCredentials: basicAuthCredentials,
|
||||
@ -257,6 +277,7 @@ func ReverseProxyHandleAddEndpoint(w http.ResponseWriter, r *http.Request) {
|
||||
MatchingDomain: subdomain,
|
||||
Domain: endpoint,
|
||||
RequireTLS: useTLS,
|
||||
BypassGlobalTLS: useBypassGlobalTLS,
|
||||
SkipCertValidations: skipTlsValidation,
|
||||
RequireBasicAuth: requireBasicAuth,
|
||||
BasicAuthCredentials: basicAuthCredentials,
|
||||
@ -281,6 +302,7 @@ func ReverseProxyHandleAddEndpoint(w http.ResponseWriter, r *http.Request) {
|
||||
Rootname: rootname,
|
||||
ProxyTarget: endpoint,
|
||||
UseTLS: useTLS,
|
||||
BypassGlobalTLS: useBypassGlobalTLS,
|
||||
SkipTlsValidation: skipTlsValidation,
|
||||
RequireBasicAuth: requireBasicAuth,
|
||||
BasicAuthCredentials: basicAuthCredentials,
|
||||
@ -332,9 +354,15 @@ func ReverseProxyHandleEditEndpoint(w http.ResponseWriter, r *http.Request) {
|
||||
if stv == "" {
|
||||
stv = "false"
|
||||
}
|
||||
|
||||
skipTlsValidation := (stv == "true")
|
||||
|
||||
//Load bypass TLS option
|
||||
bpgtls, _ := utils.PostPara(r, "bpgtls")
|
||||
if bpgtls == "" {
|
||||
bpgtls = "false"
|
||||
}
|
||||
bypassGlobalTLS := (bpgtls == "true")
|
||||
|
||||
rba, _ := utils.PostPara(r, "bauth")
|
||||
if rba == "" {
|
||||
rba = "false"
|
||||
@ -354,6 +382,7 @@ func ReverseProxyHandleEditEndpoint(w http.ResponseWriter, r *http.Request) {
|
||||
RootName: targetProxyEntry.RootOrMatchingDomain,
|
||||
Domain: endpoint,
|
||||
RequireTLS: useTLS,
|
||||
BypassGlobalTLS: false,
|
||||
SkipCertValidations: skipTlsValidation,
|
||||
RequireBasicAuth: requireBasicAuth,
|
||||
BasicAuthCredentials: targetProxyEntry.BasicAuthCredentials,
|
||||
@ -366,6 +395,7 @@ func ReverseProxyHandleEditEndpoint(w http.ResponseWriter, r *http.Request) {
|
||||
MatchingDomain: targetProxyEntry.RootOrMatchingDomain,
|
||||
Domain: endpoint,
|
||||
RequireTLS: useTLS,
|
||||
BypassGlobalTLS: bypassGlobalTLS,
|
||||
SkipCertValidations: skipTlsValidation,
|
||||
RequireBasicAuth: requireBasicAuth,
|
||||
BasicAuthCredentials: targetProxyEntry.BasicAuthCredentials,
|
||||
@ -385,6 +415,10 @@ func ReverseProxyHandleEditEndpoint(w http.ResponseWriter, r *http.Request) {
|
||||
BasicAuthCredentials: targetProxyEntry.BasicAuthCredentials,
|
||||
}
|
||||
SaveReverseProxyConfigToFile(&thisProxyConfigRecord)
|
||||
|
||||
//Update uptime monitor
|
||||
UpdateUptimeMonitorTargets()
|
||||
|
||||
utils.SendOK(w)
|
||||
}
|
||||
|
||||
@ -417,6 +451,9 @@ func DeleteProxyEndpoint(w http.ResponseWriter, r *http.Request) {
|
||||
uptimeMonitor.CleanRecords()
|
||||
}
|
||||
|
||||
//Update uptime monitor
|
||||
UpdateUptimeMonitorTargets()
|
||||
|
||||
utils.SendOK(w)
|
||||
}
|
||||
|
||||
@ -731,6 +768,35 @@ func ReverseProxyList(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
}
|
||||
|
||||
// Handle port 80 incoming traffics
|
||||
func HandleUpdatePort80Listener(w http.ResponseWriter, r *http.Request) {
|
||||
enabled, err := utils.GetPara(r, "enable")
|
||||
if err != nil {
|
||||
//Load the current status
|
||||
currentEnabled := false
|
||||
err = sysdb.Read("settings", "listenP80", ¤tEnabled)
|
||||
if err != nil {
|
||||
utils.SendErrorResponse(w, err.Error())
|
||||
return
|
||||
}
|
||||
js, _ := json.Marshal(currentEnabled)
|
||||
utils.SendJSONResponse(w, string(js))
|
||||
} else {
|
||||
if enabled == "true" {
|
||||
sysdb.Write("settings", "listenP80", true)
|
||||
SystemWideLogger.Println("Enabling port 80 listener")
|
||||
dynamicProxyRouter.UpdatePort80ListenerState(true)
|
||||
} else if enabled == "false" {
|
||||
sysdb.Write("settings", "listenP80", false)
|
||||
SystemWideLogger.Println("Disabling port 80 listener")
|
||||
dynamicProxyRouter.UpdatePort80ListenerState(true)
|
||||
} else {
|
||||
utils.SendErrorResponse(w, "invalid mode given: "+enabled)
|
||||
}
|
||||
utils.SendOK(w)
|
||||
}
|
||||
}
|
||||
|
||||
// Handle https redirect
|
||||
func HandleUpdateHttpsRedirect(w http.ResponseWriter, r *http.Request) {
|
||||
useRedirect, err := utils.GetPara(r, "set")
|
||||
@ -751,11 +817,11 @@ func HandleUpdateHttpsRedirect(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
if useRedirect == "true" {
|
||||
sysdb.Write("settings", "redirect", true)
|
||||
log.Println("Updating force HTTPS redirection to true")
|
||||
SystemWideLogger.Println("Updating force HTTPS redirection to true")
|
||||
dynamicProxyRouter.UpdateHttpToHttpsRedirectSetting(true)
|
||||
} else if useRedirect == "false" {
|
||||
sysdb.Write("settings", "redirect", false)
|
||||
log.Println("Updating force HTTPS redirection to false")
|
||||
SystemWideLogger.Println("Updating force HTTPS redirection to false")
|
||||
dynamicProxyRouter.UpdateHttpToHttpsRedirectSetting(false)
|
||||
}
|
||||
|
||||
|
20
src/start.go
20
src/start.go
@ -14,6 +14,7 @@ import (
|
||||
"imuslab.com/zoraxy/mod/dynamicproxy/redirection"
|
||||
"imuslab.com/zoraxy/mod/ganserv"
|
||||
"imuslab.com/zoraxy/mod/geodb"
|
||||
"imuslab.com/zoraxy/mod/info/logger"
|
||||
"imuslab.com/zoraxy/mod/mdns"
|
||||
"imuslab.com/zoraxy/mod/netstat"
|
||||
"imuslab.com/zoraxy/mod/pathrule"
|
||||
@ -93,10 +94,17 @@ func startupSequence() {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
//Create a system wide logger
|
||||
l, err := logger.NewLogger("zr", "./log", *logOutputToFile)
|
||||
if err == nil {
|
||||
SystemWideLogger = l
|
||||
} else {
|
||||
panic(err)
|
||||
}
|
||||
//Create a netstat buffer
|
||||
netstatBuffers, err = netstat.NewNetStatBuffer(300)
|
||||
if err != nil {
|
||||
log.Println("Failed to load network statistic info")
|
||||
SystemWideLogger.PrintAndLog("Network", "Failed to load network statistic info", err)
|
||||
panic(err)
|
||||
}
|
||||
|
||||
@ -134,13 +142,13 @@ func startupSequence() {
|
||||
BuildVersion: version,
|
||||
}, "")
|
||||
if err != nil {
|
||||
log.Println("Unable to startup mDNS service. Disabling mDNS services")
|
||||
SystemWideLogger.Println("Unable to startup mDNS service. Disabling mDNS services")
|
||||
} else {
|
||||
//Start initial scanning
|
||||
go func() {
|
||||
hosts := mdnsScanner.Scan(30, "")
|
||||
previousmdnsScanResults = hosts
|
||||
log.Println("mDNS Startup scan completed")
|
||||
SystemWideLogger.Println("mDNS Startup scan completed")
|
||||
}()
|
||||
|
||||
//Create a ticker to update mDNS results every 5 minutes
|
||||
@ -154,7 +162,7 @@ func startupSequence() {
|
||||
case <-ticker.C:
|
||||
hosts := mdnsScanner.Scan(30, "")
|
||||
previousmdnsScanResults = hosts
|
||||
log.Println("mDNS scan result updated")
|
||||
SystemWideLogger.Println("mDNS scan result updated")
|
||||
}
|
||||
}
|
||||
}()
|
||||
@ -171,7 +179,7 @@ func startupSequence() {
|
||||
if usingZtAuthToken == "" {
|
||||
usingZtAuthToken, err = ganserv.TryLoadorAskUserForAuthkey()
|
||||
if err != nil {
|
||||
log.Println("Failed to load ZeroTier controller API authtoken")
|
||||
SystemWideLogger.Println("Failed to load ZeroTier controller API authtoken")
|
||||
}
|
||||
}
|
||||
ganManager = ganserv.NewNetworkManager(&ganserv.NetworkManagerOptions{
|
||||
@ -220,7 +228,7 @@ func startupSequence() {
|
||||
|
||||
staticWebServer = webserv.NewWebServer(&webserv.WebServerOptions{
|
||||
Sysdb: sysdb,
|
||||
Port: "8081", //Default Port
|
||||
Port: "5487", //Default Port
|
||||
WebRoot: *staticWebServerRoot,
|
||||
EnableDirectoryListing: true,
|
||||
EnableWebDirManager: *allowWebFileManager,
|
||||
|
@ -75,13 +75,13 @@
|
||||
$("#rootReqTLS").parent().addClass("disabled");
|
||||
|
||||
//Check if web server is enabled. If not, ask if the user want to enable it
|
||||
if (!$("#webserv_enable").parent().checkbox("is checked")){
|
||||
/*if (!$("#webserv_enable").parent().checkbox("is checked")){
|
||||
confirmBox("Enable static web server now?", function(choice){
|
||||
if (choice == true){
|
||||
$("#webserv_enable").parent().checkbox("set checked");
|
||||
}
|
||||
});
|
||||
}
|
||||
}*/
|
||||
}else{
|
||||
$("#rootReqTLS").parent().removeClass("disabled");
|
||||
$("#proxyRoot").parent().removeClass("disabled");
|
||||
@ -89,7 +89,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
function initRootInfo(){
|
||||
function initRootInfo(callback=undefined){
|
||||
$.get("/api/proxy/list?type=root", function(data){
|
||||
if (data == null){
|
||||
|
||||
@ -97,10 +97,39 @@
|
||||
$("#proxyRoot").val(data.Domain);
|
||||
checkRootRequireTLS(data.Domain);
|
||||
}
|
||||
|
||||
if (callback != undefined){
|
||||
callback();
|
||||
}
|
||||
});
|
||||
}
|
||||
initRootInfo(function(){
|
||||
updateWebServerLinkSettings();
|
||||
});
|
||||
|
||||
//Update the current web server port settings
|
||||
function updateWebServerLinkSettings(){
|
||||
isUsingStaticWebServerAsRoot(function(isUsingWebServ){
|
||||
if (isUsingWebServ){
|
||||
$(".webservRootDisabled").addClass("disabled");
|
||||
$("#useStaticWebServer").parent().checkbox("set checked");
|
||||
}else{
|
||||
$(".webservRootDisabled").removeClass("disabled");
|
||||
$("#useStaticWebServer").parent().checkbox("set unchecked");
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function isUsingStaticWebServerAsRoot(callback){
|
||||
let currentProxyRoot = $("#proxyRoot").val().trim();
|
||||
$.get("/api/webserv/status", function(webservStatus){
|
||||
if (currentProxyRoot == "127.0.0.1:" + webservStatus.ListeningPort || currentProxyRoot == "localhost:" + webservStatus.ListeningPort){
|
||||
return callback(true);
|
||||
}
|
||||
return callback(false);
|
||||
});
|
||||
|
||||
}
|
||||
initRootInfo();
|
||||
|
||||
function updateRootSettingStates(){
|
||||
$.get("/api/cert/tls", function(data){
|
||||
@ -111,6 +140,7 @@
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
//Bind event to tab switch
|
||||
tabSwitchEventBind["setroot"] = function(){
|
||||
//On switch over to this page, update root info
|
||||
@ -137,11 +167,12 @@
|
||||
let useRedirect = $("#unsetRedirect")[0].checked;
|
||||
updateRedirectionDomainSettingInputBox(useRedirect);
|
||||
});
|
||||
})
|
||||
});
|
||||
|
||||
}
|
||||
checkCustomRedirectForUnsetSubd();
|
||||
|
||||
//Check if the given domain will redirect to https
|
||||
function checkRootRequireTLS(targetDomain){
|
||||
//Trim off the http or https from the origin
|
||||
if (targetDomain.startsWith("http://")){
|
||||
@ -168,7 +199,7 @@
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
//Set the new proxy root option
|
||||
function setProxyRoot(){
|
||||
var newpr = $("#proxyRoot").val();
|
||||
if (newpr.trim() == ""){
|
||||
@ -189,8 +220,24 @@
|
||||
msgbox(data.error, false, 5000);
|
||||
}else{
|
||||
//OK
|
||||
initRootInfo();
|
||||
msgbox("Proxy Root Updated")
|
||||
initRootInfo(function(){
|
||||
//Check if WebServ is enabled
|
||||
isUsingStaticWebServerAsRoot(function(isUsingWebServ){
|
||||
if (isUsingWebServ){
|
||||
//Force enable static web server
|
||||
//See webserv.html for details
|
||||
setWebServerRunningState(true);
|
||||
}
|
||||
|
||||
setTimeout(function(){
|
||||
//Update the checkbox
|
||||
updateWebServerLinkSettings();
|
||||
msgbox("Proxy Root Updated");
|
||||
}, 1000);
|
||||
|
||||
})
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -8,7 +8,7 @@
|
||||
<div class="field">
|
||||
<label>Proxy Type</label>
|
||||
<div class="ui selection dropdown">
|
||||
<input type="hidden" id="ptype" value="subd">
|
||||
<input type="hidden" id="ptype" value="subd" onchange="handleProxyTypeOptionChange(this.value)">
|
||||
<i class="dropdown icon"></i>
|
||||
<div class="default text">Proxy Type</div>
|
||||
<div class="menu">
|
||||
@ -22,7 +22,7 @@
|
||||
<input type="text" id="rootname" placeholder="s1.mydomain.com">
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>IP Address or Domain Name with port</label>
|
||||
<label>Target IP Address or Domain Name with port</label>
|
||||
<input type="text" id="proxyDomain" onchange="autoCheckTls(this.value);">
|
||||
<small>E.g. 192.168.0.101:8000 or example.com</small>
|
||||
</div>
|
||||
@ -44,7 +44,13 @@
|
||||
<div class="field">
|
||||
<div class="ui checkbox">
|
||||
<input type="checkbox" id="skipTLSValidation">
|
||||
<label>Ignore TLS/SSL Verification Error<br><small>E.g. 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>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<div class="ui checkbox">
|
||||
<input type="checkbox" id="bypassGlobalTLS">
|
||||
<label>Allow plain HTTP access<br><small>Allow this subdomain to be connected without TLS (Require HTTP server enabled on port 80)</small></label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
@ -123,6 +129,7 @@
|
||||
var proxyDomain = $("#proxyDomain").val();
|
||||
var useTLS = $("#reqTls")[0].checked;
|
||||
var skipTLSValidation = $("#skipTLSValidation")[0].checked;
|
||||
var bypassGlobalTLS = $("#bypassGlobalTLS")[0].checked;
|
||||
var requireBasicAuth = $("#requireBasicAuth")[0].checked;
|
||||
|
||||
if (type === "vdir") {
|
||||
@ -162,6 +169,7 @@
|
||||
tls: useTLS,
|
||||
ep: proxyDomain,
|
||||
tlsval: skipTLSValidation,
|
||||
bypassGlobalTLS: bypassGlobalTLS,
|
||||
bauth: requireBasicAuth,
|
||||
cred: JSON.stringify(credentials),
|
||||
},
|
||||
@ -206,6 +214,14 @@
|
||||
|
||||
}
|
||||
|
||||
function handleProxyTypeOptionChange(newType){
|
||||
if (newType == "subd"){
|
||||
$("#bypassGlobalTLS").parent().removeClass("disabled");
|
||||
}else if (newType == "vdir"){
|
||||
$("#bypassGlobalTLS").parent().addClass("disabled");
|
||||
}
|
||||
}
|
||||
|
||||
//Generic functions for delete rp endpoints
|
||||
function deleteEndpoint(ptype, epoint){
|
||||
if (confirm("Confirm remove proxy for :" + epoint + " (type: " + ptype + ")?")){
|
||||
@ -331,7 +347,7 @@
|
||||
var columns = row.find('td[data-label]');
|
||||
var payload = $(row).attr("payload");
|
||||
payload = JSON.parse(decodeURIComponent(payload));
|
||||
|
||||
console.log(payload);
|
||||
//console.log(payload);
|
||||
columns.each(function(index) {
|
||||
var column = $(this);
|
||||
@ -347,34 +363,37 @@
|
||||
var datatype = $(this).attr("datatype");
|
||||
if (datatype == "domain"){
|
||||
let domain = payload.Domain;
|
||||
//Target require TLS for proxying
|
||||
let tls = payload.RequireTLS;
|
||||
if (tls){
|
||||
tls = "checked";
|
||||
}else{
|
||||
tls = "";
|
||||
}
|
||||
|
||||
//Require TLS validation
|
||||
let skipTLSValidation = payload.SkipCertValidations;
|
||||
let checkstate = "";
|
||||
if (skipTLSValidation){
|
||||
checkstate = "checked";
|
||||
}
|
||||
|
||||
input = `
|
||||
<div class="ui mini fluid input">
|
||||
<input type="text" class="Domain" value="${domain}">
|
||||
</div>
|
||||
<div class="ui checkbox" style="margin-top: 0.4em;">
|
||||
<input type="checkbox" class="RequireTLS" ${tls}>
|
||||
<label>Require TLS</label>
|
||||
<label>Require TLS<br>
|
||||
<small>Proxy target require HTTPS connection</small></label>
|
||||
</div><br>
|
||||
<div class="ui checkbox" style="margin-top: 0.4em;">
|
||||
<input type="checkbox" class="SkipCertValidations" ${checkstate}>
|
||||
<label>Skip Verification<br>
|
||||
<small>Check this if proxy target is using self signed certificates</small></label>
|
||||
</div>
|
||||
`;
|
||||
column.empty().append(input);
|
||||
|
||||
}else if (datatype == "skipver"){
|
||||
let skipTLSValidation = payload.SkipCertValidations;
|
||||
let checkstate = "";
|
||||
if (skipTLSValidation){
|
||||
checkstate = "checked";
|
||||
}
|
||||
column.empty().append(`<div class="ui checkbox" style="margin-top: 0.4em;">
|
||||
<input type="checkbox" class="SkipCertValidations" ${checkstate}>
|
||||
<label>Skip Verification</label>
|
||||
<small>Check this if you are using self signed certificates</small>
|
||||
</div>`);
|
||||
}else if (datatype == "basicauth"){
|
||||
let requireBasicAuth = payload.RequireBasicAuth;
|
||||
let checkstate = "";
|
||||
@ -392,6 +411,16 @@
|
||||
<button title="Cancel" onclick="exitProxyInlineEdit('${endpointType}');" class="ui basic small circular icon button"><i class="ui remove icon"></i></button>
|
||||
<button title="Save" onclick="saveProxyInlineEdit('${uuid}');" class="ui basic small circular icon button"><i class="ui green save icon"></i></button>
|
||||
`);
|
||||
}else if (datatype == "inbound" && payload.ProxyType == 0){
|
||||
let originalContent = $(column).html();
|
||||
column.empty().append(`${originalContent}
|
||||
<div class="ui divider"></div>
|
||||
<div class="ui checkbox" style="margin-top: 0.4em;">
|
||||
<input type="checkbox" class="BypassGlobalTLS" ${payload.BypassGlobalTLS?"checked":""}>
|
||||
<label>Allow plain HTTP access<br>
|
||||
<small>Allow inbound connections without TLS/SSL</small></label>
|
||||
</div><br>
|
||||
`);
|
||||
}else{
|
||||
//Unknown field. Leave it untouched
|
||||
}
|
||||
@ -423,6 +452,7 @@
|
||||
let requireTLS = $(row).find(".RequireTLS")[0].checked;
|
||||
let skipCertValidations = $(row).find(".SkipCertValidations")[0].checked;
|
||||
let requireBasicAuth = $(row).find(".RequireBasicAuth")[0].checked;
|
||||
let bypassGlobalTLS = $(row).find(".BypassGlobalTLS")[0].checked;
|
||||
|
||||
console.log(newDomain, requireTLS, skipCertValidations, requireBasicAuth)
|
||||
|
||||
@ -433,6 +463,7 @@
|
||||
"type": epttype,
|
||||
"rootname": uuid,
|
||||
"ep":newDomain,
|
||||
"bpgtls": bypassGlobalTLS,
|
||||
"tls" :requireTLS,
|
||||
"tlsval": skipCertValidations,
|
||||
"bauth" :requireBasicAuth,
|
||||
|
@ -72,10 +72,15 @@
|
||||
<label>Use TLS to serve proxy request</label>
|
||||
</div>
|
||||
<br>
|
||||
<div id="redirect" class="ui toggle notloopbackOnly tlsEnabledOnly checkbox" style="margin-top: 0.6em;">
|
||||
<div id="listenP80" class="ui toggle notloopbackOnly tlsEnabledOnly checkbox" style="margin-top: 0.6em;" >
|
||||
<input type="checkbox">
|
||||
<label>Force redirect HTTP request to HTTPS<br>
|
||||
<small>(Only apply when listening port is not 80)</small></label>
|
||||
<label>Enable HTTP server on port 80<br>
|
||||
<small>(Only apply when TLS enabled and not using port 80)</small></label>
|
||||
</div>
|
||||
<br>
|
||||
<div id="redirect" class="ui toggle notloopbackOnly tlsEnabledOnly checkbox" style="margin-top: 0.6em; padding-left: 2em;">
|
||||
<input type="checkbox">
|
||||
<label>Force redirect HTTP request to HTTPS</label>
|
||||
</div>
|
||||
<div class="ui basic segment" style="background-color: #f7f7f7; border-radius: 1em;">
|
||||
<div class="ui accordion advanceSettings">
|
||||
@ -181,6 +186,7 @@
|
||||
$("#serverstatus").removeClass("green");
|
||||
}
|
||||
$("#incomingPort").val(data.Option.Port);
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
@ -305,6 +311,27 @@
|
||||
});
|
||||
}
|
||||
|
||||
function handleP80ListenerStateChange(enabled){
|
||||
$.ajax({
|
||||
url: "/api/proxy/listenPort80",
|
||||
data: {"enable": enabled},
|
||||
success: function(data){
|
||||
if (data.error != undefined){
|
||||
console.log(data.error);
|
||||
return;
|
||||
}
|
||||
if (enabled){
|
||||
$("#redirect").show();
|
||||
msgbox("Port 80 listener enabled");
|
||||
}else{
|
||||
$("#redirect").hide();
|
||||
msgbox("Port 80 listener disabled");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
function handlePortChange(){
|
||||
var newPortValue = $("#incomingPort").val();
|
||||
@ -323,6 +350,25 @@
|
||||
});
|
||||
}
|
||||
|
||||
function initPort80ListenerSetting(){
|
||||
$.get("/api/proxy/listenPort80", function(data){
|
||||
if (data){
|
||||
$("#listenP80").checkbox("set checked");
|
||||
$("#redirect").show();
|
||||
}else{
|
||||
$("#listenP80").checkbox("set unchecked");
|
||||
$("#redirect").hide();
|
||||
}
|
||||
|
||||
$("#listenP80").find("input").on("change", function(){
|
||||
let enabled = $(this)[0].checked;
|
||||
handleP80ListenerStateChange(enabled);
|
||||
})
|
||||
});
|
||||
|
||||
}
|
||||
initPort80ListenerSetting();
|
||||
|
||||
function initHTTPtoHTTPSRedirectSetting(){
|
||||
$.get("/api/proxy/useHttpsRedirect", function(data){
|
||||
if (data == true){
|
||||
@ -356,8 +402,6 @@
|
||||
})
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
initHTTPtoHTTPSRedirectSetting();
|
||||
|
||||
|
@ -9,7 +9,6 @@
|
||||
<tr>
|
||||
<th>Matching Domain</th>
|
||||
<th>Proxy To</th>
|
||||
<th>TLS/SSL Verification</th>
|
||||
<th>Basic Auth</th>
|
||||
<th class="no-sort" style="min-width: 7.2em;">Actions</th>
|
||||
</tr>
|
||||
@ -41,19 +40,14 @@
|
||||
let subdData = encodeURIComponent(JSON.stringify(subd));
|
||||
if (subd.RequireTLS){
|
||||
tlsIcon = `<i class="green lock icon" title="TLS Mode"></i>`;
|
||||
if (subd.SkipCertValidations){
|
||||
tlsIcon = `<i class="yellow lock icon" title="TLS/SSL mode without verification"></i>`
|
||||
}
|
||||
|
||||
let tlsVerificationField = "";
|
||||
if (subd.RequireTLS){
|
||||
tlsVerificationField = !subd.SkipCertValidations?`<i class="ui green check icon"></i>`:`<i class="ui yellow exclamation circle icon" title="TLS/SSL Verification will be skipped on this host"></i>`
|
||||
}else{
|
||||
tlsVerificationField = "N/A"
|
||||
}
|
||||
|
||||
$("#subdList").append(`<tr eptuuid="${subd.RootOrMatchingDomain}" payload="${subdData}" class="subdEntry">
|
||||
<td data-label="" editable="false"><a href="//${subd.RootOrMatchingDomain}" target="_blank">${subd.RootOrMatchingDomain}</a></td>
|
||||
<td data-label="" editable="true" datatype="inbound"><a href="//${subd.RootOrMatchingDomain}" target="_blank">${subd.RootOrMatchingDomain}</a></td>
|
||||
<td data-label="" editable="true" datatype="domain">${subd.Domain} ${tlsIcon}</td>
|
||||
<td data-label="" editable="true" datatype="skipver">${tlsVerificationField}</td>
|
||||
<td data-label="" editable="true" datatype="basicauth">${subd.RequireBasicAuth?`<i class="ui green check icon"></i>`:`<i class="ui grey remove icon"></i>`}</td>
|
||||
<td class="center aligned" editable="true" datatype="action" data-label="">
|
||||
<button class="ui circular mini basic icon button editBtn" onclick='editEndpoint("subd","${subd.RootOrMatchingDomain}")'><i class="edit icon"></i></button>
|
||||
|
@ -86,7 +86,7 @@
|
||||
|
||||
let id = value[0].ID;
|
||||
let name = value[0].Name;
|
||||
let url = value[0].URL;
|
||||
let url = value[value.length - 1].URL;
|
||||
let protocol = value[0].Protocol;
|
||||
|
||||
//Generate the status dot
|
||||
@ -112,6 +112,9 @@
|
||||
if (thisStatus.StatusCode >= 500 && thisStatus.StatusCode < 600){
|
||||
//Special type of error, cause by downstream reverse proxy
|
||||
dotType = "error";
|
||||
}else if (thisStatus.StatusCode == 401){
|
||||
//Unauthorized error
|
||||
dotType = "error";
|
||||
}else{
|
||||
dotType = "offline";
|
||||
}
|
||||
@ -141,6 +144,28 @@
|
||||
currentOnlineStatus = `<i class="exclamation circle icon"></i> Misconfigured`;
|
||||
onlineStatusCss = `color: #f38020;`;
|
||||
reminderEle = `<small style="${onlineStatusCss}">Downstream proxy server is online with misconfigured settings</small>`;
|
||||
}else if (value[value.length - 1].StatusCode >= 400 && value[value.length - 1].StatusCode <= 405){
|
||||
switch(value[value.length - 1].StatusCode){
|
||||
case 400:
|
||||
currentOnlineStatus = `<i class="exclamation circle icon"></i> Bad Request`;
|
||||
break;
|
||||
case 401:
|
||||
currentOnlineStatus = `<i class="exclamation circle icon"></i> Unauthorized`;
|
||||
break;
|
||||
case 403:
|
||||
currentOnlineStatus = `<i class="exclamation circle icon"></i> Forbidden`;
|
||||
break;
|
||||
case 404:
|
||||
currentOnlineStatus = `<i class="exclamation circle icon"></i> Not Found`;
|
||||
break;
|
||||
case 405:
|
||||
currentOnlineStatus = `<i class="exclamation circle icon"></i> Method Not Allowed`;
|
||||
break;
|
||||
}
|
||||
|
||||
onlineStatusCss = `color: #f38020;`;
|
||||
reminderEle = `<small style="${onlineStatusCss}">Target online but not accessible</small>`;
|
||||
|
||||
}else{
|
||||
currentOnlineStatus = `<i class="circle icon"></i> Offline`;
|
||||
onlineStatusCss = `color: #df484a;`;
|
||||
|
@ -9,7 +9,6 @@
|
||||
<tr>
|
||||
<th>Virtual Directory</th>
|
||||
<th>Proxy To</th>
|
||||
<th>TLS/SSL Verification</th>
|
||||
<th>Basic Auth</th>
|
||||
<th class="no-sort" style="min-width: 7.2em;">Actions</th>
|
||||
</tr>
|
||||
@ -43,6 +42,9 @@
|
||||
let vdirData = encodeURIComponent(JSON.stringify(vdir));
|
||||
if (vdir.RequireTLS){
|
||||
tlsIcon = `<i class="green lock icon" title="TLS Mode"></i>`;
|
||||
if (vdir.SkipCertValidations){
|
||||
tlsIcon = `<i class="yellow lock icon" title="TLS/SSL mode without verification"></i>`
|
||||
}
|
||||
}
|
||||
|
||||
let tlsVerificationField = "";
|
||||
@ -55,7 +57,6 @@
|
||||
$("#vdirList").append(`<tr eptuuid="${vdir.RootOrMatchingDomain}" payload="${vdirData}" class="vdirEntry">
|
||||
<td data-label="" editable="false">${vdir.RootOrMatchingDomain}</td>
|
||||
<td data-label="" editable="true" datatype="domain">${vdir.Domain} ${tlsIcon}</td>
|
||||
<td data-label="" editable="true" datatype="skipver">${tlsVerificationField}</td>
|
||||
<td data-label="" editable="true" datatype="basicauth">${vdir.RequireBasicAuth?`<i class="ui green check icon"></i>`:`<i class="ui grey remove icon"></i>`}</td>
|
||||
<td class="center aligned" editable="true" datatype="action" data-label="">
|
||||
<button class="ui circular mini basic icon button editBtn" onclick='editEndpoint("vdir","${vdir.RootOrMatchingDomain}")'><i class="edit icon"></i></button>
|
||||
|
@ -17,7 +17,7 @@
|
||||
<h3>Web Server Settings</h3>
|
||||
<div class="ui form">
|
||||
<div class="inline field">
|
||||
<div class="ui toggle checkbox">
|
||||
<div class="ui toggle checkbox webservRootDisabled">
|
||||
<input id="webserv_enable" type="checkbox" class="hidden">
|
||||
<label>Enable Static Web Server</label>
|
||||
</div>
|
||||
@ -37,7 +37,7 @@
|
||||
See the -webserv flag for more details.
|
||||
</small>
|
||||
</div>
|
||||
<div class="field">
|
||||
<div class="field webservRootDisabled">
|
||||
<label>Port Number</label>
|
||||
<input id="webserv_listenPort" type="number" step="1" min="0" max="65535" value="8081" onchange="updateWebServLinkExample(this.value);">
|
||||
<small>Use <code>http://127.0.0.1:<span class="webserv_port">8081</span></code> in proxy rules to access the web server</small>
|
||||
|
@ -39,7 +39,7 @@
|
||||
<h3 style="margin-top: 1em;">403 - Forbidden</h3>
|
||||
<div class="ui divider"></div>
|
||||
<p>You do not have permission to view this directory or page. <br>
|
||||
This might cause by the region limit setting of this site.</p>
|
||||
This might be caused by the region limit setting of this site.</p>
|
||||
<div class="ui divider"></div>
|
||||
<div style="text-align: left;">
|
||||
<small>Request time: <span id="reqtime"></span></small><br>
|
||||
|
@ -50,7 +50,7 @@
|
||||
</a>
|
||||
<div class="ui divider menudivider">Access & Connections</div>
|
||||
<a class="item" tag="cert">
|
||||
<i class="simplistic lock icon"></i> TLS / SSL certificate
|
||||
<i class="simplistic lock icon"></i> TLS / SSL certificates
|
||||
</a>
|
||||
<a class="item" tag="redirectset">
|
||||
<i class="simplistic level up alternate icon"></i> Redirection
|
||||
@ -150,14 +150,13 @@
|
||||
<br><br>
|
||||
<div class="ui divider"></div>
|
||||
<div class="ui container" style="color: grey; font-size: 90%">
|
||||
<p>CopyRight Zoraxy project and its author, 2022 - <span class="year"></span></p>
|
||||
<p>CopyRight Zoraxy Project and its authors © 2021 - <span class="year"></span></p>
|
||||
</div>
|
||||
|
||||
<div id="messageBox" class="ui green floating big compact message">
|
||||
<p><i class="green check circle icon"></i> There are no message</p>
|
||||
<p><i class="green check circle icon"></i> There are no messages</p>
|
||||
</div>
|
||||
|
||||
|
||||
<div id="confirmBox" style="display:none;">
|
||||
<div class="ui top attached progress">
|
||||
<div class="bar" style="width: 100%; min-width: 0px;"></div>
|
||||
|
@ -19,7 +19,6 @@ package main
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
@ -104,7 +103,21 @@ func HandleCountryDistrSummary(w http.ResponseWriter, r *http.Request) {
|
||||
/*
|
||||
Up Time Monitor
|
||||
*/
|
||||
//Generate uptime monitor targets from reverse proxy rules
|
||||
|
||||
// Update uptime monitor targets after rules updated
|
||||
// See https://github.com/tobychui/zoraxy/issues/77
|
||||
func UpdateUptimeMonitorTargets() {
|
||||
if uptimeMonitor != nil {
|
||||
uptimeMonitor.Config.Targets = GetUptimeTargetsFromReverseProxyRules(dynamicProxyRouter)
|
||||
go func() {
|
||||
uptimeMonitor.ExecuteUptimeCheck()
|
||||
}()
|
||||
|
||||
SystemWideLogger.PrintAndLog("Uptime", "Uptime monitor config updated", nil)
|
||||
}
|
||||
}
|
||||
|
||||
// Generate uptime monitor targets from reverse proxy rules
|
||||
func GetUptimeTargetsFromReverseProxyRules(dp *dynamicproxy.Router) []*uptime.Target {
|
||||
subds := dp.GetSDProxyEndpointsAsMap()
|
||||
vdirs := dp.GetVDProxyEndpointsAsMap()
|
||||
@ -263,7 +276,7 @@ func HandleWakeOnLan(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
log.Println("[WoL] Sending Wake on LAN magic packet to " + wake)
|
||||
SystemWideLogger.PrintAndLog("WoL", "Sending Wake on LAN magic packet to "+wake, nil)
|
||||
err := wakeonlan.WakeTarget(wake)
|
||||
if err != nil {
|
||||
utils.SendErrorResponse(w, err.Error())
|
||||
|
Reference in New Issue
Block a user