mirror of
https://github.com/pomerium/pomerium.git
synced 2025-05-03 12:26:03 +02:00
proxy: add per-route request headers setting (#346)
Signed-off-by: Bobby DeSimone <bobbydesimone@gmail.com>
This commit is contained in:
parent
c95a72e12a
commit
a96aec57d5
7 changed files with 90 additions and 13 deletions
|
@ -4,6 +4,7 @@
|
|||
|
||||
### New
|
||||
|
||||
- Allow setting request headers for back-end requests on per route basis in policy. [GH-308]
|
||||
- Add endpoint to support "forward-auth" integration with third-party ingresses and proxies. Supports [nginx]https://docs.nginx.com/nginx/admin-guide/security-controls/configuring-subrequest-authentication/, [nginx-ingress](https://kubernetes.github.io/ingress-nginx/examples/auth/oauth-external-auth/), and [Traefik](https://docs.traefik.io/middlewares/forwardauth/). [GH-324]
|
||||
- Add insecure transport support. [GH-328]
|
||||
- Add setting to override HTTPS backend's TLS Server Name. [GH-297]
|
||||
|
@ -13,7 +14,7 @@
|
|||
### Security
|
||||
|
||||
- The user's original intended location before completing the authentication process is now encrypted and kept confidential from the identity provider. [GH-316]
|
||||
- Under certain circumstances, where debug logging was enabled, pomerium's shared secret could be leaked to http access logs as a query param.
|
||||
- Under certain circumstances, where debug logging was enabled, pomerium's shared secret could be leaked to http access logs as a query param. [GH-338]
|
||||
|
||||
### Fixed
|
||||
|
||||
|
@ -288,3 +289,4 @@
|
|||
[gh-319]: https://github.com/pomerium/pomerium/issues/319
|
||||
[gh-328]: https://github.com/pomerium/pomerium/issues/328
|
||||
[gh-332]: https://github.com/pomerium/pomerium/pull/332/
|
||||
[gh-338]: https://github.com/pomerium/pomerium/issues/338
|
||||
|
|
|
@ -491,6 +491,28 @@ Note: This setting will replace (not append) the system's trust store for a give
|
|||
|
||||
Pomerium supports client certificates which can be used to enforce [mutually authenticated and encrypted TLS connections](https://en.wikipedia.org/wiki/Mutual_authentication) (mTLS). For more details, see our [mTLS example repository](https://github.com/pomerium/examples/tree/master/mutual-tls) and the [certificate docs](./certificates.md).
|
||||
|
||||
### Set Request Headers
|
||||
|
||||
- Config File Key: `set_request_headers`
|
||||
- Type: map of `strings` key value pairs
|
||||
- Optional
|
||||
|
||||
Set Request Headers allows you to set static values for given request headers. This can be useful if you want to pass along additional information to downstream applications as headers, or set authentication header to the request. For example:
|
||||
|
||||
```yaml
|
||||
- from: https://httpbin.corp.example.com
|
||||
to: https://httpbin.org
|
||||
allowed_users:
|
||||
- bdd@pomerium.io
|
||||
- bobbydesimone@gmail.com
|
||||
- bobby@tdia.com
|
||||
set_request_headers:
|
||||
# works auto-magically!
|
||||
# https://httpbin.corp.example.com/basic-auth/root/hunter42
|
||||
Authorization: Basic cm9vdDpodW50ZXI0Mg==
|
||||
X-Your-favorite-authenticating-Proxy: "Pomerium"
|
||||
```
|
||||
|
||||
# Authenticate Service
|
||||
|
||||
## Authenticate Service URL
|
||||
|
|
|
@ -66,14 +66,10 @@ type Policy struct {
|
|||
TLSClientKeyFile string `mapstructure:"tls_client_key_file" yaml:"tls_client_key_file"`
|
||||
ClientCertificate *tls.Certificate
|
||||
|
||||
// IsForwardAuthEndpoint allows for a given route to be used as a forward-auth
|
||||
// endpoint instead of a reverse proxy. Some third-party proxies that do not
|
||||
// have rich access control capabilities (nginx, envoy, ambassador, traefik)
|
||||
// allow you to delegate and authenticate each request to your website
|
||||
// with an external server or service. Pomerium can be configured to accept
|
||||
// these requests with this switch
|
||||
// todo(bdd): link to docs
|
||||
IsForwardAuthEndpoint bool
|
||||
// SetRequestHeaders adds a collection of headers to the downstream request
|
||||
// in the form of key value pairs. Note bene, this will overwrite the
|
||||
// value of any existing value of a given header key.
|
||||
SetRequestHeaders map[string]string `mapstructure:"set_request_headers" yaml:"set_request_headers"`
|
||||
}
|
||||
|
||||
// Validate checks the validity of a policy.
|
||||
|
|
|
@ -16,13 +16,13 @@ import (
|
|||
"golang.org/x/net/publicsuffix"
|
||||
)
|
||||
|
||||
// SetHeaders ensures that every response includes some basic security headers
|
||||
func SetHeaders(securityHeaders map[string]string) func(next http.Handler) http.Handler {
|
||||
// SetHeaders sets a map of response headers.
|
||||
func SetHeaders(headers map[string]string) func(next http.Handler) http.Handler {
|
||||
return func(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
ctx, span := trace.StartSpan(r.Context(), "middleware.SetHeaders")
|
||||
defer span.End()
|
||||
for key, val := range securityHeaders {
|
||||
for key, val := range headers {
|
||||
w.Header().Set(key, val)
|
||||
}
|
||||
next.ServeHTTP(w, r.WithContext(ctx))
|
||||
|
|
|
@ -111,3 +111,17 @@ func (p *Proxy) reqNeedsAuthentication(w http.ResponseWriter, r *http.Request) {
|
|||
uri := urlutil.SignedRedirectURL(p.SharedKey, p.authenticateSigninURL, urlutil.GetAbsoluteURL(r))
|
||||
http.Redirect(w, r, uri.String(), http.StatusFound)
|
||||
}
|
||||
|
||||
// SetResponseHeaders sets a map of response headers.
|
||||
func SetResponseHeaders(headers map[string]string) func(next http.Handler) http.Handler {
|
||||
return func(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
ctx, span := trace.StartSpan(r.Context(), "middleware.SetResponseHeaders")
|
||||
defer span.End()
|
||||
for key, val := range headers {
|
||||
r.Header.Set(key, val)
|
||||
}
|
||||
next.ServeHTTP(w, r.WithContext(ctx))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,9 +6,11 @@ import (
|
|||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/pomerium/pomerium/internal/identity"
|
||||
"github.com/pomerium/pomerium/internal/sessions"
|
||||
"github.com/pomerium/pomerium/proxy/clients"
|
||||
|
@ -173,3 +175,40 @@ func TestProxy_SignRequest(t *testing.T) {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestProxy_SetResponseHeaders(t *testing.T) {
|
||||
t.Parallel()
|
||||
fn := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
|
||||
w.Header().Set("X-Content-Type-Options", "nosniff")
|
||||
var sb strings.Builder
|
||||
for k, v := range r.Header {
|
||||
k = strings.ToLower(k)
|
||||
for _, h := range v {
|
||||
sb.WriteString(fmt.Sprintf("%v: %v\n", k, h))
|
||||
}
|
||||
}
|
||||
fmt.Fprint(w, sb.String())
|
||||
w.WriteHeader(http.StatusOK)
|
||||
})
|
||||
tests := []struct {
|
||||
name string
|
||||
setHeaders map[string]string
|
||||
wantHeaders string
|
||||
}{
|
||||
{"good", map[string]string{"x-gonna": "give-it-to-ya"}, "x-gonna: give-it-to-ya\n"},
|
||||
{"nil", nil, ""},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
|
||||
r := httptest.NewRequest(http.MethodGet, "/", nil)
|
||||
w := httptest.NewRecorder()
|
||||
got := SetResponseHeaders(tt.setHeaders)(fn)
|
||||
got.ServeHTTP(w, r)
|
||||
if diff := cmp.Diff(w.Body.String(), tt.wantHeaders); diff != "" {
|
||||
t.Errorf("SignRequest() :\n %s", diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -242,7 +242,11 @@ func (p *Proxy) reverseProxyHandler(r *mux.Router, policy *config.Policy) (*mux.
|
|||
}
|
||||
rp.Use(p.SignRequest(signer))
|
||||
}
|
||||
|
||||
// Optional: if additional headers are to be set for this url
|
||||
if len(policy.SetRequestHeaders) != 0 {
|
||||
log.Warn().Interface("headers", policy.SetRequestHeaders).Msg("proxy: set request headers")
|
||||
rp.Use(SetResponseHeaders(policy.SetRequestHeaders))
|
||||
}
|
||||
return r, nil
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue