Verify IP addresses

Fix lint errors
Add more test
This commit is contained in:
Richard 2015-03-25 11:11:46 -07:00
parent c6fdfc9cd5
commit e21a425f88
4 changed files with 42 additions and 20 deletions

View file

@ -13,6 +13,8 @@ Frederick F. Kautz IV <fkautz@alumni.cmu.edu>
Josh Hawn <josh.hawn@docker.com> Josh Hawn <josh.hawn@docker.com>
Nghia Tran <tcnghia@gmail.com> Nghia Tran <tcnghia@gmail.com>
Olivier Gambier <olivier@docker.com> Olivier Gambier <olivier@docker.com>
Richard <richard.scothern@gmail.com>
Shreyas Karnik <karnik.shreyas@gmail.com>
Stephen J Day <stephen.day@docker.com> Stephen J Day <stephen.day@docker.com>
Tianon Gravi <admwiggin@gmail.com> Tianon Gravi <admwiggin@gmail.com>
xiekeyang <xiekeyang@huawei.com> xiekeyang <xiekeyang@huawei.com>

View file

@ -2,6 +2,7 @@ package context
import ( import (
"errors" "errors"
"net"
"net/http" "net/http"
"strings" "strings"
"sync" "sync"
@ -17,26 +18,27 @@ var (
ErrNoRequestContext = errors.New("no http request in context") ErrNoRequestContext = errors.New("no http request in context")
) )
// http.Request.RemoteAddr is not the originating IP if the request // RemoteAddr extracts the remote address of the request, taking into
// came through a reverse proxy. // account proxy headers.
// X-Forwarded-For and X-Real-IP provide this information with decreased
// support, so check in that order. There may be multiple 'X-Forwarded-For'
// headers, but go maps don't retain order so just pick one.
func RemoteAddr(r *http.Request) string { func RemoteAddr(r *http.Request) string {
remoteAddr := r.RemoteAddr if prior := r.Header.Get("X-Forwarded-For"); prior != "" {
proxies := strings.Split(prior, ",")
if prior, ok := r.Header["X-Forwarded-For"]; ok {
proxies := strings.Split(prior[0], ",")
if len(proxies) > 0 { if len(proxies) > 0 {
remoteAddr = strings.Trim(proxies[0], " ") remoteAddr := strings.Trim(proxies[0], " ")
if net.ParseIP(remoteAddr) != nil {
return remoteAddr
}
} }
} else if realip, ok := r.Header["X-Real-Ip"]; ok { }
if len(realip) > 0 { // X-Real-Ip is less supported, but worth checking in the
remoteAddr = realip[0] // absence of X-Forwarded-For
if realIP := r.Header.Get("X-Real-Ip"); realIP != "" {
if net.ParseIP(realIP) != nil {
return realIP
} }
} }
return remoteAddr return r.RemoteAddr
} }
// WithRequest places the request on the context. The context of the request // WithRequest places the request on the context. The context of the request
@ -44,7 +46,6 @@ func RemoteAddr(r *http.Request) string {
// is available at "http.request". Other common attributes are available under // is available at "http.request". Other common attributes are available under
// the prefix "http.request.". If a request is already present on the context, // the prefix "http.request.". If a request is already present on the context,
// this method will panic. // this method will panic.
func WithRequest(ctx context.Context, r *http.Request) context.Context { func WithRequest(ctx context.Context, r *http.Request) context.Context {
if ctx.Value("http.request") != nil { if ctx.Value("http.request") != nil {
// NOTE(stevvooe): This needs to be considered a programming error. It // NOTE(stevvooe): This needs to be considered a programming error. It

View file

@ -214,16 +214,15 @@ func TestWithVars(t *testing.T) {
// at the transport layer to 127.0.0.1:<port> . However, as the X-Forwarded-For header // at the transport layer to 127.0.0.1:<port> . However, as the X-Forwarded-For header
// just contains the IP address, it is different enough for testing. // just contains the IP address, it is different enough for testing.
func TestRemoteAddr(t *testing.T) { func TestRemoteAddr(t *testing.T) {
expectedRemote := "127.0.0.1" var expectedRemote string
var actualRemote string
backend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { backend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
defer r.Body.Close() defer r.Body.Close()
if r.RemoteAddr == expectedRemote { if r.RemoteAddr == expectedRemote {
t.Errorf("Unexpected matching remote addresses") t.Errorf("Unexpected matching remote addresses")
} }
actualRemote = RemoteAddr(r)
actualRemote := RemoteAddr(r)
if expectedRemote != actualRemote { if expectedRemote != actualRemote {
t.Errorf("Mismatching remote hosts: %v != %v", expectedRemote, actualRemote) t.Errorf("Mismatching remote hosts: %v != %v", expectedRemote, actualRemote)
} }
@ -241,14 +240,35 @@ func TestRemoteAddr(t *testing.T) {
frontend := httptest.NewServer(proxy) frontend := httptest.NewServer(proxy)
defer frontend.Close() defer frontend.Close()
getReq, err := http.NewRequest("GET", frontend.URL, nil) // X-Forwarded-For set by proxy
expectedRemote = "127.0.0.1"
proxyReq, err := http.NewRequest("GET", frontend.URL, nil)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
_, err = http.DefaultClient.Do(proxyReq)
if err != nil {
t.Fatal(err)
}
// RemoteAddr in X-Real-Ip
getReq, err := http.NewRequest("GET", backend.URL, nil)
if err != nil {
t.Fatal(err)
}
expectedRemote = "1.2.3.4"
getReq.Header["X-Real-ip"] = []string{expectedRemote}
_, err = http.DefaultClient.Do(getReq) _, err = http.DefaultClient.Do(getReq)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
// Valid X-Real-Ip and invalid X-Forwarded-For
getReq.Header["X-forwarded-for"] = []string{"1.2.3"}
_, err = http.DefaultClient.Do(getReq)
if err != nil {
t.Fatal(err)
}
} }

View file

@ -43,7 +43,6 @@ func NewBridge(ub URLBuilder, source SourceRecord, actor ActorRecord, request Re
// NewRequestRecord builds a RequestRecord for use in NewBridge from an // NewRequestRecord builds a RequestRecord for use in NewBridge from an
// http.Request, associating it with a request id. // http.Request, associating it with a request id.
func NewRequestRecord(id string, r *http.Request) RequestRecord { func NewRequestRecord(id string, r *http.Request) RequestRecord {
return RequestRecord{ return RequestRecord{
ID: id, ID: id,