1
16
17 package proxy
18
19 import (
20 "fmt"
21 "net"
22 "net/http"
23 "net/url"
24 "os"
25 "regexp"
26 "strings"
27 "time"
28
29 utilnet "k8s.io/apimachinery/pkg/util/net"
30 "k8s.io/apimachinery/pkg/util/proxy"
31 "k8s.io/client-go/rest"
32 "k8s.io/client-go/transport"
33 "k8s.io/klog/v2"
34 "k8s.io/kubectl/pkg/util"
35 )
36
37 const (
38
39 DefaultHostAcceptRE = "^localhost$,^127\\.0\\.0\\.1$,^\\[::1\\]$"
40
41 DefaultPathAcceptRE = "^.*"
42
43 DefaultPathRejectRE = "^/api/.*/pods/.*/exec,^/api/.*/pods/.*/attach"
44
45 DefaultMethodRejectRE = "^$"
46 )
47
48
49 type FilterServer struct {
50
51 AcceptPaths []*regexp.Regexp
52
53 RejectPaths []*regexp.Regexp
54
55 AcceptHosts []*regexp.Regexp
56
57 RejectMethods []*regexp.Regexp
58
59 delegate http.Handler
60 }
61
62
63 func MakeRegexpArray(str string) ([]*regexp.Regexp, error) {
64 if str == "" {
65 return []*regexp.Regexp{}, nil
66 }
67 parts := strings.Split(str, ",")
68 result := make([]*regexp.Regexp, len(parts))
69 for ix := range parts {
70 re, err := regexp.Compile(parts[ix])
71 if err != nil {
72 return nil, err
73 }
74 result[ix] = re
75 }
76 return result, nil
77 }
78
79
80 func MakeRegexpArrayOrDie(str string) []*regexp.Regexp {
81 result, err := MakeRegexpArray(str)
82 if err != nil {
83 klog.Fatalf("Error compiling re: %v", err)
84 }
85 return result
86 }
87
88 func matchesRegexp(str string, regexps []*regexp.Regexp) bool {
89 for _, re := range regexps {
90 if re.MatchString(str) {
91 klog.V(6).Infof("%v matched %s", str, re)
92 return true
93 }
94 }
95 return false
96 }
97
98 func (f *FilterServer) accept(method, path, host string) bool {
99 if matchesRegexp(path, f.RejectPaths) {
100 return false
101 }
102 if matchesRegexp(method, f.RejectMethods) {
103 return false
104 }
105 if matchesRegexp(path, f.AcceptPaths) && matchesRegexp(host, f.AcceptHosts) {
106 return true
107 }
108 return false
109 }
110
111
112
113 func (f *FilterServer) HandlerFor(delegate http.Handler) *FilterServer {
114 f2 := *f
115 f2.delegate = delegate
116 return &f2
117 }
118
119
120 func extractHost(header string) (host string) {
121 host, _, err := net.SplitHostPort(header)
122 if err != nil {
123 host = header
124 }
125 return host
126 }
127
128 func (f *FilterServer) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
129 host := extractHost(req.Host)
130 if f.accept(req.Method, req.URL.Path, host) {
131 klog.V(3).Infof("Filter accepting %v %v %v", req.Method, req.URL.Path, host)
132 f.delegate.ServeHTTP(rw, req)
133 return
134 }
135 klog.V(3).Infof("Filter rejecting %v %v %v", req.Method, req.URL.Path, host)
136 http.Error(rw, http.StatusText(http.StatusForbidden), http.StatusForbidden)
137 }
138
139
140 type Server struct {
141 handler http.Handler
142 }
143
144 type responder struct{}
145
146 func (r *responder) Error(w http.ResponseWriter, req *http.Request, err error) {
147 klog.Errorf("Error while proxying request: %v", err)
148 http.Error(w, err.Error(), http.StatusInternalServerError)
149 }
150
151
152
153 func makeUpgradeTransport(config *rest.Config, keepalive time.Duration) (proxy.UpgradeRequestRoundTripper, error) {
154 transportConfig, err := config.TransportConfig()
155 if err != nil {
156 return nil, err
157 }
158 tlsConfig, err := transport.TLSConfigFor(transportConfig)
159 if err != nil {
160 return nil, err
161 }
162 rt := utilnet.SetOldTransportDefaults(&http.Transport{
163 TLSClientConfig: tlsConfig,
164 DialContext: (&net.Dialer{
165 Timeout: 30 * time.Second,
166 KeepAlive: keepalive,
167 }).DialContext,
168 })
169
170 upgrader, err := transport.HTTPWrappersForConfig(transportConfig, proxy.MirrorRequest)
171 if err != nil {
172 return nil, err
173 }
174 return proxy.NewUpgradeRequestRoundTripper(rt, upgrader), nil
175 }
176
177
178
179 func NewServer(filebase string, apiProxyPrefix string, staticPrefix string, filter *FilterServer, cfg *rest.Config, keepalive time.Duration, appendLocationPath bool) (*Server, error) {
180 proxyHandler, err := NewProxyHandler(apiProxyPrefix, filter, cfg, keepalive, appendLocationPath)
181 if err != nil {
182 return nil, err
183 }
184 mux := http.NewServeMux()
185 mux.Handle(apiProxyPrefix, proxyHandler)
186 if filebase != "" {
187
188
189 mux.Handle(staticPrefix, newFileHandler(staticPrefix, filebase))
190 }
191 return &Server{handler: mux}, nil
192 }
193
194
195 func NewProxyHandler(apiProxyPrefix string, filter *FilterServer, cfg *rest.Config, keepalive time.Duration, appendLocationPath bool) (http.Handler, error) {
196 host := cfg.Host
197 if !strings.HasSuffix(host, "/") {
198 host = host + "/"
199 }
200 target, err := url.Parse(host)
201 if err != nil {
202 return nil, err
203 }
204
205 responder := &responder{}
206 transport, err := rest.TransportFor(cfg)
207 if err != nil {
208 return nil, err
209 }
210 upgradeTransport, err := makeUpgradeTransport(cfg, keepalive)
211 if err != nil {
212 return nil, err
213 }
214 proxy := proxy.NewUpgradeAwareHandler(target, transport, false, false, responder)
215 proxy.UpgradeTransport = upgradeTransport
216 proxy.UseRequestLocation = true
217 proxy.UseLocationHost = true
218 proxy.AppendLocationPath = appendLocationPath
219
220 proxyServer := http.Handler(proxy)
221 if filter != nil {
222 proxyServer = filter.HandlerFor(proxyServer)
223 }
224
225 if !strings.HasPrefix(apiProxyPrefix, "/api") {
226 proxyServer = stripLeaveSlash(apiProxyPrefix, proxyServer)
227 }
228 return proxyServer, nil
229 }
230
231
232 func (s *Server) Listen(address string, port int) (net.Listener, error) {
233 return net.Listen("tcp", fmt.Sprintf("%s:%d", address, port))
234 }
235
236
237 func (s *Server) ListenUnix(path string) (net.Listener, error) {
238
239 fi, err := os.Stat(path)
240 if err == nil && (fi.Mode()&os.ModeSocket) != 0 {
241 os.Remove(path)
242 }
243
244 oldmask, _ := util.Umask(0077)
245 l, err := net.Listen("unix", path)
246 util.Umask(oldmask)
247 return l, err
248 }
249
250
251 func (s *Server) ServeOnListener(l net.Listener) error {
252 server := http.Server{
253 Handler: s.handler,
254 }
255 return server.Serve(l)
256 }
257
258 func newFileHandler(prefix, base string) http.Handler {
259 return http.StripPrefix(prefix, http.FileServer(http.Dir(base)))
260 }
261
262
263
264 func stripLeaveSlash(prefix string, h http.Handler) http.Handler {
265 return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
266 p := strings.TrimPrefix(req.URL.Path, prefix)
267 if len(p) >= len(req.URL.Path) {
268 http.NotFound(w, req)
269 return
270 }
271 if len(p) > 0 && p[:1] != "/" {
272 p = "/" + p
273 }
274 req.URL.Path = p
275 h.ServeHTTP(w, req)
276 })
277 }
278
View as plain text