git.iko.soy/typeable/oauth2-proxy
on ea261ca help sign in
166 lines 5.0 kB history raw
1package upstream
3import (
4 "crypto/tls"
5 "net/http"
6 "net/http/httputil"
7 "net/url"
8 "strings"
10 "github.com/mbland/hmacauth"
11 "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/middleware"
12 "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/options"
15const (
16 // SignatureHeader is the name of the request header containing the GAP Signature
17 // Part of hmacauth
18 SignatureHeader = "GAP-Signature"
20 httpScheme = "http"
21 httpsScheme = "https"
24// SignatureHeaders contains the headers to be signed by the hmac algorithm
25// Part of hmacauth
26var SignatureHeaders = []string{
27 "Content-Length",
28 "Content-Md5",
29 "Content-Type",
30 "Date",
31 "Authorization",
32 "X-Forwarded-User",
33 "X-Forwarded-Email",
34 "X-Forwarded-Preferred-User",
35 "X-Forwarded-Access-Token",
36 "Cookie",
37 "Gap-Auth",
40// newHTTPUpstreamProxy creates a new httpUpstreamProxy that can serve requests
41// to a single upstream host.
42func newHTTPUpstreamProxy(upstream options.Upstream, u *url.URL, sigData *options.SignatureData, errorHandler ProxyErrorHandler) http.Handler {
43 // Set path to empty so that request paths start at the server root
44 u.Path = ""
46 // Create a ReverseProxy
47 proxy := newReverseProxy(u, upstream, errorHandler)
49 // Set up a WebSocket proxy if required
50 var wsProxy http.Handler
51 if upstream.ProxyWebSockets == nil || *upstream.ProxyWebSockets {
52 wsProxy = newWebSocketReverseProxy(u, upstream.InsecureSkipTLSVerify)
53 }
55 var auth hmacauth.HmacAuth
56 if sigData != nil {
57 auth = hmacauth.NewHmacAuth(sigData.Hash, []byte(sigData.Key), SignatureHeader, SignatureHeaders)
58 }
60 return &httpUpstreamProxy{
61 upstream: upstream.ID,
62 handler: proxy,
63 wsHandler: wsProxy,
64 auth: auth,
65 }
68// httpUpstreamProxy represents a single HTTP(S) upstream proxy
69type httpUpstreamProxy struct {
70 upstream string
71 handler http.Handler
72 wsHandler http.Handler
73 auth hmacauth.HmacAuth
76// ServeHTTP proxies requests to the upstream provider while signing the
77// request headers
78func (h *httpUpstreamProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
79 scope := middleware.GetRequestScope(req)
80 // If scope is nil, this will panic.
81 // A scope should always be injected before this handler is called.
82 scope.Upstream = h.upstream
84 // TODO (@NickMeves) - Deprecate GAP-Signature & remove GAP-Auth
85 if h.auth != nil {
86 req.Header.Set("GAP-Auth", rw.Header().Get("GAP-Auth"))
87 h.auth.SignRequest(req)
88 }
89 if h.wsHandler != nil && strings.EqualFold(req.Header.Get("Connection"), "upgrade") && req.Header.Get("Upgrade") == "websocket" {
90 h.wsHandler.ServeHTTP(rw, req)
91 } else {
92 h.handler.ServeHTTP(rw, req)
93 }
96// newReverseProxy creates a new reverse proxy for proxying requests to upstream
97// servers based on the upstream configuration provided.
98// The proxy should render an error page if there are failures connecting to the
99// upstream server.
100func newReverseProxy(target *url.URL, upstream options.Upstream, errorHandler ProxyErrorHandler) http.Handler {
101 proxy := httputil.NewSingleHostReverseProxy(target)
103 // Configure options on the SingleHostReverseProxy
104 if upstream.FlushInterval != nil {
105 proxy.FlushInterval = upstream.FlushInterval.Duration()
106 } else {
107 proxy.FlushInterval = options.DefaultUpstreamFlushInterval
108 }
110 // InsecureSkipVerify is a configurable option we allow
111 /* #nosec G402 */
112 if upstream.InsecureSkipTLSVerify {
113 proxy.Transport = &http.Transport{
114 TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
115 }
116 }
118 // Ensure we always pass the original request path
119 setProxyDirector(proxy)
121 if upstream.PassHostHeader != nil && !*upstream.PassHostHeader {
122 setProxyUpstreamHostHeader(proxy, target)
123 }
125 // Set the error handler so that upstream connection failures render the
126 // error page instead of sending a empty response
127 if errorHandler != nil {
128 proxy.ErrorHandler = errorHandler
129 }
130 return proxy
133// setProxyUpstreamHostHeader sets the proxy.Director so that upstream requests
134// receive a host header matching the target URL.
135func setProxyUpstreamHostHeader(proxy *httputil.ReverseProxy, target *url.URL) {
136 director := proxy.Director
137 proxy.Director = func(req *http.Request) {
138 director(req)
139 req.Host = target.Host
140 }
143// setProxyDirector sets the proxy.Director so that request URIs are escaped
144// when proxying to usptream servers.
145func setProxyDirector(proxy *httputil.ReverseProxy) {
146 director := proxy.Director
147 proxy.Director = func(req *http.Request) {
148 director(req)
149 // use RequestURI so that we aren't unescaping encoded slashes in the request path
150 req.URL.Opaque = req.RequestURI
151 req.URL.RawQuery = ""
152 req.URL.ForceQuery = false
153 }
156// newWebSocketReverseProxy creates a new reverse proxy for proxying websocket connections.
157func newWebSocketReverseProxy(u *url.URL, skipTLSVerify bool) http.Handler {
158 wsProxy := httputil.NewSingleHostReverseProxy(u)
159 /* #nosec G402 */
160 if skipTLSVerify {
161 wsProxy.Transport = &http.Transport{
162 TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
163 }
164 }
165 return wsProxy