github.com/lablabs/operator-sdk@v0.8.2/pkg/ansible/proxy/kubectl.go (about) 1 /* 2 Copyright 2014 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 // This code was retrieved from 18 // https://github.com/kubernetes/kubernetes/blob/204d994/pkg/kubectl/proxy/proxy_server.go 19 // and modified for use in this project. 20 21 package proxy 22 23 import ( 24 "fmt" 25 "net" 26 "net/http" 27 "net/url" 28 "os" 29 "regexp" 30 "strings" 31 "time" 32 33 utilnet "k8s.io/apimachinery/pkg/util/net" 34 k8sproxy "k8s.io/apimachinery/pkg/util/proxy" 35 "k8s.io/client-go/rest" 36 "k8s.io/client-go/transport" 37 "k8s.io/kubernetes/pkg/kubectl/util" 38 logf "sigs.k8s.io/controller-runtime/pkg/runtime/log" 39 ) 40 41 var log = logf.Log.WithName("proxy") 42 43 const ( 44 // DefaultHostAcceptRE is the default value for which hosts to accept. 45 DefaultHostAcceptRE = "^localhost$,^127\\.0\\.0\\.1$,^\\[::1\\]$" 46 // DefaultPathAcceptRE is the default path to accept. 47 DefaultPathAcceptRE = "^.*" 48 // DefaultPathRejectRE is the default set of paths to reject. 49 DefaultPathRejectRE = "^/api/.*/pods/.*/exec,^/api/.*/pods/.*/attach" 50 // DefaultMethodRejectRE is the set of HTTP methods to reject by default. 51 DefaultMethodRejectRE = "^$" 52 ) 53 54 var ( 55 // ReverseProxyFlushInterval is the frequency to flush the reverse proxy. 56 // Only matters for long poll connections like the one used to watch. With an 57 // interval of 0 the reverse proxy will buffer content sent on any connection 58 // with transfer-encoding=chunked. 59 // TODO: Flush after each chunk so the client doesn't suffer a 100ms latency per 60 // watch event. 61 ReverseProxyFlushInterval = 100 * time.Millisecond 62 ) 63 64 // FilterServer rejects requests which don't match one of the specified regular expressions 65 type FilterServer struct { 66 // Only paths that match this regexp will be accepted 67 AcceptPaths []*regexp.Regexp 68 // Paths that match this regexp will be rejected, even if they match the above 69 RejectPaths []*regexp.Regexp 70 // Hosts are required to match this list of regexp 71 AcceptHosts []*regexp.Regexp 72 // Methods that match this regexp are rejected 73 RejectMethods []*regexp.Regexp 74 // The delegate to call to handle accepted requests. 75 delegate http.Handler 76 } 77 78 // MakeRegexpArray splits a comma separated list of regexps into an array of Regexp objects. 79 func MakeRegexpArray(str string) ([]*regexp.Regexp, error) { 80 parts := strings.Split(str, ",") 81 result := make([]*regexp.Regexp, len(parts)) 82 for ix := range parts { 83 re, err := regexp.Compile(parts[ix]) 84 if err != nil { 85 return nil, err 86 } 87 result[ix] = re 88 } 89 return result, nil 90 } 91 92 // MakeRegexpArrayOrDie creates an array of regular expression objects from a string or exits. 93 func MakeRegexpArrayOrDie(str string) []*regexp.Regexp { 94 result, err := MakeRegexpArray(str) 95 if err != nil { 96 log.Error(err, "Error compiling re") 97 os.Exit(1) 98 } 99 return result 100 } 101 102 func matchesRegexp(str string, regexps []*regexp.Regexp) bool { 103 for _, re := range regexps { 104 if re.MatchString(str) { 105 log.Info("Matched found", "MatchString", str, "Regexp", re) 106 return true 107 } 108 } 109 return false 110 } 111 112 func (f *FilterServer) accept(method, path, host string) bool { 113 if matchesRegexp(path, f.RejectPaths) { 114 return false 115 } 116 if matchesRegexp(method, f.RejectMethods) { 117 return false 118 } 119 if matchesRegexp(path, f.AcceptPaths) && matchesRegexp(host, f.AcceptHosts) { 120 return true 121 } 122 return false 123 } 124 125 // HandlerFor makes a shallow copy of f which passes its requests along to the 126 // new delegate. 127 func (f *FilterServer) HandlerFor(delegate http.Handler) *FilterServer { 128 f2 := *f 129 f2.delegate = delegate 130 return &f2 131 } 132 133 // Get host from a host header value like "localhost" or "localhost:8080" 134 func extractHost(header string) (host string) { 135 host, _, err := net.SplitHostPort(header) 136 if err != nil { 137 host = header 138 } 139 return host 140 } 141 142 func (f *FilterServer) ServeHTTP(rw http.ResponseWriter, req *http.Request) { 143 host := extractHost(req.Host) 144 if f.accept(req.Method, req.URL.Path, host) { 145 log.Info("Filter acception", "Request.Method", req.Method, "Request.URL", req.URL.Path, "Host", host) 146 f.delegate.ServeHTTP(rw, req) 147 return 148 } 149 log.Info("Filter rejection", "Request.Method", req.Method, "Request.URL", req.URL.Path, "Host", host) 150 rw.WriteHeader(http.StatusForbidden) 151 if _, err := rw.Write([]byte("<h3>Unauthorized</h3>")); err != nil { 152 log.Error(err, "Failed to write response body") 153 } 154 } 155 156 // Server is a http.Handler which proxies Kubernetes APIs to remote API server. 157 type server struct { 158 Handler http.Handler 159 } 160 161 type responder struct{} 162 163 func (r *responder) Error(w http.ResponseWriter, req *http.Request, err error) { 164 log.Error(err, "Error while proxying request") 165 http.Error(w, err.Error(), http.StatusInternalServerError) 166 } 167 168 // makeUpgradeTransport creates a transport that explicitly bypasses HTTP2 support 169 // for proxy connections that must upgrade. 170 func makeUpgradeTransport(config *rest.Config) (k8sproxy.UpgradeRequestRoundTripper, error) { 171 transportConfig, err := config.TransportConfig() 172 if err != nil { 173 return nil, err 174 } 175 tlsConfig, err := transport.TLSConfigFor(transportConfig) 176 if err != nil { 177 return nil, err 178 } 179 rt := utilnet.SetOldTransportDefaults(&http.Transport{ 180 TLSClientConfig: tlsConfig, 181 DialContext: (&net.Dialer{ 182 Timeout: 30 * time.Second, 183 }).DialContext, 184 }) 185 186 upgrader, err := transport.HTTPWrappersForConfig(transportConfig, k8sproxy.MirrorRequest) 187 if err != nil { 188 return nil, err 189 } 190 return k8sproxy.NewUpgradeRequestRoundTripper(rt, upgrader), nil 191 } 192 193 // NewServer creates and installs a new Server. 194 func newServer(apiProxyPrefix string, cfg *rest.Config) (*server, error) { 195 host := cfg.Host 196 if !strings.HasSuffix(host, "/") { 197 host = host + "/" 198 } 199 target, err := url.Parse(host) 200 if err != nil { 201 return nil, err 202 } 203 204 responder := &responder{} 205 transport, err := rest.TransportFor(cfg) 206 if err != nil { 207 return nil, err 208 } 209 upgradeTransport, err := makeUpgradeTransport(cfg) 210 if err != nil { 211 return nil, err 212 } 213 proxy := k8sproxy.NewUpgradeAwareHandler(target, transport, false, false, responder) 214 proxy.UpgradeTransport = upgradeTransport 215 proxy.UseRequestLocation = true 216 217 proxyServer := http.Handler(proxy) 218 219 if !strings.HasPrefix(apiProxyPrefix, "/api") { 220 proxyServer = stripLeaveSlash(apiProxyPrefix, proxyServer) 221 } 222 223 mux := http.NewServeMux() 224 mux.Handle(apiProxyPrefix, proxyServer) 225 return &server{Handler: mux}, nil 226 } 227 228 // Listen is a simple wrapper around net.Listen. 229 func (s *server) Listen(address string, port int) (net.Listener, error) { 230 return net.Listen("tcp", fmt.Sprintf("%s:%d", address, port)) 231 } 232 233 // ListenUnix does net.Listen for a unix socket 234 func (s *server) ListenUnix(path string) (net.Listener, error) { 235 // Remove any socket, stale or not, but fall through for other files 236 fi, err := os.Stat(path) 237 if err == nil && (fi.Mode()&os.ModeSocket) != 0 { 238 if err := os.Remove(path); err != nil { 239 return nil, err 240 } 241 } 242 // Default to only user accessible socket, caller can open up later if desired 243 oldmask, _ := util.Umask(0077) 244 l, err := net.Listen("unix", path) 245 util.Umask(oldmask) 246 return l, err 247 } 248 249 // ServeOnListener starts the server using given listener, loops forever. 250 func (s *server) ServeOnListener(l net.Listener) error { 251 server := http.Server{ 252 Handler: s.Handler, 253 } 254 return server.Serve(l) 255 } 256 257 // like http.StripPrefix, but always leaves an initial slash. (so that our 258 // regexps will work.) 259 func stripLeaveSlash(prefix string, h http.Handler) http.Handler { 260 return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { 261 p := strings.TrimPrefix(req.URL.Path, prefix) 262 if len(p) >= len(req.URL.Path) { 263 http.NotFound(w, req) 264 return 265 } 266 if len(p) > 0 && p[:1] != "/" { 267 p = "/" + p 268 } 269 req.URL.Path = p 270 h.ServeHTTP(w, req) 271 }) 272 }