Search code examples
goreverse-proxybasic-authenticationdocker-daemon

Adding basic authentication to Go based reverse proxy server


I want to secure Docker daemon REST API using Go reverse proxy server. I found this article very relevant. I have never used Go so not sure how to implement basic authentication to this with static username and password. I tried all possible ways i happened to find over Google but none worked for me.

Could some please help adding static basicAuth authentication to following code so that request so that Docker daemon API is only reachable if the request includes username and password: https://github.com/ben-lab/blog-material/blob/master/golang-reverse-proxy-2/reverse-proxy.go

package main

import (
    "fmt"
    "io"
    "log"
    "net/http"
    "time"

    "github.com/tv42/httpunix"
)

func handleHTTP(w http.ResponseWriter, req *http.Request) {

    fmt.Printf("Requested : %s\n", req.URL.Path)

    u := &httpunix.Transport{
        DialTimeout:           100 * time.Millisecond,
        RequestTimeout:        1 * time.Second,
        ResponseHeaderTimeout: 1 * time.Second,
    }
    u.RegisterLocation("docker-socket", "/var/run/docker.sock")

    req.URL.Scheme = "http+unix"
    req.URL.Host = "docker-socket"

    resp, err := u.RoundTrip(req)

    if err != nil {
        http.Error(w, err.Error(), http.StatusServiceUnavailable)
        return
    }
    defer resp.Body.Close()
    copyHeader(w.Header(), resp.Header)
    w.WriteHeader(resp.StatusCode)
    io.Copy(w, resp.Body)
}
func copyHeader(dst, src http.Header) {
    for k, vv := range src {
        for _, v := range vv {
            dst.Add(k, v)
        }
    }
}
func main() {

    server := &http.Server{
        Addr:    ":8888",
        Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { handleHTTP(w, r) }),
    }

    log.Fatal(server.ListenAndServe())
}

https://github.com/ben-lab/blog-material/blob/master/golang-reverse-proxy-2/reverse-proxy.go


Solution

  • You can access the basic auth header values by calling BasicAuth() on your

    req *http.Request object
    

    like:

    user, pass, _ := req.BasicAuth()
    

    Then compare user and pass with the static values you have.

    https://golang.org/pkg/net/http/#Request.BasicAuth

    Update:

    func handleHTTP(w http.ResponseWriter, req *http.Request) {
        user, pass, _ := req.BasicAuth()
        if user != "muuser" || pass != "mysecret" {
          // you have to import "errors"
          http.Error(w, errors.New("not authoized!!"), http. StatusUnauthorized)
            return
        }
        fmt.Printf("Requested : %s\n", req.URL.Path)
    
        u := &httpunix.Transport{
            DialTimeout:           100 * time.Millisecond,
            RequestTimeout:        1 * time.Second,
            ResponseHeaderTimeout: 1 * time.Second,
        }
        u.RegisterLocation("docker-socket", "/var/run/docker.sock")
    
        req.URL.Scheme = "http+unix"
        req.URL.Host = "docker-socket"
    
        resp, err := u.RoundTrip(req)
    
        if err != nil {
            http.Error(w, err.Error(), http.StatusServiceUnavailable)
            return
        }
        defer resp.Body.Close()
        copyHeader(w.Header(), resp.Header)
        w.WriteHeader(resp.StatusCode)
        io.Copy(w, resp.Body)
    }