Skip to content

Commit

Permalink
don't add dbl headers for X-Forwarded-Proto and X-Forwarded-Proto #172
Browse files Browse the repository at this point in the history
  • Loading branch information
umputun committed Feb 6, 2024
1 parent 8f2bfdc commit 616c1df
Show file tree
Hide file tree
Showing 4 changed files with 162 additions and 46 deletions.
10 changes: 8 additions & 2 deletions app/proxy/proxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -213,8 +213,8 @@ func (h *Http) proxyHandler() http.HandlerFunc {
keepHost := ctx.Value(ctxKeepHost).(bool)
r.Header.Add("X-Forwarded-Host", r.Host)
if h.SSLConfig.SSLMode == SSLAuto || h.SSLConfig.SSLMode == SSLStatic {
r.Header.Add("X-Forwarded-Proto", "https")
r.Header.Add("X-Forwarded-Port", "443")
h.setHeaderIfNotExists(r, "X-Forwarded-Proto", "https")
h.setHeaderIfNotExists(r, "X-Forwarded-Port", "443")
}
r.URL.Path = uu.Path
r.URL.Host = uu.Host
Expand Down Expand Up @@ -466,3 +466,9 @@ func (h *Http) discoveredServers(ctx context.Context, interval time.Duration) (s
}
return servers
}

func (h *Http) setHeaderIfNotExists(r *http.Request, key, value string) {
if _, ok := r.Header[key]; !ok {
r.Header.Set(key, value)
}
}
107 changes: 107 additions & 0 deletions app/proxy/proxy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@ package proxy
import (
"bytes"
"context"
"crypto/tls"
"fmt"
"io"
"math/rand"
"net/http"
"net/http/httptest"
"net/url"
"strconv"
"strings"
"sync/atomic"
"testing"
"time"
Expand Down Expand Up @@ -120,6 +122,111 @@ func TestHttp_Do(t *testing.T) {
})
}

func TestHttp_DoWithSSL(t *testing.T) {
port := rand.Intn(10000) + 40000
h := Http{Timeouts: Timeouts{ResponseHeader: 200 * time.Millisecond}, Address: fmt.Sprintf("localhost:%d", port),
AccessLog: io.Discard, Signature: true, ProxyHeaders: []string{"hh1:vv1", "hh2:vv2"}, StdOutEnabled: true,
Reporter: &ErrorReporter{Nice: true},
SSLConfig: SSLConfig{SSLMode: SSLStatic, Cert: "testdata/localhost.crt", Key: "testdata/localhost.key"}, Insecure: true,
}
ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond)
defer cancel()

ds := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
t.Logf("req: %v", r)
w.Header().Add("h1", "v1")
require.Equal(t, "127.0.0.1", r.Header.Get("X-Real-IP"))
require.Equal(t, "127.0.0.1", r.Header.Get("X-Forwarded-For"))
require.Equal(t, "https", r.Header.Get("X-Forwarded-Proto")) // ssl auto only
require.Equal(t, "443", r.Header.Get("X-Forwarded-Port"))
fmt.Fprintf(w, "response %s", r.URL.String())
}))

svc := discovery.NewService([]discovery.Provider{
&provider.Static{Rules: []string{
"localhost,^/api/(.*)," + strings.Replace(ds.URL, "127.0.0.1", "localhost", 1) + "/123/$1,",
"127.0.0.1,^/api/(.*)," + strings.Replace(ds.URL, "127.0.0.1", "localhost", 1) + "/567/$1,",
},
}}, time.Millisecond*10)

go func() {
_ = svc.Run(context.Background())
}()

time.Sleep(50 * time.Millisecond)
h.Matcher = svc
h.Metrics = mgmt.NewMetrics()

go func() {
_ = h.Run(ctx)
}()
time.Sleep(10 * time.Millisecond)

client := http.Client{
Transport: &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
},
}

