* 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>tags/v1.15.0-dev
; 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 |
[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 |
[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 |
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. |
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 |
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= | ||||
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= |
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 | ||||
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) |
"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" | ||||
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 |
--- | |||||
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 |
vendor/ | |||||
coverage.out |
run: | |||||
timeout: 3m | |||||
issues: | |||||
exclude-rules: | |||||
# Exclude some linters from running on tests files. | |||||
- path: _test\.go | |||||
linters: | |||||
- errcheck |
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] |
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. |
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) |
# [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), | |||||
}, | |||||
})) | |||||
``` |
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 | |||||
) |
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= |
// 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 | |||||
} |
// 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 | |||||
} |
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 |