git.iko.soy/typeable/oauth2-proxy
on request-rules help sign in
163 lines 4.9 kB history raw
1package upstream
3import (
4 "crypto/tls"
5 "net/http"
6 "net/http/httputil"
7 "net/url"
8 "strings"
9 "time"
11 "github.com/mbland/hmacauth"
12 "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/options"
13 "github.com/yhat/wsutil"
16const (
17 // SignatureHeader is the name of the request header containing the GAP Signature
18 // Part of hmacauth
19 SignatureHeader = "GAP-Signature"
21 httpScheme = "http"
22 httpsScheme = "https"
25// SignatureHeaders contains the headers to be signed by the hmac algorithm
26// Part of hmacauth
27var SignatureHeaders = []string{
28 "Content-Length",
29 "Content-Md5",
30 "Content-Type",
31 "Date",
32 "Authorization",
33 "X-Forwarded-User",
34 "X-Forwarded-Email",
35 "X-Forwarded-Preferred-User",
36 "X-Forwarded-Access-Token",
37 "Cookie",
38 "Gap-Auth",
41// newHTTPUpstreamProxy creates a new httpUpstreamProxy that can serve requests
42// to a single upstream host.
43func newHTTPUpstreamProxy(upstream options.Upstream, u *url.URL, sigData *options.SignatureData, errorHandler ProxyErrorHandler) http.Handler {
44 // Set path to empty so that request paths start at the server root
45 u.Path = ""
47 // Create a ReverseProxy
48 proxy := newReverseProxy(u, upstream, errorHandler)
50 // Set up a WebSocket proxy if required
51 var wsProxy http.Handler
52 if upstream.ProxyWebSockets {
53 wsProxy = newWebSocketReverseProxy(u, upstream.InsecureSkipTLSVerify)
54 }
56 var auth hmacauth.HmacAuth
57 if sigData != nil {
58 auth = hmacauth.NewHmacAuth(sigData.Hash, []byte(sigData.Key), SignatureHeader, SignatureHeaders)
59 }
61 return &httpUpstreamProxy{
62 upstream: upstream.ID,
63 handler: proxy,
64 wsHandler: wsProxy,
65 auth: auth,
66 }
69// httpUpstreamProxy represents a single HTTP(S) upstream proxy
70type httpUpstreamProxy struct {
71 upstream string
72 handler http.Handler
73 wsHandler http.Handler
74 auth hmacauth.HmacAuth
77// ServeHTTP proxies requests to the upstream provider while signing the
78// request headers
79func (h *httpUpstreamProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
80 rw.Header().Set("GAP-Upstream-Address", h.upstream)
81 if h.auth != nil {
82 req.Header.Set("GAP-Auth", rw.Header().Get("GAP-Auth"))
83 h.auth.SignRequest(req)
84 }
85 if h.wsHandler != nil && strings.EqualFold(req.Header.Get("Connection"), "upgrade") && req.Header.Get("Upgrade") == "websocket" {
86 h.wsHandler.ServeHTTP(rw, req)
87 } else {
88 h.handler.ServeHTTP(rw, req)
89 }
92// newReverseProxy creates a new reverse proxy for proxying requests to upstream
93// servers based on the upstream configuration provided.
94// The proxy should render an error page if there are failures connecting to the
95// upstream server.
96func newReverseProxy(target *url.URL, upstream options.Upstream, errorHandler ProxyErrorHandler) http.Handler {
97 proxy := httputil.NewSingleHostReverseProxy(target)
99 // Configure options on the SingleHostReverseProxy
100 if upstream.FlushInterval != nil {
101 proxy.FlushInterval = *upstream.FlushInterval
102 } else {
103 proxy.FlushInterval = 1 * time.Second
104 }
106 if upstream.InsecureSkipTLSVerify {
107 proxy.Transport = &http.Transport{
108 TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
109 }
110 }
112 // Set the request director based on the PassHostHeader option
113 if !upstream.PassHostHeader {
114 setProxyUpstreamHostHeader(proxy, target)
115 } else {
116 setProxyDirector(proxy)
117 }
119 // Set the error handler so that upstream connection failures render the
120 // error page instead of sending a empty response
121 if errorHandler != nil {
122 proxy.ErrorHandler = errorHandler
123 }
124 return proxy
127// setProxyUpstreamHostHeader sets the proxy.Director so that upstream requests
128// receive a host header matching the target URL.
129func setProxyUpstreamHostHeader(proxy *httputil.ReverseProxy, target *url.URL) {
130 director := proxy.Director
131 proxy.Director = func(req *http.Request) {
132 director(req)
133 // use RequestURI so that we aren't unescaping encoded slashes in the request path
134 req.Host = target.Host
135 req.URL.Opaque = req.RequestURI
136 req.URL.RawQuery = ""
137 }
140// setProxyDirector sets the proxy.Director so that request URIs are escaped
141// when proxying to usptream servers.
142func setProxyDirector(proxy *httputil.ReverseProxy) {
143 director := proxy.Director
144 proxy.Director = func(req *http.Request) {
145 director(req)
146 // use RequestURI so that we aren't unescaping encoded slashes in the request path
147 req.URL.Opaque = req.RequestURI
148 req.URL.RawQuery = ""
149 }
152// newWebSocketReverseProxy creates a new reverse proxy for proxying websocket connections.
153func newWebSocketReverseProxy(u *url.URL, skipTLSVerify bool) http.Handler {
154 // This should create the correct scheme for insecure vs secure connections
155 wsScheme := "ws" + strings.TrimPrefix(u.Scheme, "http")
156 wsURL := &url.URL{Scheme: wsScheme, Host: u.Host}
158 wsProxy := wsutil.NewSingleHostReverseProxy(wsURL)
159 if skipTLSVerify {
160 wsProxy.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
161 }
162 return wsProxy