diff options
Diffstat (limited to 'website/cmd')
-rw-r--r-- | website/cmd/server/BUILD | 13 | ||||
-rw-r--r-- | website/cmd/server/main.go | 393 | ||||
-rw-r--r-- | website/cmd/syscalldocs/BUILD | 9 | ||||
-rw-r--r-- | website/cmd/syscalldocs/main.go | 211 |
4 files changed, 0 insertions, 626 deletions
diff --git a/website/cmd/server/BUILD b/website/cmd/server/BUILD deleted file mode 100644 index e4cf91e07..000000000 --- a/website/cmd/server/BUILD +++ /dev/null @@ -1,13 +0,0 @@ -load("//tools:defs.bzl", "go_binary") - -package(licenses = ["notice"]) - -go_binary( - name = "server", - srcs = ["main.go"], - pure = True, - visibility = ["//website:__pkg__"], - deps = [ - "@com_github_google_pprof//driver:go_default_library", - ], -) diff --git a/website/cmd/server/main.go b/website/cmd/server/main.go deleted file mode 100644 index 9f0092ed6..000000000 --- a/website/cmd/server/main.go +++ /dev/null @@ -1,393 +0,0 @@ -// Copyright 2019 The gVisor Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Server is the main gvisor.dev binary. -package main - -import ( - "flag" - "fmt" - "log" - "net/http" - "net/url" - "os" - "path" - "regexp" - "strings" - - "github.com/google/pprof/driver" -) - -var redirects = map[string]string{ - // GitHub redirects. - "/change": "https://github.com/google/gvisor", - "/issue": "https://github.com/google/gvisor/issues", - "/issues": "https://github.com/google/gvisor/issues", - "/issue/new": "https://github.com/google/gvisor/issues/new", - "/pr": "https://github.com/google/gvisor/pulls", - - // For links. - "/faq": "/docs/user_guide/faq/", - - // From 2020-05-12 to 2020-06-30, the FAQ URL was uppercase. Redirect that - // back to maintain any links. - "/docs/user_guide/FAQ/": "/docs/user_guide/faq/", - - // Redirects to compatibility docs. - "/c": "/docs/user_guide/compatibility/", - "/c/linux/amd64": "/docs/user_guide/compatibility/linux/amd64/", - - // Redirect for old URLs. - "/docs/user_guide/compatibility/amd64/": "/docs/user_guide/compatibility/linux/amd64/", - "/docs/user_guide/compatibility/amd64": "/docs/user_guide/compatibility/linux/amd64/", - "/docs/user_guide/kubernetes/": "/docs/user_guide/quick_start/kubernetes/", - "/docs/user_guide/kubernetes": "/docs/user_guide/quick_start/kubernetes/", - "/docs/user_guide/oci/": "/docs/user_guide/quick_start/oci/", - "/docs/user_guide/oci": "/docs/user_guide/quick_start/oci/", - "/docs/user_guide/docker/": "/docs/user_guide/quick_start/docker/", - "/docs/user_guide/docker": "/docs/user_guide/quick_start/docker/", - "/blog/2020/09/22/platform-portability": "/blog/2020/10/22/platform-portability/", - "/blog/2020/09/22/platform-portability/": "/blog/2020/10/22/platform-portability/", - - // Deprecated, but links continue to work. - "/cl": "https://gvisor-review.googlesource.com", - - // Access package documentation. - "/gvisor": "https://pkg.go.dev/gvisor.dev/gvisor", - - // Code search root. - "/cs": "https://cs.opensource.google/gvisor/gvisor", -} - -type prefixInfo struct { - baseURL string - checkValidID bool - queryEscape bool -} - -var prefixHelpers = map[string]prefixInfo{ - "change": {baseURL: "https://github.com/google/gvisor/commit/%s", checkValidID: true}, - "issue": {baseURL: "https://github.com/google/gvisor/issues/%s", checkValidID: true}, - "issues": {baseURL: "https://github.com/google/gvisor/issues/%s", checkValidID: true}, - "pr": {baseURL: "https://github.com/google/gvisor/pull/%s", checkValidID: true}, - - // Redirects to compatibility docs. - "c/linux/amd64": {baseURL: "/docs/user_guide/compatibility/linux/amd64/#%s", checkValidID: true}, - - // Deprecated, but links continue to work. - "cl": {baseURL: "https://gvisor-review.googlesource.com/c/gvisor/+/%s", checkValidID: true}, - - // Redirect to source documentation. - "gvisor": {baseURL: "https://pkg.go.dev/gvisor.dev/gvisor/%s"}, - - // Redirect to code search, with the path as the query. - "cs": {baseURL: "https://cs.opensource.google/search?q=%s&ss=gvisor", queryEscape: true}, -} - -var ( - validID = regexp.MustCompile(`^[A-Za-z0-9-]*/?$`) - goGetHTML5 = `<!doctype html><html><head><meta charset=utf-8> -<meta name="go-import" content="gvisor.dev/gvisor git https://github.com/google/gvisor"> -<meta name="go-import" content="gvisor.dev/website git https://github.com/google/gvisor-website"> -<title>Go-get</title></head><body></html>` -) - -// cronHandler wraps an http.Handler to check that the request is from the App -// Engine Cron service. -// See: https://cloud.google.com/appengine/docs/standard/go112/scheduling-jobs-with-cron-yaml#validating_cron_requests -func cronHandler(h http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if r.Header.Get("X-Appengine-Cron") != "true" { - http.NotFound(w, r) - return - } - // Fallthrough. - h.ServeHTTP(w, r) - }) -} - -// wrappedHandler wraps an http.Handler. -// -// If the query parameters include go-get=1, then we redirect to a single -// static page that allows us to serve arbitrary Go packages. -func wrappedHandler(h http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - gg, ok := r.URL.Query()["go-get"] - if ok && len(gg) == 1 && gg[0] == "1" { - // Serve a trivial html page. - w.Write([]byte(goGetHTML5)) - return - } - // Fallthrough. - h.ServeHTTP(w, r) - }) -} - -// redirectWithQuery redirects to the given target url preserving query parameters. -func redirectWithQuery(w http.ResponseWriter, r *http.Request, target string) { - url := target - if qs := r.URL.RawQuery; qs != "" { - url += "?" + qs - } - http.Redirect(w, r, url, http.StatusFound) -} - -// hostRedirectHandler redirects the www. domain to the naked domain. -func hostRedirectHandler(h http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if strings.HasPrefix(r.Host, "www.") { - // Redirect to the naked domain. - r.URL.Scheme = "https" // Assume https. - r.URL.Host = r.Host[4:] // Remove the 'www.' - http.Redirect(w, r, r.URL.String(), http.StatusMovedPermanently) - return - } - - if *projectID != "" && r.Host == *projectID+".appspot.com" && *customHost != "" { - // Redirect to the custom domain. - r.URL.Scheme = "https" // Assume https. - r.URL.Host = *customHost - http.Redirect(w, r, r.URL.String(), http.StatusMovedPermanently) - return - } - h.ServeHTTP(w, r) - }) -} - -// prefixRedirectHandler returns a handler that redirects to the given formated url. -func prefixRedirectHandler(prefix string, info prefixInfo) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if p := r.URL.Path; p == prefix { - // Redirect /prefix/ to /prefix. - http.Redirect(w, r, p[:len(p)-1], http.StatusFound) - return - } - id := r.URL.Path[len(prefix):] - if info.checkValidID && !validID.MatchString(id) { - http.Error(w, "Not found", http.StatusNotFound) - return - } - if info.queryEscape { - id = url.QueryEscape(id) - } - target := fmt.Sprintf(info.baseURL, id) - redirectWithQuery(w, r, target) - }) -} - -// redirectHandler returns a handler that redirects to the given url. -func redirectHandler(target string) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - redirectWithQuery(w, r, target) - }) -} - -// registerRedirects registers redirect http handlers. -func registerRedirects(mux *http.ServeMux) { - for prefix, info := range prefixHelpers { - p := "/" + prefix + "/" - mux.Handle(p, hostRedirectHandler(wrappedHandler(prefixRedirectHandler(p, info)))) - } - for path, redirect := range redirects { - mux.Handle(path, hostRedirectHandler(wrappedHandler(redirectHandler(redirect)))) - } -} - -// registerStatic registers static file handlers. -func registerStatic(mux *http.ServeMux, staticDir string) { - mux.Handle("/", hostRedirectHandler(wrappedHandler(http.FileServer(http.Dir(staticDir))))) -} - -// profileMeta implements synthetic flags for pprof. -type profileMeta struct { - // Mux is the mux to register on. - Mux *http.ServeMux - - // SourceURL is the source of the profile. - SourceURL string -} - -func (*profileMeta) ExtraUsage() string { return "" } -func (*profileMeta) AddExtraUsage(string) {} -func (*profileMeta) Bool(_ string, def bool, _ string) *bool { return &def } -func (*profileMeta) Int(_ string, def int, _ string) *int { return &def } -func (*profileMeta) Float64(_ string, def float64, _ string) *float64 { return &def } -func (*profileMeta) StringList(_ string, def string, _ string) *[]*string { return new([]*string) } -func (*profileMeta) String(option string, def string, _ string) *string { - switch option { - case "http": - // Only http is specified. Other options may be accessible via - // the web interface, so we just need to spoof a valid option - // here. The server is actually bound by HTTPServer, below. - value := "localhost:80" - return &value - case "symbolize": - // Don't attempt symbolization. Most profiles should come with - // mappings built-in to the profile itself. - value := "none" - return &value - default: - return &def // Default. - } -} - -// Parse implements plugin.FlagSet.Parse. -func (p *profileMeta) Parse(usage func()) []string { - // Just return the SourceURL. This is interpreted as the profile to - // download. We validate that the URL corresponds to a Google Cloud - // Storage URL below. - return []string{p.SourceURL} -} - -// pprofFixedPrefix is used to limit the exposure to SSRF. -// -// See registerProfile below. -const pprofFixedPrefix = "https://storage.googleapis.com/" - -// allowedBuckets enforces constraints on the pprof target. -// -// If the continuous integration system is changed in the future to use -// additional buckets, they may be whitelisted here. See registerProfile. -var allowedBuckets = map[string]bool{ - "gvisor-buildkite": true, -} - -// Target returns the URL target. -func (p *profileMeta) Target() string { - return fmt.Sprintf("/profile/%s/", p.SourceURL[len(pprofFixedPrefix):]) -} - -// HTTPServer is a function passed to driver.PProf. -func (p *profileMeta) HTTPServer(args *driver.HTTPServerArgs) error { - target := p.Target() - for subpath, handler := range args.Handlers { - handlerPath := path.Join(target, subpath) - if len(handlerPath) < len(target) { - // Don't clean the target, match only as the literal - // directory path in order to keep relative links - // working in the profile. E.g. /profile/foo/ is the - // base URL for the profile at https://.../foo. - // - // The base target typically shows the dot-based graph, - // which will not work in the image (due to the lack of - // a dot binary to execute). Therefore, we redirect to - // the flamegraph handler. Everything should otherwise - // work the exact same way, except the "Graph" link. - handlerPath = target - handler = redirectHandler(path.Join(handlerPath, "flamegraph")) - } - p.Mux.Handle(handlerPath, handler) - } - return nil -} - -// registerProfile registers the profile handler. -// -// Note that this has a security surface worth considering. -// -// We are passed effectively a URL, which we fetch and parse, -// then display the profile output. We limit the possibility of -// SSRF by interpreting the URL strictly as a part to an object -// in Google Cloud Storage, and further limit the buckets that -// may be used. This contains the vast majority of concerns, -// since objects must at least be uploaded by our CI system. -// -// However, we additionally consider the possibility that users -// craft malicious profile objects (somehow) and pass those URLs -// here as well. It seems feasible that we could parse a profile -// that causes a crash (DOS), but this would be automatically -// handled without a blip. It seems unlikely that we could parse a -// profile that gives full code execution, but even so there is -// nothing in this image except this code and CA certs. At worst, -// code execution would enable someone to serve up content under the -// web domain. This would be ephemeral with the specific instance, -// and persisting such an attack would require constantly crashing -// instances in whatever way gives remote code execution. Even if -// this were possible, it's unlikely that exploiting such a crash -// could be done so constantly and consistently. -// -// The user can also fill the "disk" of this container instance, -// causing an OOM and a crash. This has similar semantics to the -// DOS scenario above, and would just be handled by Cloud Run. -// -// Note that all of the above scenarios would require uploading -// malicious profiles to controller buckets, and a clear audit -// trail would exist in those cases. -func registerProfile(mux *http.ServeMux) { - const urlPrefix = "/profile/" - mux.Handle(urlPrefix, hostRedirectHandler(wrappedHandler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - // Extract the URL; this is everything except the final /. - parts := strings.Split(r.URL.Path[len(urlPrefix):], "/") - if len(parts) == 0 { - http.Error(w, "Invalid URL: no bucket provided.", http.StatusNotFound) - return - } - if !allowedBuckets[parts[0]] { - http.Error(w, fmt.Sprintf("Invalid URL: not an allowed bucket (%s).", parts[0]), http.StatusNotFound) - return - } - url := pprofFixedPrefix + strings.Join(parts[:len(parts)-1], "/") - if url == pprofFixedPrefix { - http.Error(w, "Invalid URL: no path provided.", http.StatusNotFound) - return - } - - // Set up the meta handler. This will modify the original mux - // accordingly, and we ultimately return a redirect that - // includes all the original arguments. This means that if we - // ever hit a server that does not have this profile loaded, it - // will load and redirect again. - meta := &profileMeta{ - Mux: mux, - SourceURL: url, - } - if err := driver.PProf(&driver.Options{ - Flagset: meta, - HTTPServer: meta.HTTPServer, - }); err != nil { - http.Error(w, fmt.Sprintf("Invalid profile: %v", err), http.StatusNotImplemented) - return - } - - // Serve the path directly. - mux.ServeHTTP(w, r) - })))) -} - -func envFlagString(name, def string) string { - if val := os.Getenv(name); val != "" { - return val - } - return def -} - -var ( - addr = flag.String("http", envFlagString("HTTP", ":"+envFlagString("PORT", "8080")), "HTTP service address") - staticDir = flag.String("static-dir", envFlagString("STATIC_DIR", "_site"), "static files directory") - - // Uses the standard GOOGLE_CLOUD_PROJECT environment variable set by App Engine. - projectID = flag.String("project-id", envFlagString("GOOGLE_CLOUD_PROJECT", ""), "The App Engine project ID.") - customHost = flag.String("custom-domain", envFlagString("CUSTOM_DOMAIN", "gvisor.dev"), "The application's custom domain.") -) - -func main() { - flag.Parse() - - registerRedirects(http.DefaultServeMux) - registerStatic(http.DefaultServeMux, *staticDir) - registerProfile(http.DefaultServeMux) - - log.Printf("Listening on %s...", *addr) - log.Fatal(http.ListenAndServe(*addr, nil)) -} diff --git a/website/cmd/syscalldocs/BUILD b/website/cmd/syscalldocs/BUILD deleted file mode 100644 index c5a0ed7fe..000000000 --- a/website/cmd/syscalldocs/BUILD +++ /dev/null @@ -1,9 +0,0 @@ -load("//tools:defs.bzl", "go_binary") - -package(licenses = ["notice"]) - -go_binary( - name = "syscalldocs", - srcs = ["main.go"], - visibility = ["//website:__pkg__"], -) diff --git a/website/cmd/syscalldocs/main.go b/website/cmd/syscalldocs/main.go deleted file mode 100644 index 327537214..000000000 --- a/website/cmd/syscalldocs/main.go +++ /dev/null @@ -1,211 +0,0 @@ -// Copyright 2019 The gVisor Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Binary syscalldocs generates system call markdown. -package main - -import ( - "bufio" - "encoding/json" - "flag" - "fmt" - "io" - "os" - "path/filepath" - "sort" - "strings" - "text/template" -) - -// CompatibilityInfo is the collection of all information. -type CompatibilityInfo map[string]map[string]ArchInfo - -// ArchInfo is compatbility doc for an architecture. -type ArchInfo struct { - // Syscalls maps syscall number for the architecture to the doc. - Syscalls map[uintptr]SyscallDoc `json:"syscalls"` -} - -// SyscallDoc represents a single item of syscall documentation. -type SyscallDoc struct { - Name string `json:"name"` - Support string `json:"support"` - Note string `json:"note,omitempty"` - URLs []string `json:"urls,omitempty"` -} - -var mdTemplate = template.Must(template.New("out").Parse(`--- -title: {{.Title}} -description: Syscall Compatibility Reference Documentation for {{.OS}}/{{.Arch}} -layout: docs -category: Compatibility -weight: 50 -permalink: /docs/user_guide/compatibility/{{.OS}}/{{.Arch}}/ ---- - -This table is a reference of {{.OS}} syscalls for the {{.Arch}} architecture and -their compatibility status in gVisor. gVisor does not support all syscalls and -some syscalls may have a partial implementation. - -This page is automatically generated from the source code. - -Of {{.Total}} syscalls, {{.Supported}} syscalls have a full or partial -implementation. There are currently {{.Unsupported}} unsupported -syscalls. {{if .Undocumented}}{{.Undocumented}} syscalls are not yet documented.{{end}} - -<table> - <thead> - <tr> - <th>#</th> - <th>Name</th> - <th>Support</th> - <th>Notes</th> - </tr> - </thead> - <tbody> - {{range $i, $syscall := .Syscalls}} - <tr> - <td><a class="doc-table-anchor" id="{{.Name}}"></a>{{.Number}}</td> - <td><a href="http://man7.org/linux/man-pages/man2/{{.Name}}.2.html" target="_blank" rel="noopener">{{.Name}}</a></td> - <td>{{.Support}}</td> - <td>{{.Note}} {{range $i, $url := .URLs}}<br/>See: <a href="{{.}}">{{.}}</a>{{end}}</td> - </tr> - {{end}} - </tbody> -</table> -`)) - -// Fatalf writes a message to stderr and exits with error code 1 -func Fatalf(format string, a ...interface{}) { - fmt.Fprintf(os.Stderr, format, a...) - os.Exit(1) -} - -func main() { - inputFlag := flag.String("in", "-", "File to input ('-' for stdin)") - outputDir := flag.String("out", ".", "Directory to output files.") - - flag.Parse() - - var input io.Reader - if *inputFlag == "-" { - input = os.Stdin - } else { - i, err := os.Open(*inputFlag) - if err != nil { - Fatalf("Error opening %q: %v", *inputFlag, err) - } - input = i - } - input = bufio.NewReader(input) - - var info CompatibilityInfo - d := json.NewDecoder(input) - if err := d.Decode(&info); err != nil { - Fatalf("Error reading json: %v", err) - } - - weight := 0 - for osName, osInfo := range info { - for archName, archInfo := range osInfo { - outDir := filepath.Join(*outputDir, osName) - outFile := filepath.Join(outDir, archName+".md") - - if err := os.MkdirAll(outDir, 0755); err != nil { - Fatalf("Error creating directory %q: %v", *outputDir, err) - } - - f, err := os.OpenFile(outFile, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644) - if err != nil { - Fatalf("Error opening file %q: %v", outFile, err) - } - defer f.Close() - - weight += 10 - data := struct { - Title string - OS string - Arch string - Weight int - Total int - Supported int - Unsupported int - Undocumented int - Syscalls []struct { - Name string - Number uintptr - Support string - Note string - URLs []string - } - }{ - Title: strings.Title(osName) + "/" + archName, - OS: osName, - Arch: archName, - Weight: weight, - Total: 0, - Supported: 0, - Unsupported: 0, - Undocumented: 0, - Syscalls: []struct { - Name string - Number uintptr - Support string - Note string - URLs []string - }{}, - } - - for num, s := range archInfo.Syscalls { - switch s.Support { - case "Full Support", "Partial Support": - data.Supported++ - case "Unimplemented": - data.Unsupported++ - default: - data.Undocumented++ - } - data.Total++ - - for i := range s.URLs { - if !strings.HasPrefix(s.URLs[i], "http://") && !strings.HasPrefix(s.URLs[i], "https://") { - s.URLs[i] = "https://" + s.URLs[i] - } - } - - data.Syscalls = append(data.Syscalls, struct { - Name string - Number uintptr - Support string - Note string - URLs []string - }{ - Name: s.Name, - Number: num, - Support: s.Support, - Note: s.Note, // TODO urls - URLs: s.URLs, - }) - } - - sort.Slice(data.Syscalls, func(i, j int) bool { - return data.Syscalls[i].Number < data.Syscalls[j].Number - }) - - if err := mdTemplate.Execute(f, data); err != nil { - Fatalf("Error writing file %q: %v", outFile, err) - } - } - } -} |