Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Prevent accidental change of network-key for active authorities #3852

Merged
merged 28 commits into from
Apr 15, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
e798480
Prevent accidental change of network-key for active authorities
alexggh Mar 27, 2024
f32aca3
Make tests happy
alexggh Mar 27, 2024
ec2c1cc
Fix tests
alexggh Mar 27, 2024
ffcabfd
Fixup node key as well
alexggh Mar 27, 2024
f17fa19
Make clippy happy
alexggh Mar 27, 2024
41a37f7
Fix typo
alexggh Mar 27, 2024
6b5d8ff
Update substrate/client/cli/src/commands/generate_node_key.rs
alexggh Mar 27, 2024
4771ad5
Update substrate/client/cli/src/commands/generate_node_key.rs
alexggh Mar 27, 2024
5a581ba
Update polkadot/.rpm/polkadot.spec
alexggh Mar 28, 2024
f7b25ed
Update polkadot/scripts/packaging/deb-maintainer-scripts/postinst
alexggh Mar 28, 2024
f0c0653
Generate key with PreExecStart
alexggh Mar 28, 2024
361255e
Address review feedback
alexggh Mar 28, 2024
f14821b
Trivial updates
alexggh Mar 29, 2024
4b96b9f
Remove PreStartExec
alexggh Mar 29, 2024
c15527b
Merge remote-tracking branch 'origin/master' into alexaggh/node_identity
alexggh Apr 3, 2024
35291ca
Address review feedback
alexggh Apr 4, 2024
c4b9658
Fixup unittest
alexggh Apr 4, 2024
369cc13
Cli update
alexggh Apr 5, 2024
93ac920
Fix unused
alexggh Apr 5, 2024
db5e1d7
Cargo fmt
alexggh Apr 9, 2024
89635bb
Merge remote-tracking branch 'origin/master' into alexaggh/node_identity
alexggh Apr 9, 2024
37534d2
Merge branch 'master' into alexaggh/node_identity
alexggh Apr 10, 2024
6b94cc7
Update substrate/client/cli/src/error.rs
alexggh Apr 11, 2024
9f13c16
Add prdoc
alexggh Apr 11, 2024
64e40e5
Merge branch 'master' into alexaggh/node_identity
alexggh Apr 11, 2024
d05cc30
Merge branch 'master' into alexaggh/node_identity
alexggh Apr 12, 2024
6630a1e
Merge branch 'master' into alexaggh/node_identity
bkchr Apr 12, 2024
a85f870
Merge branch 'master' into alexaggh/node_identity
alexggh Apr 15, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions polkadot/.rpm/polkadot.spec
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ getent passwd polkadot >/dev/null || \
if [ ! -e "$config_file" ]; then
echo 'POLKADOT_CLI_ARGS=""' > /etc/default/polkadot
fi
polkadot key generate-node-key --default-base-path
alexggh marked this conversation as resolved.
Show resolved Hide resolved
exit 0

