Secure network key file and data directory.

This commit is contained in:
cheatfate 2020-08-19 16:12:10 +03:00 committed by zah
parent d9738b43b3
commit c5c788a9db
5 changed files with 168 additions and 39 deletions

View File

@ -188,8 +188,8 @@ testnet0 testnet1: | beacon_node signing_process
$(GOERLI_TESTNETS_PARAMS) $(NODE_PARAMS)
# https://www.gnu.org/software/make/manual/html_node/Multi_002dLine.html
define CONNECT_TO_NETWORK
mkdir -p build/data/shared_$(1)_$(NODE_ID)
define CONNECT_TO_NETWORK =
mkdir -m 0750 -p build/data/shared_$(1)_$(NODE_ID)
scripts/make_prometheus_config.sh \
--nodes 1 \
@ -204,8 +204,8 @@ define CONNECT_TO_NETWORK
$(GOERLI_TESTNETS_PARAMS) $(NODE_PARAMS)
endef
define CONNECT_TO_NETWORK_IN_DEV_MODE
mkdir -p build/data/shared_$(1)_$(NODE_ID)
define CONNECT_TO_NETWORK_IN_DEV_MODE =
mkdir -m 0750 -p build/data/shared_$(1)_$(NODE_ID)
scripts/make_prometheus_config.sh \
--nodes 1 \
@ -221,7 +221,7 @@ endef
define CONNECT_TO_NETWORK_WITH_VALIDATOR_CLIENT
# if launching a VC as well - send the BN looking nowhere for validators/secrets
mkdir -p build/data/shared_$(1)_$(NODE_ID)/empty_dummy_folder
mkdir -m 0750 -p build/data/shared_$(1)_$(NODE_ID)/empty_dummy_folder
scripts/make_prometheus_config.sh \
--nodes 1 \

View File

