Adding basic authentication to Go based reverse proxy server

1.1k views Asked by At

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

2

There are 2 answers

6
gipsy On BEST ANSWER

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)
}
1
alessiosavi On

Here you are, you can copy the logic from my following little project.

https://github.com/alessiosavi/StreamingServer/blob/0f65dbfc77f667777d3047fa1a6b1a2cbd8aaf26/auth/authutils.go

In first instance you need a server for store the users (I've used Redis).

Than you need 3 function for the user

  • LoginUser
  • RegisterUser
  • DeleteUser

During the login/register phase, you generate a cookie hashing username/password and setting the cookie into a Redis table

Than you verify every time that an API is called.

Feel free to copy the code that you need.

Open an issue if something is not well understandable.