t.Run("to localhost, good", func(t *testing.T) {
req, err := http.NewRequest("GET", "https://localhost:"+strconv.Itoa(port)+"/api/something", http.NoBody)
require.NoError(t, err)
resp, err := client.Do(req)
require.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode)
t.Logf("%+v", resp.Header)

body, err := io.ReadAll(resp.Body)
require.NoError(t, err)
assert.Equal(t, "response /123/something", string(body))
assert.Equal(t, "reproxy", resp.Header.Get("App-Name"))
assert.Equal(t, "v1", resp.Header.Get("h1"))
assert.Equal(t, "vv1", resp.Header.Get("hh1"))
assert.Equal(t, "vv2", resp.Header.Get("hh2"))
})

t.Run("to localhost, request with X-Forwarded-Proto and X-Forwarded-Port", func(t *testing.T) {
req, err := http.NewRequest("GET", "https://localhost:"+strconv.Itoa(port)+"/api/something", http.NoBody)
req.Header.Set("X-Forwarded-Proto", "https")
req.Header.Set("X-Forwarded-Port", "443")
require.NoError(t, err)
resp, err := client.Do(req)
require.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode)
t.Logf("%+v", resp.Header)

body, err := io.ReadAll(resp.Body)
require.NoError(t, err)
assert.Equal(t, "response /123/something", string(body))
assert.Equal(t, "reproxy", resp.Header.Get("App-Name"))
assert.Equal(t, "v1", resp.Header.Get("h1"))
assert.Equal(t, "vv1", resp.Header.Get("hh1"))
assert.Equal(t, "vv2", resp.Header.Get("hh2"))
})

t.Run("to 127.0.0.1", func(t *testing.T) {
req, err := http.NewRequest("GET", "https://127.0.0.1:"+strconv.Itoa(port)+"/api/something", http.NoBody)
req.Header.Set("X-Forwarded-Proto", "https")
req.Header.Set("X-Forwarded-Port", "443")
require.NoError(t, err)
resp, err := client.Do(req)
require.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode)
t.Logf("%+v", resp.Header)

body, err := io.ReadAll(resp.Body)
require.NoError(t, err)
assert.Equal(t, "response /567/something", string(body))
assert.Equal(t, "reproxy", resp.Header.Get("App-Name"))
assert.Equal(t, "v1", resp.Header.Get("h1"))
assert.Equal(t, "vv1", resp.Header.Get("hh1"))
assert.Equal(t, "vv2", resp.Header.Get("hh2"))
})
}

