mirror of
https://github.com/umputun/reproxy.git
synced 2024-10-26 10:10:12 +03:00
add ability to drop incoming headers #108
In some cases proxy should sanitize incoming headers. --drop-header and $DROP_HEADERS set list of headers (keys) and those headers removed from the request.
This commit is contained in:
parent
f908fa6fe5
commit
76fa56777f
4
.github/workflows/ci.yml
vendored
4
.github/workflows/ci.yml
vendored
@ -11,10 +11,10 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: set up go 1.16
|
- name: set up go 1.17
|
||||||
uses: actions/setup-go@v2
|
uses: actions/setup-go@v2
|
||||||
with:
|
with:
|
||||||
go-version: 1.16
|
go-version: 1.17
|
||||||
id: go
|
id: go
|
||||||
|
|
||||||
- name: checkout
|
- name: checkout
|
||||||
|
@ -18,7 +18,7 @@ Reproxy is a simple edge HTTP(s) server / reverse proxy supporting various provi
|
|||||||
- Docker container distribution
|
- Docker container distribution
|
||||||
- Built-in static assets server with optional "SPA friendly" mode
|
- Built-in static assets server with optional "SPA friendly" mode
|
||||||
- Support for redirect rules
|
- Support for redirect rules
|
||||||
- Optional limiter for the overall activity as well as for user's activity
|
- Optional limiter for the overall activity as well as for user's activity
|
||||||
- Live health check and fail-over/load-balancing
|
- Live health check and fail-over/load-balancing
|
||||||
- Management server with routes info and prometheus metrics
|
- Management server with routes info and prometheus metrics
|
||||||
- Plugins support via RPC to implement custom functionality
|
- Plugins support via RPC to implement custom functionality
|
||||||
@ -225,6 +225,7 @@ supported codes:
|
|||||||
- `--gzip` enables gzip compression for responses.
|
- `--gzip` enables gzip compression for responses.
|
||||||
- `--max=N` allows to set the maximum size of request (default 64k). Setting it to `0` disables the size check.
|
- `--max=N` allows to set the maximum size of request (default 64k). Setting it to `0` disables the size check.
|
||||||
- `--header` sets extra header(s) added to each proxied response.
|
- `--header` sets extra header(s) added to each proxied response.
|
||||||
|
- `--drop-header` drops headers from incoming request.
|
||||||
|
|
||||||
For example this is how it can be done with the docker compose:
|
For example this is how it can be done with the docker compose:
|
||||||
|
|
||||||
@ -324,7 +325,8 @@ This is the list of all options supporting multiple elements:
|
|||||||
-l, --listen= listen on host:port (default: 0.0.0.0:8080/8443 under docker, 127.0.0.1:80/443 without) [$LISTEN]
|
-l, --listen= listen on host:port (default: 0.0.0.0:8080/8443 under docker, 127.0.0.1:80/443 without) [$LISTEN]
|
||||||
-m, --max= max request size (default: 64K) [$MAX_SIZE]
|
-m, --max= max request size (default: 64K) [$MAX_SIZE]
|
||||||
-g, --gzip enable gz compression [$GZIP]
|
-g, --gzip enable gz compression [$GZIP]
|
||||||
-x, --header= proxy headers
|
-x, --header= outgoing proxy headers to add
|
||||||
|
--drop-header= incoming headers to drop [$DROP_HEADERS]
|
||||||
--lb-type=[random|failover] load balancer type (default: random) [$LB_TYPE]
|
--lb-type=[random|failover] load balancer type (default: random) [$LB_TYPE]
|
||||||
--signature enable reproxy signature headers [$SIGNATURE]
|
--signature enable reproxy signature headers [$SIGNATURE]
|
||||||
--dbg debug mode [$DEBUG]
|
--dbg debug mode [$DEBUG]
|
||||||
@ -407,6 +409,7 @@ plugin:
|
|||||||
|
|
||||||
Help Options:
|
Help Options:
|
||||||
-h, --help Show this help message
|
-h, --help Show this help message
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Status
|
## Status
|
||||||
|
@ -33,7 +33,8 @@ var opts struct {
|
|||||||
Listen string `short:"l" long:"listen" env:"LISTEN" description:"listen on host:port (default: 0.0.0.0:8080/8443 under docker, 127.0.0.1:80/443 without)"`
|
Listen string `short:"l" long:"listen" env:"LISTEN" description:"listen on host:port (default: 0.0.0.0:8080/8443 under docker, 127.0.0.1:80/443 without)"`
|
||||||
MaxSize string `short:"m" long:"max" env:"MAX_SIZE" default:"64K" description:"max request size"`
|
MaxSize string `short:"m" long:"max" env:"MAX_SIZE" default:"64K" description:"max request size"`
|
||||||
GzipEnabled bool `short:"g" long:"gzip" env:"GZIP" description:"enable gz compression"`
|
GzipEnabled bool `short:"g" long:"gzip" env:"GZIP" description:"enable gz compression"`
|
||||||
ProxyHeaders []string `short:"x" long:"header" description:"proxy headers"` // env HEADER split in code to allow , inside ""
|
ProxyHeaders []string `short:"x" long:"header" description:"outgoing proxy headers to add"` // env HEADER split in code to allow , inside ""
|
||||||
|
DropHeaders []string `long:"drop-header" env:"DROP_HEADERS" description:"incoming headers to drop" env-delim:","`
|
||||||
|
|
||||||
LBType string `long:"lb-type" env:"LB_TYPE" description:"load balancer type" choice:"random" choice:"failover" default:"random"` //nolint
|
LBType string `long:"lb-type" env:"LB_TYPE" description:"load balancer type" choice:"random" choice:"failover" default:"random"` //nolint
|
||||||
|
|
||||||
@ -239,6 +240,7 @@ func run() error {
|
|||||||
GzEnabled: opts.GzipEnabled,
|
GzEnabled: opts.GzipEnabled,
|
||||||
SSLConfig: sslConfig,
|
SSLConfig: sslConfig,
|
||||||
ProxyHeaders: proxyHeaders,
|
ProxyHeaders: proxyHeaders,
|
||||||
|
DropHeader: opts.DropHeaders,
|
||||||
AccessLog: accessLog,
|
AccessLog: accessLog,
|
||||||
StdOutEnabled: opts.Logger.StdOut,
|
StdOutEnabled: opts.Logger.StdOut,
|
||||||
Signature: opts.Signature,
|
Signature: opts.Signature,
|
||||||
@ -258,7 +260,7 @@ func run() error {
|
|||||||
Reporter: errReporter,
|
Reporter: errReporter,
|
||||||
PluginConductor: makePluginConductor(ctx),
|
PluginConductor: makePluginConductor(ctx),
|
||||||
ThrottleSystem: opts.Throttle.System * 3,
|
ThrottleSystem: opts.Throttle.System * 3,
|
||||||
ThottleUser: opts.Throttle.User,
|
ThrottleUser: opts.Throttle.User,
|
||||||
}
|
}
|
||||||
|
|
||||||
err = px.Run(ctx)
|
err = px.Run(ctx)
|
||||||
|
@ -45,7 +45,7 @@ func NewMetrics() *Metrics {
|
|||||||
Buckets: []float64{0.01, 0.1, 0.5, 1, 2, 3, 5},
|
Buckets: []float64{0.01, 0.1, 0.5, 1, 2, 3, 5},
|
||||||
}, []string{"path"})
|
}, []string{"path"})
|
||||||
|
|
||||||
prometheus.Unregister(prometheus.NewGoCollector())
|
prometheus.Unregister(prometheus.NewGoCollector()) //nolint
|
||||||
|
|
||||||
if err := prometheus.Register(res.totalRequests); err != nil {
|
if err := prometheus.Register(res.totalRequests); err != nil {
|
||||||
log.Printf("[WARN] can't register prometheus totalRequests, %v", err)
|
log.Printf("[WARN] can't register prometheus totalRequests, %v", err)
|
||||||
|
@ -14,21 +14,28 @@ import (
|
|||||||
"github.com/umputun/reproxy/app/discovery"
|
"github.com/umputun/reproxy/app/discovery"
|
||||||
)
|
)
|
||||||
|
|
||||||
func headersHandler(headers []string) func(next http.Handler) http.Handler {
|
func headersHandler(addHeaders, dropHeaders []string) func(next http.Handler) http.Handler {
|
||||||
|
|
||||||
return func(next http.Handler) http.Handler {
|
return func(next http.Handler) http.Handler {
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
if len(headers) == 0 {
|
if len(addHeaders) == 0 && len(dropHeaders) == 0 {
|
||||||
next.ServeHTTP(w, r)
|
next.ServeHTTP(w, r)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
for _, h := range headers {
|
// add headers to response
|
||||||
|
for _, h := range addHeaders {
|
||||||
elems := strings.Split(h, ":")
|
elems := strings.Split(h, ":")
|
||||||
if len(elems) != 2 {
|
if len(elems) != 2 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
w.Header().Set(strings.TrimSpace(elems[0]), strings.TrimSpace(elems[1]))
|
w.Header().Set(strings.TrimSpace(elems[0]), strings.TrimSpace(elems[1]))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// drop headers from request
|
||||||
|
for _, h := range dropHeaders {
|
||||||
|
r.Header.Del(h)
|
||||||
|
}
|
||||||
|
|
||||||
next.ServeHTTP(w, r)
|
next.ServeHTTP(w, r)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -18,11 +18,17 @@ import (
|
|||||||
|
|
||||||
func Test_headersHandler(t *testing.T) {
|
func Test_headersHandler(t *testing.T) {
|
||||||
wr := httptest.NewRecorder()
|
wr := httptest.NewRecorder()
|
||||||
handler := headersHandler([]string{"k1:v1", "k2:v2"})(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
handler := headersHandler([]string{"k1:v1", "k2:v2"}, []string{"r1", "r2"})(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
t.Logf("req: %v", r)
|
t.Logf("req: %v", r)
|
||||||
|
assert.Equal(t, "", r.Header.Get("r1"), "r1 header dropped")
|
||||||
|
assert.Equal(t, "", r.Header.Get("r2"), "r2 header dropped")
|
||||||
|
assert.Equal(t, "rv3", r.Header.Get("r3"), "r3 kept")
|
||||||
}))
|
}))
|
||||||
req, err := http.NewRequest("GET", "http://example.com", nil)
|
req, err := http.NewRequest("GET", "http://example.com", nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
req.Header.Set("r1", "rv1")
|
||||||
|
req.Header.Set("r2", "rv2")
|
||||||
|
req.Header.Set("r3", "rv3")
|
||||||
handler.ServeHTTP(wr, req)
|
handler.ServeHTTP(wr, req)
|
||||||
assert.Equal(t, "v1", wr.Result().Header.Get("k1"))
|
assert.Equal(t, "v1", wr.Result().Header.Get("k1"))
|
||||||
assert.Equal(t, "v2", wr.Result().Header.Get("k2"))
|
assert.Equal(t, "v2", wr.Result().Header.Get("k2"))
|
||||||
|
@ -32,6 +32,7 @@ type Http struct { // nolint golint
|
|||||||
MaxBodySize int64
|
MaxBodySize int64
|
||||||
GzEnabled bool
|
GzEnabled bool
|
||||||
ProxyHeaders []string
|
ProxyHeaders []string
|
||||||
|
DropHeader []string
|
||||||
SSLConfig SSLConfig
|
SSLConfig SSLConfig
|
||||||
Version string
|
Version string
|
||||||
AccessLog io.Writer
|
AccessLog io.Writer
|
||||||
@ -45,7 +46,7 @@ type Http struct { // nolint golint
|
|||||||
LBSelector func(len int) int
|
LBSelector func(len int) int
|
||||||
|
|
||||||
ThrottleSystem int
|
ThrottleSystem int
|
||||||
ThottleUser int
|
ThrottleUser int
|
||||||
}
|
}
|
||||||
|
|
||||||
// Matcher source info (server and route) to the destination url
|
// Matcher source info (server and route) to the destination url
|
||||||
@ -110,17 +111,17 @@ func (h *Http) Run(ctx context.Context) error {
|
|||||||
}()
|
}()
|
||||||
|
|
||||||
handler := R.Wrap(h.proxyHandler(),
|
handler := R.Wrap(h.proxyHandler(),
|
||||||
R.Recoverer(log.Default()), // recover on errors
|
R.Recoverer(log.Default()), // recover on errors
|
||||||
signatureHandler(h.Signature, h.Version), // send app signature
|
signatureHandler(h.Signature, h.Version), // send app signature
|
||||||
h.pingHandler, // respond to /ping
|
h.pingHandler, // respond to /ping
|
||||||
h.healthMiddleware, // respond to /health
|
h.healthMiddleware, // respond to /health
|
||||||
h.matchHandler, // set matched routes to context
|
h.matchHandler, // set matched routes to context
|
||||||
limiterSystemHandler(h.ThrottleSystem), // limit total requests/sec
|
limiterSystemHandler(h.ThrottleSystem), // limit total requests/sec
|
||||||
limiterUserHandler(h.ThottleUser), // req/seq per user/route match
|
limiterUserHandler(h.ThrottleUser), // req/seq per user/route match
|
||||||
h.mgmtHandler(), // handles /metrics and /routes for prometheus
|
h.mgmtHandler(), // handles /metrics and /routes for prometheus
|
||||||
h.pluginHandler(), // prc to external plugins
|
h.pluginHandler(), // prc to external plugins
|
||||||
headersHandler(h.ProxyHeaders), // set response headers
|
headersHandler(h.ProxyHeaders, h.DropHeader), // add response headers and delete some request headers
|
||||||
accessLogHandler(h.AccessLog), // apache-format log file
|
accessLogHandler(h.AccessLog), // apache-format log file
|
||||||
stdoutLogHandler(h.StdOutEnabled, logger.New(logger.Log(log.Default()), logger.Prefix("[INFO]")).Handler),
|
stdoutLogHandler(h.StdOutEnabled, logger.New(logger.Log(log.Default()), logger.Prefix("[INFO]")).Handler),
|
||||||
maxReqSizeHandler(h.MaxBodySize), // limit request max size
|
maxReqSizeHandler(h.MaxBodySize), // limit request max size
|
||||||
gzipHandler(h.GzEnabled), // gzip response
|
gzipHandler(h.GzEnabled), // gzip response
|
||||||
|
Loading…
Reference in New Issue
Block a user