From 505ef3b98285d3275f9b59f16c6eaaa9c454a572 Mon Sep 17 00:00:00 2001 From: Akilan Date: Thu, 22 Aug 2024 17:14:13 +0100 Subject: [PATCH] added base reverse proxy --- Bindings/Client.go | 4 +- abstractions/base.go | 4 +- client/MAPPort.go | 4 +- cmd/action.go | 2 +- cmd/flags.go | 8 +++ config/config.go | 1 + config/generate/generate.go | 1 + p2p/iptable.go | 1 + p2p/upnp_test.go | 14 ++-- plugin/TestAnsible/site.yml | 2 +- server/ReverseProxy.go | 135 ++++++++++++++++++++++++++++++++++++ server/server.go | 63 ++++++++++++++++- 12 files changed, 221 insertions(+), 18 deletions(-) create mode 100644 server/ReverseProxy.go diff --git a/Bindings/Client.go b/Bindings/Client.go index 5c6fea2..0d3c66d 100644 --- a/Bindings/Client.go +++ b/Bindings/Client.go @@ -134,8 +134,8 @@ func EscapeFirewall(HostOutsideNATIP string, HostOutsideNATPort string, internal } //export MapPort -func MapPort(Port string) *C.char { - entireAddress, _, err := abstractions.MapPort(Port) +func MapPort(Port string, DomainName string) *C.char { + entireAddress, _, err := abstractions.MapPort(Port, DomainName) if err != nil { return C.CString(err.Error()) } diff --git a/abstractions/base.go b/abstractions/base.go index 0e9ff15..56e02d7 100644 --- a/abstractions/base.go +++ b/abstractions/base.go @@ -52,8 +52,8 @@ func Start() (*gin.Engine, error) { } // MapPort Creates a reverse proxy connection and maps the appropriate port -func MapPort(port string) (entireAddres string, mapPort string, err error) { - entireAddres, mapPort, err = server.MapPort(port) +func MapPort(port string, domainName string) (entireAddres string, mapPort string, err error) { + entireAddres, mapPort, err = server.MapPort(port, domainName) return } diff --git a/client/MAPPort.go b/client/MAPPort.go index 851a213..71f3727 100644 --- a/client/MAPPort.go +++ b/client/MAPPort.go @@ -6,14 +6,14 @@ import ( "net/http" ) -func MAPPort(port string) (string, error) { +func MAPPort(port string, domainName string) (string, error) { Config, err := config.ConfigInit(nil, nil) if err != nil { return "", err } //if version == "version 6" { - URL := "http://0.0.0.0:" + Config.ServerPort + "/MAPPort?port=" + port + URL := "http://0.0.0.0:" + Config.ServerPort + "/MAPPort?port=" + port + "&domain_name=" + domainName //} else { // URL = "http://" + IP + ":" + serverPort + "/server_info" //} diff --git a/cmd/action.go b/cmd/action.go index 9f118d7..4014cc9 100644 --- a/cmd/action.go +++ b/cmd/action.go @@ -283,7 +283,7 @@ var CliAction = func(ctx *cli.Context) error { } if MAPPort != "" { - address, err := client.MAPPort(MAPPort) + address, err := client.MAPPort(MAPPort, DomainName) if err != nil { return err } diff --git a/cmd/flags.go b/cmd/flags.go index 1403e96..04b3996 100644 --- a/cmd/flags.go +++ b/cmd/flags.go @@ -30,6 +30,7 @@ var ( RemoveContainerGroup bool RemoveGroup string MAPPort string + DomainName string //FRPProxy bool // Generate only allowed in dev release // -- REMOVE ON REGULAR RELEASE -- @@ -212,6 +213,13 @@ var AppConfigFlags = []cli.Flag{ EnvVars: []string{"MAPPORT"}, Destination: &MAPPort, }, + &cli.StringFlag{ + Name: "DomainName", + Aliases: []string{"dn"}, + Usage: "While mapping ports allows to set a domain name to create a mapping in the proxy server", + EnvVars: []string{"DOMAINNAME"}, + Destination: &DomainName, + }, // Generate only allowed in dev release // -- REMOVE ON REGULAR RELEASE -- &cli.StringFlag{ diff --git a/config/config.go b/config/config.go index 62572c5..947e4e7 100644 --- a/config/config.go +++ b/config/config.go @@ -27,6 +27,7 @@ type Config struct { PluginPath string TrackContainersPath string ServerPort string + ProxyPort string GroupTrackContainersPath string FRPServerPort string BehindNAT string diff --git a/config/generate/generate.go b/config/generate/generate.go index 695021f..26b07ac 100644 --- a/config/generate/generate.go +++ b/config/generate/generate.go @@ -77,6 +77,7 @@ func SetDefaults(envName string, forceDefault bool, CustomConfig interface{}, No Defaults.TrackContainersPath = defaultPath + "client/trackcontainers/trackcontainers.json" Defaults.GroupTrackContainersPath = defaultPath + "client/trackcontainers/grouptrackcontainers.json" Defaults.ServerPort = "8088" + Defaults.ProxyPort = "" Defaults.FRPServerPort = "True" Defaults.CustomConfig = CustomConfig Defaults.BehindNAT = "True" diff --git a/p2p/iptable.go b/p2p/iptable.go index 0f67d42..372d88d 100644 --- a/p2p/iptable.go +++ b/p2p/iptable.go @@ -30,6 +30,7 @@ type IpAddress struct { BareMetalSSHPort string `json:"BareMetalSSHPort"` NAT string `json:"NAT"` EscapeImplementation string `json:"EscapeImplementation"` + ProxyServer string `json:"ProxyServer"` CustomInformation string `json:"CustomInformation"` //CustomInformationKey []byte `json:"CustomInformationKey"` } diff --git a/p2p/upnp_test.go b/p2p/upnp_test.go index ef53493..6ac79cd 100644 --- a/p2p/upnp_test.go +++ b/p2p/upnp_test.go @@ -1,15 +1,15 @@ package p2p import ( - "fmt" - "testing" + "fmt" + "testing" ) // Tests if the current has UPNP support func TestForwardUPNPPort(t *testing.T) { - err := ForwardPort(6586) - if err != nil { - fmt.Println(err) - t.Fail() - } + err := ForwardPort(6586) + if err != nil { + fmt.Println(err) + t.Fail() + } } diff --git a/plugin/TestAnsible/site.yml b/plugin/TestAnsible/site.yml index 3dc78ac..f263373 100644 --- a/plugin/TestAnsible/site.yml +++ b/plugin/TestAnsible/site.yml @@ -5,4 +5,4 @@ tasks: - name: simple-ansibleplaybook debug: - msg: Your are running 'simple-ansibleplaybook' example \ No newline at end of file + msg: Your are running 'simple-ansibleplaybook' example diff --git a/server/ReverseProxy.go b/server/ReverseProxy.go new file mode 100644 index 0000000..7670c2c --- /dev/null +++ b/server/ReverseProxy.go @@ -0,0 +1,135 @@ +// source:https://github.com/afoley587/go-rev-proxy/tree/main +package server + +import ( + "errors" + "fmt" + "github.com/gin-gonic/gin" + "log" + "net/http" + "net/http/httputil" + "net/url" +) + +var ( + //RunPort = 2002 // The server port to run on + //ReverseServerAddr = fmt.Sprint("0.0.0.0:", RunPort) // this is our reverse server ip address + //InsideProxyHostname = fmt.Sprint("proxy:", RunPort) // Requests from private network + //OutsideProxyHostname = fmt.Sprint("registration.localhost:", RunPort) // Requests from public network + KnownAddresses = map[string]string{} // Known Addresses +) + +type RegistrationRequest struct { + OutsideHost string // Outside host address. The hostname or IP on the public network. For example foo.bar.com or foo.localhost + InsideHost string // Inside host address. The hostname or IP on the internal network. For example 192.168.10.5 +} + +// This function checks if TLS was enabled on the request +// and translates it to the proper scheme (http or https) +func GetScheme(c *gin.Context) string { + if c.Request.TLS != nil { + return "https" + } else { + return "http" + } +} + +// IsRegistrationRequest checks if an incoming request is meant to register +// a new inside/outside hostname pair by checking if the hostname and +// path match a specific combination. +// If the host is our registration.localhost or proxy hostname +// AND if the path is /register, we will register a new endpoint +//func IsRegistrationRequest(c *gin.Context) bool { +// isRR := ((c.Request.Host == InsideProxyHostname || c.Request.Host == OutsideProxyHostname) && +// c.Request.URL.String() == "/register") +// return isRR +//} +// +//// SaveRegistrationRequest saves an inside/outside hostname pairing +//// to our KnownAddresses map so we can use it later. Effectively +//// registering a new endpoint for us +//func SaveRegistrationRequest(c *gin.Context) error { +// var rr RegistrationRequest +// log.Println(c.Request.Body) +// err := c.BindJSON(&rr) +// +// if err != nil { +// return err +// } +// +// log.Println("Registering", rr.OutsideHost, "to", rr.InsideHost) +// KnownAddresses[rr.OutsideHost] = rr.InsideHost +// return nil +//} + +// SaveRegistration Creates registration of proxy node +func SaveRegistration(OutsideHost string, InsideHost string) error { + _, ok := KnownAddresses[OutsideHost] + if !ok { + return errors.New("domain name provided already exists") + } + KnownAddresses[OutsideHost] = InsideHost + + return nil +} + +// Proxy runs the actual proxy and will look at the +// hostnames requested from the received request. It will +// then translate that to the inside hostname and forward the +// request +func Proxy(c *gin.Context) { + + // Get if HTTP or HTTPS + scheme := GetScheme(c) + + log.Println(scheme, c.Request.Host, c.Request.URL.String()) + + // Translate the outside hostname to the inside hostname + forwardTo, ok := KnownAddresses[c.Request.Host] + + if !ok { + log.Printf("Unkown Host: %v", c.Request.Host) + c.String(400, "Unkown Host") + return + } + + rUrl := fmt.Sprintf("%v://%v%v", scheme, forwardTo, c.Request.URL) + + remote, err := url.Parse(rUrl) + + if err != nil { + log.Println(err) + c.String(500, "Error Proxying Host") + return + } + + log.Println("Forwarding request to", remote) + + // Forward the request to the inside remote server + // https://pkg.go.dev/net/http/httputil#NewSingleHostReverseProxy + proxy := httputil.NewSingleHostReverseProxy(remote) + + // Director is a function which modifies + // the request into a new request to be sent + // https://pkg.go.dev/net/http/httputil#ReverseProxy + proxy.Director = func(req *http.Request) { + req.Header = c.Request.Header + req.Host = remote.Host + req.URL.Scheme = remote.Scheme + req.URL.Host = remote.Host + req.URL.Path = c.Param("path") + } + + proxy.ServeHTTP(c.Writer, c.Request) +} + +func ProxyRun(port string) { + r := gin.Default() + + // all paths should be handled by Proxy() + r.Any("/*path", Proxy) + + if err := r.Run(fmt.Sprint("0.0.0.0:", port)); err != nil { + log.Printf("Error: %v", err) + } +} diff --git a/server/server.go b/server/server.go index 43fc27a..6816a9d 100644 --- a/server/server.go +++ b/server/server.go @@ -16,9 +16,24 @@ import ( "time" ) +type ReverseProxy struct { + IPAddress string + Port string +} + +// ReverseProxies Reverse to the map such as ReverseProxies[]ReverseProxy type +var ReverseProxies map[string]ReverseProxy + func Server() (*gin.Engine, error) { r := gin.Default() + // "The make function allocates and initializes a hash map data + //structure and returns a map value that points to it. + //The specifics of that data structure are an implementation + //detail of the runtime and are not specified by the language itself." + // source: https://go.dev/blog/maps + ReverseProxies = make(map[string]ReverseProxy) + //Get Server port based on the config file config, err := config.ConfigInit(nil, nil) if err != nil { @@ -175,7 +190,8 @@ func Server() (*gin.Engine, error) { r.GET("/MAPPort", func(c *gin.Context) { Ports := c.DefaultQuery("port", "0") - url, _, err := MapPort(Ports) + DomainName := c.DefaultQuery("domain_name", "0") + url, _, err := MapPort(Ports, DomainName) if err != nil { c.String(http.StatusInternalServerError, fmt.Sprintf("error: %s", err)) } @@ -183,6 +199,42 @@ func Server() (*gin.Engine, error) { c.String(http.StatusOK, url) }) + r.GET("/AddProxy", func(c *gin.Context) { + DomainName := c.DefaultQuery("domain_name", "") + ip_address := c.DefaultQuery("ip_address", "") + Ports := c.DefaultQuery("port", "") + + if DomainName == "" || ip_address == "" || Ports == "" { + c.String(http.StatusInternalServerError, fmt.Sprintf("All get parameters npt provided"+ + " do ensure domain_name, ip_Address and port no is provided")) + } + + SaveRegistration(DomainName, ip_address+":"+Ports) + + //_, ok := ReverseProxies[DomainName] + //// To check if the subdomain entry exists + //if ok { + // c.String(http.StatusInternalServerError, fmt.Sprintf("The domain entry already exists as a reverse"+ + // " proxy entry")) + //} + // + //// added proxy as a map entry + //ReverseProxies[DomainName] = ReverseProxy{IPAddress: ip_address, Port: Ports} + + }) + + r.GET("/RemoveProxy", func(c *gin.Context) { + DomainName := c.DefaultQuery("domain_name", "") + + _, ok := ReverseProxies[DomainName] + if !ok { + c.String(http.StatusInternalServerError, fmt.Sprintf("Domain name does exist in entries of proxies")) + } else { + delete(ReverseProxies, DomainName) + } + + }) + // If there is a proxy port specified // then starts the FRP server //if config.FRPServerPort != "0" { @@ -233,6 +285,7 @@ func Server() (*gin.Engine, error) { ProxyIpAddr.ServerPort = proxyPort ProxyIpAddr.Name = config.MachineName ProxyIpAddr.NAT = "False" + ProxyIpAddr.ProxyServer = "False" ProxyIpAddr.EscapeImplementation = "FRP" // Sorry Jan it's a string @@ -240,7 +293,7 @@ func Server() (*gin.Engine, error) { // to a boolean operator // But I am way too tired if config.BareMetal == "True" { - _, SSHPort, err := MapPort("22") + _, SSHPort, err := MapPort("22", "") if err != nil { return nil, err } @@ -269,13 +322,17 @@ func Server() (*gin.Engine, error) { } + if config.ProxyPort != "" { + go ProxyRun(config.ProxyPort) + } + // Run gin server on the specified port go r.Run(":" + config.ServerPort) return r, nil } -func MapPort(port string) (string, string, error) { +func MapPort(port string, domainName string) (string, string, error) { //Get Server port based on the config file config, err := config.ConfigInit(nil, nil) if err != nil {