Skip to content

net/httputil: Lost user credentials in reverse proxy requests. #51022

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
vkuznet opened this issue Feb 4, 2022 · 1 comment
Closed

net/httputil: Lost user credentials in reverse proxy requests. #51022

vkuznet opened this issue Feb 4, 2022 · 1 comment

Comments

@vkuznet
Copy link

vkuznet commented Feb 4, 2022

What version of Go are you using (go version)?

$ go version
go version go1.17.6 darwin/amd64

Does this issue reproduce with the latest release?

yes

What operating system and processor architecture are you using (go env)?

go env Output
$ go env
GO111MODULE=""
GOARCH="amd64"
GOBIN=""
GOCACHE="/Users/vk/Library/Caches/go-build"
GOENV="/Users/vk/Library/Application Support/go/env"
GOEXE=""
GOEXPERIMENT=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="darwin"
GOINSECURE=""
GOMODCACHE="/Users/vk/Work/Languages/Go/gopath/pkg/mod"
GONOPROXY=""
GONOSUMDB="github.com/galeone/tensorflow"
GOOS="darwin"
GOPATH="/Users/vk/Work/Languages/Go/gopath"
GOPRIVATE=""
GOPROXY="https://proxy.golang.org,direct"
GOROOT="/opt/local/lib/go"
GOSUMDB="sum.golang.org"
GOTMPDIR=""
GOTOOLDIR="/opt/local/lib/go/pkg/tool/darwin_amd64"
GOVCS=""
GOVERSION="go1.17.6"
GCCGO="gccgo"
AR="ar"
CC="/usr/bin/clang"
CXX="clang++"
CGO_ENABLED="1"
GOMOD="/Users/vk/Work/Languages/Go/gopath/src/github.com/vkuznet/wh-server/go.mod"
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -arch x86_64 -m64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -fdebug-prefix-map=/var/folders/vp/p89gqpvd4f93zzqpz4g5jnvm0000gp/T/go-build1005891228=/tmp/go-build -gno-record-gcc-switches -fno-common"

What did you do?

Hi,
I'm trying to understand how to properly write proxy server which will properly handle user credentials in HTTP requests. For instance, let's say I have a CouchDB with admin:admin credentials, i.e. it should be accessed via http://admin:admin@localhost:5984 URL. We can easily write simple program to access it via default HTTP client. My question is how to do the same using HTTP reverse proxy.

Below you can find a fully working code which uses both HTTP client and HTTP reverse proxy. In both cases I pass around the same HTTP request, but results are completely different. For this demo, I setup CouchDB with admin username and admin password, I used stdout writer which implements HTTP response writer, and I omit errors for simplicity:

package main

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

// StdoutWriter provides the same functionality as http.ResponseWriter
// and print results to stdout.
type StdoutWriter string

// Header implements Header() API of http.ResponseWriter interface
func (s StdoutWriter) Header() http.Header {
	return http.Header{}
}

// Write implements Write API of http.ResponseWriter interface
func (s StdoutWriter) Write(b []byte) (int, error) {
	v := string(b)
	log.Println(v)
	return len(v), nil
}

// WriteHeader implements WriteHeader API of http.ResponseWriter interface
func (s StdoutWriter) WriteHeader(statusCode int) {
	log.Println("statusCode", statusCode)
}

func main() {
	surl := "http://admin:admin@localhost:5984/_all_dbs"
	rurl, _ := url.Parse(surl)
	log.Println("url", rurl, rurl.Host, rurl.Scheme, rurl.User)

	r, _ := http.NewRequest("GET", surl, nil)
	w := StdoutWriter("")

	// make HTTP call via HTTP client
	client := http.Client{}
	resp, _ := client.Do(r)
	log.Println("HTTP response", resp.Status)

	// do the same via proxy
	log.Println("proxy request with", rurl)
	proxy := httputil.NewSingleHostReverseProxy(rurl)
	proxy.ServeHTTP(w, r)
}

When you'll run this program (assuming that you have your local CouchDB running) you'll get the following output:

2022/02/04 16:01:19 url http://admin:admin@localhost:5984/_all_dbs localhost:5984 http admin:admin
2022/02/04 16:01:19 HTTP response 200 OK
2022/02/04 16:01:19 proxy request with http://admin:admin@localhost:5984/_all_dbs
2022/02/04 16:01:19 statusCode 401
2022/02/04 16:01:19 {"error":"unauthorized","reason":"You are not a server admin."}

As you can see the http client works as expected and provides 200 OK, while proxy fails because it does not pass properly user credentials.

What did you expect to see?

The proxy server should keep user credentials when passing HTTP request.

What did you see instead?

I see that reverse proxy does not pass user info within HTTP request and can't access requested resource.

@seankhliao
Copy link
Member

NewSingleHostReverseProxy returns a new ReverseProxy that routes URLs to the scheme, host, and base path provided in target
...
To rewrite Host headers, use ReverseProxy directly with a custom Director policy

Closing as working as intended

@golang golang locked and limited conversation to collaborators Feb 4, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

3 participants