mirror of
https://github.com/umputun/reproxy.git
synced 2024-09-11 08:55:25 +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
|
||||
|
||||
steps:
|
||||
- name: set up go 1.16
|
||||
- name: set up go 1.17
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: 1.16
|
||||
go-version: 1.17
|
||||
id: go
|
||||
|
||||
- name: checkout
|
||||
|
@ -18,7 +18,7 @@ Reproxy is a simple edge HTTP(s) server / reverse proxy supporting various provi
|
||||
- Docker container distribution
|
||||
- Built-in static assets server with optional "SPA friendly" mode
|
||||
- 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
|
||||
- Management server with routes info and prometheus metrics
|
||||
- Plugins support via RPC to implement custom functionality
|
||||
@ -225,6 +225,7 @@ supported codes:
|
||||
- `--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.
|
||||
- `--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:
|
||||
|
||||
@ -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]
|
||||
-m, --max= max request size (default: 64K) [$MAX_SIZE]
|
||||
-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]
|
||||
--signature enable reproxy signature headers [$SIGNATURE]
|
||||
--dbg debug mode [$DEBUG]
|
||||
@ -407,6 +409,7 @@ plugin:
|
||||
|
||||
Help Options:
|
||||
-h, --help Show this help message
|
||||
|
||||
```
|
||||
|
||||
## 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)"`
|
||||
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"`
|
||||
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
|
||||
|
||||
@ -239,6 +240,7 @@ func run() error {
|
||||
GzEnabled: opts.GzipEnabled,
|
||||
SSLConfig: sslConfig,
|
||||
ProxyHeaders: proxyHeaders,
|
||||
DropHeader: opts.DropHeaders,
|
||||
AccessLog: accessLog,
|
||||
StdOutEnabled: opts.Logger.StdOut,
|
||||
Signature: opts.Signature,
|
||||
@ -258,7 +260,7 @@ func run() error {
|
||||
Reporter: errReporter,
|
||||
PluginConductor: makePluginConductor(ctx),
|
||||
ThrottleSystem: opts.Throttle.System * 3,
|
||||
ThottleUser: opts.Throttle.User,
|
||||
ThrottleUser: opts.Throttle.User,
|
||||
}
|
||||
|
||||
err = px.Run(ctx)
|
||||
|
@ -45,7 +45,7 @@ func NewMetrics() *Metrics {
|
||||
Buckets: []float64{0.01, 0.1, 0.5, 1, 2, 3, 5},
|
||||
}, []string{"path"})
|
||||
|
||||
prometheus.Unregister(prometheus.NewGoCollector())
|
||||
prometheus.Unregister(prometheus.NewGoCollector()) //nolint
|
||||
|
||||
if err := prometheus.Register(res.totalRequests); err != nil {
|
||||
log.Printf("[WARN] can't register prometheus totalRequests, %v", err)
|
||||
|
@ -14,21 +14,28 @@ import (
|
||||
"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 http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if len(headers) == 0 {
|
||||
if len(addHeaders) == 0 && len(dropHeaders) == 0 {
|
||||
next.ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
for _, h := range headers {
|
||||
// add headers to response
|
||||
for _, h := range addHeaders {
|
||||
elems := strings.Split(h, ":")
|
||||
if len(elems) != 2 {
|
||||
continue
|
||||
}
|
||||
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)
|
||||
})
|
||||
}
|
||||
|
@ -18,11 +18,17 @@ import (
|
||||
|
||||
func Test_headersHandler(t *testing.T) {
|
||||
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)
|
||||
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)
|
||||
require.NoError(t, err)
|
||||
req.Header.Set("r1", "rv1")
|
||||
req.Header.Set("r2", "rv2")
|
||||
req.Header.Set("r3", "rv3")
|
||||
handler.ServeHTTP(wr, req)
|
||||
assert.Equal(t, "v1", wr.Result().Header.Get("k1"))
|
||||
assert.Equal(t, "v2", wr.Result().Header.Get("k2"))
|
||||
|
@ -32,6 +32,7 @@ type Http struct { // nolint golint
|
||||
MaxBodySize int64
|
||||
GzEnabled bool
|
||||
ProxyHeaders []string
|
||||
DropHeader []string
|
||||
SSLConfig SSLConfig
|
||||
Version string
|
||||
AccessLog io.Writer
|
||||
@ -45,7 +46,7 @@ type Http struct { // nolint golint
|
||||
LBSelector func(len int) int
|
||||
|
||||
ThrottleSystem int
|
||||
ThottleUser int
|
||||
ThrottleUser int
|
||||
}
|
||||
|
||||
// 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(),
|
||||
R.Recoverer(log.Default()), // recover on errors
|
||||
signatureHandler(h.Signature, h.Version), // send app signature
|
||||
h.pingHandler, // respond to /ping
|
||||
h.healthMiddleware, // respond to /health
|
||||
h.matchHandler, // set matched routes to context
|
||||
limiterSystemHandler(h.ThrottleSystem), // limit total requests/sec
|
||||
limiterUserHandler(h.ThottleUser), // req/seq per user/route match
|
||||
h.mgmtHandler(), // handles /metrics and /routes for prometheus
|
||||
h.pluginHandler(), // prc to external plugins
|
||||
headersHandler(h.ProxyHeaders), // set response headers
|
||||
accessLogHandler(h.AccessLog), // apache-format log file
|
||||
R.Recoverer(log.Default()), // recover on errors
|
||||
signatureHandler(h.Signature, h.Version), // send app signature
|
||||
h.pingHandler, // respond to /ping
|
||||
h.healthMiddleware, // respond to /health
|
||||
h.matchHandler, // set matched routes to context
|
||||
limiterSystemHandler(h.ThrottleSystem), // limit total requests/sec
|
||||
limiterUserHandler(h.ThrottleUser), // req/seq per user/route match
|
||||
h.mgmtHandler(), // handles /metrics and /routes for prometheus
|
||||
h.pluginHandler(), // prc to external plugins
|
||||
headersHandler(h.ProxyHeaders, h.DropHeader), // add response headers and delete some request headers
|
||||
accessLogHandler(h.AccessLog), // apache-format log file
|
||||
stdoutLogHandler(h.StdOutEnabled, logger.New(logger.Log(log.Default()), logger.Prefix("[INFO]")).Handler),
|
||||
maxReqSizeHandler(h.MaxBodySize), // limit request max size
|
||||
gzipHandler(h.GzEnabled), // gzip response
|
||||
|
Loading…
Reference in New Issue
Block a user