More decoy pages
This commit is contained in:
@@ -1547,7 +1547,7 @@
|
||||
} }, ['debug', 'info', 'warn', 'error'].map((l) => h('option', { value: l, selected: s.log.level === l }, l)));
|
||||
|
||||
// decoy
|
||||
const decoyPages = [['nginx', 'nginx welcome page'], ['apache', 'Apache "It works!" page'], ['soon', '"Coming soon" page']];
|
||||
const decoyPages = [['nginx', 'nginx welcome page'], ['apache', 'Apache "It works!" page'], ['soon', '"Coming soon" page'], ['blank', 'Blank page'], ['forbidden', '"Forbidden" page'], ['private', '"Private server" page']];
|
||||
const decoyBox = h('input', { type: 'checkbox', id: 'dc', checked: s.decoy.enabled, onChange: async (e) => {
|
||||
const on = e.target.checked;
|
||||
if (on) {
|
||||
|
||||
@@ -11,15 +11,20 @@ import (
|
||||
// front page is its stock welcome page and everything else is its stock
|
||||
// error page. Only /api/v1 and live setup links get past it.
|
||||
type decoyPage struct {
|
||||
server string // Server header, "" for none
|
||||
index func(host string) string // the front page
|
||||
error func(code int, r *http.Request) string // body for 404 and 405
|
||||
server string // Server header, "" for none
|
||||
index func(host string) string // the front page
|
||||
indexCode int // status of the front page, 0 for 200
|
||||
error func(code int, r *http.Request) string // body for 404 and 405
|
||||
}
|
||||
|
||||
var decoyPages = map[string]decoyPage{
|
||||
"nginx": {server: nginxServer, index: func(string) string { return nginxIndex }, error: nginxError},
|
||||
"apache": {server: apacheServer, index: func(string) string { return apacheIndex }, error: apacheError},
|
||||
"soon": {index: soonIndex, error: soonError},
|
||||
// Generic pages that name no server software.
|
||||
"blank": {index: func(string) string { return "" }, error: func(int, *http.Request) string { return "" }},
|
||||
"forbidden": {index: func(string) string { return forbiddenIndex }, indexCode: http.StatusForbidden, error: soonError},
|
||||
"private": {index: func(string) string { return privateIndex }, error: soonError},
|
||||
}
|
||||
|
||||
// serveDecoy writes the decoy's answer for r. It drops the headers the web
|
||||
@@ -43,6 +48,9 @@ func serveDecoy(w http.ResponseWriter, r *http.Request, name string) {
|
||||
code, body = http.StatusMethodNotAllowed, d.error(http.StatusMethodNotAllowed, r)
|
||||
case r.URL.Path == "/" || r.URL.Path == "/index.html":
|
||||
body = d.index(hostOnly(r.Host))
|
||||
if d.indexCode != 0 {
|
||||
code = d.indexCode
|
||||
}
|
||||
default:
|
||||
code, body = http.StatusNotFound, d.error(http.StatusNotFound, r)
|
||||
}
|
||||
@@ -537,3 +545,36 @@ const apacheIndex = `<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//
|
||||
</body>
|
||||
</html>
|
||||
`
|
||||
|
||||
const forbiddenIndex = `<!DOCTYPE html>
|
||||
<html>
|
||||
<head><title>403 Forbidden</title></head>
|
||||
<body>
|
||||
<h1>Forbidden</h1>
|
||||
<p>You don't have permission to access this resource.</p>
|
||||
</body>
|
||||
</html>
|
||||
`
|
||||
|
||||
const privateIndex = `<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Private</title>
|
||||
<style>
|
||||
html, body { height: 100%; margin: 0; }
|
||||
body { display: flex; align-items: center; justify-content: center; background: #111; color: #999;
|
||||
font-family: Georgia, serif; text-align: center; }
|
||||
h1 { font-size: 28px; font-weight: normal; letter-spacing: 0.04em; color: #fff; margin: 0 0 10px; }
|
||||
p { margin: 0; font-size: 15px; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<main>
|
||||
<h1>Private server</h1>
|
||||
<p>Nothing to see here.</p>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
`
|
||||
|
||||
@@ -954,6 +954,21 @@ func TestDecoy(t *testing.T) {
|
||||
if b, h := get("/", 200); !strings.Contains(b, "<p class=\"host\">127.0.0.1</p>") || h.Get("Server") != "" {
|
||||
t.Fatalf("soon decoy: %q", b)
|
||||
}
|
||||
set(func(c *Config) { c.Decoy.Page = "blank" })
|
||||
if b, _ := get("/", 200); b != "" {
|
||||
t.Fatalf("blank decoy: %q", b)
|
||||
}
|
||||
if b, _ := get("/app.js", 404); b != "" {
|
||||
t.Fatalf("blank 404: %q", b)
|
||||
}
|
||||
set(func(c *Config) { c.Decoy.Page = "forbidden" })
|
||||
if b, _ := get("/", 403); !strings.Contains(b, "Forbidden") {
|
||||
t.Fatalf("forbidden decoy: %q", b)
|
||||
}
|
||||
set(func(c *Config) { c.Decoy.Page = "private" })
|
||||
if b, _ := get("/", 200); !strings.Contains(b, "Private server") {
|
||||
t.Fatalf("private decoy: %q", b)
|
||||
}
|
||||
if err := store.Update(func(c *Config) error { c.Decoy.Page = "iis"; return nil }); err == nil {
|
||||
t.Fatal("unknown decoy page accepted")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user