func TestHttp_DoWithAssets(t *testing.T) {
port := rand.Intn(10000) + 40000
cc := NewCacheControl(time.Hour * 12)
Expand Down
37 changes: 20 additions & 17 deletions app/proxy/testdata/localhost.crt
Original file line number Diff line number Diff line change
@@ -1,18 +1,21 @@
-----BEGIN CERTIFICATE-----
MIIC5TCCAc2gAwIBAgIJALiRj0+veRc9MA0GCSqGSIb3DQEBCwUAMBQxEjAQBgNV
BAMMCWxvY2FsaG9zdDAeFw0yMTA0MjcwNzQ1MzlaFw0yMTA1MjcwNzQ1MzlaMBQx
EjAQBgNVBAMMCWxvY2FsaG9zdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
ggEBAKku5qZGexf0uvgVY+u8DOSnq8W10w+yQ1wSkMR5T0ag3hyimIDmDhNB3dI0
5/B9IsIC9ZwUxOGHVCjP+TODXAGYIGYRx3BQb8si2369UXTQYNvVNQzPFdThjdcN
gXUmLN08a3ZMdXDC62DcOcVv1+oHd1qbuugkHqwA9CEn/aMJeYPso/4cfbgXX0WD
LEYkzgYwkyVf4JxyDWjm5d5fh/Tgpuu3lgH8qgKi51BmuSdiMiWsZ+AZykjpwAY6
FsNHHmi1NZ1gLj/bzXNjziK50SIgIBiqTIxYdQBrYliRYC3RD252M4Sjk+WAOkVs
RvLDvYLcl0kqclayzBr2qI/2RkECAwEAAaM6MDgwFAYDVR0RBA0wC4IJbG9jYWxo
b3N0MAsGA1UdDwQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcDATANBgkqhkiG9w0B
AQsFAAOCAQEARvUEtFQC/l73/cTyEy24Om3t7uwHWzYRBQDz2dkj2V+OHuXaXKQW
9ucv9DupNYxuXLVg93IPZgDGlFUZMlNnTvP5APBdQZyuoyZER7pqlJg8Sfo1BO5P
KUqaspgdIzd8BmIYMkOaDgd/kOgqjGKjXJwHVvSl7oBcZ8WrxDBMuopXuFosYHVK
z9ZsknqbxibgWBhwLKgDSOwLwVRIPIXlgJpSIyfDpQt0D3PbiFfcXYHqZ09ocZ/b
mIRqF7/OpWW/15Zdo3+gqohWr5qoDZIH8gtBWRs6Ai3cbE/D5jSNdfDrrjRs0HkN
GHmkcX4ABMze6SN7cWFpgeIvy1mxKfMoZw==
-----END CERTIFICATE-----
MIIDhzCCAm+gAwIBAgIUKMrw5NqY2c4v/ziNzA+JGC7keMgwDQYJKoZIhvcNAQEL
BQAwUjELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAklMMRAwDgYDVQQHDAdDaGljYWdv
MRAwDgYDVQQKDAdyZXByb3h5MRIwEAYDVQQDDAlsb2NhbGhvc3QwIBcNMjQwMjA2
MDQ1NDIxWhgPMjA1MTA2MjQwNDU0MjFaMFIxCzAJBgNVBAYTAlVTMQswCQYDVQQI
DAJJTDEQMA4GA1UEBwwHQ2hpY2FnbzEQMA4GA1UECgwHcmVwcm94eTESMBAGA1UE
AwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxvql
fklS0YOZhy2KmP+LlGvYpt2j5GgUr7FWXS/nUdYPtT8GhTzhP6AuVO5a63vmUuoY
XP61DtnirC571eIpgxNiZSUzRYn/IFR75BT025hZ6hwpAU+6ccpJjbQs48O74cgS
xL+QML2/zqgWIvT9hZg5+/fi8gO76Gcwyi/r1pwk4Gac1HJTdicQes5AewUuFQt+
AdN/VSklzDmQAn+mgWmXqxnTMgccGBlsiiYTjKkBSIrUB5TEXIpASzU12EzF8LCU
mG2g4/wUGgKAXEUXI/KGdhm83XR7a0vWRsKNZDokNIeFfohr0JI1h1uJNM+2bfN5
bTaLEjnxxwnw7j+ApwIDAQABo1MwUTAdBgNVHQ4EFgQUYiSDgICi9jUqX3K5hDXz
sZlksBswHwYDVR0jBBgwFoAUYiSDgICi9jUqX3K5hDXzsZlksBswDwYDVR0TAQH/
BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEArSpccPKHbnS4Xc0SUdqhBiEpM49v
u3yVXvUFaFq6drIJNeAjmMundw4qPuSl8w56YVwzHwmv8OSAyb15RUl+1mSpT//i
sIqn3Ph3y6AcnyrkobCYOPho4dZMTsuSPfa9WRuFdvpUuZcn9JlvzlPZeYs6ba32
wZfGBu6We26u89dcUKstKLEWZOI6rvTwViDiNafpnQc2JmP8U27wkyQe0uRuwUUY
nLlB44QmWWHgct2YJz7wIJ0R9LQB32rhvLvkDnpl2VRA87xl8Q4z6yGfZVSzkRtq
2K8HX+VuCB3NB61tC+eYbNEehkcdfJmuXzO/fLcoMWKEH5Q1ExmPMn+BKQ==
-----END CERTIFICATE-----
54 changes: 27 additions & 27 deletions app/proxy/testdata/localhost.key
Original file line number Diff line number Diff line change
@@ -1,28 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCpLuamRnsX9Lr4
FWPrvAzkp6vFtdMPskNcEpDEeU9GoN4copiA5g4TQd3SNOfwfSLCAvWcFMThh1Qo
z/kzg1wBmCBmEcdwUG/LItt+vVF00GDb1TUMzxXU4Y3XDYF1JizdPGt2THVwwutg
3DnFb9fqB3dam7roJB6sAPQhJ/2jCXmD7KP+HH24F19FgyxGJM4GMJMlX+Cccg1o
5uXeX4f04Kbrt5YB/KoCoudQZrknYjIlrGfgGcpI6cAGOhbDRx5otTWdYC4/281z
Y84iudEiICAYqkyMWHUAa2JYkWAt0Q9udjOEo5PlgDpFbEbyw72C3JdJKnJWsswa
9qiP9kZBAgMBAAECggEAcCZ7F3ZZWwQMfTAQ0NAT6++KWsGxbBJLvNlBxjx0ZOl2
05ylY60dX36mQRZ5Ol55kArOLe1GpgpDq9pR4+gMMbJap87ZWoa31P0Ca/2r5bfM
vW2UgS0116y9jfWR/8qSqwXGZuFAaMONrOPQGCWQB79zS0k4mXJ4MqVfMCuGY3Bs
IkeyT96/pcYg36KeTGfvZOrX1GvH3udht5x2BxwuWdNjLEqL391OetnZe1H3NArs
fklboxgc/G0kRfx6X9/pqB+zytqeYec1nqc13I+3z6xjD1a2dK7rldY6CAp0IhwU
54hmu8MO/Laq3159xbB0BR48ClIwox+4z0pT5hDtEQKBgQDbfVMOma9TYmWGd7Or
/iuOVS64Jm2vrGySg/JlSBFskogy1vTtzwuAWe/3NLy5FVt1W7WC2Wt8OM1LIsh1
lBiWM4Fz2bqLAgu1Y48Y0DsNVtckgo1jmwT4i1Ub664NMVvsaXLNtiSWzf53Jmh9
J5zyg9ZiLXA7q+P5QDpZdskmowKBgQDFU1jC0LoaQaw+vH06eIoXVWYYwU+xpAK+
AXZv7N3nBafhYTaTfWT/j54xoJr4kCnavXADMZq3oeHzR20B9oc4F1qT/6rEzYlu
pi7EtxXHf406Huf1uLOU8VckYEZd31i9dM0By7RD+EopYcI1TEIN1UR5XYv/bwHa
tZHjAFoBywKBgQCjnrZG4QxRFb3nUt6OrYgcr6WHQ6Zq2heJ1XDiTaonjMiZVaL6
kGjbgrAfUaIKO0CVqQsTgy7cSJ/JjiFvfToi5jxvd3TXYWwHCTPIZJpQ5Fa3cdci
1JINEhkdGkECtrP29djOPyThgqhafDhSbDBUnTE4uPS8lvP4gAe/X4yuDwKBgQC6
AGaavMWwGleSi3o/s3/3nrgufYnxmPg8woQx3MUPD3XALTKUtI6Pl4E2pn1t7/aE
Ci2b1RZSInYqLBnEz+2GIf1vpIAEIvp5IozTQQF2m/Uz5A4iwYgFzbimwVmTAwVT
ENZt6uZxa4n8l/nI46kgAPgaruNYU/sbfiuWHq65IQKBgCMYfLML4oeRt+Xm5wcy
T+PrP0qKt20wW/nqn4TVjuO606ywD2aLS/AJ96k04maEEAebahy6ODoEjNb3erQW
VGsMydDBZ0QlMs6x7LDh3JLP0+JNETCezd1Nl20ykjpaoZ8Unzf/gpgaVjZON+wF
ON1xMW7NBrs9xvcjhBVgtn6r
-----END PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDG+qV+SVLRg5mH
LYqY/4uUa9im3aPkaBSvsVZdL+dR1g+1PwaFPOE/oC5U7lrre+ZS6hhc/rUO2eKs
LnvV4imDE2JlJTNFif8gVHvkFPTbmFnqHCkBT7pxykmNtCzjw7vhyBLEv5Awvb/O
qBYi9P2FmDn79+LyA7voZzDKL+vWnCTgZpzUclN2JxB6zkB7BS4VC34B039VKSXM
OZACf6aBaZerGdMyBxwYGWyKJhOMqQFIitQHlMRcikBLNTXYTMXwsJSYbaDj/BQa
AoBcRRcj8oZ2GbzddHtrS9ZGwo1kOiQ0h4V+iGvQkjWHW4k0z7Zt83ltNosSOfHH
CfDuP4CnAgMBAAECggEAVx8xzOyf5XqAg26OS9VAMTlTQCS1ePGVdSPpk53A49Ud
RZeV7EquuWQSRT+j8Y1rWIyFJFqlvh3qoMctk4WV9X1MTMsP+vekDGzRXhlK6Md5
PwcbcSaOlPokYHYuXX+7SO2IQjs6EA1U6VAxeRbZ1l/Dq65q4Np/sQ9VjoGS+oDX
ApTYm5lKiKjBzf4xJ9jE61YaC9phxE099Tl8CEDRUUCEFL7fd6I+cPpCZE6094P5
OHjMJYXAONclhXCXmPeGjtio9VnGgrGaclnBzjHs94XcW+slXBZdzDubXsi5Uhb3
6P+GVpuyhpgFLEqtAbl6HUcGtS/Gbq4uYW+2FhlpWQKBgQD5pmKiJ7P2W2a3gZto
/Rb9r6Q2YnbtAPigKCaN6Ntzahrej1we7Qqi4lYS1z9YmRaXxyvySdNiTbYo/qLm
XEK2F8AnJUpaEK0+QJ7EQbTww8bElhPnpG9246+qMTbUwSgf6sRZ+RToGMQ5EBTY
deFgz4H0qXrDj2RQkOC1ayvDRQKBgQDMClBjsZa+FKoTWBGnHFlBzQQ9X0jAaSno
sPPsd9N8pcHZ2P3NehSEGeCeRc8xcZZib4TK1APnRGg+56tX+QSvwGT4P8C5GHvM
S3T8xojgpFK6JGai/jrcBWqOCa3xXrP3miiF8Es01Q2Wy3EuMffkBlUBgsW66PPU
A3W4UwWc+wKBgGtz4iBJVnxC+wMhFfMqfCrU3qlJ2EZKlLjajz2lbE9Q7B+/NLda
76kMImAZpXpM6hyJ7bBrdkBpkm4yq4rbSxt1PY+bzVTWuLqCtdNjNK4slfEnZ4nc
jN1vQrzOUftg6BRUyA6x1v3PKyYkddR1aHxy1EyqZdyma1cCBLYRWtTBAoGAUpD5
5t2+OjzyddF1k0INfGsSBCPCtNnZc6fnjREQK6iHwTflvHhiRPKTynhFV6S3Ti4C
dnFFAxjTdmEZHQhPtS8NrMdfnYci0ZDXTlKooP7d2yVPwzVNbCtk6wVPthS0jsV7
EHgkdsSgMx0wN5lQzp0hWPMqQHBz+p9Ly8MMynECgYEAr+zCEd1v3n6okfaZKFV9
czy1jigLgXOdDSzqAe8FE53hFQiCkYn5fj7byNRYrCmUb1dtoQJsM7AmN1LkWVv3
5hC8FKHS78SBkeCRplWIr329z+xoBrV+9b4DubUQXACW7zQh911404TdbZMjGohW
oJdJyZA2raAm/gaOyTIqDz0=
-----END PRIVATE KEY-----

0 comments on commit 616c1df

Please sign in to comment.