From ec068dea94057d2273eb7aa9b074be0d4ef7dbfa Mon Sep 17 00:00:00 2001 From: Guillaume Rose Date: Mon, 13 Sep 2021 16:29:02 +0200 Subject: [PATCH 1/2] Add endpoint to configure port forwarding without going to the host --- cmd/gvproxy/main.go | 51 ++++++++++++++++++++++----------- pkg/services/forwarder/ports.go | 23 ++++++++++++++- test/port_forwarding_test.go | 29 +++++++++++++++++++ 3 files changed, 85 insertions(+), 18 deletions(-) diff --git a/cmd/gvproxy/main.go b/cmd/gvproxy/main.go index cf1d6b4c9..03ab0338e 100644 --- a/cmd/gvproxy/main.go +++ b/cmd/gvproxy/main.go @@ -37,6 +37,8 @@ var ( exitCode int ) +const gatewayIP = "192.168.127.1" + func main() { flag.Var(&endpoints, "listen", fmt.Sprintf("URL where the tap send packets (default %s)", transport.DefaultURL)) flag.BoolVar(&debug, "debug", false, "Print debug info") @@ -107,7 +109,7 @@ func main() { CaptureFile: captureFile(), MTU: mtu, Subnet: "192.168.127.0/24", - GatewayIP: "192.168.127.1", + GatewayIP: gatewayIP, GatewayMacAddress: "5a:94:ef:e4:0c:dd", DHCPStaticLeases: map[string]string{ "192.168.127.2": "5a:94:ef:e4:0c:ee", @@ -118,7 +120,7 @@ func main() { Records: []types.Record{ { Name: "gateway", - IP: net.ParseIP("192.168.127.1"), + IP: net.ParseIP(gatewayIP), }, { Name: "host", @@ -131,7 +133,7 @@ func main() { Records: []types.Record{ { Name: "gateway", - IP: net.ParseIP("192.168.127.1"), + IP: net.ParseIP(gatewayIP), }, { Name: "host", @@ -207,21 +209,19 @@ func run(ctx context.Context, g *errgroup.Group, configuration *types.Configurat if err != nil { return errors.Wrap(err, "cannot listen") } - g.Go(func() error { - <-ctx.Done() - return ln.Close() - }) - g.Go(func() error { - err := http.Serve(ln, withProfiler(vn)) - if err != nil { - if err != http.ErrServerClosed { - return err - } - return err - } - return nil - }) + httpServe(ctx, g, ln, withProfiler(vn)) } + + ln, err := vn.Listen("tcp", fmt.Sprintf("%s:80", gatewayIP)) + if err != nil { + return err + } + mux := http.NewServeMux() + mux.Handle("/services/forwarder/all", vn.Mux()) + mux.Handle("/services/forwarder/expose", vn.Mux()) + mux.Handle("/services/forwarder/unexpose", vn.Mux()) + httpServe(ctx, g, ln, mux) + if debug { g.Go(func() error { debugLog: @@ -290,6 +290,23 @@ func run(ctx context.Context, g *errgroup.Group, configuration *types.Configurat return nil } +func httpServe(ctx context.Context, g *errgroup.Group, ln net.Listener, mux http.Handler) { + g.Go(func() error { + <-ctx.Done() + return ln.Close() + }) + g.Go(func() error { + err := http.Serve(ln, mux) + if err != nil { + if err != http.ErrServerClosed { + return err + } + return err + } + return nil + }) +} + func withProfiler(vn *virtualnetwork.VirtualNetwork) http.Handler { mux := vn.Mux() if debug { diff --git a/pkg/services/forwarder/ports.go b/pkg/services/forwarder/ports.go index f2211fab5..4f4e037cd 100644 --- a/pkg/services/forwarder/ports.go +++ b/pkg/services/forwarder/ports.go @@ -160,7 +160,12 @@ func (f *PortsForwarder) Mux() http.Handler { if req.Protocol == "" { req.Protocol = types.TCP } - if err := f.Expose(req.Protocol, req.Local, req.Remote); err != nil { + remote, err := remote(req, r.RemoteAddr) + if err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + if err := f.Expose(req.Protocol, req.Local, remote); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } @@ -187,3 +192,19 @@ func (f *PortsForwarder) Mux() http.Handler { }) return mux } + +// if the request doesn't have an IP in the remote field, use the IP from the incoming http request. +func remote(req types.ExposeRequest, ip string) (string, error) { + remoteIP, _, err := net.SplitHostPort(req.Remote) + if err != nil { + return "", err + } + if remoteIP == "" { + host, _, err := net.SplitHostPort(ip) + if err != nil { + return "", err + } + return fmt.Sprintf("%s%s", host, req.Remote), nil + } + return req.Remote, nil +} diff --git a/test/port_forwarding_test.go b/test/port_forwarding_test.go index be9872fae..1e85f52be 100644 --- a/test/port_forwarding_test.go +++ b/test/port_forwarding_test.go @@ -141,4 +141,33 @@ address=/foobar/1.2.3.4 g.Expect(resp.StatusCode).To(Equal(http.StatusOK)) }).Should(Succeed()) }) + + It("should reach a http server in the VM using dynamic port forwarding configured within the VM", func() { + _, err := sshExec("sudo podman run --rm --name http-test -d -p 8080:80 -t docker.io/library/nginx:alpine") + Expect(err).ShouldNot(HaveOccurred()) + defer func() { + _, err := sshExec("sudo podman stop http-test") + Expect(err).ShouldNot(HaveOccurred()) + }() + + _, err = net.Dial("tcp", "127.0.0.1:9090") + Expect(err.Error()).To(HaveSuffix("connection refused")) + + _, err = sshExec(`curl http://gateway.containers.internal/services/forwarder/expose -X POST -d'{"local":":9090", "remote":":8080"}'`) + Expect(err).ShouldNot(HaveOccurred()) + + Eventually(func(g Gomega) { + resp, err := http.Get("http://127.0.0.1:9090") + g.Expect(err).ShouldNot(HaveOccurred()) + g.Expect(resp.StatusCode).To(Equal(http.StatusOK)) + }).Should(Succeed()) + + _, err = sshExec(`curl http://gateway.containers.internal/services/forwarder/unexpose -X POST -d'{"local":":9090"}'`) + Expect(err).ShouldNot(HaveOccurred()) + + Eventually(func(g Gomega) { + _, err = net.Dial("tcp", "127.0.0.1:9090") + g.Expect(err.Error()).To(HaveSuffix("connection refused")) + }).Should(Succeed()) + }) }) From 384e40bdde906b436864115415619d7952a4e337 Mon Sep 17 00:00:00 2001 From: Guillaume Rose Date: Mon, 27 Sep 2021 10:27:39 +0200 Subject: [PATCH 2/2] gvproxy can start without listening a port or a socket on the host gvproxy can be controlled from the inside network directly. --- cmd/gvproxy/main.go | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/cmd/gvproxy/main.go b/cmd/gvproxy/main.go index 03ab0338e..c967ff592 100644 --- a/cmd/gvproxy/main.go +++ b/cmd/gvproxy/main.go @@ -40,7 +40,7 @@ var ( const gatewayIP = "192.168.127.1" func main() { - flag.Var(&endpoints, "listen", fmt.Sprintf("URL where the tap send packets (default %s)", transport.DefaultURL)) + flag.Var(&endpoints, "listen", "control endpoint") flag.BoolVar(&debug, "debug", false, "Print debug info") flag.IntVar(&mtu, "mtu", 1500, "Set the MTU") flag.IntVar(&sshPort, "ssh-port", 2222, "Port to access the guest virtual machine. Must be between 1024 and 65535") @@ -60,9 +60,7 @@ func main() { if debug { log.SetLevel(log.DebugLevel) } - if len(endpoints) == 0 { - endpoints = append(endpoints, transport.DefaultURL) - } + // Make sure the qemu socket provided is valid syntax if len(qemuSocket) > 0 { uri, err := url.Parse(qemuSocket)