Question

I want to serve two or more web applications running in a VM (different ports and some time in different directory under the same port) from the host machine and because I need the user to be logged before he can access those apps I can not use a static proxy like Nginx or Apache.

So here is my situation :

192.168.1.1 : is the host ip
192.168.1.2 : is the VM ip

Inside the VM I have this :

192.168.1.2/owncloud : owncloud address
192.168.1.2:8080 : an other app
192.168.1.2:8888 : 3rd app

I want to have this :

192.168.1.1/app1 --> 192.168.1.2/owncloud
192.168.1.1/app2 --> 192.168.1.2:8080
192.168.1.1/app2 --> 192.168.1.2:8888

I have tried to use golang httputil.ReverseProxy to achieve this routing but with no much success: my code is based on this work : gist

package main

import(
    "log"
    "net/url"
    "net/http"
    "net/http/httputil"
)

func main() {
    remote, err := url.Parse("http://192.168.1.2:8080")
    if err != nil {
            panic(err)
    }

    proxy := httputil.NewSingleHostReverseProxy(remote)
    http.HandleFunc("/app2", handler(proxy))
    err = http.ListenAndServe(":80", nil)
    if err != nil {
            panic(err)
    }
}

func handler(p *httputil.ReverseProxy) func(http.ResponseWriter, *http.Request) {
    return func(w http.ResponseWriter, r *http.Request) {
        log.Println(r.URL)
        r.URL.Path = "/"                
            p.ServeHTTP(w, r)
    }
}

Edit :
I have changed the vm ip address: 192.168.1.2 not 192.168.1.1

Was it helpful?

Solution

You'd be better off doing this by hostname rather than URL. eg

owncloud.domain.com -> IP 192.168.1.2,
app2.domain.com     -> IP 192.168.1.3

If you weren't already aware, the hostname is just a HTTP request header (Host: domain.com), so you can have several hostnames per IP (Apache calls this "named virtual hosts").

The benefit of using hostnames rather than URLs is that the web app at the other end isn't aware of the URLs you're prefixing yet needs to honour them, so you can run into problems with the URLs being written by the web app not working against the URLs being expected by the reverse proxy. Where as hostname based proxies should work as most web apps wont rewrite the domain name. (this is a huge generalisation though, some web apps will allow you to add a proxy address - but generally you'll run into less issues with hostnames)

The biggest issue is having to set up sub-domains on your name server. I'm assuming your registra / DNS providers allow you to create subdomains free of charge (most should), but if you're using something like dynamic DNS with this running off your home broadband connection, then you will run into problems and will have to buy your own domain name with the subdomains CNAME'ed to your dynamic DNS address (or use a paid account with your dynamic DNS providers if they offer subdomains).

One last thing, if you're looking into owncloud then you might also want to take a look at Pydio (formally AjaxExplore) as well. They both have different strengths and weaknesses, but in my personal opinion Pydio is a better product.

OTHER TIPS

Make a map like this

hostTarget = map[string]string{
    "app1.domain.com": "http://192.168.1.2/owncloud",
    "app2.domain.com": "http://192.168.1.2:8080",
    "app3.domain.com": "http://192.168.1.2:8888",
}

Use httputil.ReverseProxy build your handler

type baseHandle struct{}

func (h *baseHandle) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    host := r.Host

    if target, ok := hostTarget[host]; ok {
        remoteUrl, err := url.Parse(target)
        if err != nil {
            log.Println("target parse fail:", err)
            return
        }

        proxy := httputil.NewSingleHostReverseProxy(remoteUrl)
        proxy.ServeHTTP(w, r)
        return
    }
    w.Write([]byte("403: Host forbidden " + host))
}

ListenAndServe

h := &baseHandle{}
http.Handle("/", h)

server := &http.Server{
    Addr:    ":8080",
    Handler: h,
}
log.Fatal(server.ListenAndServe())

You can cached httputil.ReverseProxy in global map, all in file above.

Here is a SSLDocker project seen to match with you best.

Your passing the wrong IP to the reverse proxy. Should be your VM's 192.168.1.2.

In the docs

NewSingleHostReverseProxy returns a new ReverseProxy that rewrites URLs to the scheme, host, and base path provided in target.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top