all: strict unlock API by rpc

This commit is contained in:
rjl493456442 2019-03-13 14:44:43 +08:00 committed by Péter Szilágyi
parent d79b8b5492
commit 848ab4682b
No known key found for this signature in database
GPG key ID: E9AE538CEDF8293D
12 changed files with 63 additions and 49 deletions

View file

@ -45,7 +45,7 @@ type Manager struct {
// NewManager creates a generic account manager to sign transaction via various // NewManager creates a generic account manager to sign transaction via various
// supported backends. // supported backends.
func NewManager(insecureUnlockAllowed bool, backends ...Backend) *Manager { func NewManager(config *Config, backends ...Backend) *Manager {
// Retrieve the initial list of wallets from the backends and sort by URL // Retrieve the initial list of wallets from the backends and sort by URL
var wallets []Wallet var wallets []Wallet
for _, backend := range backends { for _, backend := range backends {
@ -60,7 +60,7 @@ func NewManager(insecureUnlockAllowed bool, backends ...Backend) *Manager {
} }
// Assemble the account manager and return // Assemble the account manager and return
am := &Manager{ am := &Manager{
config: &Config{insecureUnlockAllowed}, config: config,
backends: make(map[reflect.Type][]Backend), backends: make(map[reflect.Type][]Backend),
updaters: subs, updaters: subs,
updates: updates, updates: updates,

View file

@ -397,18 +397,9 @@ func startNode(ctx *cli.Context, stack *node.Node) {
// unlockAccounts unlocks any account specifically requested. // unlockAccounts unlocks any account specifically requested.
func unlockAccounts(ctx *cli.Context, stack *node.Node) { func unlockAccounts(ctx *cli.Context, stack *node.Node) {
// personalExposed checks whether personal API is exposed by http. // If insecure account unlocking is not allowed if node's APIs are exposed to external.
personalExposed := func() bool { // Print warning log to user and skip unlocking.
for _, module := range stack.Config().HTTPModules { if !stack.Config().InsecureUnlockAllowed && stack.Config().ExtRPCEnabled() {
if module == "personal" {
return true
}
}
return false
}
// If insecure account unlocking is not allowed and account-related APIs are exposed by http,
// print warning log to user and skip unlocking.
if !stack.Config().InsecureUnlockAllowed && personalExposed() {
log.Warn("Not safe to unlock account, please enable `allow-insecure-unlock` flag if necessary") log.Warn("Not safe to unlock account, please enable `allow-insecure-unlock` flag if necessary")
return return
} }

View file

@ -38,8 +38,9 @@ import (
// EthAPIBackend implements ethapi.Backend for full nodes // EthAPIBackend implements ethapi.Backend for full nodes
type EthAPIBackend struct { type EthAPIBackend struct {
eth *Ethereum extRPCEnabled bool
gpo *gasprice.Oracle eth *Ethereum
gpo *gasprice.Oracle
} }
// ChainConfig returns the active chain configuration. // ChainConfig returns the active chain configuration.
@ -213,6 +214,10 @@ func (b *EthAPIBackend) AccountManager() *accounts.Manager {
return b.eth.AccountManager() return b.eth.AccountManager()
} }
func (b *EthAPIBackend) ExtRPCEnabled() bool {
return b.extRPCEnabled
}
func (b *EthAPIBackend) BloomStatus() (uint64, uint64) { func (b *EthAPIBackend) BloomStatus() (uint64, uint64) {
sections, _, _ := b.eth.bloomIndexer.Sections() sections, _, _ := b.eth.bloomIndexer.Sections()
return params.BloomBitsBlocks, sections return params.BloomBitsBlocks, sections

View file

@ -197,7 +197,7 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) {
eth.miner = miner.New(eth, chainConfig, eth.EventMux(), eth.engine, config.MinerRecommit, config.MinerGasFloor, config.MinerGasCeil, eth.isLocalBlock) eth.miner = miner.New(eth, chainConfig, eth.EventMux(), eth.engine, config.MinerRecommit, config.MinerGasFloor, config.MinerGasCeil, eth.isLocalBlock)
eth.miner.SetExtra(makeExtraData(config.MinerExtraData)) eth.miner.SetExtra(makeExtraData(config.MinerExtraData))
eth.APIBackend = &EthAPIBackend{eth, nil} eth.APIBackend = &EthAPIBackend{ctx.ExtRPCEnabled(), eth, nil}
gpoParams := config.GPO gpoParams := config.GPO
if gpoParams.Default == nil { if gpoParams.Default == nil {
gpoParams.Default = config.MinerGasPrice gpoParams.Default = config.MinerGasPrice

View file

@ -318,9 +318,10 @@ func (s *PrivateAccountAPI) ImportRawKey(privkey string, password string) (commo
// the given password for duration seconds. If duration is nil it will use a // the given password for duration seconds. If duration is nil it will use a
// default of 300 seconds. It returns an indication if the account was unlocked. // default of 300 seconds. It returns an indication if the account was unlocked.
func (s *PrivateAccountAPI) UnlockAccount(ctx context.Context, addr common.Address, password string, duration *uint64) (bool, error) { func (s *PrivateAccountAPI) UnlockAccount(ctx context.Context, addr common.Address, password string, duration *uint64) (bool, error) {
// When the API is exposed by HTTP, unless the user explicitly specifies to // When the API is exposed by external RPC(http, ws etc), unless the user
// allow the insecure account unlocking, otherwise it is disabled. // explicitly specifies to allow the insecure account unlocking, otherwise
if isHttp, ok := ctx.Value("http").(bool); ok && isHttp && !s.b.AccountManager().Config().InsecureUnlockAllowed { // it is disabled.
if s.b.ExtRPCEnabled() && !s.b.AccountManager().Config().InsecureUnlockAllowed {
return false, errors.New("not safe to unlock account, please enable `allow-insecure-unlock` flag if necessary") return false, errors.New("not safe to unlock account, please enable `allow-insecure-unlock` flag if necessary")
} }

View file

@ -44,6 +44,7 @@ type Backend interface {
ChainDb() ethdb.Database ChainDb() ethdb.Database
EventMux() *event.TypeMux EventMux() *event.TypeMux
AccountManager() *accounts.Manager AccountManager() *accounts.Manager
ExtRPCEnabled() bool
// BlockChain API // BlockChain API
SetHead(number uint64) SetHead(number uint64)

View file

@ -39,8 +39,9 @@ import (
) )
type LesApiBackend struct { type LesApiBackend struct {
eth *LightEthereum extRPCEnabled bool
gpo *gasprice.Oracle eth *LightEthereum
gpo *gasprice.Oracle
} }
func (b *LesApiBackend) ChainConfig() *params.ChainConfig { func (b *LesApiBackend) ChainConfig() *params.ChainConfig {
@ -187,6 +188,10 @@ func (b *LesApiBackend) AccountManager() *accounts.Manager {
return b.eth.accountManager return b.eth.accountManager
} }
func (b *LesApiBackend) ExtRPCEnabled() bool {
return b.extRPCEnabled
}
func (b *LesApiBackend) BloomStatus() (uint64, uint64) { func (b *LesApiBackend) BloomStatus() (uint64, uint64) {
if b.eth.bloomIndexer == nil { if b.eth.bloomIndexer == nil {
return 0, 0 return 0, 0

View file

@ -166,7 +166,7 @@ func New(ctx *node.ServiceContext, config *eth.Config) (*LightEthereum, error) {
log.Warn("Ultra light client is enabled", "trustedNodes", len(leth.protocolManager.ulc.trustedKeys), "minTrustedFraction", leth.protocolManager.ulc.minTrustedFraction) log.Warn("Ultra light client is enabled", "trustedNodes", len(leth.protocolManager.ulc.trustedKeys), "minTrustedFraction", leth.protocolManager.ulc.minTrustedFraction)
leth.blockchain.DisableCheckFreq() leth.blockchain.DisableCheckFreq()
} }
leth.ApiBackend = &LesApiBackend{leth, nil} leth.ApiBackend = &LesApiBackend{ctx.ExtRPCEnabled(), leth, nil}
gpoParams := config.GPO gpoParams := config.GPO
if gpoParams.Default == nil { if gpoParams.Default == nil {

View file

@ -109,29 +109,6 @@ type Config struct {
// for ephemeral nodes). // for ephemeral nodes).
HTTPPort int `toml:",omitempty"` HTTPPort int `toml:",omitempty"`
// GraphQLHost is the host interface on which to start the GraphQL server. If this
// field is empty, no GraphQL API endpoint will be started.
GraphQLHost string `toml:",omitempty"`
// GraphQLPort is the TCP port number on which to start the GraphQL server. The
// default zero value is/ valid and will pick a port number randomly (useful
// for ephemeral nodes).
GraphQLPort int `toml:",omitempty"`
// GraphQLCors is the Cross-Origin Resource Sharing header to send to requesting
// clients. Please be aware that CORS is a browser enforced security, it's fully
// useless for custom HTTP clients.
GraphQLCors []string `toml:",omitempty"`
// GraphQLVirtualHosts is the list of virtual hostnames which are allowed on incoming requests.
// This is by default {'localhost'}. Using this prevents attacks like
// DNS rebinding, which bypasses SOP by simply masquerading as being within the same
// origin. These attacks do not utilize CORS, since they are not cross-domain.
// By explicitly checking the Host-header, the server will not allow requests
// made against the server with a malicious host domain.
// Requests using ip address directly are not affected
GraphQLVirtualHosts []string `toml:",omitempty"`
// HTTPCors is the Cross-Origin Resource Sharing header to send to requesting // HTTPCors is the Cross-Origin Resource Sharing header to send to requesting
// clients. Please be aware that CORS is a browser enforced security, it's fully // clients. Please be aware that CORS is a browser enforced security, it's fully
// useless for custom HTTP clients. // useless for custom HTTP clients.
@ -181,6 +158,29 @@ type Config struct {
// private APIs to untrusted users is a major security risk. // private APIs to untrusted users is a major security risk.
WSExposeAll bool `toml:",omitempty"` WSExposeAll bool `toml:",omitempty"`
// GraphQLHost is the host interface on which to start the GraphQL server. If this
// field is empty, no GraphQL API endpoint will be started.
GraphQLHost string `toml:",omitempty"`
// GraphQLPort is the TCP port number on which to start the GraphQL server. The
// default zero value is/ valid and will pick a port number randomly (useful
// for ephemeral nodes).
GraphQLPort int `toml:",omitempty"`
// GraphQLCors is the Cross-Origin Resource Sharing header to send to requesting
// clients. Please be aware that CORS is a browser enforced security, it's fully
// useless for custom HTTP clients.
GraphQLCors []string `toml:",omitempty"`
// GraphQLVirtualHosts is the list of virtual hostnames which are allowed on incoming requests.
// This is by default {'localhost'}. Using this prevents attacks like
// DNS rebinding, which bypasses SOP by simply masquerading as being within the same
// origin. These attacks do not utilize CORS, since they are not cross-domain.
// By explicitly checking the Host-header, the server will not allow requests
// made against the server with a malicious host domain.
// Requests using ip address directly are not affected
GraphQLVirtualHosts []string `toml:",omitempty"`
// Logger is a custom logger to use with the p2p.Server. // Logger is a custom logger to use with the p2p.Server.
Logger log.Logger `toml:",omitempty"` Logger log.Logger `toml:",omitempty"`
@ -273,6 +273,12 @@ func DefaultWSEndpoint() string {
return config.WSEndpoint() return config.WSEndpoint()
} }
// ExtRPCEnabled returns the indicator whether node enables the external
// RPC(http, ws or graphql).
func (c *Config) ExtRPCEnabled() bool {
return c.HTTPHost != "" || c.WSHost != "" || c.GraphQLHost != ""
}
// NodeName returns the devp2p node identifier. // NodeName returns the devp2p node identifier.
func (c *Config) NodeName() string { func (c *Config) NodeName() string {
name := c.name() name := c.name()
@ -500,7 +506,7 @@ func makeAccountManager(conf *Config) (*accounts.Manager, string, error) {
} }
} }
return accounts.NewManager(conf.InsecureUnlockAllowed, backends...), ephemeral, nil return accounts.NewManager(&accounts.Config{InsecureUnlockAllowed: conf.InsecureUnlockAllowed}, backends...), ephemeral, nil
} }
var warnLock sync.Mutex var warnLock sync.Mutex

View file

@ -68,6 +68,12 @@ func (ctx *ServiceContext) Service(service interface{}) error {
return ErrServiceUnknown return ErrServiceUnknown
} }
// ExtRPCEnabled returns the indicator whether node enables the external
// RPC(http, ws or graphql).
func (ctx *ServiceContext) ExtRPCEnabled() bool {
return ctx.config.ExtRPCEnabled()
}
// ServiceConstructor is the function signature of the constructors needed to be // ServiceConstructor is the function signature of the constructors needed to be
// registered for service instantiation. // registered for service instantiation.
type ServiceConstructor func(ctx *ServiceContext) (Service, error) type ServiceConstructor func(ctx *ServiceContext) (Service, error)

View file

@ -256,7 +256,6 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
ctx = context.WithValue(ctx, "remote", r.RemoteAddr) ctx = context.WithValue(ctx, "remote", r.RemoteAddr)
ctx = context.WithValue(ctx, "scheme", r.Proto) ctx = context.WithValue(ctx, "scheme", r.Proto)
ctx = context.WithValue(ctx, "local", r.Host) ctx = context.WithValue(ctx, "local", r.Host)
ctx = context.WithValue(ctx, "http", true)
if ua := r.Header.Get("User-Agent"); ua != "" { if ua := r.Header.Get("User-Agent"); ua != "" {
ctx = context.WithValue(ctx, "User-Agent", ua) ctx = context.WithValue(ctx, "User-Agent", ua)
} }

View file

@ -140,7 +140,7 @@ func StartClefAccountManager(ksLocation string, nousb, lightKDF bool) *accounts.
} }
} }
// Clef doesn't allow insecure http account unlock. // Clef doesn't allow insecure http account unlock.
return accounts.NewManager(false, backends...) return accounts.NewManager(&accounts.Config{InsecureUnlockAllowed: false}, backends...)
} }
// MetadataFromContext extracts Metadata from a given context.Context // MetadataFromContext extracts Metadata from a given context.Context