13

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

Amine
  • 133
  • 1
  • 1
  • 7

3 Answers3

6

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.

user2924994
  • 71
  • 1
  • 3
4

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.

Laurence
  • 391
  • 1
  • 2
  • 8
  • In fact, that is the best solution for my needs. Thank you for help ! – Amine Jan 18 '14 at 17:06
  • 1
    I should have also said, seeming as you're listening on port 80 (and thus running this as root) then you should also change the user ID and group ID in case a vulnerability gets found and exploited: `err = syscall.Setgid(daemon_group_id); err = syscall.Setuid(daemon_user_id)` – Laurence Jan 19 '14 at 17:51
1

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.

alpe1
  • 350
  • 1
  • 9
  • Thank you for your responce: ip address used here are not my real configuration. I have fixe it – Amine Jan 14 '14 at 00:19
  • I believe that what I am looking for is just not possible without modifying the web application to server under app1, app2, ... directories then hooking them with my Golang program under port 80 of the host machine. – Amine Jan 14 '14 at 00:25
  • How can i modify response header in reverse proxy ? For example if i want to modify 'Server' header. – BigSack Apr 15 '14 at 11:23