mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-19 02:12:23 +00:00
rpc: support http basic authentication for RPC server.
This commit is contained in:
parent
ccc0debb63
commit
a1698fdc8b
9 changed files with 62 additions and 9 deletions
|
|
@ -414,8 +414,8 @@ func signer(c *cli.Context) error {
|
||||||
cors := splitAndTrim(c.GlobalString(utils.RPCCORSDomainFlag.Name))
|
cors := splitAndTrim(c.GlobalString(utils.RPCCORSDomainFlag.Name))
|
||||||
|
|
||||||
// start http server
|
// start http server
|
||||||
httpEndpoint := fmt.Sprintf("%s:%d", c.String(utils.RPCListenAddrFlag.Name), c.Int(rpcPortFlag.Name))
|
httpEndpoint := fmt.Sprintf("%s:%d", c.String(utils.RPCListenAddrFlag.Name), c.Int(rpcPortFlag.Name)))
|
||||||
listener, _, err := rpc.StartHTTPEndpoint(httpEndpoint, rpcAPI, []string{"account"}, cors, vhosts)
|
listener, _, err := rpc.StartHTTPEndpoint(httpEndpoint, rpcAPI, []string{"account"}, cors, vhosts, c.String(utils.RPCUserFlag.Name, c.String(utils.RPCPasswordFlag.Name))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.Fatalf("Could not start RPC api: %v", err)
|
utils.Fatalf("Could not start RPC api: %v", err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -123,6 +123,8 @@ var (
|
||||||
|
|
||||||
rpcFlags = []cli.Flag{
|
rpcFlags = []cli.Flag{
|
||||||
utils.RPCEnabledFlag,
|
utils.RPCEnabledFlag,
|
||||||
|
utils.RPCUserFlag,
|
||||||
|
utils.RPCPasswordFlag,
|
||||||
utils.RPCListenAddrFlag,
|
utils.RPCListenAddrFlag,
|
||||||
utils.RPCPortFlag,
|
utils.RPCPortFlag,
|
||||||
utils.RPCApiFlag,
|
utils.RPCApiFlag,
|
||||||
|
|
|
||||||
|
|
@ -145,6 +145,8 @@ var AppHelpFlagGroups = []flagGroup{
|
||||||
Name: "API AND CONSOLE",
|
Name: "API AND CONSOLE",
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
utils.RPCEnabledFlag,
|
utils.RPCEnabledFlag,
|
||||||
|
utils.RPCUserFlag,
|
||||||
|
utils.RPCPasswordFlag,
|
||||||
utils.RPCListenAddrFlag,
|
utils.RPCListenAddrFlag,
|
||||||
utils.RPCPortFlag,
|
utils.RPCPortFlag,
|
||||||
utils.RPCApiFlag,
|
utils.RPCApiFlag,
|
||||||
|
|
|
||||||
|
|
@ -377,6 +377,16 @@ var (
|
||||||
Name: "rpc",
|
Name: "rpc",
|
||||||
Usage: "Enable the HTTP-RPC server",
|
Usage: "Enable the HTTP-RPC server",
|
||||||
}
|
}
|
||||||
|
RPCUserFlag = cli.StringFlag{
|
||||||
|
Name: "rpcuser",
|
||||||
|
Usage: "HTTP-RPC server user for basic authentication",
|
||||||
|
Value: "",
|
||||||
|
}
|
||||||
|
RPCPasswordFlag = cli.StringFlag{
|
||||||
|
Name: "rpcpassword",
|
||||||
|
Usage: "HTTP-RPC server password for basic authentication",
|
||||||
|
Value: "",
|
||||||
|
}
|
||||||
RPCListenAddrFlag = cli.StringFlag{
|
RPCListenAddrFlag = cli.StringFlag{
|
||||||
Name: "rpcaddr",
|
Name: "rpcaddr",
|
||||||
Usage: "HTTP-RPC server listening interface",
|
Usage: "HTTP-RPC server listening interface",
|
||||||
|
|
@ -684,6 +694,12 @@ func setHTTP(ctx *cli.Context, cfg *node.Config) {
|
||||||
if ctx.GlobalIsSet(RPCPortFlag.Name) {
|
if ctx.GlobalIsSet(RPCPortFlag.Name) {
|
||||||
cfg.HTTPPort = ctx.GlobalInt(RPCPortFlag.Name)
|
cfg.HTTPPort = ctx.GlobalInt(RPCPortFlag.Name)
|
||||||
}
|
}
|
||||||
|
if ctx.GlobalIsSet(RPCUserFlag.Name) {
|
||||||
|
cfg.HTTPUser = ctx.GlobalString(RPCUserFlag.Name)
|
||||||
|
}
|
||||||
|
if ctx.GlobalIsSet(RPCPasswordFlag.Name) {
|
||||||
|
cfg.HTTPPassword = ctx.GlobalString(RPCPasswordFlag.Name)
|
||||||
|
}
|
||||||
if ctx.GlobalIsSet(RPCCORSDomainFlag.Name) {
|
if ctx.GlobalIsSet(RPCCORSDomainFlag.Name) {
|
||||||
cfg.HTTPCors = splitAndTrim(ctx.GlobalString(RPCCORSDomainFlag.Name))
|
cfg.HTTPCors = splitAndTrim(ctx.GlobalString(RPCCORSDomainFlag.Name))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -133,6 +133,10 @@ func (api *PrivateAdminAPI) StartRPC(host *string, port *int, cors *string, apis
|
||||||
port = &api.node.config.HTTPPort
|
port = &api.node.config.HTTPPort
|
||||||
}
|
}
|
||||||
|
|
||||||
|
user := api.node.config.HTTPUser
|
||||||
|
|
||||||
|
password := api.node.config.HTTPPassword
|
||||||
|
|
||||||
allowedOrigins := api.node.config.HTTPCors
|
allowedOrigins := api.node.config.HTTPCors
|
||||||
if cors != nil {
|
if cors != nil {
|
||||||
allowedOrigins = nil
|
allowedOrigins = nil
|
||||||
|
|
@ -157,7 +161,7 @@ func (api *PrivateAdminAPI) StartRPC(host *string, port *int, cors *string, apis
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := api.node.startHTTP(fmt.Sprintf("%s:%d", *host, *port), api.node.rpcAPIs, modules, allowedOrigins, allowedVHosts); err != nil {
|
if err := api.node.startHTTP(fmt.Sprintf("%s:%d", *host, *port), api.node.rpcAPIs, modules, allowedOrigins, allowedVHosts, user, password); err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
return true, nil
|
return true, nil
|
||||||
|
|
|
||||||
|
|
@ -95,6 +95,14 @@ type Config struct {
|
||||||
// field is empty, no HTTP API endpoint will be started.
|
// field is empty, no HTTP API endpoint will be started.
|
||||||
HTTPHost string `toml:",omitempty"`
|
HTTPHost string `toml:",omitempty"`
|
||||||
|
|
||||||
|
// HTTPUser is the username on which the HTTP RPC server uses for basic authentication. If
|
||||||
|
// this field is empty, no basic authentication is used.
|
||||||
|
HTTPUser string `toml:",omitempty"`
|
||||||
|
|
||||||
|
// HTTPPassword is the password on which the HTTP RPC server uses for basic authentication. If
|
||||||
|
// this field is empty, no basic authentication is used.
|
||||||
|
HTTPPassword string `toml:",omitempty"`
|
||||||
|
|
||||||
// HTTPPort is the TCP port number on which to start the HTTP RPC server. The
|
// HTTPPort is the TCP port number on which to start the HTTP RPC server. The
|
||||||
// default zero value is/ valid and will pick a port number randomly (useful
|
// default zero value is/ valid and will pick a port number randomly (useful
|
||||||
// for ephemeral nodes).
|
// for ephemeral nodes).
|
||||||
|
|
|
||||||
|
|
@ -263,7 +263,7 @@ func (n *Node) startRPC(services map[reflect.Type]Service) error {
|
||||||
n.stopInProc()
|
n.stopInProc()
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := n.startHTTP(n.httpEndpoint, apis, n.config.HTTPModules, n.config.HTTPCors, n.config.HTTPVirtualHosts); err != nil {
|
if err := n.startHTTP(n.httpEndpoint, apis, n.config.HTTPModules, n.config.HTTPCors, n.config.HTTPVirtualHosts, n.config.HTTPUser, n.config.HTTPUser); err != nil {
|
||||||
n.stopIPC()
|
n.stopIPC()
|
||||||
n.stopInProc()
|
n.stopInProc()
|
||||||
return err
|
return err
|
||||||
|
|
@ -331,12 +331,12 @@ func (n *Node) stopIPC() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// startHTTP initializes and starts the HTTP RPC endpoint.
|
// startHTTP initializes and starts the HTTP RPC endpoint.
|
||||||
func (n *Node) startHTTP(endpoint string, apis []rpc.API, modules []string, cors []string, vhosts []string) error {
|
func (n *Node) startHTTP(endpoint string, apis []rpc.API, modules []string, cors []string, vhosts []string, user string, password string) error {
|
||||||
// Short circuit if the HTTP endpoint isn't being exposed
|
// Short circuit if the HTTP endpoint isn't being exposed
|
||||||
if endpoint == "" {
|
if endpoint == "" {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
listener, handler, err := rpc.StartHTTPEndpoint(endpoint, apis, modules, cors, vhosts)
|
listener, handler, err := rpc.StartHTTPEndpoint(endpoint, apis, modules, cors, vhosts, user, password)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// StartHTTPEndpoint starts the HTTP RPC endpoint, configured with cors/vhosts/modules
|
// StartHTTPEndpoint starts the HTTP RPC endpoint, configured with cors/vhosts/modules
|
||||||
func StartHTTPEndpoint(endpoint string, apis []API, modules []string, cors []string, vhosts []string) (net.Listener, *Server, error) {
|
func StartHTTPEndpoint(endpoint string, apis []API, modules []string, cors []string, vhosts []string, user string, password string) (net.Listener, *Server, error) {
|
||||||
// Generate the whitelist based on the allowed modules
|
// Generate the whitelist based on the allowed modules
|
||||||
whitelist := make(map[string]bool)
|
whitelist := make(map[string]bool)
|
||||||
for _, module := range modules {
|
for _, module := range modules {
|
||||||
|
|
@ -47,7 +47,7 @@ func StartHTTPEndpoint(endpoint string, apis []API, modules []string, cors []str
|
||||||
if listener, err = net.Listen("tcp", endpoint); err != nil {
|
if listener, err = net.Listen("tcp", endpoint); err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
go NewHTTPServer(cors, vhosts, handler).Serve(listener)
|
go NewHTTPServer(cors, vhosts, user, password, handler).Serve(listener)
|
||||||
return listener, handler, err
|
return listener, handler, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
23
rpc/http.go
23
rpc/http.go
|
|
@ -19,6 +19,7 @@ package rpc
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
|
"crypto/subtle"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
@ -31,6 +32,7 @@ import (
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/rs/cors"
|
"github.com/rs/cors"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -161,10 +163,15 @@ func (t *httpReadWriteNopCloser) Close() error {
|
||||||
// NewHTTPServer creates a new HTTP RPC server around an API provider.
|
// NewHTTPServer creates a new HTTP RPC server around an API provider.
|
||||||
//
|
//
|
||||||
// Deprecated: Server implements http.Handler
|
// Deprecated: Server implements http.Handler
|
||||||
func NewHTTPServer(cors []string, vhosts []string, srv *Server) *http.Server {
|
func NewHTTPServer(cors []string, vhosts []string, user string, password string, srv *Server) *http.Server {
|
||||||
// Wrap the CORS-handler within a host-handler
|
// Wrap the CORS-handler within a host-handler
|
||||||
handler := newCorsHandler(srv, cors)
|
handler := newCorsHandler(srv, cors)
|
||||||
handler = newVHostHandler(vhosts, handler)
|
handler = newVHostHandler(vhosts, handler)
|
||||||
|
|
||||||
|
if user != "" && password != "" {
|
||||||
|
log.Info("HTTP endpoint is secured by basic authentication.")
|
||||||
|
handler = newBasicAuthHandler(user, password, handler)
|
||||||
|
}
|
||||||
return &http.Server{Handler: handler}
|
return &http.Server{Handler: handler}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -272,3 +279,17 @@ func newVHostHandler(vhosts []string, next http.Handler) http.Handler {
|
||||||
}
|
}
|
||||||
return &virtualHostHandler{vhostMap, next}
|
return &virtualHostHandler{vhostMap, next}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func newBasicAuthHandler(username string, password string, next http.Handler) http.Handler {
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
user, pass, ok := r.BasicAuth()
|
||||||
|
|
||||||
|
if !ok || subtle.ConstantTimeCompare([]byte(user), []byte(username)) != 1 || subtle.ConstantTimeCompare([]byte(pass), []byte(password)) != 1 {
|
||||||
|
w.Header().Set("WWW-Authenticate", `Basic realm="Please use the right user and password"`)
|
||||||
|
http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
next.ServeHTTP(w, r)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue