git.iko.soy/typeable/oauth2-proxy
on 597ffeb help sign in
165 lines 5.1 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/options"
12 "github.com/yhat/wsutil"
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 rw.Header().Set("GAP-Upstream-Address", h.upstream)
80 if h.auth != nil {
81 req.Header.Set("GAP-Auth", rw.Header().Get("GAP-Auth"))
82 h.auth.SignRequest(req)
83 }
84 if h.wsHandler != nil && strings.EqualFold(req.Header.Get("Connection"), "upgrade") && req.Header.Get("Upgrade") == "websocket" {
85 h.wsHandler.ServeHTTP(rw, req)
86 } else {
87 h.handler.ServeHTTP(rw, req)
88 }
91// newReverseProxy creates a new reverse proxy for proxying requests to upstream
92// servers based on the upstream configuration provided.
93// The proxy should render an error page if there are failures connecting to the
94// upstream server.
95func newReverseProxy(target *url.URL, upstream options.Upstream, errorHandler ProxyErrorHandler) http.Handler {
96 proxy := httputil.NewSingleHostReverseProxy(target)
98 // Configure options on the SingleHostReverseProxy
99 if upstream.FlushInterval != nil {
100 proxy.FlushInterval = upstream.FlushInterval.Duration()
101 } else {
102 proxy.FlushInterval = options.DefaultUpstreamFlushInterval
103 }
105 // InsecureSkipVerify is a configurable option we allow
106 /* #nosec G402 */
107 if upstream.InsecureSkipTLSVerify {
108 proxy.Transport = &http.Transport{
109 TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
110 }
111 }
113 // Set the request director based on the PassHostHeader option
114 if upstream.PassHostHeader != nil && !*upstream.PassHostHeader {
115 setProxyUpstreamHostHeader(proxy, target)
116 } else {
117 setProxyDirector(proxy)
118 }
120 // Set the error handler so that upstream connection failures render the
121 // error page instead of sending a empty response
122 if errorHandler != nil {
123 proxy.ErrorHandler = errorHandler
124 }
125 return proxy
128// setProxyUpstreamHostHeader sets the proxy.Director so that upstream requests
129// receive a host header matching the target URL.
130func setProxyUpstreamHostHeader(proxy *httputil.ReverseProxy, target *url.URL) {
131 director := proxy.Director
132 proxy.Director = func(req *http.Request) {
133 director(req)
134 // use RequestURI so that we aren't unescaping encoded slashes in the request path
135 req.Host = target.Host
136 req.URL.Opaque = req.RequestURI
137 req.URL.RawQuery = ""
138 }
141// setProxyDirector sets the proxy.Director so that request URIs are escaped
142// when proxying to usptream servers.
143func setProxyDirector(proxy *httputil.ReverseProxy) {
144 director := proxy.Director
145 proxy.Director = func(req *http.Request) {
146 director(req)
147 // use RequestURI so that we aren't unescaping encoded slashes in the request path
148 req.URL.Opaque = req.RequestURI
149 req.URL.RawQuery = ""
150 }
153// newWebSocketReverseProxy creates a new reverse proxy for proxying websocket connections.
154func newWebSocketReverseProxy(u *url.URL, skipTLSVerify bool) http.Handler {
155 // This should create the correct scheme for insecure vs secure connections
156 wsScheme := "ws" + strings.TrimPrefix(u.Scheme, "http")
157 wsURL := &url.URL{Scheme: wsScheme, Host: u.Host}
159 wsProxy := wsutil.NewSingleHostReverseProxy(wsURL)
160 /* #nosec G402 */
161 if skipTLSVerify {
162 wsProxy.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
163 }
164 return wsProxy