forked from gitea/gitea
Add reverse proxy configuration support for remote IP address (#14959)
* Add reverse proxy configuration support for remote IP address validation * Trust all IP addresses in containerized environments by default * Use single option to specify networks and proxy IP addresses. By default trust all loopback IPs Co-authored-by: techknowlogick <techknowlogick@gitea.io>
This commit is contained in:
parent
6e423d5573
commit
044cd4d016
|
@ -549,6 +549,10 @@ COOKIE_REMEMBER_NAME = gitea_incredible
|
||||||
; Reverse proxy authentication header name of user name
|
; Reverse proxy authentication header name of user name
|
||||||
REVERSE_PROXY_AUTHENTICATION_USER = X-WEBAUTH-USER
|
REVERSE_PROXY_AUTHENTICATION_USER = X-WEBAUTH-USER
|
||||||
REVERSE_PROXY_AUTHENTICATION_EMAIL = X-WEBAUTH-EMAIL
|
REVERSE_PROXY_AUTHENTICATION_EMAIL = X-WEBAUTH-EMAIL
|
||||||
|
; Interpret X-Forwarded-For header or the X-Real-IP header and set this as the remote IP for the request
|
||||||
|
REVERSE_PROXY_LIMIT = 1
|
||||||
|
; List of IP addresses and networks seperated by comma of trusted proxy servers. Use `*` to trust all.
|
||||||
|
REVERSE_PROXY_TRUSTED_PROXIES = 127.0.0.0/8,::1/128
|
||||||
; The minimum password length for new Users
|
; The minimum password length for new Users
|
||||||
MIN_PASSWORD_LENGTH = 6
|
MIN_PASSWORD_LENGTH = 6
|
||||||
; Set to true to allow users to import local server paths
|
; Set to true to allow users to import local server paths
|
||||||
|
|
|
@ -53,6 +53,8 @@ ROOT_PATH = /data/gitea/log
|
||||||
[security]
|
[security]
|
||||||
INSTALL_LOCK = $INSTALL_LOCK
|
INSTALL_LOCK = $INSTALL_LOCK
|
||||||
SECRET_KEY = $SECRET_KEY
|
SECRET_KEY = $SECRET_KEY
|
||||||
|
REVERSE_PROXY_LIMIT = 1
|
||||||
|
REVERSE_PROXY_TRUSTED_PROXIES = *
|
||||||
|
|
||||||
[service]
|
[service]
|
||||||
DISABLE_REGISTRATION = $DISABLE_REGISTRATION
|
DISABLE_REGISTRATION = $DISABLE_REGISTRATION
|
||||||
|
|
|
@ -49,6 +49,8 @@ ROOT_PATH = $GITEA_WORK_DIR/data/log
|
||||||
[security]
|
[security]
|
||||||
INSTALL_LOCK = $INSTALL_LOCK
|
INSTALL_LOCK = $INSTALL_LOCK
|
||||||
SECRET_KEY = $SECRET_KEY
|
SECRET_KEY = $SECRET_KEY
|
||||||
|
REVERSE_PROXY_LIMIT = 1
|
||||||
|
REVERSE_PROXY_TRUSTED_PROXIES = *
|
||||||
|
|
||||||
[service]
|
[service]
|
||||||
DISABLE_REGISTRATION = $DISABLE_REGISTRATION
|
DISABLE_REGISTRATION = $DISABLE_REGISTRATION
|
||||||
|
|
|
@ -390,6 +390,9 @@ relation to port exhaustion.
|
||||||
authentication.
|
authentication.
|
||||||
- `REVERSE_PROXY_AUTHENTICATION_EMAIL`: **X-WEBAUTH-EMAIL**: Header name for reverse proxy
|
- `REVERSE_PROXY_AUTHENTICATION_EMAIL`: **X-WEBAUTH-EMAIL**: Header name for reverse proxy
|
||||||
authentication provided email.
|
authentication provided email.
|
||||||
|
- `REVERSE_PROXY_LIMIT`: **1**: Interpret X-Forwarded-For header or the X-Real-IP header and set this as the remote IP for the request.
|
||||||
|
Number of trusted proxy count. Set to zero to not use these headers.
|
||||||
|
- `REVERSE_PROXY_TRUSTED_PROXIES`: **127.0.0.0/8,::1/128**: List of IP addresses and networks separated by comma of trusted proxy servers. Use `*` to trust all.
|
||||||
- `DISABLE_GIT_HOOKS`: **true**: Set to `false` to enable users with git hook privilege to create custom git hooks.
|
- `DISABLE_GIT_HOOKS`: **true**: Set to `false` to enable users with git hook privilege to create custom git hooks.
|
||||||
WARNING: Custom git hooks can be used to perform arbitrary code execution on the host operating system.
|
WARNING: Custom git hooks can be used to perform arbitrary code execution on the host operating system.
|
||||||
This enables the users to access and modify this config file and the Gitea database and interrupt the Gitea service.
|
This enables the users to access and modify this config file and the Gitea database and interrupt the Gitea service.
|
||||||
|
|
1
go.mod
1
go.mod
|
@ -22,6 +22,7 @@ require (
|
||||||
github.com/boombuler/barcode v1.0.1 // indirect
|
github.com/boombuler/barcode v1.0.1 // indirect
|
||||||
github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b // indirect
|
github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b // indirect
|
||||||
github.com/caddyserver/certmagic v0.12.0
|
github.com/caddyserver/certmagic v0.12.0
|
||||||
|
github.com/chi-middleware/proxy v1.1.1
|
||||||
github.com/couchbase/go-couchbase v0.0.0-20210224140812-5740cd35f448 // indirect
|
github.com/couchbase/go-couchbase v0.0.0-20210224140812-5740cd35f448 // indirect
|
||||||
github.com/couchbase/gomemcached v0.1.2 // indirect
|
github.com/couchbase/gomemcached v0.1.2 // indirect
|
||||||
github.com/couchbase/goutils v0.0.0-20210118111533-e33d3ffb5401 // indirect
|
github.com/couchbase/goutils v0.0.0-20210118111533-e33d3ffb5401 // indirect
|
||||||
|
|
4
go.sum
4
go.sum
|
@ -194,6 +194,8 @@ github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
|
||||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||||
github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=
|
github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=
|
||||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||||
|
github.com/chi-middleware/proxy v1.1.1 h1:4HaXUp8o2+bhHr1OhVy+VjN0+L7/07JDcn6v7YrTjrQ=
|
||||||
|
github.com/chi-middleware/proxy v1.1.1/go.mod h1:jQwMEJct2tz9VmtCELxvnXoMfa+SOdikvbVJVHv/M+0=
|
||||||
github.com/chris-ramon/douceur v0.2.0 h1:IDMEdxlEUUBYBKE4z/mJnFyVXox+MjuEVDJNN27glkU=
|
github.com/chris-ramon/douceur v0.2.0 h1:IDMEdxlEUUBYBKE4z/mJnFyVXox+MjuEVDJNN27glkU=
|
||||||
github.com/chris-ramon/douceur v0.2.0/go.mod h1:wDW5xjJdeoMm1mRt4sD4c/LbF/mWdEpRXQKjTR8nIBE=
|
github.com/chris-ramon/douceur v0.2.0/go.mod h1:wDW5xjJdeoMm1mRt4sD4c/LbF/mWdEpRXQKjTR8nIBE=
|
||||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||||
|
@ -318,6 +320,8 @@ github.com/go-asn1-ber/asn1-ber v1.5.3/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkPro
|
||||||
github.com/go-chi/chi v1.5.1/go.mod h1:REp24E+25iKvxgeTfHmdUoL5x15kBiDBlnIl5bCwe2k=
|
github.com/go-chi/chi v1.5.1/go.mod h1:REp24E+25iKvxgeTfHmdUoL5x15kBiDBlnIl5bCwe2k=
|
||||||
github.com/go-chi/chi v1.5.4 h1:QHdzF2szwjqVV4wmByUnTcsbIg7UGaQ0tPF2t5GcAIs=
|
github.com/go-chi/chi v1.5.4 h1:QHdzF2szwjqVV4wmByUnTcsbIg7UGaQ0tPF2t5GcAIs=
|
||||||
github.com/go-chi/chi v1.5.4/go.mod h1:uaf8YgoFazUOkPBG7fxPftUylNumIev9awIWOENIuEg=
|
github.com/go-chi/chi v1.5.4/go.mod h1:uaf8YgoFazUOkPBG7fxPftUylNumIev9awIWOENIuEg=
|
||||||
|
github.com/go-chi/chi/v5 v5.0.1 h1:ALxjCrTf1aflOlkhMnCUP86MubbWFrzB3gkRPReLpTo=
|
||||||
|
github.com/go-chi/chi/v5 v5.0.1/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
|
||||||
github.com/go-chi/cors v1.1.1 h1:eHuqxsIw89iXcWnWUN8R72JMibABJTN/4IOYI5WERvw=
|
github.com/go-chi/cors v1.1.1 h1:eHuqxsIw89iXcWnWUN8R72JMibABJTN/4IOYI5WERvw=
|
||||||
github.com/go-chi/cors v1.1.1/go.mod h1:K2Yje0VW/SJzxiyMYu6iPQYa7hMjQX2i/F491VChg1I=
|
github.com/go-chi/cors v1.1.1/go.mod h1:K2Yje0VW/SJzxiyMYu6iPQYa7hMjQX2i/F491VChg1I=
|
||||||
github.com/go-enry/go-enry/v2 v2.6.1 h1:ckFkMVj2NeHpaQDFDiSjanVjNy2IiuMNivhXDB4c5Q0=
|
github.com/go-enry/go-enry/v2 v2.6.1 h1:ckFkMVj2NeHpaQDFDiSjanVjNy2IiuMNivhXDB4c5Q0=
|
||||||
|
|
|
@ -169,6 +169,8 @@ var (
|
||||||
CookieRememberName string
|
CookieRememberName string
|
||||||
ReverseProxyAuthUser string
|
ReverseProxyAuthUser string
|
||||||
ReverseProxyAuthEmail string
|
ReverseProxyAuthEmail string
|
||||||
|
ReverseProxyLimit int
|
||||||
|
ReverseProxyTrustedProxies []string
|
||||||
MinPasswordLength int
|
MinPasswordLength int
|
||||||
ImportLocalPaths bool
|
ImportLocalPaths bool
|
||||||
DisableGitHooks bool
|
DisableGitHooks bool
|
||||||
|
@ -819,8 +821,16 @@ func NewContext() {
|
||||||
LogInRememberDays = sec.Key("LOGIN_REMEMBER_DAYS").MustInt(7)
|
LogInRememberDays = sec.Key("LOGIN_REMEMBER_DAYS").MustInt(7)
|
||||||
CookieUserName = sec.Key("COOKIE_USERNAME").MustString("gitea_awesome")
|
CookieUserName = sec.Key("COOKIE_USERNAME").MustString("gitea_awesome")
|
||||||
CookieRememberName = sec.Key("COOKIE_REMEMBER_NAME").MustString("gitea_incredible")
|
CookieRememberName = sec.Key("COOKIE_REMEMBER_NAME").MustString("gitea_incredible")
|
||||||
|
|
||||||
ReverseProxyAuthUser = sec.Key("REVERSE_PROXY_AUTHENTICATION_USER").MustString("X-WEBAUTH-USER")
|
ReverseProxyAuthUser = sec.Key("REVERSE_PROXY_AUTHENTICATION_USER").MustString("X-WEBAUTH-USER")
|
||||||
ReverseProxyAuthEmail = sec.Key("REVERSE_PROXY_AUTHENTICATION_EMAIL").MustString("X-WEBAUTH-EMAIL")
|
ReverseProxyAuthEmail = sec.Key("REVERSE_PROXY_AUTHENTICATION_EMAIL").MustString("X-WEBAUTH-EMAIL")
|
||||||
|
|
||||||
|
ReverseProxyLimit = sec.Key("REVERSE_PROXY_LIMIT").MustInt(1)
|
||||||
|
ReverseProxyTrustedProxies = sec.Key("REVERSE_PROXY_TRUSTED_PROXIES").Strings(",")
|
||||||
|
if len(ReverseProxyTrustedProxies) == 0 {
|
||||||
|
ReverseProxyTrustedProxies = []string{"127.0.0.0/8", "::1/128"}
|
||||||
|
}
|
||||||
|
|
||||||
MinPasswordLength = sec.Key("MIN_PASSWORD_LENGTH").MustInt(6)
|
MinPasswordLength = sec.Key("MIN_PASSWORD_LENGTH").MustInt(6)
|
||||||
ImportLocalPaths = sec.Key("IMPORT_LOCAL_PATHS").MustBool(false)
|
ImportLocalPaths = sec.Key("IMPORT_LOCAL_PATHS").MustBool(false)
|
||||||
DisableGitHooks = sec.Key("DISABLE_GIT_HOOKS").MustBool(true)
|
DisableGitHooks = sec.Key("DISABLE_GIT_HOOKS").MustBool(true)
|
||||||
|
|
|
@ -46,6 +46,7 @@ import (
|
||||||
"gitea.com/go-chi/captcha"
|
"gitea.com/go-chi/captcha"
|
||||||
"gitea.com/go-chi/session"
|
"gitea.com/go-chi/session"
|
||||||
"github.com/NYTimes/gziphandler"
|
"github.com/NYTimes/gziphandler"
|
||||||
|
"github.com/chi-middleware/proxy"
|
||||||
"github.com/go-chi/chi/middleware"
|
"github.com/go-chi/chi/middleware"
|
||||||
"github.com/go-chi/cors"
|
"github.com/go-chi/cors"
|
||||||
"github.com/prometheus/client_golang/prometheus"
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
|
@ -65,14 +66,30 @@ func commonMiddlewares() []func(http.Handler) http.Handler {
|
||||||
next.ServeHTTP(context.NewResponse(resp), req)
|
next.ServeHTTP(context.NewResponse(resp), req)
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
middleware.RealIP,
|
|
||||||
middleware.StripSlashes,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if setting.ReverseProxyLimit > 0 {
|
||||||
|
opt := proxy.NewForwardedHeadersOptions().
|
||||||
|
WithForwardLimit(setting.ReverseProxyLimit).
|
||||||
|
ClearTrustedProxies()
|
||||||
|
for _, n := range setting.ReverseProxyTrustedProxies {
|
||||||
|
if !strings.Contains(n, "/") {
|
||||||
|
opt.AddTrustedProxy(n)
|
||||||
|
} else {
|
||||||
|
opt.AddTrustedNetwork(n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
handlers = append(handlers, proxy.ForwardedHeaders(opt))
|
||||||
|
}
|
||||||
|
|
||||||
|
handlers = append(handlers, middleware.StripSlashes)
|
||||||
|
|
||||||
if !setting.DisableRouterLog && setting.RouterLogLevel != log.NONE {
|
if !setting.DisableRouterLog && setting.RouterLogLevel != log.NONE {
|
||||||
if log.GetLogger("router").GetLevel() <= setting.RouterLogLevel {
|
if log.GetLogger("router").GetLevel() <= setting.RouterLogLevel {
|
||||||
handlers = append(handlers, LoggerHandler(setting.RouterLogLevel))
|
handlers = append(handlers, LoggerHandler(setting.RouterLogLevel))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handlers = append(handlers, func(next http.Handler) http.Handler {
|
handlers = append(handlers, func(next http.Handler) http.Handler {
|
||||||
return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
|
return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
|
||||||
// Why we need this? The Recovery() will try to render a beautiful
|
// Why we need this? The Recovery() will try to render a beautiful
|
||||||
|
|
|
@ -0,0 +1,42 @@
|
||||||
|
---
|
||||||
|
kind: pipeline
|
||||||
|
name: compliance
|
||||||
|
|
||||||
|
platform:
|
||||||
|
os: linux
|
||||||
|
arch: amd64
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: lint
|
||||||
|
pull: always
|
||||||
|
image: golang:1.14
|
||||||
|
commands:
|
||||||
|
- make fmt-check
|
||||||
|
- make misspell-check
|
||||||
|
- make lint
|
||||||
|
|
||||||
|
- name: test
|
||||||
|
pull: always
|
||||||
|
image: golang:1.14
|
||||||
|
commands:
|
||||||
|
- make test
|
||||||
|
depends_on:
|
||||||
|
- lint
|
||||||
|
|
||||||
|
- name: coverage
|
||||||
|
pull: always
|
||||||
|
image: robertstettner/drone-codecov
|
||||||
|
settings:
|
||||||
|
files:
|
||||||
|
- coverage.out
|
||||||
|
environment:
|
||||||
|
CODECOV_TOKEN:
|
||||||
|
from_secret: codecov_token
|
||||||
|
depends_on:
|
||||||
|
- test
|
||||||
|
when:
|
||||||
|
branch:
|
||||||
|
- master
|
||||||
|
event:
|
||||||
|
- push
|
||||||
|
- pull_request
|
|
@ -0,0 +1,2 @@
|
||||||
|
vendor/
|
||||||
|
coverage.out
|
|
@ -0,0 +1,9 @@
|
||||||
|
run:
|
||||||
|
timeout: 3m
|
||||||
|
|
||||||
|
issues:
|
||||||
|
exclude-rules:
|
||||||
|
# Exclude some linters from running on tests files.
|
||||||
|
- path: _test\.go
|
||||||
|
linters:
|
||||||
|
- errcheck
|
|
@ -0,0 +1,25 @@
|
||||||
|
ignoreGeneratedHeader = false
|
||||||
|
severity = "warning"
|
||||||
|
confidence = 0.8
|
||||||
|
errorCode = 1
|
||||||
|
warningCode = 1
|
||||||
|
|
||||||
|
[rule.blank-imports]
|
||||||
|
[rule.context-as-argument]
|
||||||
|
[rule.context-keys-type]
|
||||||
|
[rule.dot-imports]
|
||||||
|
[rule.error-return]
|
||||||
|
[rule.error-strings]
|
||||||
|
[rule.error-naming]
|
||||||
|
[rule.exported]
|
||||||
|
[rule.if-return]
|
||||||
|
[rule.increment-decrement]
|
||||||
|
[rule.var-naming]
|
||||||
|
[rule.var-declaration]
|
||||||
|
[rule.package-comments]
|
||||||
|
[rule.range]
|
||||||
|
[rule.receiver-naming]
|
||||||
|
[rule.time-naming]
|
||||||
|
[rule.unexported-return]
|
||||||
|
[rule.indent-error-flow]
|
||||||
|
[rule.errorf]
|
|
@ -0,0 +1,19 @@
|
||||||
|
Copyright (c) 2020 Lauris BH
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
|
@ -0,0 +1,59 @@
|
||||||
|
GO ?= go
|
||||||
|
HAS_GO = $(shell hash $(GO) > /dev/null 2>&1 && echo "GO" || echo "NOGO" )
|
||||||
|
ifeq ($(HAS_GO), GO)
|
||||||
|
GOPATH ?= $(shell $(GO) env GOPATH)
|
||||||
|
export PATH := $(GOPATH)/bin:$(PATH)
|
||||||
|
endif
|
||||||
|
|
||||||
|
GOFMT ?= gofmt -s
|
||||||
|
|
||||||
|
ifneq ($(RACE_ENABLED),)
|
||||||
|
GOTESTFLAGS ?= -race
|
||||||
|
endif
|
||||||
|
|
||||||
|
GO_SOURCES := $(wildcard *.go)
|
||||||
|
GO_SOURCES_OWN := $(filter-out vendor/%, $(GO_SOURCES))
|
||||||
|
GO_PACKAGES ?= $(shell $(GO) list ./... | grep -v /vendor/)
|
||||||
|
|
||||||
|
.PHONY: revive
|
||||||
|
revive:
|
||||||
|
@hash revive > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||||
|
$(GO) get -u github.com/mgechev/revive; \
|
||||||
|
fi
|
||||||
|
revive -config .revive.toml -exclude=./vendor/... ./... || exit 1
|
||||||
|
|
||||||
|
.PHONY: golangci-lint
|
||||||
|
golangci-lint:
|
||||||
|
@hash golangci-lint > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||||
|
export BINARY="golangci-lint"; \
|
||||||
|
curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh -s -- -b $(GOPATH)/bin v1.26.0; \
|
||||||
|
fi
|
||||||
|
golangci-lint run --timeout 5m
|
||||||
|
|
||||||
|
.PHONY: lint
|
||||||
|
lint: golangci-lint revive
|
||||||
|
|
||||||
|
.PHONY: fmt
|
||||||
|
fmt:
|
||||||
|
$(GOFMT) -w $(GO_SOURCES_OWN)
|
||||||
|
|
||||||
|
.PHONY: fmt-check
|
||||||
|
fmt-check:
|
||||||
|
# get all go files and run go fmt on them
|
||||||
|
@diff=$$($(GOFMT) -d $(GO_SOURCES_OWN)); \
|
||||||
|
if [ -n "$$diff" ]; then \
|
||||||
|
echo "Please run 'make fmt' and commit the result:"; \
|
||||||
|
echo "$${diff}"; \
|
||||||
|
exit 1; \
|
||||||
|
fi;
|
||||||
|
|
||||||
|
.PHONY: misspell-check
|
||||||
|
misspell-check:
|
||||||
|
@hash misspell > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||||
|
$(GO) get -u github.com/client9/misspell/cmd/misspell; \
|
||||||
|
fi
|
||||||
|
misspell -error $(GO_SOURCES_OWN)
|
||||||
|
|
||||||
|
.PHONY: test
|
||||||
|
test:
|
||||||
|
$(GO) test -cover -coverprofile coverage.out $(GOTESTFLAGS) $(GO_PACKAGES)
|
|
@ -0,0 +1,46 @@
|
||||||
|
# [Chi](https://github.com/go-chi/chi) proxy middleware
|
||||||
|
|
||||||
|
Forwarded headers middleware to use if application is run behind reverse proxy.
|
||||||
|
|
||||||
|
[![Documentation](https://godoc.org/github.com/chi-middleware/proxy?status.svg)](https://pkg.go.dev/github.com/chi-middleware/proxy)
|
||||||
|
[![codecov](https://codecov.io/gh/chi-middleware/proxy/branch/master/graph/badge.svg)](https://codecov.io/gh/chi-middleware/proxy)
|
||||||
|
[![Go Report Card](https://goreportcard.com/badge/github.com/chi-middleware/proxy)](https://goreportcard.com/report/github.com/chi-middleware/proxy)
|
||||||
|
[![Build Status](https://cloud.drone.io/api/badges/chi-middleware/proxy/status.svg?ref=refs/heads/master)](https://cloud.drone.io/chi-middleware/proxy)
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
Import using:
|
||||||
|
|
||||||
|
```go
|
||||||
|
import "github.com/chi-middleware/proxy"
|
||||||
|
```
|
||||||
|
|
||||||
|
Use middleware with default options (trusted from proxy `127.0.0.1` and trusts only last IP address provided in header):
|
||||||
|
|
||||||
|
```go
|
||||||
|
r := chi.NewRouter()
|
||||||
|
r.Use(proxy.ForwardedHeaders())
|
||||||
|
```
|
||||||
|
|
||||||
|
Extend default options:
|
||||||
|
|
||||||
|
```go
|
||||||
|
r := chi.NewRouter()
|
||||||
|
r.Use(proxy.ForwardedHeaders(
|
||||||
|
proxy.NewForwardedHeadersOptions().
|
||||||
|
WithForwardLimit(2).
|
||||||
|
ClearTrustedProxies().AddTrustedProxy("10.0.0.1"),
|
||||||
|
))
|
||||||
|
```
|
||||||
|
|
||||||
|
Provide custom options:
|
||||||
|
|
||||||
|
```go
|
||||||
|
r := chi.NewRouter()
|
||||||
|
r.Use(proxy.ForwardedHeaders(&ForwardedHeadersOptions{
|
||||||
|
ForwardLimit: 1,
|
||||||
|
TrustedProxies: []net.IP{
|
||||||
|
net.IPv4(10, 0, 0, 1),
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
```
|
|
@ -0,0 +1,8 @@
|
||||||
|
module github.com/chi-middleware/proxy
|
||||||
|
|
||||||
|
go 1.14
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/go-chi/chi/v5 v5.0.1
|
||||||
|
github.com/stretchr/testify v1.7.0
|
||||||
|
)
|
|
@ -0,0 +1,14 @@
|
||||||
|
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
||||||
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/go-chi/chi/v5 v5.0.1 h1:ALxjCrTf1aflOlkhMnCUP86MubbWFrzB3gkRPReLpTo=
|
||||||
|
github.com/go-chi/chi/v5 v5.0.1/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4=
|
||||||
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||||
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
|
@ -0,0 +1,77 @@
|
||||||
|
// Copyright 2020 Lauris BH. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package proxy
|
||||||
|
|
||||||
|
// Ported from Goji's middleware, source:
|
||||||
|
// https://github.com/zenazn/goji/tree/master/web/middleware
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
var xForwardedFor = http.CanonicalHeaderKey("X-Forwarded-For")
|
||||||
|
var xRealIP = http.CanonicalHeaderKey("X-Real-IP")
|
||||||
|
|
||||||
|
// ForwardedHeaders is a middleware that sets a http.Request's RemoteAddr to the results
|
||||||
|
// of parsing either the X-Real-IP header or the X-Forwarded-For header (in that
|
||||||
|
// order).
|
||||||
|
func ForwardedHeaders(options ...*ForwardedHeadersOptions) func(h http.Handler) http.Handler {
|
||||||
|
opt := defaultOptions
|
||||||
|
if len(options) > 0 {
|
||||||
|
opt = options[0]
|
||||||
|
}
|
||||||
|
return func(h http.Handler) http.Handler {
|
||||||
|
fn := func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
// Treat unix socket as 127.0.0.1
|
||||||
|
if r.RemoteAddr == "@" {
|
||||||
|
r.RemoteAddr = "127.0.0.1:0"
|
||||||
|
}
|
||||||
|
if rip := realIP(r, opt); len(rip) > 0 {
|
||||||
|
r.RemoteAddr = net.JoinHostPort(rip, "0")
|
||||||
|
}
|
||||||
|
|
||||||
|
h.ServeHTTP(w, r)
|
||||||
|
}
|
||||||
|
|
||||||
|
return http.HandlerFunc(fn)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func realIP(r *http.Request, options *ForwardedHeadersOptions) string {
|
||||||
|
host, _, err := net.SplitHostPort(r.RemoteAddr)
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
if !options.isTrustedProxy(net.ParseIP(host)) {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
var ip string
|
||||||
|
|
||||||
|
if xrip := r.Header.Get(xRealIP); xrip != "" {
|
||||||
|
ip = xrip
|
||||||
|
} else if xff := r.Header.Get(xForwardedFor); xff != "" {
|
||||||
|
p := 0
|
||||||
|
for i := options.ForwardLimit; i > 0; i-- {
|
||||||
|
if p > 0 {
|
||||||
|
xff = xff[:p-2]
|
||||||
|
}
|
||||||
|
p = strings.LastIndex(xff, ", ")
|
||||||
|
if p < 0 {
|
||||||
|
p = 0
|
||||||
|
break
|
||||||
|
} else {
|
||||||
|
p += 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ip = xff[p:]
|
||||||
|
}
|
||||||
|
|
||||||
|
return ip
|
||||||
|
}
|
|
@ -0,0 +1,115 @@
|
||||||
|
// Copyright 2020 Lauris BH. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package proxy
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ForwardedHeadersOptions represents options for forwarded header middleware
|
||||||
|
type ForwardedHeadersOptions struct {
|
||||||
|
// ForwardLimit limits the number of entries in the headers that will be processed.
|
||||||
|
// The default value is 1. Set to 0 to disable the limit.
|
||||||
|
ForwardLimit int
|
||||||
|
// TrustingAllProxies option sets to trust all proxies.
|
||||||
|
TrustingAllProxies bool
|
||||||
|
// KnownProxies represents addresses of trusted proxies.
|
||||||
|
TrustedProxies []net.IP
|
||||||
|
// TrustedNetworks represents addresses of trusted networks.
|
||||||
|
TrustedNetworks []*net.IPNet
|
||||||
|
}
|
||||||
|
|
||||||
|
var defaultOptions = &ForwardedHeadersOptions{
|
||||||
|
ForwardLimit: 1,
|
||||||
|
TrustedProxies: []net.IP{
|
||||||
|
net.IPv4(127, 0, 0, 1),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewForwardedHeadersOptions creates new middleware options
|
||||||
|
func NewForwardedHeadersOptions() *ForwardedHeadersOptions {
|
||||||
|
return &ForwardedHeadersOptions{
|
||||||
|
ForwardLimit: defaultOptions.ForwardLimit,
|
||||||
|
TrustedProxies: defaultOptions.TrustedProxies,
|
||||||
|
TrustedNetworks: defaultOptions.TrustedNetworks,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithForwardLimit sets number of entries to be processed
|
||||||
|
func (opts *ForwardedHeadersOptions) WithForwardLimit(limit int) *ForwardedHeadersOptions {
|
||||||
|
opts.ForwardLimit = limit
|
||||||
|
return opts
|
||||||
|
}
|
||||||
|
|
||||||
|
// TrustAllProxies sets to trust all proxies
|
||||||
|
func (opts *ForwardedHeadersOptions) TrustAllProxies() *ForwardedHeadersOptions {
|
||||||
|
opts.TrustingAllProxies = true
|
||||||
|
return opts
|
||||||
|
}
|
||||||
|
|
||||||
|
// ClearTrustedProxies clears trusted proxy list
|
||||||
|
func (opts *ForwardedHeadersOptions) ClearTrustedProxies() *ForwardedHeadersOptions {
|
||||||
|
opts.TrustingAllProxies = false
|
||||||
|
opts.TrustedProxies = make([]net.IP, 0)
|
||||||
|
return opts
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddTrustedProxy adds proxy IP to trusted proxy list
|
||||||
|
func (opts *ForwardedHeadersOptions) AddTrustedProxy(ip string) *ForwardedHeadersOptions {
|
||||||
|
// Special option to trust all proxies if IP address is set as wildcard
|
||||||
|
if ip == "*" {
|
||||||
|
opts.TrustingAllProxies = true
|
||||||
|
return opts
|
||||||
|
}
|
||||||
|
|
||||||
|
ipaddr := net.ParseIP(ip)
|
||||||
|
if ipaddr == nil {
|
||||||
|
return opts
|
||||||
|
}
|
||||||
|
|
||||||
|
opts.TrustedProxies = append(opts.TrustedProxies, ipaddr)
|
||||||
|
return opts
|
||||||
|
}
|
||||||
|
|
||||||
|
// ClearTrustedNetworks clears trusted network list
|
||||||
|
func (opts *ForwardedHeadersOptions) ClearTrustedNetworks() *ForwardedHeadersOptions {
|
||||||
|
opts.TrustedNetworks = make([]*net.IPNet, 0)
|
||||||
|
return opts
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddTrustedNetwork adds network to trusted network list
|
||||||
|
func (opts *ForwardedHeadersOptions) AddTrustedNetwork(cidr string) *ForwardedHeadersOptions {
|
||||||
|
_, netmask, err := net.ParseCIDR(cidr)
|
||||||
|
if err != nil || netmask == nil {
|
||||||
|
return opts
|
||||||
|
}
|
||||||
|
|
||||||
|
opts.TrustedNetworks = append(opts.TrustedNetworks, netmask)
|
||||||
|
return opts
|
||||||
|
}
|
||||||
|
|
||||||
|
func (opts *ForwardedHeadersOptions) isTrustedProxy(ip net.IP) bool {
|
||||||
|
if opts.TrustingAllProxies {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if ip == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tip := range opts.TrustedProxies {
|
||||||
|
if tip.Equal(ip) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tnet := range opts.TrustedNetworks {
|
||||||
|
if tnet.Contains(ip) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
|
@ -175,6 +175,9 @@ github.com/bradfitz/gomemcache/memcache
|
||||||
github.com/caddyserver/certmagic
|
github.com/caddyserver/certmagic
|
||||||
# github.com/cespare/xxhash/v2 v2.1.1
|
# github.com/cespare/xxhash/v2 v2.1.1
|
||||||
github.com/cespare/xxhash/v2
|
github.com/cespare/xxhash/v2
|
||||||
|
# github.com/chi-middleware/proxy v1.1.1
|
||||||
|
## explicit
|
||||||
|
github.com/chi-middleware/proxy
|
||||||
# github.com/chris-ramon/douceur v0.2.0
|
# github.com/chris-ramon/douceur v0.2.0
|
||||||
github.com/chris-ramon/douceur/parser
|
github.com/chris-ramon/douceur/parser
|
||||||
# github.com/couchbase/go-couchbase v0.0.0-20210224140812-5740cd35f448
|
# github.com/couchbase/go-couchbase v0.0.0-20210224140812-5740cd35f448
|
||||||
|
|
Loading…
Reference in New Issue