proxy_server.go 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248
  1. /*
  2. Copyright 2014 The Kubernetes Authors.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package kubectl
  14. import (
  15. "fmt"
  16. "net"
  17. "net/http"
  18. "net/http/httputil"
  19. "net/url"
  20. "os"
  21. "regexp"
  22. "strings"
  23. "time"
  24. "github.com/golang/glog"
  25. "k8s.io/kubernetes/pkg/client/restclient"
  26. "k8s.io/kubernetes/pkg/util"
  27. )
  28. const (
  29. DefaultHostAcceptRE = "^localhost$,^127\\.0\\.0\\.1$,^\\[::1\\]$"
  30. DefaultPathAcceptRE = "^/.*"
  31. DefaultPathRejectRE = "^/api/.*/pods/.*/exec,^/api/.*/pods/.*/attach"
  32. DefaultMethodRejectRE = "POST,PUT,PATCH"
  33. )
  34. var (
  35. // The reverse proxy will periodically flush the io writer at this frequency.
  36. // Only matters for long poll connections like the one used to watch. With an
  37. // interval of 0 the reverse proxy will buffer content sent on any connection
  38. // with transfer-encoding=chunked.
  39. // TODO: Flush after each chunk so the client doesn't suffer a 100ms latency per
  40. // watch event.
  41. ReverseProxyFlushInterval = 100 * time.Millisecond
  42. )
  43. // FilterServer rejects requests which don't match one of the specified regular expressions
  44. type FilterServer struct {
  45. // Only paths that match this regexp will be accepted
  46. AcceptPaths []*regexp.Regexp
  47. // Paths that match this regexp will be rejected, even if they match the above
  48. RejectPaths []*regexp.Regexp
  49. // Hosts are required to match this list of regexp
  50. AcceptHosts []*regexp.Regexp
  51. // Methods that match this regexp are rejected
  52. RejectMethods []*regexp.Regexp
  53. // The delegate to call to handle accepted requests.
  54. delegate http.Handler
  55. }
  56. // Splits a comma separated list of regexps into an array of Regexp objects.
  57. func MakeRegexpArray(str string) ([]*regexp.Regexp, error) {
  58. parts := strings.Split(str, ",")
  59. result := make([]*regexp.Regexp, len(parts))
  60. for ix := range parts {
  61. re, err := regexp.Compile(parts[ix])
  62. if err != nil {
  63. return nil, err
  64. }
  65. result[ix] = re
  66. }
  67. return result, nil
  68. }
  69. func MakeRegexpArrayOrDie(str string) []*regexp.Regexp {
  70. result, err := MakeRegexpArray(str)
  71. if err != nil {
  72. glog.Fatalf("Error compiling re: %v", err)
  73. }
  74. return result
  75. }
  76. func matchesRegexp(str string, regexps []*regexp.Regexp) bool {
  77. for _, re := range regexps {
  78. if re.MatchString(str) {
  79. glog.V(6).Infof("%v matched %s", str, re)
  80. return true
  81. }
  82. }
  83. return false
  84. }
  85. func (f *FilterServer) accept(method, path, host string) bool {
  86. if matchesRegexp(path, f.RejectPaths) {
  87. glog.V(3).Infof("Filter rejecting %v %v %v", method, path, host)
  88. return false
  89. }
  90. if matchesRegexp(method, f.RejectMethods) {
  91. glog.V(3).Infof("Filter rejecting %v %v %v", method, path, host)
  92. return false
  93. }
  94. if matchesRegexp(path, f.AcceptPaths) && matchesRegexp(host, f.AcceptHosts) {
  95. glog.V(3).Infof("Filter accepting %v %v %v", method, path, host)
  96. return true
  97. }
  98. glog.V(3).Infof("Filter rejecting %v %v %v", method, path, host)
  99. return false
  100. }
  101. // Make a copy of f which passes requests along to the new delegate.
  102. func (f *FilterServer) HandlerFor(delegate http.Handler) *FilterServer {
  103. f2 := *f
  104. f2.delegate = delegate
  105. return &f2
  106. }
  107. // Get host from a host header value like "localhost" or "localhost:8080"
  108. func extractHost(header string) (host string) {
  109. host, _, err := net.SplitHostPort(header)
  110. if err != nil {
  111. host = header
  112. }
  113. return host
  114. }
  115. func (f *FilterServer) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
  116. host := extractHost(req.Host)
  117. if f.accept(req.Method, req.URL.Path, host) {
  118. f.delegate.ServeHTTP(rw, req)
  119. return
  120. }
  121. rw.WriteHeader(http.StatusForbidden)
  122. rw.Write([]byte("<h3>Unauthorized</h3>"))
  123. }
  124. // ProxyServer is a http.Handler which proxies Kubernetes APIs to remote API server.
  125. type ProxyServer struct {
  126. handler http.Handler
  127. }
  128. // NewProxyServer creates and installs a new ProxyServer.
  129. // It automatically registers the created ProxyServer to http.DefaultServeMux.
  130. // 'filter', if non-nil, protects requests to the api only.
  131. func NewProxyServer(filebase string, apiProxyPrefix string, staticPrefix string, filter *FilterServer, cfg *restclient.Config) (*ProxyServer, error) {
  132. host := cfg.Host
  133. if !strings.HasSuffix(host, "/") {
  134. host = host + "/"
  135. }
  136. target, err := url.Parse(host)
  137. if err != nil {
  138. return nil, err
  139. }
  140. proxy := newProxy(target)
  141. if proxy.Transport, err = restclient.TransportFor(cfg); err != nil {
  142. return nil, err
  143. }
  144. proxyServer := http.Handler(proxy)
  145. if filter != nil {
  146. proxyServer = filter.HandlerFor(proxyServer)
  147. }
  148. if !strings.HasPrefix(apiProxyPrefix, "/api") {
  149. proxyServer = stripLeaveSlash(apiProxyPrefix, proxyServer)
  150. }
  151. mux := http.NewServeMux()
  152. mux.Handle(apiProxyPrefix, proxyServer)
  153. if filebase != "" {
  154. // Require user to explicitly request this behavior rather than
  155. // serving their working directory by default.
  156. mux.Handle(staticPrefix, newFileHandler(staticPrefix, filebase))
  157. }
  158. return &ProxyServer{handler: mux}, nil
  159. }
  160. // Listen is a simple wrapper around net.Listen.
  161. func (s *ProxyServer) Listen(address string, port int) (net.Listener, error) {
  162. return net.Listen("tcp", fmt.Sprintf("%s:%d", address, port))
  163. }
  164. // ListenUnix does net.Listen for a unix socket
  165. func (s *ProxyServer) ListenUnix(path string) (net.Listener, error) {
  166. // Remove any socket, stale or not, but fall through for other files
  167. fi, err := os.Stat(path)
  168. if err == nil && (fi.Mode()&os.ModeSocket) != 0 {
  169. os.Remove(path)
  170. }
  171. // Default to only user accessible socket, caller can open up later if desired
  172. oldmask, _ := util.Umask(0077)
  173. l, err := net.Listen("unix", path)
  174. util.Umask(oldmask)
  175. return l, err
  176. }
  177. // Serve starts the server using given listener, loops forever.
  178. func (s *ProxyServer) ServeOnListener(l net.Listener) error {
  179. server := http.Server{
  180. Handler: s.handler,
  181. }
  182. return server.Serve(l)
  183. }
  184. func newProxy(target *url.URL) *httputil.ReverseProxy {
  185. director := func(req *http.Request) {
  186. req.URL.Scheme = target.Scheme
  187. req.URL.Host = target.Host
  188. req.URL.Path = singleJoiningSlash(target.Path, req.URL.Path)
  189. }
  190. return &httputil.ReverseProxy{Director: director, FlushInterval: ReverseProxyFlushInterval}
  191. }
  192. func newFileHandler(prefix, base string) http.Handler {
  193. return http.StripPrefix(prefix, http.FileServer(http.Dir(base)))
  194. }
  195. func singleJoiningSlash(a, b string) string {
  196. aslash := strings.HasSuffix(a, "/")
  197. bslash := strings.HasPrefix(b, "/")
  198. switch {
  199. case aslash && bslash:
  200. return a + b[1:]
  201. case !aslash && !bslash:
  202. return a + "/" + b
  203. }
  204. return a + b
  205. }
  206. // like http.StripPrefix, but always leaves an initial slash. (so that our
  207. // regexps will work.)
  208. func stripLeaveSlash(prefix string, h http.Handler) http.Handler {
  209. return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
  210. p := strings.TrimPrefix(req.URL.Path, prefix)
  211. if len(p) >= len(req.URL.Path) {
  212. http.NotFound(w, req)
  213. return
  214. }
  215. if len(p) > 0 && p[:1] != "/" {
  216. p = "/" + p
  217. }
  218. req.URL.Path = p
  219. h.ServeHTTP(w, req)
  220. })
  221. }