mirror of
https://github.com/tobychui/zoraxy.git
synced 2025-10-13 14:19:43 +02:00
Compare commits
9 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
9ed9d9ede4 | ||
![]() |
ed1b0ec673 | ||
![]() |
b22e011131 | ||
![]() |
d155ea3795 | ||
![]() |
dd610e5f75 | ||
![]() |
c424b92036 | ||
![]() |
2c270640e9 | ||
![]() |
cf2cf18136 | ||
![]() |
e77f947d1d |
@@ -27,6 +27,7 @@ import (
|
|||||||
"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/info/logger"
|
||||||
|
"imuslab.com/zoraxy/mod/netutils"
|
||||||
"imuslab.com/zoraxy/mod/utils"
|
"imuslab.com/zoraxy/mod/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -432,18 +433,18 @@ func (a *ACMEHandler) HandleGetExpiredDomains(w http.ResponseWriter, r *http.Req
|
|||||||
// to renew the certificate, and sends a JSON response indicating the result of the renewal process.
|
// to renew the certificate, and sends a JSON response indicating the result of the renewal process.
|
||||||
func (a *ACMEHandler) HandleRenewCertificate(w http.ResponseWriter, r *http.Request) {
|
func (a *ACMEHandler) HandleRenewCertificate(w http.ResponseWriter, r *http.Request) {
|
||||||
domainPara, err := utils.PostPara(r, "domains")
|
domainPara, err := utils.PostPara(r, "domains")
|
||||||
|
|
||||||
//Clean each domain
|
//Clean each domain
|
||||||
cleanedDomains := []string{}
|
cleanedDomains := []string{}
|
||||||
if (domainPara != "") {
|
if domainPara != "" {
|
||||||
for _, d := range strings.Split(domainPara, ",") {
|
for _, d := range strings.Split(domainPara, ",") {
|
||||||
// Apply normalization on each domain
|
// Apply normalization on each domain
|
||||||
nd, err := NormalizeDomain(d)
|
nd, err := netutils.NormalizeDomain(d)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.SendErrorResponse(w, jsonEscape(err.Error()))
|
utils.SendErrorResponse(w, jsonEscape(err.Error()))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
cleanedDomains = append(cleanedDomains, nd)
|
cleanedDomains = append(cleanedDomains, nd)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -507,7 +508,6 @@ func (a *ACMEHandler) HandleRenewCertificate(w http.ResponseWriter, r *http.Requ
|
|||||||
dns = true
|
dns = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Default propagation timeout is 300 seconds
|
// Default propagation timeout is 300 seconds
|
||||||
propagationTimeout := 300
|
propagationTimeout := 300
|
||||||
if dns {
|
if dns {
|
||||||
@@ -549,7 +549,6 @@ func (a *ACMEHandler) HandleRenewCertificate(w http.ResponseWriter, r *http.Requ
|
|||||||
a.Logf("Could not extract SANs from PEM, using domainPara only", err)
|
a.Logf("Could not extract SANs from PEM, using domainPara only", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Extract DNS servers from the request
|
// Extract DNS servers from the request
|
||||||
var dnsServers []string
|
var dnsServers []string
|
||||||
dnsServersPara, err := utils.PostPara(r, "dnsServers")
|
dnsServersPara, err := utils.PostPara(r, "dnsServers")
|
||||||
|
@@ -7,8 +7,6 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"time"
|
"time"
|
||||||
"strings"
|
|
||||||
"unicode"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Get the issuer name from pem file
|
// Get the issuer name from pem file
|
||||||
@@ -42,8 +40,6 @@ func ExtractDomains(certBytes []byte) ([]string, error) {
|
|||||||
return []string{}, errors.New("decode cert bytes failed")
|
return []string{}, errors.New("decode cert bytes failed")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
func ExtractIssuerName(certBytes []byte) (string, error) {
|
func ExtractIssuerName(certBytes []byte) (string, error) {
|
||||||
// Parse the PEM block
|
// Parse the PEM block
|
||||||
block, _ := pem.Decode(certBytes)
|
block, _ := pem.Decode(certBytes)
|
||||||
@@ -73,9 +69,9 @@ func ExtractDomainsFromPEM(pemFilePath string) ([]string, error) {
|
|||||||
|
|
||||||
certBytes, err := os.ReadFile(pemFilePath)
|
certBytes, err := os.ReadFile(pemFilePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
domains,err := ExtractDomains(certBytes)
|
domains, err := ExtractDomains(certBytes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -116,48 +112,3 @@ func CertExpireSoon(certBytes []byte, numberOfDays int) bool {
|
|||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// NormalizeDomain cleans and validates a domain string.
|
|
||||||
// - Trims spaces around the domain
|
|
||||||
// - Converts to lowercase
|
|
||||||
// - Removes trailing dot (FQDN canonicalization)
|
|
||||||
// - Checks that the domain conforms to standard rules:
|
|
||||||
// * Each label ≤ 63 characters
|
|
||||||
// * Only letters, digits, and hyphens
|
|
||||||
// * Labels do not start or end with a hyphen
|
|
||||||
// * Full domain ≤ 253 characters
|
|
||||||
// Returns an empty string if the domain is invalid.
|
|
||||||
func NormalizeDomain(d string) (string, error) {
|
|
||||||
d = strings.TrimSpace(d)
|
|
||||||
d = strings.ToLower(d)
|
|
||||||
d = strings.TrimSuffix(d, ".")
|
|
||||||
|
|
||||||
if len(d) == 0 {
|
|
||||||
return "", errors.New("domain is empty")
|
|
||||||
}
|
|
||||||
if len(d) > 253 {
|
|
||||||
return "", errors.New("domain exceeds 253 characters")
|
|
||||||
}
|
|
||||||
|
|
||||||
labels := strings.Split(d, ".")
|
|
||||||
for _, label := range labels {
|
|
||||||
if len(label) == 0 {
|
|
||||||
return "", errors.New("Domain '" + d + "' not valid: Empty label")
|
|
||||||
}
|
|
||||||
if len(label) > 63 {
|
|
||||||
return "", errors.New("Domain not valid: label exceeds 63 characters")
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, r := range label {
|
|
||||||
if !(unicode.IsLetter(r) || unicode.IsDigit(r) || r == '-') {
|
|
||||||
return "", errors.New("Domain '" + d + "' not valid: Invalid character '" + string(r) + "' in label")
|
|
||||||
}
|
|
||||||
if (i == 0 || i == len(label)-1) && r == '-' {
|
|
||||||
return "", errors.New("Domain '" + d + "' not valid: label '"+ label +"' starts or ends with hyphen")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return d, nil
|
|
||||||
}
|
|
||||||
|
@@ -92,7 +92,6 @@ func (h *ProxyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//Plugin routing
|
//Plugin routing
|
||||||
|
|
||||||
if h.Parent.Option.PluginManager != nil && h.Parent.Option.PluginManager.HandleRoute(w, r, sep.Tags) {
|
if h.Parent.Option.PluginManager != nil && h.Parent.Option.PluginManager.HandleRoute(w, r, sep.Tags) {
|
||||||
//Request handled by subroute
|
//Request handled by subroute
|
||||||
return
|
return
|
||||||
|
@@ -438,7 +438,15 @@ func (p *ReverseProxy) ProxyHTTPS(rw http.ResponseWriter, req *http.Request) (in
|
|||||||
if !strings.Contains(host, ":") {
|
if !strings.Contains(host, ":") {
|
||||||
host += ":443"
|
host += ":443"
|
||||||
}
|
}
|
||||||
serverName := req.URL.Hostname()
|
serverName := ""
|
||||||
|
//if p.Transport != nil {
|
||||||
|
// if tr, ok := p.Transport.(*http.Transport); ok && tr.TLSClientConfig != nil && tr.TLSClientConfig.ServerName != "" {
|
||||||
|
// serverName = tr.TLSClientConfig.ServerName
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
if serverName == "" {
|
||||||
|
serverName = req.URL.Hostname()
|
||||||
|
}
|
||||||
|
|
||||||
// Connect with SNI offload
|
// Connect with SNI offload
|
||||||
tlsConfig := &tls.Config{
|
tlsConfig := &tls.Config{
|
||||||
|
@@ -272,6 +272,11 @@ func (ep *ProxyEndpoint) Remove() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if the proxy endpoint is enabled
|
||||||
|
func (ep *ProxyEndpoint) IsEnabled() bool {
|
||||||
|
return !ep.Disabled
|
||||||
|
}
|
||||||
|
|
||||||
// Write changes to runtime without respawning the proxy handler
|
// Write changes to runtime without respawning the proxy handler
|
||||||
// use prepare -> remove -> add if you change anything in the endpoint
|
// use prepare -> remove -> add if you change anything in the endpoint
|
||||||
// that effects the proxy routing src / dest
|
// that effects the proxy routing src / dest
|
||||||
|
@@ -12,6 +12,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"imuslab.com/zoraxy/mod/dynamicproxy/dpcore"
|
"imuslab.com/zoraxy/mod/dynamicproxy/dpcore"
|
||||||
|
"imuslab.com/zoraxy/mod/dynamicproxy/loadbalance"
|
||||||
"imuslab.com/zoraxy/mod/dynamicproxy/rewrite"
|
"imuslab.com/zoraxy/mod/dynamicproxy/rewrite"
|
||||||
"imuslab.com/zoraxy/mod/netutils"
|
"imuslab.com/zoraxy/mod/netutils"
|
||||||
"imuslab.com/zoraxy/mod/statistic"
|
"imuslab.com/zoraxy/mod/statistic"
|
||||||
@@ -95,27 +96,47 @@ func (router *Router) GetProxyEndpointFromHostname(hostname string) *ProxyEndpoi
|
|||||||
return targetSubdomainEndpoint
|
return targetSubdomainEndpoint
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clearn URL Path (without the http:// part) replaces // in a URL to /
|
|
||||||
func (router *Router) clearnURL(targetUrlOPath string) string {
|
|
||||||
return strings.ReplaceAll(targetUrlOPath, "//", "/")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Rewrite URL rewrite the prefix part of a virtual directory URL with /
|
// Rewrite URL rewrite the prefix part of a virtual directory URL with /
|
||||||
func (router *Router) rewriteURL(rooturl string, requestURL string) string {
|
func (router *Router) rewriteURL(rooturl string, requestURL string) string {
|
||||||
rewrittenURL := requestURL
|
rewrittenURL := requestURL
|
||||||
rewrittenURL = strings.TrimPrefix(rewrittenURL, strings.TrimSuffix(rooturl, "/"))
|
rewrittenURL = strings.TrimPrefix(rewrittenURL, strings.TrimSuffix(rooturl, "/"))
|
||||||
|
|
||||||
if strings.Contains(rewrittenURL, "//") {
|
if strings.Contains(rewrittenURL, "//") {
|
||||||
rewrittenURL = router.clearnURL(rewrittenURL)
|
rewrittenURL = strings.ReplaceAll(rewrittenURL, "//", "/")
|
||||||
}
|
}
|
||||||
return rewrittenURL
|
return rewrittenURL
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// upstreamHostSwap check if this loopback to one of the proxy rule in the system. If yes, do a shortcut target swap
|
||||||
|
// this prevents unnecessary external DNS lookup and connection, return true if swapped and request is already handled
|
||||||
|
// by the loopback handler. Only continue if return is false
|
||||||
|
func (h *ProxyHandler) upstreamHostSwap(w http.ResponseWriter, r *http.Request, selectedUpstream *loadbalance.Upstream) bool {
|
||||||
|
upstreamHostname := selectedUpstream.OriginIpOrDomain
|
||||||
|
if strings.Contains(upstreamHostname, ":") {
|
||||||
|
upstreamHostname = strings.Split(upstreamHostname, ":")[0]
|
||||||
|
}
|
||||||
|
loopbackProxyEndpoint := h.Parent.GetProxyEndpointFromHostname(upstreamHostname)
|
||||||
|
if loopbackProxyEndpoint != nil {
|
||||||
|
//This is a loopback request. Swap the target to the loopback target
|
||||||
|
//h.Parent.Option.Logger.PrintAndLog("proxy", "Detected a loopback request to self. Swap the target to "+loopbackProxyEndpoint.RootOrMatchingDomain, nil)
|
||||||
|
if loopbackProxyEndpoint.IsEnabled() {
|
||||||
|
h.hostRequest(w, r, loopbackProxyEndpoint)
|
||||||
|
} else {
|
||||||
|
//Endpoint disabled, return 503
|
||||||
|
http.ServeFile(w, r, "./web/rperror.html")
|
||||||
|
h.Parent.logRequest(r, false, 521, "host-http", r.Host, upstreamHostname)
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// Handle host request
|
// Handle host request
|
||||||
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)
|
||||||
reqHostname := r.Host
|
reqHostname := r.Host
|
||||||
|
|
||||||
/* Load balancing */
|
/* 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 {
|
||||||
@@ -125,6 +146,12 @@ func (h *ProxyHandler) hostRequest(w http.ResponseWriter, r *http.Request, targe
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Upstream Host Swap (use to detect loopback to self) */
|
||||||
|
if h.upstreamHostSwap(w, r, selectedUpstream) {
|
||||||
|
//Request handled by the loopback handler
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
/* WebSocket automatic proxy */
|
/* 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" {
|
||||||
|
@@ -2,10 +2,13 @@ package netutils
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"unicode"
|
||||||
|
|
||||||
"github.com/likexian/whois"
|
"github.com/likexian/whois"
|
||||||
"imuslab.com/zoraxy/mod/utils"
|
"imuslab.com/zoraxy/mod/utils"
|
||||||
@@ -167,3 +170,53 @@ func CheckIfPortOccupied(portNumber int) bool {
|
|||||||
listener.Close()
|
listener.Close()
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NormalizeDomain cleans and validates a domain string.
|
||||||
|
// - Trims spaces around the domain
|
||||||
|
// - Converts to lowercase
|
||||||
|
// - Removes trailing dot (FQDN canonicalization)
|
||||||
|
// - Checks that the domain conforms to standard rules:
|
||||||
|
// - Each label ≤ 63 characters
|
||||||
|
// - Only letters, digits, and hyphens
|
||||||
|
// - Labels do not start or end with a hyphen
|
||||||
|
// - Full domain ≤ 253 characters
|
||||||
|
//
|
||||||
|
// Returns an empty string if the domain is invalid.
|
||||||
|
func NormalizeDomain(d string) (string, error) {
|
||||||
|
d = strings.TrimSpace(d)
|
||||||
|
d = strings.ToLower(d)
|
||||||
|
d = strings.TrimSuffix(d, ".")
|
||||||
|
|
||||||
|
if len(d) == 0 {
|
||||||
|
return "", errors.New("domain is empty")
|
||||||
|
}
|
||||||
|
if len(d) > 253 {
|
||||||
|
return "", errors.New("domain exceeds 253 characters")
|
||||||
|
}
|
||||||
|
|
||||||
|
labels := strings.Split(d, ".")
|
||||||
|
for index, label := range labels {
|
||||||
|
if index == 0 {
|
||||||
|
if len(label) == 1 && label == "*" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(label) == 0 {
|
||||||
|
return "", errors.New("Domain '" + d + "' not valid: Empty label")
|
||||||
|
}
|
||||||
|
if len(label) > 63 {
|
||||||
|
return "", errors.New("Domain not valid: label exceeds 63 characters")
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, r := range label {
|
||||||
|
if !unicode.IsLetter(r) && !unicode.IsDigit(r) && r != '-' {
|
||||||
|
return "", errors.New("Domain '" + d + "' not valid: Invalid character '" + string(r) + "' in label")
|
||||||
|
}
|
||||||
|
if (i == 0 || i == len(label)-1) && r == '-' {
|
||||||
|
return "", errors.New("Domain '" + d + "' not valid: label '" + label + "' starts or ends with hyphen")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return d, nil
|
||||||
|
}
|
||||||
|
@@ -211,7 +211,6 @@ func getWebsiteStatus(url string) (int, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
resp, err := client.Do(req)
|
resp, err := client.Do(req)
|
||||||
//resp, err := client.Get(url)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
//Try replace the http with https and vise versa
|
//Try replace the http with https and vise versa
|
||||||
rewriteURL := ""
|
rewriteURL := ""
|
||||||
|
@@ -199,4 +199,4 @@ func ValidateListeningAddress(address string) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
@@ -238,6 +238,13 @@ func ReverseProxyHandleAddEndpoint(w http.ResponseWriter, r *http.Request) {
|
|||||||
bypassGlobalTLS, _ := utils.PostPara(r, "bypassGlobalTLS")
|
bypassGlobalTLS, _ := utils.PostPara(r, "bypassGlobalTLS")
|
||||||
if bypassGlobalTLS == "" {
|
if bypassGlobalTLS == "" {
|
||||||
bypassGlobalTLS = "false"
|
bypassGlobalTLS = "false"
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enable uptime monitor?
|
||||||
|
enableUtm, err := utils.PostBool(r, "enableUtm")
|
||||||
|
if err != nil {
|
||||||
|
enableUtm = true
|
||||||
}
|
}
|
||||||
|
|
||||||
useBypassGlobalTLS := bypassGlobalTLS == "true"
|
useBypassGlobalTLS := bypassGlobalTLS == "true"
|
||||||
@@ -410,7 +417,8 @@ func ReverseProxyHandleAddEndpoint(w http.ResponseWriter, r *http.Request) {
|
|||||||
RequireRateLimit: requireRateLimit,
|
RequireRateLimit: requireRateLimit,
|
||||||
RateLimit: int64(proxyRateLimit),
|
RateLimit: int64(proxyRateLimit),
|
||||||
|
|
||||||
Tags: tags,
|
Tags: tags,
|
||||||
|
DisableUptimeMonitor: !enableUtm,
|
||||||
}
|
}
|
||||||
|
|
||||||
preparedEndpoint, err := dynamicProxyRouter.PrepareProxyRoute(&thisProxyEndpoint)
|
preparedEndpoint, err := dynamicProxyRouter.PrepareProxyRoute(&thisProxyEndpoint)
|
||||||
|
@@ -62,6 +62,13 @@
|
|||||||
<input type="checkbox" id="useStickySessionLB">
|
<input type="checkbox" id="useStickySessionLB">
|
||||||
<label>Sticky Session<br><small>Enable stick session on upstream load balancing</small></label>
|
<label>Sticky Session<br><small>Enable stick session on upstream load balancing</small></label>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<div class="ui checkbox">
|
||||||
|
<input type="checkbox" id="enableUtm" checked>
|
||||||
|
<label>Enable uptime monitor<br><small>Automatically check upstream status and switch to another if offline</small>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label>Tags</label>
|
<label>Tags</label>
|
||||||
@@ -168,22 +175,78 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="six wide column">
|
<div class="six wide column">
|
||||||
<div class="ui basic segment rulesInstructions">
|
<div class="ui basic segment rulesInstructions">
|
||||||
<span style="font-size: 1.2em; font-weight: 300;"><i class="ui yellow star icon"></i> Domain</span><br>
|
<div class="ui fluid styled accordion" id="matchingKeywordExamplesAccordion" style="background-color: transparent !important;">
|
||||||
Example of domain matching keyword:<br>
|
<div class="title active" style="color: white;">
|
||||||
<code>aroz.org</code> <br>Any acess requesting aroz.org will be proxy to the IP address below<br>
|
<i class="dropdown icon"></i>
|
||||||
<div class="ui divider"></div>
|
Matching Keyword Examples
|
||||||
<span style="font-size: 1.2em; font-weight: 300;"><i class="ui yellow star icon"></i> Subdomain</span><br>
|
</div>
|
||||||
Example of subdomain matching keyword:<br>
|
<div class="content active">
|
||||||
<code>s1.aroz.org</code> <br>Any request starting with s1.aroz.org will be proxy to the IP address below<br>
|
<span style="font-size: 1.2em; font-weight: 300;"><i class="ui yellow star icon"></i> Domain</span><br>
|
||||||
<div class="ui divider"></div>
|
Example of domain matching keyword:<br>
|
||||||
<span style="font-size: 1.2em; font-weight: 300;"><i class="ui yellow star icon"></i> Wildcard</span><br>
|
<code>aroz.org</code> <br>Any acess requesting aroz.org will be proxy to the IP address below<br>
|
||||||
Example of wildcard matching keyword:<br>
|
<div class="ui divider"></div>
|
||||||
<code>*.aroz.org</code> <br>Any request with a host name matching *.aroz.org will be proxy to the IP address below. Here are some examples.<br>
|
<span style="font-size: 1.2em; font-weight: 300;"><i class="ui yellow star icon"></i> Subdomain</span><br>
|
||||||
<div class="ui list">
|
Example of subdomain matching keyword:<br>
|
||||||
<div class="item"><code>www.aroz.org</code></div>
|
<code>s1.aroz.org</code> <br>Any request starting with s1.aroz.org will be proxy to the IP address below<br>
|
||||||
<div class="item"><code>foo.bar.aroz.org</code></div>
|
<div class="ui divider"></div>
|
||||||
|
<span style="font-size: 1.2em; font-weight: 300;"><i class="ui yellow star icon"></i> Wildcard</span><br>
|
||||||
|
Example of wildcard matching keyword:<br>
|
||||||
|
<code>*.aroz.org</code> <br>Any request with a host name matching *.aroz.org will be proxy to the IP address below. Here are some examples.<br>
|
||||||
|
<div class="ui list">
|
||||||
|
<div class="item"><code>www.aroz.org</code></div>
|
||||||
|
<div class="item"><code>foo.bar.aroz.org</code></div>
|
||||||
|
</div>
|
||||||
|
<br>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="title" style="color: white;">
|
||||||
|
<i class="dropdown icon"></i>
|
||||||
|
Remote Target Require TLS
|
||||||
|
</div>
|
||||||
|
<div class="content">
|
||||||
|
<span style="font-size: 1.2em; font-weight: 300;"><i class="ui green lock icon"></i> Upstream TLS Requirement</span><br>
|
||||||
|
<p>
|
||||||
|
When you enable <b>Proxy Target require TLS Connection</b>, it means the <b>upstream server</b> (the target you are proxying to) requires a secure (HTTPS) connection.<br>
|
||||||
|
<b>This does not affect whether clients connect to this proxy endpoint using HTTP or HTTPS.</b>
|
||||||
|
</p>
|
||||||
|
<div class="ui divider"></div>
|
||||||
|
<span style="font-size: 1.2em; font-weight: 300;"><i class="ui yellow star icon"></i> Example</span><br>
|
||||||
|
<code>Matching Keyword: mydomain.com<br>
|
||||||
|
Target: example.com:443 (TLS enabled)</code><br>
|
||||||
|
<ul>
|
||||||
|
<li>Client connects to <b>mydomain.com</b> (HTTP or HTTPS, depending on your proxy setup)</li>
|
||||||
|
<li>Proxy forwards requests to <b>example.com:443</b> using <b>HTTPS</b></li>
|
||||||
|
</ul>
|
||||||
|
<small>
|
||||||
|
Use this option if your upstream server only accepts secure connections.<br>
|
||||||
|
If your upstream uses a self-signed certificate, check the <b>Ignore TLS/SSL Verification Error</b> option in Advance Settings.
|
||||||
|
</small>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="title" style="color: white;">
|
||||||
|
<i class="dropdown icon"></i>
|
||||||
|
What is Sticky Session?
|
||||||
|
</div>
|
||||||
|
<div class="content">
|
||||||
|
<span style="font-size: 1.2em; font-weight: 300;"><i class="ui green sync icon"></i> Sticky Session (Session Affinity)</span><br>
|
||||||
|
<p>
|
||||||
|
Sticky session ensures that requests from the same client are always forwarded to the same upstream server. This is useful for applications that store session data locally and require the client to consistently connect to the same backend.<br>
|
||||||
|
</p>
|
||||||
|
<div class="ui divider"></div>
|
||||||
|
<span style="font-size: 1.2em; font-weight: 300;"><i class="ui yellow star icon"></i> How to Add Multiple Upstreams</span><br>
|
||||||
|
<ul>
|
||||||
|
<li>Go to <b>HTTP Proxy</b> in the sidebar.</li>
|
||||||
|
<li>Click <b>Edit</b> on your proxy rule.</li>
|
||||||
|
<li>Use the <b>Upstreams</b> section to add more upstream endpoints for load balancing.</li>
|
||||||
|
</ul>
|
||||||
|
<small>
|
||||||
|
Sticky session will only work if you have more than one upstream endpoint configured.
|
||||||
|
</small>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<br>
|
<script>
|
||||||
|
$('#matchingKeywordExamplesAccordion').accordion();
|
||||||
|
</script>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -204,6 +267,7 @@
|
|||||||
let accessRuleToUse = $("#newProxyRuleAccessFilter").val();
|
let accessRuleToUse = $("#newProxyRuleAccessFilter").val();
|
||||||
let useStickySessionLB = $("#useStickySessionLB")[0].checked;
|
let useStickySessionLB = $("#useStickySessionLB")[0].checked;
|
||||||
let tags = $("#proxyTags").val().trim();
|
let tags = $("#proxyTags").val().trim();
|
||||||
|
let enableUtm = $("#enableUtm")[0].checked;
|
||||||
|
|
||||||
if (rootname.trim() == ""){
|
if (rootname.trim() == ""){
|
||||||
$("#rootname").parent().addClass("error");
|
$("#rootname").parent().addClass("error");
|
||||||
@@ -238,6 +302,7 @@
|
|||||||
access: accessRuleToUse,
|
access: accessRuleToUse,
|
||||||
stickysess: useStickySessionLB,
|
stickysess: useStickySessionLB,
|
||||||
tags: tags,
|
tags: tags,
|
||||||
|
enableUtm: enableUtm,
|
||||||
},
|
},
|
||||||
success: function(data){
|
success: function(data){
|
||||||
if (data.error != undefined){
|
if (data.error != undefined){
|
||||||
|
Reference in New Issue
Block a user