Merge pull request #2589 from ianroberts/file-upstream-rewrite
Add support for rewriteTarget in file: upstreams
This commit is contained in:
commit
e293dddef4
|
|
@ -15,6 +15,7 @@
|
|||
- [#2459](https://github.com/oauth2-proxy/oauth2-proxy/pull/2459) chore(deps): Updated to ginkgo v2 (@kvanzuijlen, @tuunit)
|
||||
- [#2112](https://github.com/oauth2-proxy/oauth2-proxy/pull/2112) docs: update list of providers which support refresh tokens (@mikefab-msf)
|
||||
- [#2734](https://github.com/oauth2-proxy/oauth2-proxy/pull/2734) Added s390x architecture option support (@priby05)
|
||||
- [#2589](https://github.com/oauth2-proxy/oauth2-proxy/pull/2589) Added support for regex path matching and rewriting when using a static `file:` upstream (@ianroberts)
|
||||
|
||||
# V7.6.0
|
||||
|
||||
|
|
|
|||
|
|
@ -524,7 +524,7 @@ Requests will be proxied to this upstream if the path matches the request path.
|
|||
| ----- | ---- | ----------- |
|
||||
| `id` | _string_ | ID should be a unique identifier for the upstream.<br/>This value is required for all upstreams. |
|
||||
| `path` | _string_ | Path is used to map requests to the upstream server.<br/>The closest match will take precedence and all Paths must be unique.<br/>Path can also take a pattern when used with RewriteTarget.<br/>Path segments can be captured and matched using regular experessions.<br/>Eg:<br/>- `^/foo$`: Match only the explicit path `/foo`<br/>- `^/bar/$`: Match any path prefixed with `/bar/`<br/>- `^/baz/(.*)$`: Match any path prefixed with `/baz` and capture the remaining path for use with RewriteTarget |
|
||||
| `rewriteTarget` | _string_ | RewriteTarget allows users to rewrite the request path before it is sent to<br/>the upstream server.<br/>Use the Path to capture segments for reuse within the rewrite target.<br/>Eg: With a Path of `^/baz/(.*)`, a RewriteTarget of `/foo/$1` would rewrite<br/>the request `/baz/abc/123` to `/foo/abc/123` before proxying to the<br/>upstream server. |
|
||||
| `rewriteTarget` | _string_ | RewriteTarget allows users to rewrite the request path before it is sent to<br/>the upstream server (for an HTTP/HTTPS upstream) or mapped to the filesystem<br/>(for a `file:` upstream).<br/>Use the Path to capture segments for reuse within the rewrite target.<br/>Eg: With a Path of `^/baz/(.*)`, a RewriteTarget of `/foo/$1` would rewrite<br/>the request `/baz/abc/123` to `/foo/abc/123` before proxying to the<br/>upstream server. Or if the upstream were `file:///app`, a request for<br/>`/baz/info.html` would return the contents of the file `/app/foo/info.html`. |
|
||||
| `uri` | _string_ | The URI of the upstream server. This may be an HTTP(S) server of a File<br/>based URL. It may include a path, in which case all requests will be served<br/>under that path.<br/>Eg:<br/>- http://localhost:8080<br/>- https://service.localhost<br/>- https://service.localhost/path<br/>- file://host/path<br/>If the URI's path is "/base" and the incoming request was for "/dir",<br/>the upstream request will be for "/base/dir". |
|
||||
| `insecureSkipTLSVerify` | _bool_ | InsecureSkipTLSVerify will skip TLS verification of upstream HTTPS hosts.<br/>This option is insecure and will allow potential Man-In-The-Middle attacks<br/>between OAuth2 Proxy and the upstream server.<br/>Defaults to false. |
|
||||
| `static` | _bool_ | Static will make all requests to this upstream have a static response.<br/>The response will have a body of "Authenticated" and a response code<br/>matching StaticCode.<br/>If StaticCode is not set, the response will return a 200 response. |
|
||||
|
|
|
|||
|
|
@ -39,11 +39,13 @@ type Upstream struct {
|
|||
Path string `json:"path,omitempty"`
|
||||
|
||||
// RewriteTarget allows users to rewrite the request path before it is sent to
|
||||
// the upstream server.
|
||||
// the upstream server (for an HTTP/HTTPS upstream) or mapped to the filesystem
|
||||
// (for a `file:` upstream).
|
||||
// Use the Path to capture segments for reuse within the rewrite target.
|
||||
// Eg: With a Path of `^/baz/(.*)`, a RewriteTarget of `/foo/$1` would rewrite
|
||||
// the request `/baz/abc/123` to `/foo/abc/123` before proxying to the
|
||||
// upstream server.
|
||||
// upstream server. Or if the upstream were `file:///app`, a request for
|
||||
// `/baz/info.html` would return the contents of the file `/app/foo/info.html`.
|
||||
RewriteTarget string `json:"rewriteTarget,omitempty"`
|
||||
|
||||
// The URI of the upstream server. This may be an HTTP(S) server of a File
|
||||
|
|
|
|||
|
|
@ -2,9 +2,12 @@ package upstream
|
|||
|
||||
import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/options"
|
||||
|
||||
"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/middleware"
|
||||
)
|
||||
|
||||
|
|
@ -12,22 +15,53 @@ const fileScheme = "file"
|
|||
|
||||
// newFileServer creates a new fileServer that can serve requests
|
||||
// to a file system location.
|
||||
func newFileServer(id, path, fileSystemPath string) http.Handler {
|
||||
func newFileServer(upstream options.Upstream, fileSystemPath string) http.Handler {
|
||||
handler := newFileServerForPath(fileSystemPath)
|
||||
|
||||
if upstream.RewriteTarget == "" {
|
||||
// if the upstream does not have a rewrite target, strip off the Path prefix
|
||||
// (so e.g. a request for /static/some-file.html looks for some-file.html
|
||||
// relative to the fileSystemPath rather than static/some-file.html).
|
||||
handler = http.StripPrefix(upstream.Path, handler)
|
||||
} else {
|
||||
// if the upstream *does* have a rewrite target then that means the target
|
||||
// path relative to the fileSystemPath will be the one in the (rewritten)
|
||||
// RequestURI.
|
||||
handler = requestURIToURL(handler)
|
||||
}
|
||||
return &fileServer{
|
||||
upstream: id,
|
||||
handler: newFileServerForPath(path, fileSystemPath),
|
||||
upstream: upstream.ID,
|
||||
handler: handler,
|
||||
}
|
||||
}
|
||||
|
||||
// newFileServerForPath creates a http.Handler to serve files from the filesystem
|
||||
func newFileServerForPath(path string, filesystemPath string) http.Handler {
|
||||
func newFileServerForPath(filesystemPath string) http.Handler {
|
||||
// Windows fileSSystemPath will be be prefixed with `/`, eg`/C:/...,
|
||||
// if they were parsed by url.Parse`
|
||||
if runtime.GOOS == "windows" {
|
||||
filesystemPath = strings.TrimPrefix(filesystemPath, "/")
|
||||
}
|
||||
|
||||
return http.StripPrefix(path, http.FileServer(http.Dir(filesystemPath)))
|
||||
return http.FileServer(http.Dir(filesystemPath))
|
||||
}
|
||||
|
||||
// requestURIToURL returns a Handler that replaces the URL in its request with
|
||||
// the result of parsing req.RequestURI. This is necessary for file handlers
|
||||
// that have a rewrite target, since http.FileServer uses req.URL.Path when
|
||||
// looking for the target file, but the rewrite handler only updates the
|
||||
// RequestURI, leaving the original path in the URL.
|
||||
func requestURIToURL(handler http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
||||
reqURL, err := url.ParseRequestURI(req.RequestURI)
|
||||
if err != nil {
|
||||
http.Error(rw, "500 Internal Server Error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
req.URL = reqURL
|
||||
handler.ServeHTTP(rw, req)
|
||||
})
|
||||
}
|
||||
|
||||
// fileServer represents a single filesystem upstream proxy
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@ import (
|
|||
"net/http/httptest"
|
||||
"os"
|
||||
|
||||
"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/options"
|
||||
|
||||
middlewareapi "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/middleware"
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
|
|
@ -31,8 +33,12 @@ var _ = Describe("File Server Suite", func() {
|
|||
_, err := io.ReadFull(rand.Reader, idBytes)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
id = string(idBytes)
|
||||
upstream := options.Upstream{
|
||||
ID: id,
|
||||
Path: "/files",
|
||||
}
|
||||
|
||||
handler = newFileServer(id, "/files", filesDir)
|
||||
handler = newFileServer(upstream, filesDir)
|
||||
})
|
||||
|
||||
AfterEach(func() {
|
||||
|
|
|
|||
|
|
@ -81,7 +81,7 @@ func (m *multiUpstreamProxy) registerStaticResponseHandler(upstream options.Upst
|
|||
// registerFileServer registers a new fileServer based on the configuration given.
|
||||
func (m *multiUpstreamProxy) registerFileServer(upstream options.Upstream, u *url.URL, writer pagewriter.Writer) error {
|
||||
logger.Printf("mapping path %q => file system %q", upstream.Path, u.Path)
|
||||
return m.registerHandler(upstream, newFileServer(upstream.ID, upstream.Path, u.Path), writer)
|
||||
return m.registerHandler(upstream, newFileServer(upstream, u.Path), writer)
|
||||
}
|
||||
|
||||
// registerHTTPUpstreamProxy registers a new httpUpstreamProxy based on the configuration given.
|
||||
|
|
|
|||
|
|
@ -51,6 +51,12 @@ var _ = Describe("Proxy Suite", func() {
|
|||
Path: "/files/",
|
||||
URI: fmt.Sprintf("file:///%s", filesDir),
|
||||
},
|
||||
{
|
||||
ID: "rewrite-file-backend",
|
||||
Path: "^/rewrite-files/.*/(.*)$",
|
||||
RewriteTarget: "/$1",
|
||||
URI: fmt.Sprintf("file:///%s", filesDir),
|
||||
},
|
||||
{
|
||||
ID: "static-backend",
|
||||
Path: "/static/",
|
||||
|
|
@ -174,6 +180,17 @@ var _ = Describe("Proxy Suite", func() {
|
|||
},
|
||||
upstream: "file-backend",
|
||||
}),
|
||||
Entry("with a request to the File backend with rewrite", &proxyTableInput{
|
||||
target: "http://example.localhost/rewrite-files/anything-at-all/foo",
|
||||
response: testHTTPResponse{
|
||||
code: 200,
|
||||
header: map[string][]string{
|
||||
contentType: {textPlainUTF8},
|
||||
},
|
||||
raw: "foo",
|
||||
},
|
||||
upstream: "rewrite-file-backend",
|
||||
}),
|
||||
Entry("with a request to the Static backend", &proxyTableInput{
|
||||
target: "http://example.localhost/static/bar",
|
||||
response: testHTTPResponse{
|
||||
|
|
|
|||
Loading…
Reference in New Issue