1

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

Community
  • 1
  • 1

2 Answers2

1

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)
}
Peter
  • 29,454
  • 5
  • 48
  • 60
gipsy
  • 3,859
  • 1
  • 13
  • 21
  • can you please share how to call BasicAuth from re *http.Request object as i tried look at some example code on google but couldn't make it work. Thanks in advance – anwar ul hasan Dec 11 '20 at 03:12
  • Updated my answer to show how to do it in your code. Please not you have to import "errors" – gipsy Dec 11 '20 at 03:44
  • imported "errors" but unable to build due to following error: reverse-proxy.go:17:31: cannot use errors.New("not authoized!!") (type error) as type string in argument to http.Error – anwar ul hasan Dec 11 '20 at 11:27
  • it worked like charm, thanks a lot for the help mate. Just had to change: http.Error(w, errors.New("not authoized!!"), http. StatusUnauthorized) to http.Error(w, errors.New("not authoized!!").Error(), http. StatusUnauthorized) – anwar ul hasan Dec 11 '20 at 12:05
  • Need one more help, could you please guide me how to connect using docker client? It works fine with curl, but doesn't work with docker client. I tried it using docker login but i think getting unauthorized 401 error. Could you please help me with that too? Like how can i add credentials to this command? docker -H=127.0.0.1:8888 container ls – anwar ul hasan Dec 16 '20 at 15:54
  • Maybe try -H=username:password@127.0.0.1:8888 – gipsy Dec 19 '20 at 20:46
0

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.

alessiosavi
  • 2,753
  • 2
  • 19
  • 38
  • that's awesome thanks i'll definitely try this one. I am not that proficient with golang, could you please share a function where a hardcoded username and password could be used. I don't need user register and delete functionality just need to protect the through password. thanks a lot for your help. – anwar ul hasan Dec 11 '20 at 03:08