%clean
Expand Down
1 change: 1 addition & 0 deletions polkadot/scripts/packaging/deb-maintainer-scripts/postinst
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,6 @@ if [ "$action" = "configure" ]; then
--ingroup polkadot polkadot
if [ ! -e "$config_file" ]; then
echo 'POLKADOT_CLI_ARGS=""' > /etc/default/polkadot
/usr/bin/polkadot key generate-node-key --default-base-path
fi
alexggh marked this conversation as resolved.
Show resolved Hide resolved
fi
8 changes: 7 additions & 1 deletion polkadot/tests/benchmark_block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,13 @@ async fn build_chain(runtime: &str, base_path: &Path) {
let mut cmd = Command::new(cargo_bin("polkadot"))
.stdout(process::Stdio::piped())
.stderr(process::Stdio::piped())
.args(["--chain", runtime, "--force-authoring", "--alice"])
.args([
"--chain",
runtime,
"--force-authoring",
"--alice",
"--unsafe-force-node-key-generation",
])
.arg("-d")
.arg(base_path)
.arg("--no-hardware-benchmarks")
Expand Down
110 changes: 102 additions & 8 deletions substrate/client/cli/src/commands/generate_node_key.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,10 @@

//! Implementation of the `generate-node-key` subcommand

use crate::Error;
use crate::{Error, SubstrateCli, DEFAULT_NETWORK_CONFIG_PATH, NODE_KEY_ED25519_FILE};
use clap::Parser;
use libp2p_identity::{ed25519, Keypair};
use sc_service::BasePath;
use std::{
fs,
io::{self, Write},
Expand All @@ -36,18 +37,28 @@ use std::{
pub struct GenerateNodeKeyCmd {
/// Name of file to save secret key to.
/// If not given, the secret key is printed to stdout.
#[arg(long)]
#[arg(long, conflicts_with_all = ["base_path", "default_base_path"])]
file: Option<PathBuf>,

/// The output is in raw binary format.
/// If not given, the output is written as an hex encoded string.
#[arg(long)]
bin: bool,

/// A directory where the key should be saved, if a key already
/// exists in the directory it won't be overwritten.
alexggh marked this conversation as resolved.
Show resolved Hide resolved
#[arg(long, conflicts_with_all = ["file", "default_base_path"])]
base_path: Option<PathBuf>,

/// Save the key in the default directory, if a key already
/// exists in the directory it won't be overwritten
alexggh marked this conversation as resolved.
Show resolved Hide resolved
#[arg(long, conflicts_with_all = ["base_path", "file"])]
default_base_path: bool,
}

impl GenerateNodeKeyCmd {
/// Run the command
pub fn run(&self) -> Result<(), Error> {
pub fn run<C: SubstrateCli>(&self, _cli: &C) -> Result<(), Error> {
let keypair = ed25519::Keypair::generate();

let secret = keypair.secret();
Expand All @@ -58,9 +69,32 @@ impl GenerateNodeKeyCmd {
array_bytes::bytes2hex("", secret).into_bytes()
};

match &self.file {
Some(file) => fs::write(file, file_data)?,
None => io::stdout().lock().write_all(&file_data)?,
match (&self.file, &self.base_path, self.default_base_path) {
(Some(file), None, false) => fs::write(file, file_data)?,
(None, Some(_), false) | (None, None, true) => {
let network_path = self
.base_path
.clone()
.unwrap_or_else(|| {
BasePath::from_project("", "", &C::executable_name()).path().to_path_buf()
alexggh marked this conversation as resolved.
Show resolved Hide resolved
})
.join(DEFAULT_NETWORK_CONFIG_PATH);

fs::create_dir_all(network_path.as_path())?;
let key_path = network_path.join(NODE_KEY_ED25519_FILE);
if key_path.exists() {
eprintln!("Skip generation, a key already exists in {:?}", key_path);
return Ok(());
alexggh marked this conversation as resolved.
Show resolved Hide resolved
} else {
eprintln!("Generating key in {:?}", key_path);
fs::write(key_path, file_data)?
}
},
(None, None, false) => io::stdout().lock().write_all(&file_data)?,
dmitry-markin marked this conversation as resolved.
Show resolved Hide resolved
(_, _, _) => {
// This should not happen, arguments are marked as mutually exclusive.
return Err(Error::Input("Mutually exclusive arguments provided".into()));
},
}

eprintln!("{}", Keypair::from(keypair).public().to_peer_id());
Expand All @@ -70,19 +104,79 @@ impl GenerateNodeKeyCmd {
}

#[cfg(test)]
mod tests {
pub mod tests {
use super::*;
use sc_service::{ChainSpec, ChainType, GenericChainSpec, NoExtension};
use std::io::Read;
use tempfile::Builder;

struct Cli;

impl SubstrateCli for Cli {
fn impl_name() -> String {
"test".into()
}

fn impl_version() -> String {
"2.0".into()
}

fn description() -> String {
"test".into()
}

fn support_url() -> String {
"test.test".into()
}

fn copyright_start_year() -> i32 {
2021
}

fn author() -> String {
"test".into()
}

fn load_spec(&self, _: &str) -> std::result::Result<Box<dyn ChainSpec>, String> {
Ok(Box::new(
GenericChainSpec::<()>::builder(Default::default(), NoExtension::None)
.with_name("test")
.with_id("test_id")
.with_chain_type(ChainType::Development)
.with_genesis_config_patch(Default::default())
.build(),
))
}
}

#[test]
fn generate_node_key() {
let mut file = Builder::new().prefix("keyfile").tempfile().unwrap();
let file_path = file.path().display().to_string();
let generate = GenerateNodeKeyCmd::parse_from(&["generate-node-key", "--file", &file_path]);
assert!(generate.run().is_ok());
assert!(generate.run(&Cli).is_ok());
let mut buf = String::new();
assert!(file.read_to_string(&mut buf).is_ok());
assert!(array_bytes::hex2bytes(&buf).is_ok());
}

#[test]
fn generate_node_key_base_path() {
let base_dir = Builder::new().prefix("keyfile").tempdir().unwrap();
let key_path =
base_dir.path().join(DEFAULT_NETWORK_CONFIG_PATH).join(NODE_KEY_ED25519_FILE);
let base_path = base_dir.path().display().to_string();
let generate =
GenerateNodeKeyCmd::parse_from(&["generate-node-key", "--base-path", &base_path]);
assert!(generate.run(&Cli).is_ok());
let buf = fs::read_to_string(key_path.as_path()).unwrap();
assert!(array_bytes::hex2bytes(&buf).is_ok());

assert!(generate.run(&Cli).is_ok());
let new_buf = fs::read_to_string(key_path).unwrap();
assert_eq!(
array_bytes::hex2bytes(&new_buf).unwrap(),
array_bytes::hex2bytes(&buf).unwrap()
);
}
}
44 changes: 43 additions & 1 deletion substrate/client/cli/src/commands/inspect_node_key.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,15 +79,57 @@ impl InspectNodeKeyCmd {

#[cfg(test)]
mod tests {
use crate::SubstrateCli;

use super::{super::GenerateNodeKeyCmd, *};
use sc_service::{ChainSpec, ChainType, GenericChainSpec, NoExtension};

struct Cli;

impl SubstrateCli for Cli {
fn impl_name() -> String {
"test".into()
}

fn impl_version() -> String {
"2.0".into()
}

fn description() -> String {
"test".into()
}

fn support_url() -> String {
"test.test".into()
}

fn copyright_start_year() -> i32 {
2021
}

fn author() -> String {
"test".into()
}

fn load_spec(&self, _: &str) -> std::result::Result<Box<dyn ChainSpec>, String> {
Ok(Box::new(
GenericChainSpec::<()>::builder(Default::default(), NoExtension::None)
.with_name("test")
.with_id("test_id")
.with_chain_type(ChainType::Development)
.with_genesis_config_patch(Default::default())
.build(),
))
}
}

#[test]
fn inspect_node_key() {
let path = tempfile::tempdir().unwrap().into_path().join("node-id").into_os_string();
let path = path.to_str().unwrap();
let cmd = GenerateNodeKeyCmd::parse_from(&["generate-node-key", "--file", path]);

assert!(cmd.run().is_ok());
assert!(cmd.run(&Cli).is_ok());

let cmd = InspectNodeKeyCmd::parse_from(&["inspect-node-key", "--file", path]);
assert!(cmd.run().is_ok());
Expand Down
2 changes: 1 addition & 1 deletion substrate/client/cli/src/commands/key.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ impl KeySubcommand {
/// run the key subcommands
pub fn run<C: SubstrateCli>(&self, cli: &C) -> Result<(), Error> {
match self {
KeySubcommand::GenerateNodeKey(cmd) => cmd.run(),
KeySubcommand::GenerateNodeKey(cmd) => cmd.run(cli),
KeySubcommand::Generate(cmd) => cmd.run(),
KeySubcommand::Inspect(cmd) => cmd.run(),
KeySubcommand::Insert(cmd) => cmd.run(cli),
Expand Down
4 changes: 3 additions & 1 deletion substrate/client/cli/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -428,8 +428,10 @@ pub trait CliConfiguration<DCV: DefaultConfigurationValues = ()>: Sized {
/// By default this is retrieved from `NodeKeyParams` if it is available. Otherwise its
/// `NodeKeyConfig::default()`.
fn node_key(&self, net_config_dir: &PathBuf) -> Result<NodeKeyConfig> {
let is_dev = self.is_dev()?;
let role = self.role(is_dev)?;
self.node_key_params()
.map(|x| x.node_key(net_config_dir))
.map(|x| x.node_key(net_config_dir, role, is_dev))
.unwrap_or_else(|| Ok(Default::default()))
}

Expand Down
12 changes: 12 additions & 0 deletions substrate/client/cli/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@

//! Initialization errors.

use std::path::PathBuf;

use sp_core::crypto;

/// Result type alias for the CLI.
Expand Down Expand Up @@ -78,6 +80,16 @@ pub enum Error {

#[error(transparent)]
GlobalLoggerError(#[from] sc_tracing::logging::Error),

#[error(
"Starting an authorithy without network key in {0}.
\n This is not a safe operation because the old identity still lives in the dht for 36 hours.
\n Because of it your node might suffer from not being properly connected to other nodes for validation purposes.
alexggh marked this conversation as resolved.
Show resolved Hide resolved
\n If it is the first time running your node you could use one of the following methods.
\n 1. Pass --unsafe-force-node-key-generation and make sure you remove it for subsequent node restarts
\n 2. Separetly generate the key with: polkadot key generate-node-key --file <YOUR_PATH_TO_NODE_KEY>"
)]
NetworkKeyNotFound(PathBuf),
}

impl From<&str> for Error {
Expand Down
Loading
Loading