@ -11,7 +11,7 @@ import
std/[osproc, random],
# Nimble packages
stew/[objects, byteutils, endians2], stew/shims/macros,
stew/[objects, byteutils, endians2, io2], stew/shims/macros,
chronos, confutils, metrics, json_rpc/[rpcserver, jsonmarshal],
chronicles, bearssl, blscurve,
json_serialization/std/[options, sets, net], serialization/errors,
@ -243,7 +243,7 @@ proc init*(T: type BeaconNode,
enrForkId = enrForkIdFromState(chainDag.headState.data.data)
topicBeaconBlocks = getBeaconBlocksTopic(enrForkId.forkDigest)
topicAggregateAndProofs = getAggregateAndProofsTopic(enrForkId.forkDigest)
network = createEth2Node(rng, conf, enrForkId)
network = createEth2Node(rng, conf, netKeys, enrForkId)
attestationPool = newClone(AttestationPool.init(chainDag, quarantine))
exitPool = newClone(ExitPool.init(chainDag, quarantine))
var res = BeaconNode(
@ -894,10 +894,50 @@ proc run*(node: BeaconNode) =
var gPidFile: string
proc createPidFile(filename: string) =
createDir splitFile(filename).dir
writeFile filename, $os.getCurrentProcessId()
gPidFile = filename
addQuitProc proc {.noconv.} = removeFile gPidFile
addQuitProc proc {.noconv.} = discard io2.removeFile(gPidFile)
proc checkDataDir(conf: BeaconNodeConf) =
## Checks `conf.dataDir`.
## If folder exists, prcoedure will check it for access and
## permissions `0750 (rwxr-x---)`, if folder do not exists it will be created
## with permissions `0750 (rwxr-x---)`.
let dataDir = string(conf.dataDir)
when defined(posix):
let amask = {AccessFlags.Read, AccessFlags.Write, AccessFlags.Execute}
if fileAccessible(dataDir, amask):
let gmask = {UserRead, UserWrite, UserExec, GroupRead, GroupExec}
let pmask = {OtherRead, OtherWrite, OtherExec, GroupWrite}
let pres = getPermissionsSet(dataDir)
if pres.isErr():
fatal "Could not check data folder permissions",
data_dir = dataDir, errorCode = $pres.error,
errorMsg = ioErrorMsg(pres.error)
quit QuitFailure
let insecurePermissions = pres.get() * pmask
if insecurePermissions != {}:
fatal "Data folder has insecure permissions",
data_dir = dataDir,
insecure_permissions = $insecurePermissions,
current_permissions = pres.get().toString(),
required_permissions = gmask.toString()
quit QuitFailure
else:
let res = createPath(dataDir, 0o750)
if res.isErr():
fatal "Could not create data folder", data_dir = dataDir,
errorMsg = ioErrorMsg(res.error), errorCode = $res.error
quit QuitFailure
elif defined(windows):
let res = createPath(dataDir, 0o750)
if res.isErr():
fatal "Could not create data folder", data_dir = dataDir,
errorMsg = ioErrorMsg(res.error), errorCode = $res.error
quit QuitFailure
else:
fatal "Unsupported operation system"
quit QuitFailure
proc initializeNetworking(node: BeaconNode) {.async.} =
await node.network.startListening()
@ -1133,6 +1173,8 @@ programMain:
case config.cmd
of createTestnet:
checkDataDir(config)
let launchPadDeposits = try:
Json.loadFile(config.testnetDepositsFile.string, seq[LaunchPadDeposit])
except SerializationError as err:
@ -1193,6 +1235,8 @@ programMain:
cmdParams = commandLineParams(),
config
checkDataDir(config)
createPidFile(config.dataDir.string / "beacon_node.pid")
config.createDumpDirs()

View File

@ -218,6 +218,12 @@ type
desc: "Write SSZ dumps of blocks, attestations and states to data dir"
name: "dump" }: bool
netKeyFile* {.
defaultValue: "random",
desc: "Source of network (secp256k1) private key file " &
"(random|<path>) (default: random)"
name: "netkey-file" }: string
of createTestnet:
testnetDepositsFile* {.
desc: "A LaunchPad deposits file for the genesis state validators"
@ -265,6 +271,10 @@ type
desc: "Output file with list of bootstrap nodes for the network"
name: "output-bootstrap-file" }: OutFile
outputNetkeyFile* {.
desc: "Output file with network private key for the network"
name: "output-netkey-file" }: OutFile
of wallets:
case walletsCmd* {.command.}: WalletsCmd
of WalletsCmd.create:

View File

@ -4,7 +4,7 @@ import
std/options as stdOptions,
# Status libs
stew/[varints, base58, base64, endians2, results, byteutils], bearssl,
stew/[varints, base58, base64, endians2, results, byteutils, io2], bearssl,
stew/shims/net as stewNet,
stew/shims/[macros, tables],
faststreams/[inputs, outputs, buffers], snappy, snappy/framing,
@ -204,7 +204,6 @@ type
const
clientId* = "Nimbus beacon node v" & fullVersionStr
networkKeyFilename = "privkey.protobuf"
nodeMetadataFilename = "node-metadata.json"
TCP = net.Protocol.IPPROTO_TCP
@ -1201,21 +1200,103 @@ proc initAddress*(T: type MultiAddress, str: string): T =
template tcpEndPoint(address, port): auto =
MultiAddress.init(address, tcpProtocol, port)
proc getPersistentNetKeys*(
rng: var BrHmacDrbgContext, conf: BeaconNodeConf): KeyPair =
let
privKeyPath = conf.dataDir / networkKeyFilename
privKey =
if not fileExists(privKeyPath):
createDir conf.dataDir.string
let key = PrivateKey.random(Secp256k1, rng).tryGet()
writeFile(privKeyPath, key.getBytes().tryGet())
key
else:
let keyBytes = readFile(privKeyPath)
PrivateKey.init(keyBytes.toOpenArrayByte(0, keyBytes.high)).tryGet()
proc getPersistentNetKeys*(rng: var BrHmacDrbgContext,
conf: BeaconNodeConf): KeyPair =
case conf.cmd
of noCommand:
if conf.netKeyFile == "random":
let res = PrivateKey.random(Secp256k1, rng)
if res.isErr():
fatal "Could not generate random network key file"
quit QuitFailure
let privKey = res.get()
return KeyPair(seckey: privKey, pubkey: privKey.getKey().tryGet())
else:
let keyPath =
if isAbsolute(conf.netKeyFile):
conf.netKeyFile
else:
conf.dataDir / conf.netKeyFile
KeyPair(seckey: privKey, pubkey: privKey.getKey().tryGet())
if fileAccessible(keyPath, {AccessFlags.Find}):
let gmask = {UserRead, UserWrite}
let pmask = {UserExec,
GroupRead, GroupWrite, GroupExec,
OtherRead, OtherWrite, OtherExec}
let pres = getPermissionsSet(keyPath)
if pres.isErr():
fatal "Could not check key file permissions",
key_path = keyPath, errorCode = $pres.error,
errorMsg = ioErrorMsg(pres.error)
quit QuitFailure
let insecurePermissions = pres.get() * pmask
if insecurePermissions != {}:
fatal "Network key file has insecure permissions",
key_path = keyPath,
insecure_permissions = $insecurePermissions,
current_permissions = pres.get().toString(),
required_permissions = gmask.toString()
quit QuitFailure
let kres = readAllFile(keyPath)
if not(kres.isOk()):
fatal "Could not read network key file", key_path = keyPath
quit QuitFailure
let keyBytes = kres.get()
let rres = PrivateKey.init(keyBytes)
if not(rres.isOk()):
fatal "Incorrect network key file", key_path = keyPath
quit QuitFailure
let privKey = rres.get()
return KeyPair(seckey: privKey, pubkey: privKey.getKey().tryGet())
else:
let res = PrivateKey.random(Secp256k1, rng)
if res.isErr():
fatal "Could not generate random network key file"
quit QuitFailure
let privKey = res.get()
let wres = writeFile(keyPath, privKey.getBytes().tryGet(), 0o600)
if not(wres.isOk()):
fatal "Could not write network key file", key_path = keyPath
quit QuitFailure
return KeyPair(seckey: privKey, pubkey: privkey.getKey().tryGet())
of createTestnet:
let netKeyFile = string(conf.outputNetkeyFile)
let keyPath =
if isAbsolute(netKeyFile):
netKeyFile
else:
conf.dataDir / netKeyFile
let res = PrivateKey.random(Secp256k1, rng)
if res.isErr():
fatal "Could not generate random network key file"
quit QuitFailure
let privKey = res.get()
let wres = writeFile(keyPath, privKey.getBytes().tryGet(), 0o600)
if not(wres.isOk()):
fatal "Could not write network key file", key_path = keyPath
quit QuitFailure
return KeyPair(seckey: privKey, pubkey: privkey.getKey().tryGet())
else:
let res = PrivateKey.random(Secp256k1, rng)
if res.isErr():
fatal "Could not generate random network key file"
quit QuitFailure
let privKey = res.get()
return KeyPair(seckey: privKey, pubkey: privkey.getKey().tryGet())
func gossipId(data: openArray[byte]): string =
# https://github.com/ethereum/eth2.0-specs/blob/v0.12.3/specs/phase0/p2p-interface.md#topics-and-messages
@ -1224,9 +1305,10 @@ func gossipId(data: openArray[byte]): string =
func msgIdProvider(m: messages.Message): string =
gossipId(m.data)
proc createEth2Node*(
rng: ref BrHmacDrbgContext, conf: BeaconNodeConf,
enrForkId: ENRForkID): Eth2Node =
proc createEth2Node*(rng: ref BrHmacDrbgContext,
conf: BeaconNodeConf,
netKeys: KeyPair,
enrForkId: ENRForkID): Eth2Node =
var
(extIp, extTcpPort, extUdpPort) = setupNat(conf)
hostAddress = tcpEndPoint(conf.listenAddress, conf.tcpPort)
@ -1236,11 +1318,10 @@ proc createEth2Node*(
notice "Initializing networking", hostAddress,
announcedAddresses
let keys = getPersistentNetKeys(rng[], conf)
# TODO nim-libp2p still doesn't have support for announcing addresses
# that are different from the host address (this is relevant when we
# are running behind a NAT).
var switch = newStandardSwitch(some keys.seckey, hostAddress,
var switch = newStandardSwitch(some netKeys.seckey, hostAddress,
transportFlags = {ServerFlags.ReuseAddr},
secureManagers = [
SecureProtocol.Noise, # Only noise in ETH2!
@ -1257,16 +1338,10 @@ proc createEth2Node*(
result = Eth2Node.init(conf, enrForkId, switch, pubsub,
extIp, extTcpPort, extUdpPort,
keys.seckey.asEthKey, discovery = conf.discv5Enabled,
netKeys.seckey.asEthKey,
discovery = conf.discv5Enabled,
rng = rng)
proc getPersistenBootstrapAddr*(rng: var BrHmacDrbgContext, conf: BeaconNodeConf,
ip: ValidIpAddress, port: Port): EnrResult[enr.Record] =
let pair = getPersistentNetKeys(rng, conf)
return enr.Record.init(1'u64, # sequence number
pair.seckey.asEthKey,
some(ip), port, port, @[])
proc announcedENR*(node: Eth2Node): enr.Record =
doAssert node.discovery != nil, "The Eth2Node must be initialized"
node.discovery.localNode.record

2
vendor/nim-stew vendored

@ -1 +1 @@
Subproject commit 47ff49aae7fdcf9fb7b4af44c6ab2377f04c731e
Subproject commit 7a2b6dbddafeeba8c33f5587f08276f58d26deae