Ver código fonte

cmd/faucet: protocol relative websockets, noauth mode

Péter Szilágyi 8 anos atrás
pai
commit
51a86f61be

+ 17 - 2
cmd/faucet/faucet.go

@@ -83,7 +83,8 @@ var (
 	captchaToken  = flag.String("captcha.token", "", "Recaptcha site key to authenticate client side")
 	captchaSecret = flag.String("captcha.secret", "", "Recaptcha secret key to authenticate server side")
 
-	logFlag = flag.Int("loglevel", 3, "Log level to use for Ethereum and the faucet")
+	noauthFlag = flag.Bool("noauth", false, "Enables funding requests without authentication")
+	logFlag    = flag.Int("loglevel", 3, "Log level to use for Ethereum and the faucet")
 )
 
 var (
@@ -132,6 +133,7 @@ func main() {
 		"Amounts":   amounts,
 		"Periods":   periods,
 		"Recaptcha": *captchaToken,
+		"NoAuth":    *noauthFlag,
 	})
 	if err != nil {
 		log.Crit("Failed to render the faucet template", "err", err)
@@ -374,7 +376,7 @@ func (f *faucet) apiHandler(conn *websocket.Conn) {
 		if err = websocket.JSON.Receive(conn, &msg); err != nil {
 			return
 		}
-		if !strings.HasPrefix(msg.URL, "https://gist.github.com/") && !strings.HasPrefix(msg.URL, "https://twitter.com/") &&
+		if !*noauthFlag && !strings.HasPrefix(msg.URL, "https://gist.github.com/") && !strings.HasPrefix(msg.URL, "https://twitter.com/") &&
 			!strings.HasPrefix(msg.URL, "https://plus.google.com/") && !strings.HasPrefix(msg.URL, "https://www.facebook.com/") {
 			if err = sendError(conn, errors.New("URL doesn't link to supported services")); err != nil {
 				log.Warn("Failed to send URL error to client", "err", err)
@@ -442,6 +444,8 @@ func (f *faucet) apiHandler(conn *websocket.Conn) {
 			username, avatar, address, err = authGooglePlus(msg.URL)
 		case strings.HasPrefix(msg.URL, "https://www.facebook.com/"):
 			username, avatar, address, err = authFacebook(msg.URL)
+		case *noauthFlag:
+			username, avatar, address, err = authNoAuth(msg.URL)
 		default:
 			err = errors.New("Something funky happened, please open an issue at https://github.com/ethereum/go-ethereum/issues")
 		}
@@ -776,3 +780,14 @@ func authFacebook(url string) (string, string, common.Address, error) {
 	}
 	return username + "@facebook", avatar, address, nil
 }
+
+// authNoAuth tries to interpret a faucet request as a plain Ethereum address,
+// without actually performing any remote authentication. This mode is prone to
+// Byzantine attack, so only ever use for truly private networks.
+func authNoAuth(url string) (string, string, common.Address, error) {
+	address := common.HexToAddress(regexp.MustCompile("0x[0-9a-fA-F]{40}").FindString(url))
+	if address == (common.Address{}) {
+		return "", "", common.Address{}, errors.New("No Ethereum address found to fund")
+	}
+	return address.Hex() + "@noauth", "", address, nil
+}

+ 6 - 6
cmd/faucet/faucet.html

@@ -93,6 +93,11 @@
 
 							<dt style="width: auto; margin-left: 40px;"><i class="fa fa-facebook" aria-hidden="true" style="font-size: 36px;"></i></dt>
 							<dd style="margin-left: 88px; margin-bottom: 10px;"></i> To request funds via Facebook, publish a new <strong>public</strong> post with your Ethereum address embedded into the content (surrounding text doesn't matter).<br/>Copy-paste the <a href="https://www.facebook.com/help/community/question/?id=282662498552845" target="_about:blank">posts URL</a> into the above input box and fire away!</dd>
+
+							{{if .NoAuth}}
+								<dt class="text-danger" style="width: auto; margin-left: 40px;"><i class="fa fa-unlock-alt" aria-hidden="true" style="font-size: 36px;"></i></dt>
+								<dd class="text-danger" style="margin-left: 88px; margin-bottom: 10px;"></i> To request funds <strong>without authentication</strong>, simply copy-paste your Ethereum address into the above input box (surrounding text doesn't matter) and fire away.<br/>This mode is susceptible to Byzantine attacks. Only use for debugging or private networks!</dd>
+							{{end}}
 						</dl>
 						<p>You can track the current pending requests below the input field to see how much you have to wait until your turn comes.</p>
 						{{if .Recaptcha}}<em>The faucet is running invisible reCaptcha protection against bots.</em>{{end}}
@@ -126,12 +131,7 @@
 			};
 			// Define a method to reconnect upon server loss
 			var reconnect = function() {
-				if (attempt % 2 == 0) {
-					server = new WebSocket("wss://" + location.host + "/api");
-				} else {
-					server = new WebSocket("ws://" + location.host + "/api");
-				}
-				attempt++;
+				server = new WebSocket(((window.location.protocol === "https:") ? "wss://" : "ws://") + window.location.host + "/api");
 
 				server.onmessage = function(event) {
 					var msg = JSON.parse(event.data);

Diferenças do arquivo suprimidas por serem muito extensas
+ 0 - 0
cmd/faucet/website.go


+ 18 - 6
cmd/puppeth/module_faucet.go

@@ -53,10 +53,10 @@ ADD account.pass /account.pass
 EXPOSE 8080
 
 CMD [ \
-	"/faucet", "--genesis", "/genesis.json", "--network", "{{.NetworkID}}", "--bootnodes", "{{.Bootnodes}}", "--ethstats", "{{.Ethstats}}", "--ethport", "{{.EthPort}}", \
-	"--faucet.name", "{{.FaucetName}}", "--faucet.amount", "{{.FaucetAmount}}", "--faucet.minutes", "{{.FaucetMinutes}}", "--faucet.tiers", "{{.FaucetTiers}}",          \
-	"--github.user", "{{.GitHubUser}}", "--github.token", "{{.GitHubToken}}", "--account.json", "/account.json", "--account.pass", "/account.pass"                       \
-	{{if .CaptchaToken}}, "--captcha.token", "{{.CaptchaToken}}", "--captcha.secret", "{{.CaptchaSecret}}"{{end}}                                                        \
+	"/faucet", "--genesis", "/genesis.json", "--network", "{{.NetworkID}}", "--bootnodes", "{{.Bootnodes}}", "--ethstats", "{{.Ethstats}}", "--ethport", "{{.EthPort}}",    \
+	"--faucet.name", "{{.FaucetName}}", "--faucet.amount", "{{.FaucetAmount}}", "--faucet.minutes", "{{.FaucetMinutes}}", "--faucet.tiers", "{{.FaucetTiers}}",             \
+	{{if .GitHubUser}}"--github.user", "{{.GitHubUser}}", "--github.token", "{{.GitHubToken}}", {{end}}"--account.json", "/account.json", "--account.pass", "/account.pass" \
+	{{if .CaptchaToken}}, "--captcha.token", "{{.CaptchaToken}}", "--captcha.secret", "{{.CaptchaSecret}}"{{end}}{{if .NoAuth}}, "--noauth"{{end}}                          \
 ]`
 
 // faucetComposefile is the docker-compose.yml file required to deploy and maintain
@@ -81,7 +81,8 @@ services:
       - GITHUB_USER={{.GitHubUser}}
       - GITHUB_TOKEN={{.GitHubToken}}
       - CAPTCHA_TOKEN={{.CaptchaToken}}
-      - CAPTCHA_SECRET={{.CaptchaSecret}}{{if .VHost}}
+      - CAPTCHA_SECRET={{.CaptchaSecret}}
+      - NO_AUTH={{.NoAuth}}{{if .VHost}}
       - VIRTUAL_HOST={{.VHost}}
       - VIRTUAL_PORT=8080{{end}}
     logging:
@@ -114,6 +115,7 @@ func deployFaucet(client *sshClient, network string, bootnodes []string, config
 		"FaucetAmount":  config.amount,
 		"FaucetMinutes": config.minutes,
 		"FaucetTiers":   config.tiers,
+		"NoAuth":        config.noauth,
 	})
 	files[filepath.Join(workdir, "Dockerfile")] = dockerfile.Bytes()
 
@@ -132,6 +134,7 @@ func deployFaucet(client *sshClient, network string, bootnodes []string, config
 		"FaucetAmount":  config.amount,
 		"FaucetMinutes": config.minutes,
 		"FaucetTiers":   config.tiers,
+		"NoAuth":        config.noauth,
 	})
 	files[filepath.Join(workdir, "docker-compose.yaml")] = composefile.Bytes()
 
@@ -161,6 +164,7 @@ type faucetInfos struct {
 	amount        int
 	minutes       int
 	tiers         int
+	noauth        bool
 	githubUser    string
 	githubToken   string
 	captchaToken  string
@@ -179,7 +183,14 @@ func (info *faucetInfos) Report() map[string]string {
 		"Funding tiers":                strconv.Itoa(info.tiers),
 		"Captha protection":            fmt.Sprintf("%v", info.captchaToken != ""),
 		"Ethstats username":            info.node.ethstats,
-		"GitHub authentication":        info.githubUser,
+	}
+	if info.githubUser != "" {
+		report["GitHub authentication"] = info.githubUser
+	} else {
+		report["GitHub authentication"] = "disabled, rate-limited"
+	}
+	if info.noauth {
+		report["Debug mode (no auth)"] = "enabled"
 	}
 	if info.node.keyJSON != "" {
 		var key struct {
@@ -255,5 +266,6 @@ func checkFaucet(client *sshClient, network string) (*faucetInfos, error) {
 		githubToken:   infos.envvars["GITHUB_TOKEN"],
 		captchaToken:  infos.envvars["CAPTCHA_TOKEN"],
 		captchaSecret: infos.envvars["CAPTCHA_SECRET"],
+		noauth:        infos.envvars["NO_AUTH"] == "true",
 	}, nil
 }

+ 45 - 29
cmd/puppeth/wizard_faucet.go

@@ -87,34 +87,38 @@ func (w *wizard) deployFaucet() {
 	if infos.githubUser == "" {
 		// No previous authorization (or new one requested)
 		fmt.Println()
-		fmt.Println("Which GitHub user to verify Gists through?")
-		infos.githubUser = w.readString()
+		fmt.Println("Which GitHub user to verify Gists through? (default = none = rate-limited API)")
+		infos.githubUser = w.readDefaultString("")
 
-		fmt.Println()
-		fmt.Println("What is the GitHub personal access token of the user? (won't be echoed)")
-		infos.githubToken = w.readPassword()
-
-		// Do a sanity check query against github to ensure it's valid
-		req, _ := http.NewRequest("GET", "https://api.github.com/user", nil)
-		req.SetBasicAuth(infos.githubUser, infos.githubToken)
-		res, err := http.DefaultClient.Do(req)
-		if err != nil {
-			log.Error("Failed to verify GitHub authentication", "err", err)
-			return
-		}
-		defer res.Body.Close()
+		if infos.githubUser == "" {
+			log.Warn("Funding requests via GitHub will be heavily rate-limited")
+		} else {
+			fmt.Println()
+			fmt.Println("What is the GitHub personal access token of the user? (won't be echoed)")
+			infos.githubToken = w.readPassword()
+
+			// Do a sanity check query against github to ensure it's valid
+			req, _ := http.NewRequest("GET", "https://api.github.com/user", nil)
+			req.SetBasicAuth(infos.githubUser, infos.githubToken)
+			res, err := http.DefaultClient.Do(req)
+			if err != nil {
+				log.Error("Failed to verify GitHub authentication", "err", err)
+				return
+			}
+			defer res.Body.Close()
 
-		var msg struct {
-			Login   string `json:"login"`
-			Message string `json:"message"`
-		}
-		if err = json.NewDecoder(res.Body).Decode(&msg); err != nil {
-			log.Error("Failed to decode authorization response", "err", err)
-			return
-		}
-		if msg.Login != infos.githubUser {
-			log.Error("GitHub authorization failed", "user", infos.githubUser, "message", msg.Message)
-			return
+			var msg struct {
+				Login   string `json:"login"`
+				Message string `json:"message"`
+			}
+			if err = json.NewDecoder(res.Body).Decode(&msg); err != nil {
+				log.Error("Failed to decode authorization response", "err", err)
+				return
+			}
+			if msg.Login != infos.githubUser {
+				log.Error("GitHub authorization failed", "user", infos.githubUser, "message", msg.Message)
+				return
+			}
 		}
 	}
 	// Accessing the reCaptcha service requires API authorizations, request it
@@ -129,7 +133,9 @@ func (w *wizard) deployFaucet() {
 		// No previous authorization (or old one discarded)
 		fmt.Println()
 		fmt.Println("Enable reCaptcha protection against robots (y/n)? (default = no)")
-		if w.readDefaultString("n") == "y" {
+		if w.readDefaultString("n") == "n" {
+			log.Warn("Users will be able to requests funds via automated scripts")
+		} else {
 			// Captcha protection explicitly requested, read the site and secret keys
 			fmt.Println()
 			fmt.Printf("What is the reCaptcha site key to authenticate human users?\n")
@@ -175,7 +181,7 @@ func (w *wizard) deployFaucet() {
 			}
 		}
 	}
-	if infos.node.keyJSON == "" {
+	for i := 0; i < 3 && infos.node.keyJSON == ""; i++ {
 		fmt.Println()
 		fmt.Println("Please paste the faucet's funding account key JSON:")
 		infos.node.keyJSON = w.readJSON()
@@ -186,9 +192,19 @@ func (w *wizard) deployFaucet() {
 
 		if _, err := keystore.DecryptKey([]byte(infos.node.keyJSON), infos.node.keyPass); err != nil {
 			log.Error("Failed to decrypt key with given passphrase")
-			return
+			infos.node.keyJSON = ""
+			infos.node.keyPass = ""
 		}
 	}
+	// Check if the user wants to run the faucet in debug mode (noauth)
+	noauth := "n"
+	if infos.noauth {
+		noauth = "y"
+	}
+	fmt.Println()
+	fmt.Printf("Permit non-authenticated funding requests (y/n)? (default = %v)\n", infos.noauth)
+	infos.noauth = w.readDefaultString(noauth) != "n"
+
 	// Try to deploy the faucet server on the host
 	fmt.Println()
 	fmt.Printf("Should the faucet be built from scratch (y/n)? (default = no)\n")

Alguns arquivos não foram mostrados porque muitos arquivos mudaram nesse diff