Skip to content

Switch to Rewrite, also add ErrorHandler #89

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
May 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions .github/workflows/gochecks.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
name: go-checks

on:
push:
branches: [ main ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ main ]

jobs:
check:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Setup Go environment
uses: actions/[email protected]
- name: Run Vulncheck
run: |
go install golang.org/x/vuln/cmd/govulncheck@latest
govulncheck ./...
- name: Run golangci-lint
uses: golangci/golangci-lint-action@v3
5 changes: 5 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@ dev-prefix:
-loglevel debug \
-routes.json '[{"prefix":"/fgrpc", "destination":"http://localhost:8079/"}, {"host":"*", "destination":"http://localhost:8080/"}]'

dev-prefix-only:
go run -race . -http-port 8001 -https-port disabled -redirect-port disabled\
-loglevel debug \
-routes.json '[{"prefix":"/debug", "destination":"http://localhost:8080/"}]'

dev-grpc:
go run -race . -h2 -http-port 8001 -https-port disabled -redirect-port disabled\
-loglevel debug \
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ Front end for running fortio report for instance standalone with TLS / Autocert

# Install

using golang 1.18+
using golang 1.20+ (improved ReverseProxy api and security from 1.18)

```shell
go install fortio.org/proxy@latest
Expand Down
18 changes: 9 additions & 9 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,23 +1,23 @@
module fortio.org/proxy

go 1.18
go 1.20

require (
fortio.org/cli v1.1.0
fortio.org/dflag v1.5.2
fortio.org/fortio v1.54.1
fortio.org/fortio v1.54.2
fortio.org/log v1.3.0
fortio.org/scli v1.3.1
golang.org/x/crypto v0.7.0
golang.org/x/net v0.8.0
fortio.org/scli v1.4.0
golang.org/x/crypto v0.8.0
golang.org/x/net v0.9.0
)

require (
fortio.org/sets v1.0.2 // indirect
fortio.org/sets v1.0.3 // indirect
fortio.org/version v1.0.2 // indirect
github.com/fsnotify/fsnotify v1.6.0 // indirect
github.com/google/uuid v1.3.0 // indirect
golang.org/x/exp v0.0.0-20230303215020-44a13b063f3e // indirect
golang.org/x/sys v0.6.0 // indirect
golang.org/x/text v0.8.0 // indirect
golang.org/x/exp v0.0.0-20230425010034-47ecfdc1ba53 // indirect
golang.org/x/sys v0.7.0 // indirect
golang.org/x/text v0.9.0 // indirect
)
34 changes: 17 additions & 17 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,30 +1,30 @@
fortio.org/assert v1.1.4 h1:Za1RaG+OjsTMpQS3J3UCvTF6wc4+IOHCz+jAOU37Y4o=
fortio.org/assert v1.2.0 h1:XscfvR8yp4xW7OMCvNbCsieRFDxlwdEcb69+JZRp6LA=
fortio.org/cli v1.1.0 h1:ATIxi7DgA7WAexUCF8p5a0qlGYk48ZgkwSEDrvwXeN4=
fortio.org/cli v1.1.0/go.mod h1:O3nVImKwJSvHKbMYWkqMbEagAXCS1xvSv1YbHwkKJjY=
fortio.org/dflag v1.5.2 h1:F9XVRj4Qr2IbJP7BMj7XZc9wB0Q/RZ61Ool+4YPVad8=
fortio.org/dflag v1.5.2/go.mod h1:ppb/A8u+KKg+qUUYZNYuvRnXuVb8IsdHb/XGzsmjkN8=
fortio.org/fortio v1.54.1 h1:M3RALr1cWye+NfGDcYxUCubP5Oo95kzLN+FSOJSXCik=
fortio.org/fortio v1.54.1/go.mod h1:8aCS1Vxbggk1wprOQ3tUPu6EQEX7D7MZkS3wwW2Ylno=
fortio.org/fortio v1.54.2 h1:so0yEUbi5wYPCh985i+Jjp5tEVJopownfx58QjksTLs=
fortio.org/fortio v1.54.2/go.mod h1:Eg8ypXab0VwDDyw8snho0+BrESXMgSLS/AsP7B/kPI8=
fortio.org/log v1.3.0 h1:bESPvuQGKejw7rrx41Sg3GoF+tsrB7oC08PxBs5/AM0=
fortio.org/log v1.3.0/go.mod h1:u/8/2lyczXq52aT5Nw6reD+3cR6m/EbS2jBiIYhgiTU=
fortio.org/scli v1.3.1 h1:tQb46yH0djAokwXqguVUkp8C01LuCtURGJYLodvesvI=
fortio.org/scli v1.3.1/go.mod h1:3Cnfp8mCj7TQSz97Y1oqA0GzLcFK8D9btfam4alE9B0=
fortio.org/sets v1.0.2 h1:gSWZFg9rgzl1zJfI/93lDJKBFw8WZ3Uxe3oQ5uDM4T4=
fortio.org/sets v1.0.2/go.mod h1:xVjulHr0FhlmReSymI+AhDtQ4FgjiazQ3JmuNpYFMs8=
fortio.org/scli v1.4.0 h1:hWULZt2eCuN9NMGX9pFMubMY/lNAxFWnNfwxPn2pztE=
fortio.org/scli v1.4.0/go.mod h1:wj7RDObUezKJkPCLHuNb6ktrnRhUYOLCkMhnFhhsfxA=
fortio.org/sets v1.0.3 h1:HzewdGjH69YmyW06yzplL35lGr+X4OcqQt0qS6jbaO4=
fortio.org/sets v1.0.3/go.mod h1:QZVj0r6KP/ZD9ebySW9SgxVNy/NjghUfyHW9NN+WU+4=
fortio.org/version v1.0.2 h1:8NwxdX58aoeKx7T5xAPO0xlUu1Hpk42nRz5s6e6eKZ0=
fortio.org/version v1.0.2/go.mod h1:2JQp9Ax+tm6QKiGuzR5nJY63kFeANcgrZ0osoQFDVm0=
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A=
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
golang.org/x/exp v0.0.0-20230303215020-44a13b063f3e h1:S8xf0d0OEmWrClvbMiUSp+7cGD00txONylwExlf9wR0=
golang.org/x/exp v0.0.0-20230303215020-44a13b063f3e/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ=
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
golang.org/x/crypto v0.8.0 h1:pd9TJtTueMTVQXzk8E2XESSMQDj/U7OUu0PqJqPXQjQ=
golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE=
golang.org/x/exp v0.0.0-20230425010034-47ecfdc1ba53 h1:5llv2sWeaMSnA3w2kS57ouQQ4pudlXrR0dCgw51QK9o=
golang.org/x/exp v0.0.0-20230425010034-47ecfdc1ba53/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w=
golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM=
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68=
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU=
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
7 changes: 4 additions & 3 deletions proxy_main.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ var (
acert *autocert.Manager
)

func hostPolicy(ctx context.Context, host string) error {
func hostPolicy(_ context.Context, host string) error {
log.LogVf("cert host policy called for %q", host)
allowed := certsFor.Get()
if _, found := allowed[host]; found {
Expand Down Expand Up @@ -95,9 +95,10 @@ func main() {

if *port == "disabled" {
log.Infof("No TLS server port.")
select {}
} else {
go startTLSProxy(s)
}
startTLSProxy(s)
scli.UntilInterrupted()
}

func startTLSProxy(s *http.Server) {
Expand Down
43 changes: 27 additions & 16 deletions rp/reverse_proxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import (
"net"
"net/http"
"net/http/httputil"
"net/url"
"sort"
"time"

Expand Down Expand Up @@ -37,30 +36,39 @@ func GetRoutes() []config.Route {
return *routes
}

func setDestination(req *http.Request, url *url.URL) {
req.URL.Scheme = url.Scheme
req.URL.Host = url.Host
// Horrible hack to workaround golang behavior with User-Agent: addition
// same "fix" as https://github.com/golang/go/commit/6a6c1d9841a1957a2fd292df776ea920ae38ea00
if _, ok := req.Header["User-Agent"]; !ok {
// explicitly disable User-Agent so it's not set to default value
req.Header.Set("User-Agent", "")
}
}
const noRouteMarker = "no-route"

// Director is the object used by the ReverseProxy to pick the route/destination.
func Director(req *http.Request) {
// Rewrite is how incoming request are processed for the ReverseProxy
// to pick the route/destination.
func Rewrite(pr *httputil.ProxyRequest) {
routes := GetRoutes()
log.LogVf("Directing %+v", req)
log.LogVf("RP rewrite %+v", pr)
req := pr.In
for _, route := range routes {
log.LogVf("Evaluating req %q vs route %q and path %q vs prefix %q for dest %s",
req.Host, route.Host, req.URL.Path, route.Prefix, route.Destination.URL.String())
if route.MatchServerReq(req) {
pr.SetXForwarded()
pr.SetURL(&route.Destination.URL)
log.LogRequest(req, route.Destination.Str)
setDestination(req, &route.Destination.URL)
return
}
}
// No route matched, log and return 404.
log.Errf("No route matched for %q %q", req.Host, req.URL.Path)
pr.Out.URL.Scheme = noRouteMarker
}

// ErrorHandler is the error handler for the ReverseProxy. We use
// a Scheme marker to know that the error is just there was no route
// and treat that as 404 and everything else remains a 502.
func ErrorHandler(w http.ResponseWriter, r *http.Request, err error) {
if r.URL.Scheme == noRouteMarker {
http.Error(w, "No route matched", http.StatusNotFound)
} else {
log.Errf("Proxy error: %v", err)
w.WriteHeader(http.StatusBadGateway)
}
}

// PrintRoutes prints the current value of the routes config (dflag).
Expand All @@ -78,7 +86,10 @@ func PrintRoutes() {
func ReverseProxy() *httputil.ReverseProxy {
PrintRoutes()

revp := httputil.ReverseProxy{Director: Director}
revp := httputil.ReverseProxy{
Rewrite: Rewrite,
ErrorHandler: ErrorHandler,
}

// TODO: make h2c vs regular client more dynamic based on route config instead of all or nothing
// (or maybe some day it will just ge the default behavior of the base http client)
Expand Down