mirror of
https://github.com/tobychui/zoraxy.git
synced 2025-06-03 06:07:20 +02:00
3.0.1 init commit
- Removed Go HTTP client UA - Added optional bypass of websocket origin check #107 - Added basic forward proxy for debug - Fixed UI error in network utils tab
This commit is contained in:
parent
9b2168466c
commit
200c924acd
@ -4,7 +4,7 @@ import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
strip "github.com/grokify/html-strip-tags-go"
|
||||
"github.com/microcosm-cc/bluemonday"
|
||||
"imuslab.com/zoraxy/mod/geodb"
|
||||
"imuslab.com/zoraxy/mod/utils"
|
||||
)
|
||||
@ -137,7 +137,8 @@ func handleCountryWhitelistAdd(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
comment, _ := utils.PostPara(r, "comment")
|
||||
comment = strip.StripTags(comment)
|
||||
p := bluemonday.StrictPolicy()
|
||||
comment = p.Sanitize(comment)
|
||||
|
||||
geodbStore.AddCountryCodeToWhitelist(countryCode, comment)
|
||||
|
||||
@ -164,7 +165,8 @@ func handleIpWhitelistAdd(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
comment, _ := utils.PostPara(r, "comment")
|
||||
comment = strip.StripTags(comment)
|
||||
p := bluemonday.StrictPolicy()
|
||||
comment = p.Sanitize(comment)
|
||||
|
||||
geodbStore.AddIPToWhiteList(ipAddr, comment)
|
||||
}
|
||||
|
@ -163,6 +163,8 @@ func initAPIs() {
|
||||
authRouter.HandleFunc("/api/tools/smtp/set", HandleSMTPSet)
|
||||
authRouter.HandleFunc("/api/tools/smtp/admin", HandleAdminEmailGet)
|
||||
authRouter.HandleFunc("/api/tools/smtp/test", HandleTestEmailSend)
|
||||
authRouter.HandleFunc("/api/tools/fwdproxy/enable", forwardProxy.HandleToogle)
|
||||
authRouter.HandleFunc("/api/tools/fwdproxy/port", forwardProxy.HandlePort)
|
||||
|
||||
//Account Reset
|
||||
http.HandleFunc("/api/account/reset", HandleAdminAccountResetEmail)
|
||||
|
@ -10,11 +10,10 @@ require (
|
||||
github.com/gorilla/sessions v1.2.1
|
||||
github.com/gorilla/websocket v1.5.0
|
||||
github.com/grandcat/zeroconf v1.0.0
|
||||
github.com/grokify/html-strip-tags-go v0.1.0
|
||||
github.com/likexian/whois v1.15.1
|
||||
github.com/microcosm-cc/bluemonday v1.0.25
|
||||
golang.org/x/net v0.14.0
|
||||
golang.org/x/sys v0.11.0
|
||||
golang.org/x/text v0.12.0
|
||||
golang.org/x/net v0.20.0
|
||||
golang.org/x/sys v0.16.0
|
||||
golang.org/x/text v0.14.0
|
||||
golang.org/x/tools v0.12.0 // indirect
|
||||
)
|
||||
|
15
src/go.sum
15
src/go.sum
@ -740,8 +740,6 @@ github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWm
|
||||
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/grandcat/zeroconf v1.0.0 h1:uHhahLBKqwWBV6WZUDAT71044vwOTL+McW0mBJvo6kE=
|
||||
github.com/grandcat/zeroconf v1.0.0/go.mod h1:lTKmG1zh86XyCoUeIHSA4FJMBwCJiQmGfcP2PdzytEs=
|
||||
github.com/grokify/html-strip-tags-go v0.1.0 h1:03UrQLjAny8xci+R+qjCce/MYnpNXCtgzltlQbOBae4=
|
||||
github.com/grokify/html-strip-tags-go v0.1.0/go.mod h1:ZdzgfHEzAfz9X6Xe5eBLVblWIxXfYSQ40S/VKrAOGpc=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
|
||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
|
||||
@ -1146,8 +1144,9 @@ golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE
|
||||
golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0=
|
||||
golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45I=
|
||||
golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio=
|
||||
golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk=
|
||||
golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw=
|
||||
golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc=
|
||||
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
@ -1268,8 +1267,9 @@ golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
|
||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||
golang.org/x/net v0.11.0/go.mod h1:2L/ixqYpgIVXmeoSA/4Lu7BzTG4KIyPIryS4IsOd1oQ=
|
||||
golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA=
|
||||
golang.org/x/net v0.14.0 h1:BONx9s002vGdD9umnlX1Po8vOZmrgH34qlHcD1MfK14=
|
||||
golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI=
|
||||
golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo=
|
||||
golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
@ -1421,8 +1421,9 @@ golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM=
|
||||
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
|
||||
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
@ -1437,6 +1438,7 @@ golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
||||
golang.org/x/term v0.9.0/go.mod h1:M6DEAAIenWoTxdKrOltXcmDY3rSplQUkrvaDU5FcQyo=
|
||||
golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o=
|
||||
golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU=
|
||||
golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
@ -1455,8 +1457,9 @@ golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/text v0.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc=
|
||||
golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
|
@ -17,6 +17,7 @@ import (
|
||||
"imuslab.com/zoraxy/mod/database"
|
||||
"imuslab.com/zoraxy/mod/dynamicproxy/redirection"
|
||||
"imuslab.com/zoraxy/mod/email"
|
||||
"imuslab.com/zoraxy/mod/forwardproxy"
|
||||
"imuslab.com/zoraxy/mod/ganserv"
|
||||
"imuslab.com/zoraxy/mod/geodb"
|
||||
"imuslab.com/zoraxy/mod/info/logger"
|
||||
@ -49,9 +50,9 @@ var logOutputToFile = flag.Bool("log", true, "Log terminal output to file")
|
||||
|
||||
var (
|
||||
name = "Zoraxy"
|
||||
version = "3.0.0"
|
||||
version = "3.0.1"
|
||||
nodeUUID = "generic"
|
||||
development = false //Set this to false to use embedded web fs
|
||||
development = true //Set this to false to use embedded web fs
|
||||
bootTime = time.Now().Unix()
|
||||
|
||||
/*
|
||||
@ -79,6 +80,7 @@ var (
|
||||
acmeHandler *acme.ACMEHandler //Handler for ACME Certificate renew
|
||||
acmeAutoRenewer *acme.AutoRenewer //Handler for ACME auto renew ticking
|
||||
staticWebServer *webserv.WebServer //Static web server for hosting simple stuffs
|
||||
forwardProxy *forwardproxy.Handler //HTTP Forward proxy, basically VPN for web browser
|
||||
|
||||
//Helper modules
|
||||
EmailSender *email.Sender //Email sender that handle email sending
|
||||
|
@ -272,6 +272,14 @@ func removeHeaders(header http.Header, noCache bool) {
|
||||
header.Del("Cache-Control")
|
||||
header.Set("Cache-Control", "no-store")
|
||||
}
|
||||
|
||||
//Hide Go-HTTP-Client UA if the client didnt sent us one
|
||||
if _, ok := header["User-Agent"]; !ok {
|
||||
// If the outbound request doesn't have a User-Agent header set,
|
||||
// don't send the default Go HTTP client User-Agent.
|
||||
header.Set("User-Agent", "")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func addXForwardedForHeader(req *http.Request) {
|
||||
@ -365,6 +373,12 @@ func (p *ReverseProxy) ProxyHTTP(rw http.ResponseWriter, req *http.Request, rrr
|
||||
}
|
||||
}
|
||||
|
||||
//if res.StatusCode == 501 || res.StatusCode == 500 {
|
||||
// fmt.Println(outreq.Proto, outreq.RemoteAddr, outreq.RequestURI)
|
||||
// fmt.Println(">>>", outreq.Method, res.Header, res.ContentLength, res.StatusCode)
|
||||
// fmt.Println(outreq.Header, req.Host)
|
||||
//}
|
||||
|
||||
//Custom header rewriter functions
|
||||
if res.Header.Get("Location") != "" {
|
||||
locationRewrite := res.Header.Get("Location")
|
||||
|
@ -114,7 +114,10 @@ func (h *ProxyHandler) hostRequest(w http.ResponseWriter, r *http.Request, targe
|
||||
u, _ = url.Parse("wss://" + wsRedirectionEndpoint + requestURL)
|
||||
}
|
||||
h.logRequest(r, true, 101, "subdomain-websocket", target.Domain)
|
||||
wspHandler := websocketproxy.NewProxy(u, target.SkipCertValidations)
|
||||
wspHandler := websocketproxy.NewProxy(u, websocketproxy.Options{
|
||||
SkipTLSValidation: target.SkipCertValidations,
|
||||
SkipOriginCheck: target.SkipWebSocketOriginCheck,
|
||||
})
|
||||
wspHandler.ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
@ -178,7 +181,10 @@ func (h *ProxyHandler) vdirRequest(w http.ResponseWriter, r *http.Request, targe
|
||||
u, _ = url.Parse("wss://" + wsRedirectionEndpoint + r.URL.String())
|
||||
}
|
||||
h.logRequest(r, true, 101, "vdir-websocket", target.Domain)
|
||||
wspHandler := websocketproxy.NewProxy(u, target.SkipCertValidations)
|
||||
wspHandler := websocketproxy.NewProxy(u, websocketproxy.Options{
|
||||
SkipTLSValidation: target.SkipCertValidations,
|
||||
SkipOriginCheck: target.parent.SkipWebSocketOriginCheck,
|
||||
})
|
||||
wspHandler.ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
|
@ -95,9 +95,10 @@ type ProxyEndpoint struct {
|
||||
Domain string //Domain or IP to proxy to
|
||||
|
||||
//TLS/SSL Related
|
||||
RequireTLS bool //Target domain require TLS
|
||||
BypassGlobalTLS bool //Bypass global TLS setting options if TLS Listener enabled (parent.tlsListener != nil)
|
||||
SkipCertValidations bool //Set to true to accept self signed certs
|
||||
RequireTLS bool //Target domain require TLS
|
||||
BypassGlobalTLS bool //Bypass global TLS setting options if TLS Listener enabled (parent.tlsListener != nil)
|
||||
SkipCertValidations bool //Set to true to accept self signed certs
|
||||
SkipWebSocketOriginCheck bool //Skip origin check on websocket upgrade connections
|
||||
|
||||
//Virtual Directories
|
||||
VirtualDirectories []*VirtualDirectoryEndpoint
|
||||
@ -115,6 +116,7 @@ type ProxyEndpoint struct {
|
||||
DefaultSiteValue string //Fallback routing target, optional
|
||||
|
||||
Disabled bool //If the rule is disabled
|
||||
|
||||
//Internal Logic Elements
|
||||
parent *Router
|
||||
proxy *dpcore.ReverseProxy `json:"-"`
|
||||
|
25
src/mod/forwardproxy/cproxy/LICENSE.md
Normal file
25
src/mod/forwardproxy/cproxy/LICENSE.md
Normal file
@ -0,0 +1,25 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2022 Smarty
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
NOTE: Various optional and subordinate components carry their own licensing
|
||||
requirements and restrictions. Use of those components is subject to the terms
|
||||
and conditions outlined the respective license of each component.
|
109
src/mod/forwardproxy/cproxy/config.go
Normal file
109
src/mod/forwardproxy/cproxy/config.go
Normal file
@ -0,0 +1,109 @@
|
||||
package cproxy
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
func New(options ...option) http.Handler {
|
||||
var this configuration
|
||||
Options.apply(options...)(&this)
|
||||
return newHandler(this.Filter, this.ClientConnector, this.ServerConnector, this.Monitor)
|
||||
}
|
||||
|
||||
var Options singleton
|
||||
|
||||
type singleton struct{}
|
||||
type option func(*configuration)
|
||||
|
||||
type configuration struct {
|
||||
DialTimeout time.Duration
|
||||
Filter Filter
|
||||
DialAddress string
|
||||
Dialer Dialer
|
||||
LogConnections bool
|
||||
ProxyProtocol bool
|
||||
Initializer initializer
|
||||
ClientConnector clientConnector
|
||||
ServerConnector serverConnector
|
||||
Monitor monitor
|
||||
Logger logger
|
||||
}
|
||||
|
||||
func (singleton) DialTimeout(value time.Duration) option {
|
||||
return func(this *configuration) { this.DialTimeout = value }
|
||||
}
|
||||
func (singleton) Filter(value Filter) option {
|
||||
return func(this *configuration) { this.Filter = value }
|
||||
}
|
||||
func (singleton) ClientConnector(value clientConnector) option {
|
||||
return func(this *configuration) { this.ClientConnector = value }
|
||||
}
|
||||
func (singleton) DialAddress(value string) option {
|
||||
return func(this *configuration) { this.DialAddress = value }
|
||||
}
|
||||
func (singleton) Dialer(value Dialer) option {
|
||||
return func(this *configuration) { this.Dialer = value }
|
||||
}
|
||||
func (singleton) LogConnections(value bool) option {
|
||||
return func(this *configuration) { this.LogConnections = value }
|
||||
}
|
||||
func (singleton) ProxyProtocol(value bool) option {
|
||||
return func(this *configuration) { this.ProxyProtocol = value }
|
||||
}
|
||||
func (singleton) Initializer(value initializer) option {
|
||||
return func(this *configuration) { this.Initializer = value }
|
||||
}
|
||||
func (singleton) ServerConnector(value serverConnector) option {
|
||||
return func(this *configuration) { this.ServerConnector = value }
|
||||
}
|
||||
func (singleton) Monitor(value monitor) option {
|
||||
return func(this *configuration) { this.Monitor = value }
|
||||
}
|
||||
func (singleton) Logger(value logger) option {
|
||||
return func(this *configuration) { this.Logger = value }
|
||||
}
|
||||
|
||||
func (singleton) apply(options ...option) option {
|
||||
return func(this *configuration) {
|
||||
for _, item := range Options.defaults(options...) {
|
||||
item(this)
|
||||
}
|
||||
|
||||
if this.Dialer == nil {
|
||||
this.Dialer = newDialer(this)
|
||||
}
|
||||
|
||||
this.Dialer = newRoutingDialer(this)
|
||||
|
||||
if this.ProxyProtocol {
|
||||
this.Initializer = newProxyProtocolInitializer()
|
||||
}
|
||||
|
||||
if this.Initializer == nil {
|
||||
this.Initializer = nop{}
|
||||
}
|
||||
|
||||
this.Initializer = newLoggingInitializer(this)
|
||||
|
||||
if this.ServerConnector == nil {
|
||||
this.ServerConnector = newServerConnector(this.Dialer, this.Initializer)
|
||||
}
|
||||
}
|
||||
}
|
||||
func (singleton) defaults(options ...option) []option {
|
||||
return append([]option{
|
||||
Options.DialTimeout(time.Second * 10),
|
||||
Options.Filter(newFilter()),
|
||||
Options.ClientConnector(newClientConnector()),
|
||||
Options.Initializer(nop{}),
|
||||
Options.Monitor(nop{}),
|
||||
Options.Logger(nop{}),
|
||||
}, options...)
|
||||
}
|
||||
|
||||
type nop struct{}
|
||||
|
||||
func (nop) Measure(int) {}
|
||||
func (nop) Printf(string, ...interface{}) {}
|
||||
func (nop) Initialize(Socket, Socket) bool { return true }
|
19
src/mod/forwardproxy/cproxy/default_client_connector.go
Normal file
19
src/mod/forwardproxy/cproxy/default_client_connector.go
Normal file
@ -0,0 +1,19 @@
|
||||
package cproxy
|
||||
|
||||
import "net/http"
|
||||
|
||||
type defaultClientConnector struct{}
|
||||
|
||||
func newClientConnector() *defaultClientConnector {
|
||||
return &defaultClientConnector{}
|
||||
}
|
||||
|
||||
func (this *defaultClientConnector) Connect(response http.ResponseWriter) Socket {
|
||||
if hijacker, ok := response.(http.Hijacker); !ok {
|
||||
return nil
|
||||
} else if socket, _, _ := hijacker.Hijack(); socket == nil {
|
||||
return nil // this 'else if' exists to avoid the pointer nil != interface nil issue
|
||||
} else {
|
||||
return socket
|
||||
}
|
||||
}
|
25
src/mod/forwardproxy/cproxy/default_dialer.go
Normal file
25
src/mod/forwardproxy/cproxy/default_dialer.go
Normal file
@ -0,0 +1,25 @@
|
||||
package cproxy
|
||||
|
||||
import (
|
||||
"net"
|
||||
"time"
|
||||
)
|
||||
|
||||
type defaultDialer struct {
|
||||
timeout time.Duration
|
||||
logger logger
|
||||
}
|
||||
|
||||
func newDialer(config *configuration) *defaultDialer {
|
||||
return &defaultDialer{timeout: config.DialTimeout, logger: config.Logger}
|
||||
}
|
||||
|
||||
func (this *defaultDialer) Dial(address string) Socket {
|
||||
if socket, err := net.DialTimeout("tcp", address, this.timeout); err == nil {
|
||||
return socket
|
||||
} else {
|
||||
this.logger.Printf("[INFO] Unable to establish connection to [%s]: %s", address, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
9
src/mod/forwardproxy/cproxy/default_filter.go
Normal file
9
src/mod/forwardproxy/cproxy/default_filter.go
Normal file
@ -0,0 +1,9 @@
|
||||
package cproxy
|
||||
|
||||
import "net/http"
|
||||
|
||||
type defaultFilter struct{}
|
||||
|
||||
func newFilter() *defaultFilter { return &defaultFilter{} }
|
||||
|
||||
func (this *defaultFilter) IsAuthorized(http.ResponseWriter, *http.Request) bool { return true }
|
56
src/mod/forwardproxy/cproxy/default_handler.go
Normal file
56
src/mod/forwardproxy/cproxy/default_handler.go
Normal file
@ -0,0 +1,56 @@
|
||||
package cproxy
|
||||
|
||||
import "net/http"
|
||||
|
||||
type defaultHandler struct {
|
||||
filter Filter
|
||||
clientConnector clientConnector
|
||||
serverConnector serverConnector
|
||||
meter monitor
|
||||
}
|
||||
|
||||
func newHandler(filter Filter, clientConnector clientConnector, serverConnector serverConnector, meter monitor) *defaultHandler {
|
||||
return &defaultHandler{
|
||||
filter: filter,
|
||||
clientConnector: clientConnector,
|
||||
serverConnector: serverConnector,
|
||||
meter: meter,
|
||||
}
|
||||
}
|
||||
|
||||
func (this *defaultHandler) ServeHTTP(response http.ResponseWriter, request *http.Request) {
|
||||
this.meter.Measure(MeasurementHTTPRequest)
|
||||
|
||||
if request.Method != "CONNECT" {
|
||||
this.meter.Measure(MeasurementBadMethod)
|
||||
writeResponseStatus(response, http.StatusMethodNotAllowed)
|
||||
|
||||
} else if !this.filter.IsAuthorized(response, request) {
|
||||
this.meter.Measure(MeasurementUnauthorizedRequest)
|
||||
//writeResponseStatus(response, http.StatusUnauthorized)
|
||||
|
||||
} else if client := this.clientConnector.Connect(response); client == nil {
|
||||
this.meter.Measure(MeasurementClientConnectionFailed)
|
||||
writeResponseStatus(response, http.StatusNotImplemented)
|
||||
|
||||
} else if connection := this.serverConnector.Connect(client, request.URL.Host); connection == nil {
|
||||
this.meter.Measure(MeasurementServerConnectionFailed)
|
||||
_, _ = client.Write(statusBadGateway)
|
||||
_ = client.Close()
|
||||
|
||||
} else {
|
||||
this.meter.Measure(MeasurementProxyReady)
|
||||
_, _ = client.Write(statusReady)
|
||||
connection.Proxy()
|
||||
this.meter.Measure(MeasurementProxyComplete)
|
||||
}
|
||||
}
|
||||
|
||||
func writeResponseStatus(response http.ResponseWriter, statusCode int) {
|
||||
http.Error(response, http.StatusText(statusCode), statusCode)
|
||||
}
|
||||
|
||||
var (
|
||||
statusBadGateway = []byte("HTTP/1.1 502 Bad Gateway\r\n\r\n")
|
||||
statusReady = []byte("HTTP/1.1 200 OK\r\n\r\n")
|
||||
)
|
54
src/mod/forwardproxy/cproxy/default_proxy.go
Normal file
54
src/mod/forwardproxy/cproxy/default_proxy.go
Normal file
@ -0,0 +1,54 @@
|
||||
package cproxy
|
||||
|
||||
import (
|
||||
"io"
|
||||
"sync"
|
||||
)
|
||||
|
||||
type defaultProxy struct {
|
||||
client Socket
|
||||
server Socket
|
||||
waiter *sync.WaitGroup
|
||||
}
|
||||
|
||||
func newProxy(client, server Socket) *defaultProxy {
|
||||
waiter := &sync.WaitGroup{}
|
||||
waiter.Add(2) // wait on both client->server and server->client streams
|
||||
|
||||
return &defaultProxy{
|
||||
waiter: waiter,
|
||||
client: client,
|
||||
server: server,
|
||||
}
|
||||
}
|
||||
|
||||
func (this *defaultProxy) Proxy() {
|
||||
go this.streamAndClose(this.client, this.server)
|
||||
go this.streamAndClose(this.server, this.client)
|
||||
this.closeSockets()
|
||||
}
|
||||
|
||||
func (this *defaultProxy) streamAndClose(reader, writer Socket) {
|
||||
_, _ = io.Copy(writer, reader)
|
||||
|
||||
tryCloseRead(reader)
|
||||
tryCloseWrite(writer)
|
||||
|
||||
this.waiter.Done()
|
||||
}
|
||||
func tryCloseRead(socket Socket) {
|
||||
if tcp, ok := socket.(tcpSocket); ok {
|
||||
_ = tcp.CloseRead()
|
||||
}
|
||||
}
|
||||
func tryCloseWrite(socket Socket) {
|
||||
if tcp, ok := socket.(tcpSocket); ok {
|
||||
_ = tcp.CloseWrite()
|
||||
}
|
||||
}
|
||||
|
||||
func (this *defaultProxy) closeSockets() {
|
||||
this.waiter.Wait()
|
||||
_ = this.client.Close()
|
||||
_ = this.server.Close()
|
||||
}
|
24
src/mod/forwardproxy/cproxy/default_server_connector.go
Normal file
24
src/mod/forwardproxy/cproxy/default_server_connector.go
Normal file
@ -0,0 +1,24 @@
|
||||
package cproxy
|
||||
|
||||
type defaultServerConnector struct {
|
||||
dialer Dialer
|
||||
initializer initializer
|
||||
}
|
||||
|
||||
func newServerConnector(dialer Dialer, initializer initializer) *defaultServerConnector {
|
||||
return &defaultServerConnector{dialer: dialer, initializer: initializer}
|
||||
}
|
||||
|
||||
func (this *defaultServerConnector) Connect(client Socket, serverAddress string) proxy {
|
||||
server := this.dialer.Dial(serverAddress)
|
||||
if server == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if !this.initializer.Initialize(client, server) {
|
||||
_ = server.Close()
|
||||
return nil
|
||||
}
|
||||
|
||||
return newProxy(client, server)
|
||||
}
|
32
src/mod/forwardproxy/cproxy/hostname_filter.go
Normal file
32
src/mod/forwardproxy/cproxy/hostname_filter.go
Normal file
@ -0,0 +1,32 @@
|
||||
package cproxy
|
||||
|
||||
import "net/http"
|
||||
|
||||
type hostnameFilter struct {
|
||||
authorized []string
|
||||
}
|
||||
|
||||
func NewHostnameFilter(authorized []string) Filter {
|
||||
return &hostnameFilter{authorized: authorized}
|
||||
}
|
||||
|
||||
func (this hostnameFilter) IsAuthorized(_ http.ResponseWriter, request *http.Request) bool {
|
||||
if len(this.authorized) == 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
host := request.URL.Host
|
||||
hostLength := len(host)
|
||||
for _, authorized := range this.authorized {
|
||||
if authorized[:2] == "*." {
|
||||
have, want := hostLength, len(authorized)-1
|
||||
if have > want && authorized[1:] == host[hostLength-want:] {
|
||||
return true
|
||||
}
|
||||
} else if authorized == host {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
26
src/mod/forwardproxy/cproxy/hostname_suffix_filter.go
Normal file
26
src/mod/forwardproxy/cproxy/hostname_suffix_filter.go
Normal file
@ -0,0 +1,26 @@
|
||||
package cproxy
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type hostnameSuffixFilter struct {
|
||||
authorized []string
|
||||
}
|
||||
|
||||
func NewHostnameSuffixFilter(authorized []string) Filter {
|
||||
return &hostnameSuffixFilter{authorized: authorized}
|
||||
}
|
||||
|
||||
func (this hostnameSuffixFilter) IsAuthorized(_ http.ResponseWriter, request *http.Request) bool {
|
||||
host := request.URL.Host
|
||||
|
||||
for _, authorized := range this.authorized {
|
||||
if strings.HasSuffix(host, authorized) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
67
src/mod/forwardproxy/cproxy/interfaces.go
Normal file
67
src/mod/forwardproxy/cproxy/interfaces.go
Normal file
@ -0,0 +1,67 @@
|
||||
package cproxy
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type (
|
||||
Filter interface {
|
||||
IsAuthorized(http.ResponseWriter, *http.Request) bool
|
||||
}
|
||||
|
||||
clientConnector interface {
|
||||
Connect(http.ResponseWriter) Socket
|
||||
}
|
||||
)
|
||||
|
||||
type (
|
||||
Dialer interface {
|
||||
Dial(string) Socket
|
||||
}
|
||||
|
||||
serverConnector interface {
|
||||
Connect(Socket, string) proxy
|
||||
}
|
||||
|
||||
initializer interface {
|
||||
Initialize(Socket, Socket) bool
|
||||
}
|
||||
|
||||
proxy interface {
|
||||
Proxy()
|
||||
}
|
||||
)
|
||||
|
||||
type (
|
||||
Socket interface {
|
||||
io.ReadWriteCloser
|
||||
RemoteAddr() net.Addr
|
||||
}
|
||||
|
||||
tcpSocket interface {
|
||||
Socket
|
||||
CloseRead() error
|
||||
CloseWrite() error
|
||||
}
|
||||
)
|
||||
|
||||
type (
|
||||
monitor interface {
|
||||
Measure(int)
|
||||
}
|
||||
logger interface {
|
||||
Printf(string, ...interface{})
|
||||
}
|
||||
)
|
||||
|
||||
const (
|
||||
MeasurementHTTPRequest int = iota
|
||||
MeasurementBadMethod
|
||||
MeasurementUnauthorizedRequest
|
||||
MeasurementClientConnectionFailed
|
||||
MeasurementServerConnectionFailed
|
||||
MeasurementProxyReady
|
||||
MeasurementProxyComplete
|
||||
)
|
24
src/mod/forwardproxy/cproxy/logging_initializer.go
Normal file
24
src/mod/forwardproxy/cproxy/logging_initializer.go
Normal file
@ -0,0 +1,24 @@
|
||||
package cproxy
|
||||
|
||||
type loggingInitializer struct {
|
||||
logger logger
|
||||
inner initializer
|
||||
}
|
||||
|
||||
func newLoggingInitializer(config *configuration) initializer {
|
||||
if !config.LogConnections {
|
||||
return config.Initializer
|
||||
}
|
||||
|
||||
return &loggingInitializer{inner: config.Initializer, logger: config.Logger}
|
||||
}
|
||||
|
||||
func (this *loggingInitializer) Initialize(client, server Socket) bool {
|
||||
result := this.inner.Initialize(client, server)
|
||||
|
||||
if !result {
|
||||
this.logger.Printf("[INFO] Connection failed [%s] -> [%s]", client.RemoteAddr(), server.RemoteAddr())
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
36
src/mod/forwardproxy/cproxy/proxy_protocol_initializer.go
Normal file
36
src/mod/forwardproxy/cproxy/proxy_protocol_initializer.go
Normal file
@ -0,0 +1,36 @@
|
||||
package cproxy
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type proxyProtocolInitializer struct{}
|
||||
|
||||
func newProxyProtocolInitializer() *proxyProtocolInitializer {
|
||||
return &proxyProtocolInitializer{}
|
||||
}
|
||||
|
||||
func (this *proxyProtocolInitializer) Initialize(client, server Socket) bool {
|
||||
header := formatHeader(client.RemoteAddr(), server.RemoteAddr())
|
||||
_, err := io.WriteString(server, header)
|
||||
return err == nil
|
||||
}
|
||||
func formatHeader(client, server net.Addr) string {
|
||||
clientAddress, clientPort := parseAddress(client.String())
|
||||
serverAddress, serverPort := parseAddress(server.String())
|
||||
if strings.Contains(clientAddress, ":") {
|
||||
return fmt.Sprintf(proxyProtocolIPv6Preamble, clientAddress, serverAddress, clientPort, serverPort)
|
||||
}
|
||||
|
||||
return fmt.Sprintf(proxyProtocolIPv4Preamble, clientAddress, serverAddress, clientPort, serverPort)
|
||||
}
|
||||
func parseAddress(address string) (string, string) {
|
||||
address, port, _ := net.SplitHostPort(address)
|
||||
return address, port
|
||||
}
|
||||
|
||||
const proxyProtocolIPv4Preamble = "PROXY TCP4 %s %s %s %s\r\n"
|
||||
const proxyProtocolIPv6Preamble = "PROXY TCP6 %s %s %s %s\r\n"
|
18
src/mod/forwardproxy/cproxy/routing_dialer.go
Normal file
18
src/mod/forwardproxy/cproxy/routing_dialer.go
Normal file
@ -0,0 +1,18 @@
|
||||
package cproxy
|
||||
|
||||
type routingDialer struct {
|
||||
inner Dialer
|
||||
targetAddress string
|
||||
}
|
||||
|
||||
func newRoutingDialer(config *configuration) Dialer {
|
||||
if len(config.DialAddress) == 0 {
|
||||
return config.Dialer
|
||||
}
|
||||
|
||||
return &routingDialer{inner: config.Dialer, targetAddress: config.DialAddress}
|
||||
}
|
||||
|
||||
func (this *routingDialer) Dial(string) Socket {
|
||||
return this.inner.Dial(this.targetAddress)
|
||||
}
|
137
src/mod/forwardproxy/forwardproxy.go
Normal file
137
src/mod/forwardproxy/forwardproxy.go
Normal file
@ -0,0 +1,137 @@
|
||||
package forwardproxy
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"log"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"imuslab.com/zoraxy/mod/database"
|
||||
"imuslab.com/zoraxy/mod/forwardproxy/cproxy"
|
||||
"imuslab.com/zoraxy/mod/info/logger"
|
||||
"imuslab.com/zoraxy/mod/utils"
|
||||
)
|
||||
|
||||
type ZrFilter struct {
|
||||
//To be implemented
|
||||
}
|
||||
|
||||
type Handler struct {
|
||||
server *http.Server
|
||||
handler *http.Handler
|
||||
running bool
|
||||
db *database.Database
|
||||
logger *logger.Logger
|
||||
Port int
|
||||
}
|
||||
|
||||
func NewForwardProxy(sysdb *database.Database, port int, logger *logger.Logger) *Handler {
|
||||
thisFilter := ZrFilter{}
|
||||
handler := cproxy.New(cproxy.Options.Filter(thisFilter))
|
||||
|
||||
return &Handler{
|
||||
db: sysdb,
|
||||
server: nil,
|
||||
handler: &handler,
|
||||
running: false,
|
||||
logger: logger,
|
||||
Port: port,
|
||||
}
|
||||
}
|
||||
|
||||
// Start the forward proxy
|
||||
func (h *Handler) Start() error {
|
||||
if h.running {
|
||||
return errors.New("forward proxy already running")
|
||||
}
|
||||
server := &http.Server{Addr: ":" + strconv.Itoa(h.Port), Handler: *h.handler}
|
||||
h.server = server
|
||||
|
||||
go func() {
|
||||
if err := server.ListenAndServe(); err != nil {
|
||||
if err != nil {
|
||||
log.Println(err.Error())
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
h.running = true
|
||||
return nil
|
||||
}
|
||||
|
||||
// Stop the forward proxy
|
||||
func (h *Handler) Stop() error {
|
||||
if h.running && h.server != nil {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
if err := h.server.Shutdown(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
h.running = false
|
||||
h.server = nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Update the port number of the forward proxy
|
||||
func (h *Handler) UpdatePort(newPort int) error {
|
||||
h.Stop()
|
||||
h.Port = newPort
|
||||
return h.Start()
|
||||
}
|
||||
|
||||
func (it ZrFilter) IsAuthorized(w http.ResponseWriter, r *http.Request) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// Handle port change of the forward proxy
|
||||
func (h *Handler) HandlePort(w http.ResponseWriter, r *http.Request) {
|
||||
port, err := utils.PostInt(r, "port")
|
||||
if err != nil {
|
||||
js, _ := json.Marshal(h.Port)
|
||||
utils.SendJSONResponse(w, string(js))
|
||||
} else {
|
||||
//Update the port
|
||||
err = h.UpdatePort(port)
|
||||
if err != nil {
|
||||
utils.SendErrorResponse(w, err.Error())
|
||||
return
|
||||
}
|
||||
h.logger.PrintAndLog("Forward Proxy", "HTTP Forward Proxy port updated to :"+strconv.Itoa(h.Port), nil)
|
||||
h.db.Write("fwdproxy", "port", port)
|
||||
utils.SendOK(w)
|
||||
}
|
||||
}
|
||||
|
||||
// Handle power toggle of the forward proxys
|
||||
func (h *Handler) HandleToogle(w http.ResponseWriter, r *http.Request) {
|
||||
enabled, err := utils.PostBool(r, "enable")
|
||||
if err != nil {
|
||||
//Get the current state of the forward proxy
|
||||
js, _ := json.Marshal(h.running)
|
||||
utils.SendJSONResponse(w, string(js))
|
||||
} else {
|
||||
if enabled {
|
||||
err = h.Start()
|
||||
if err != nil {
|
||||
h.logger.PrintAndLog("Forward Proxy", "Unable to start forward proxy server", err)
|
||||
utils.SendErrorResponse(w, err.Error())
|
||||
return
|
||||
}
|
||||
h.logger.PrintAndLog("Forward Proxy", "HTTP Forward Proxy Started, listening on :"+strconv.Itoa(h.Port), nil)
|
||||
} else {
|
||||
err = h.Stop()
|
||||
if err != nil {
|
||||
h.logger.PrintAndLog("Forward Proxy", "Unable to stop forward proxy server", err)
|
||||
utils.SendErrorResponse(w, err.Error())
|
||||
return
|
||||
}
|
||||
h.logger.PrintAndLog("Forward Proxy", "HTTP Forward Proxy Stopped", nil)
|
||||
}
|
||||
h.db.Write("fwdproxy", "enabled", enabled)
|
||||
utils.SendOK(w)
|
||||
}
|
||||
}
|
@ -85,7 +85,10 @@ func (m *Manager) HandleHttpByInstanceId(instanceId string, w http.ResponseWrite
|
||||
r.Header.Set("Zr-Origin-Upgrade", "websocket")
|
||||
requestURL = strings.TrimPrefix(requestURL, "/")
|
||||
u, _ := url.Parse("ws://127.0.0.1:" + strconv.Itoa(targetInstance.AssignedPort) + "/" + requestURL)
|
||||
wspHandler := websocketproxy.NewProxy(u, false)
|
||||
wspHandler := websocketproxy.NewProxy(u, websocketproxy.Options{
|
||||
SkipTLSValidation: false,
|
||||
SkipOriginCheck: false,
|
||||
})
|
||||
wspHandler.ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
|
@ -47,19 +47,26 @@ type WebsocketProxy struct {
|
||||
// If nil, DefaultDialer is used.
|
||||
Dialer *websocket.Dialer
|
||||
|
||||
Verbal bool
|
||||
SkipTlsValidation bool
|
||||
Verbal bool
|
||||
|
||||
Options Options
|
||||
}
|
||||
|
||||
// Additional options for websocket proxy runtime
|
||||
type Options struct {
|
||||
SkipTLSValidation bool //Skip backend TLS validation
|
||||
SkipOriginCheck bool //Skip origin check
|
||||
}
|
||||
|
||||
// ProxyHandler returns a new http.Handler interface that reverse proxies the
|
||||
// request to the given target.
|
||||
func ProxyHandler(target *url.URL, skipTlsValidation bool) http.Handler {
|
||||
return NewProxy(target, skipTlsValidation)
|
||||
func ProxyHandler(target *url.URL, options Options) http.Handler {
|
||||
return NewProxy(target, options)
|
||||
}
|
||||
|
||||
// NewProxy returns a new Websocket reverse proxy that rewrites the
|
||||
// URL's to the scheme, host and base path provider in target.
|
||||
func NewProxy(target *url.URL, skipTlsValidation bool) *WebsocketProxy {
|
||||
func NewProxy(target *url.URL, options Options) *WebsocketProxy {
|
||||
backend := func(r *http.Request) *url.URL {
|
||||
// Shallow copy
|
||||
u := *target
|
||||
@ -68,7 +75,7 @@ func NewProxy(target *url.URL, skipTlsValidation bool) *WebsocketProxy {
|
||||
u.RawQuery = r.URL.RawQuery
|
||||
return &u
|
||||
}
|
||||
return &WebsocketProxy{Backend: backend, Verbal: false, SkipTlsValidation: skipTlsValidation}
|
||||
return &WebsocketProxy{Backend: backend, Verbal: false, Options: options}
|
||||
}
|
||||
|
||||
// ServeHTTP implements the http.Handler that proxies WebSocket connections.
|
||||
@ -88,7 +95,7 @@ func (w *WebsocketProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
||||
|
||||
dialer := w.Dialer
|
||||
if w.Dialer == nil {
|
||||
if w.SkipTlsValidation {
|
||||
if w.Options.SkipTLSValidation {
|
||||
//Disable TLS secure check if target allow skip verification
|
||||
bypassDialer := websocket.DefaultDialer
|
||||
bypassDialer.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
|
||||
@ -171,6 +178,13 @@ func (w *WebsocketProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
||||
upgrader = DefaultUpgrader
|
||||
}
|
||||
|
||||
//Fixing issue #107 by bypassing request origin check
|
||||
if w.Options.SkipOriginCheck {
|
||||
upgrader.CheckOrigin = func(r *http.Request) bool {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// Only pass those headers to the upgrader.
|
||||
upgradeHeader := http.Header{}
|
||||
if hdr := resp.Header.Get("Sec-Websocket-Protocol"); hdr != "" {
|
||||
|
@ -28,7 +28,10 @@ func TestProxy(t *testing.T) {
|
||||
}
|
||||
|
||||
u, _ := url.Parse(backendURL)
|
||||
proxy := NewProxy(u, false)
|
||||
proxy := NewProxy(u, Options{
|
||||
SkipTLSValidation: false,
|
||||
SkipOriginCheck: false,
|
||||
})
|
||||
proxy.Upgrader = upgrader
|
||||
|
||||
mux := http.NewServeMux()
|
||||
|
14
src/start.go
14
src/start.go
@ -12,6 +12,7 @@ import (
|
||||
"imuslab.com/zoraxy/mod/auth"
|
||||
"imuslab.com/zoraxy/mod/database"
|
||||
"imuslab.com/zoraxy/mod/dynamicproxy/redirection"
|
||||
"imuslab.com/zoraxy/mod/forwardproxy"
|
||||
"imuslab.com/zoraxy/mod/ganserv"
|
||||
"imuslab.com/zoraxy/mod/geodb"
|
||||
"imuslab.com/zoraxy/mod/info/logger"
|
||||
@ -219,6 +220,18 @@ func startupSequence() {
|
||||
//Create an analytic loader
|
||||
AnalyticLoader = analytic.NewDataLoader(sysdb, statisticCollector)
|
||||
|
||||
//Create basic forward proxy
|
||||
sysdb.NewTable("fwdproxy")
|
||||
fwdProxyEnabled := false
|
||||
fwdProxyPort := 5587
|
||||
sysdb.Read("fwdproxy", "port", &fwdProxyPort)
|
||||
sysdb.Read("fwdproxy", "enabled", &fwdProxyEnabled)
|
||||
forwardProxy = forwardproxy.NewForwardProxy(sysdb, fwdProxyPort, SystemWideLogger)
|
||||
if fwdProxyEnabled {
|
||||
SystemWideLogger.PrintAndLog("Forward Proxy", "HTTP Forward Proxy Listening on :"+strconv.Itoa(forwardProxy.Port), nil)
|
||||
forwardProxy.Start()
|
||||
}
|
||||
|
||||
/*
|
||||
ACME API
|
||||
|
||||
@ -241,4 +254,5 @@ func finalSequence() {
|
||||
|
||||
//Inject routing rules
|
||||
registerBuildInRoutingRules()
|
||||
|
||||
}
|
||||
|
@ -84,7 +84,23 @@
|
||||
Paste to Terminal <code style="float: right;">Shift + Insert</code>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="ui divider"></div>
|
||||
<h2>Forward Proxy</h2>
|
||||
<p>Setup a basic HTTP forward proxy to access web server in another LAN<br>
|
||||
To enable forward proxy in your domain, add a proxy rule to 127.0.0.1:{selected_port}</p>
|
||||
<form class="ui form">
|
||||
<div class="field">
|
||||
<label>Listening Port</label>
|
||||
<div class="ui action input">
|
||||
<input id="forwardProxyPort" type="number" placeholder="5587" step="1", min="1024" max="65535" value="5587">
|
||||
<button onclick="updateForwardProxyPort(); event.preventDefault();" class="ui basic button"><i class="ui green check icon"></i> Apply</button>
|
||||
</div>
|
||||
</div>
|
||||
<div id="forwardProxyButtons" class="field">
|
||||
<button onclick="toggleForwadProxy(true); event.preventDefault();" class="ui basic small green button startBtn"><i class="ui green arrow alternate circle up icon"></i> Start</button>
|
||||
<button onclick="toggleForwadProxy(false); event.preventDefault();" class="ui basic small red button stopBtn"><i class="ui red minus circle icon"></i> Stop</button>
|
||||
</div>
|
||||
</form>
|
||||
<div class="ui divider"></div>
|
||||
<h2>Wake On LAN</h2>
|
||||
<p>Wake up a remote server by WOL Magic Packet or an IoT device</p>
|
||||
@ -558,6 +574,68 @@ function renderWhoisDomainTable(jsonData) {
|
||||
}
|
||||
|
||||
|
||||
//Forward Proxy
|
||||
function initForwardProxyInfo(){
|
||||
$.get("/api/tools/fwdproxy/enable", function(data){
|
||||
if (data == true){
|
||||
//Disable the start btn
|
||||
$("#forwardProxyButtons").find(".startBtn").addClass('disabled');
|
||||
$("#forwardProxyButtons").find(".stopBtn").removeClass('disabled');
|
||||
}else{
|
||||
$("#forwardProxyButtons").find(".startBtn").removeClass('disabled');
|
||||
$("#forwardProxyButtons").find(".stopBtn").addClass('disabled');
|
||||
}
|
||||
});
|
||||
|
||||
$.get("/api/tools/fwdproxy/port", function(data){
|
||||
$("#forwardProxyPort").val(data);
|
||||
})
|
||||
}
|
||||
initForwardProxyInfo();
|
||||
|
||||
function toggleForwadProxy(enabled){
|
||||
$.ajax({
|
||||
url: "/api/tools/fwdproxy/enable",
|
||||
method: "POST",
|
||||
data: {
|
||||
"enable": enabled
|
||||
},
|
||||
success: function(data){
|
||||
if (data.error != undefined){
|
||||
msgbox(data.error, false);
|
||||
}else{
|
||||
msgbox(`Forward proxy ${enabled?"enabled":"disabled"}`)
|
||||
}
|
||||
|
||||
initForwardProxyInfo();
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function updateForwardProxyPort(){
|
||||
let newPortNumber = $("#forwardProxyPort").val();
|
||||
if (newPortNumber < 1024 || newPortNumber > 65535){
|
||||
$("#newPortNumber").parent().addClass('error');
|
||||
}else{
|
||||
$("#newPortNumber").parent().removeClass('error');
|
||||
}
|
||||
|
||||
$.ajax({
|
||||
url: "/api/tools/fwdproxy/port",
|
||||
method: "POST",
|
||||
data: {
|
||||
"port": newPortNumber
|
||||
},
|
||||
success: function(data){
|
||||
if (data.error != undefined){
|
||||
msgbox(data.error, false);
|
||||
}
|
||||
msgbox("Forward proxy port updated");
|
||||
initForwardProxyInfo();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
|
@ -4,12 +4,12 @@
|
||||
<p>You might find these tools or information helpful when setting up your gateway server</p>
|
||||
</div>
|
||||
<div class="ui top attached tabular menu">
|
||||
<a class="nettools item active" data-tab="tab1"><i class="ui user circle blue icon"></i> Accounts</a>
|
||||
<a class="nettools item" data-tab="tab2">Toolbox</a>
|
||||
<a class="nettools item" data-tab="tab3">System</a>
|
||||
<a class="utils item active" data-tab="utiltab1"><i class="ui user circle blue icon"></i> Accounts</a>
|
||||
<a class="utils item" data-tab="utiltab2">Toolbox</a>
|
||||
<a class="utils item" data-tab="utiltab3">System</a>
|
||||
</div>
|
||||
|
||||
<div class="ui bottom attached tab segment nettoolstab active" data-tab="tab1">
|
||||
<div class="ui bottom attached tab segment utilitiesTabs active" data-tab="utiltab1">
|
||||
<div class="extAuthOnly" style="display:none;">
|
||||
<div class="ui basic segment">
|
||||
<i class="ui green circle check icon"></i> Account options are not available due to -noauth flag is set to true.
|
||||
@ -99,7 +99,7 @@
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ui bottom attached tab segment nettoolstab" data-tab="tab2">
|
||||
<div class="ui bottom attached tab segment utilitiesTabs" data-tab="utiltab2">
|
||||
<h3> IP Address to CIDR</h3>
|
||||
<p>No experience with CIDR notations? Here are some tools you can use to make setting up easier.</p>
|
||||
<div class="ui basic segment">
|
||||
@ -128,7 +128,7 @@
|
||||
</div>
|
||||
<div class="ui divider"></div>
|
||||
</div>
|
||||
<div class="ui bottom attached tab segment nettoolstab" data-tab="tab3">
|
||||
<div class="ui bottom attached tab segment utilitiesTabs" data-tab="utiltab3">
|
||||
<!-- Config Tools -->
|
||||
<h3>System Backup & Restore</h3>
|
||||
<p>Options related to system backup, migrate and restore.</p>
|
||||
@ -175,7 +175,16 @@
|
||||
<br>
|
||||
</div>
|
||||
<script>
|
||||
$('.menu .nettools.item').tab();
|
||||
$('.menu .utils.item').tab();
|
||||
// Switch tabs when clicking on the menu items
|
||||
$('.menu .utils.item').on('click', function() {
|
||||
$('.menu .utils.item').removeClass('active');
|
||||
$(this).addClass('active');
|
||||
var tab = $(this).attr('data-tab');
|
||||
$('.utilitiesTabs.tab.segment').removeClass('active');
|
||||
$('div[data-tab="' + tab + '"]').addClass('active');
|
||||
});
|
||||
|
||||
/*
|
||||
Account Password utilities
|
||||
*/
|
||||
|
Loading…
x
Reference in New Issue
Block a user