peers_allow/peers_deny in grin.toml (#665)

* first pass at allow/deny lists for hard-coded peers (not just seeds)

* commit

* add peers_allow and peers_deny examples and comments to grin.toml

* fix build issue with simulnet tests

* fix p2p tests
This commit is contained in:
Antioch Peverell 2018-01-30 16:44:13 -05:00 committed by GitHub
parent 94bba20488
commit 5fd47fb875
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 102 additions and 29 deletions

View file

@ -31,7 +31,7 @@ db_root = ".grin"
#If seeding_type = List, the list of peers to connect to.
#
#seeds = ["192.168.0.1:8080","192.168.0.2:8080"]
#seeds = ["192.168.0.1:13414","192.168.0.2:13414"]
#The chain type, which defines the genesis block and the set of cuckoo
#parameters used for mining. Can be:
@ -57,6 +57,12 @@ capabilities = [7]
host = "0.0.0.0"
port = 13414
#hardcoded peer lists for allow/deny
#will *only* connect to peers in allow list
#peers_allow = ["192.168.0.1:13414", "192.168.0.2:13414"]
#will *never* connect to peers in deny list
#peers_deny = ["192.168.0.3:13414", "192.168.0.4:13414"]
#########################################
### LOGGING CONFIGURATION ###
#########################################

View file

@ -123,10 +123,12 @@ impl Server {
// work from the main run loop in p2p_server
let cpu_pool = CpuPool::new(1);
let p2p_config = config.p2p_config.clone();
let p2p_server = Arc::new(p2p::Server::new(
config.db_root.clone(),
config.capabilities,
config.p2p_config.unwrap(),
p2p_config,
net_adapter.clone(),
genesis.hash(),
cpu_pool.clone(),
@ -188,7 +190,7 @@ impl Server {
warn!(LOGGER, "Grin server started.");
Ok(Server {
config: config,
config: config.clone(),
evt_handle: evt_handle.clone(),
p2p: p2p_server,
chain: shared_chain,
@ -222,7 +224,7 @@ impl Server {
let currently_syncing = self.currently_syncing.clone();
let mut miner = miner::Miner::new(config.clone(), self.chain.clone(), self.tx_pool.clone());
miner.set_debug_output_id(format!("Port {}", self.config.p2p_config.unwrap().port));
miner.set_debug_output_id(format!("Port {}", self.config.p2p_config.port));
let _ = thread::Builder::new()
.name("miner".to_string())
.spawn(move || {

View file

@ -120,15 +120,17 @@ pub struct ServerConfig {
#[serde(default)]
pub seeding_type: Seeding,
/// TODO - move this into p2p_config?
/// The list of seed nodes, if using Seeding as a seed type
pub seeds: Option<Vec<String>>,
/// TODO - move this into p2p_config?
/// Capabilities expose by this node, also conditions which other peers this
/// node will have an affinity toward when connection.
pub capabilities: p2p::Capabilities,
/// Configuration for the peer-to-peer server
pub p2p_config: Option<p2p::P2PConfig>,
pub p2p_config: p2p::P2PConfig,
/// Configuration for the mining daemon
pub mining_config: Option<pow::types::MinerConfig>,
@ -150,7 +152,7 @@ impl Default for ServerConfig {
capabilities: p2p::FULL_NODE,
seeding_type: Seeding::default(),
seeds: None,
p2p_config: Some(p2p::P2PConfig::default()),
p2p_config: p2p::P2PConfig::default(),
mining_config: Some(pow::types::MinerConfig::default()),
chain_type: ChainTypes::default(),
pool_config: pool::PoolConfig::default(),
@ -159,7 +161,7 @@ impl Default for ServerConfig {
}
}
/// Thread-safe container to return all sever related stats that other
/// Thread-safe container to return all server related stats that other
/// consumers might be interested in, such as test results
///
///

View file

@ -205,10 +205,10 @@ impl LocalServerContainer {
grin::ServerConfig {
api_http_addr: api_addr,
db_root: format!("{}/.grin", self.working_dir),
p2p_config: Some(p2p::P2PConfig {
p2p_config: p2p::P2PConfig {
port: self.config.p2p_server_port,
..p2p::P2PConfig::default()
}),
},
seeds: Some(seeds),
seeding_type: seeding_type,
chain_type: core::global::ChainTypes::AutomatedTesting,

View file

@ -216,10 +216,10 @@ fn a_simulate_block_propagation() {
grin::ServerConfig {
api_http_addr: format!("127.0.0.1:{}", 19000 + n),
db_root: format!("target/{}/grin-prop-{}", test_name_dir, n),
p2p_config: Some(p2p::P2PConfig {
p2p_config: p2p::P2PConfig {
port: 18000 + n,
..p2p::P2PConfig::default()
}),
},
seeding_type: grin::Seeding::List,
seeds: Some(vec!["127.0.0.1:18000".to_string()]),
chain_type: core::global::ChainTypes::AutomatedTesting,
@ -279,10 +279,10 @@ fn simulate_full_sync() {
let config = grin::ServerConfig {
api_http_addr: format!("127.0.0.1:{}", 19000 + n),
db_root: format!("target/{}/grin-sync-{}", test_name_dir, n),
p2p_config: Some(p2p::P2PConfig {
p2p_config: p2p::P2PConfig {
port: 11000 + n,
..p2p::P2PConfig::default()
}),
},
seeding_type: grin::Seeding::List,
seeds: Some(vec!["127.0.0.1:11000".to_string()]),
chain_type: core::global::ChainTypes::AutomatedTesting,

View file

@ -24,6 +24,7 @@ use tokio_core::net::TcpStream;
use core::core::target::Difficulty;
use core::core::hash::Hash;
use msg::*;
use peer::Peer;
use types::*;
use protocol::ProtocolV1;
use util::LOGGER;
@ -39,6 +40,7 @@ pub struct Handshake {
/// The genesis block header of the chain seen by this node.
/// We only want to connect to other nodes seeing the same chain (forks are ok).
genesis: Hash,
config: P2PConfig,
}
unsafe impl Sync for Handshake {}
@ -46,10 +48,11 @@ unsafe impl Send for Handshake {}
impl Handshake {
/// Creates a new handshake handler
pub fn new(genesis: Hash) -> Handshake {
pub fn new(genesis: Hash, config: P2PConfig) -> Handshake {
Handshake {
nonces: Arc::new(RwLock::new(VecDeque::with_capacity(NONCES_CAP))),
genesis: genesis,
genesis,
config,
}
}
@ -80,7 +83,8 @@ impl Handshake {
user_agent: USER_AGENT.to_string(),
};
let genesis = self.genesis;
let genesis = self.genesis.clone();
let config = self.config.clone();
// write and read the handshake response
Box::new(
@ -106,6 +110,12 @@ impl Handshake {
total_difficulty: shake.total_difficulty,
};
// If denied then we want to close the connection
// (without providing our peer with any details why).
if Peer::is_denied(config, peer_info.addr) {
return Err(Error::ConnectionClose);
}
debug!(
LOGGER,
"Connected! Cumulative {} offered from {:?} {:?} {:?}",
@ -131,6 +141,8 @@ impl Handshake {
) -> Box<Future<Item = (TcpStream, ProtocolV1, PeerInfo), Error = Error>> {
let nonces = self.nonces.clone();
let genesis = self.genesis.clone();
let config = self.config.clone();
Box::new(
read_msg::<Hand>(conn)
.and_then(move |(conn, hand)| {
@ -160,6 +172,15 @@ impl Handshake {
version: hand.version,
total_difficulty: hand.total_difficulty,
};
// At this point we know the published ip and port of the peer
// so check if we are configured to explicitly allow or deny it.
// If denied then we want to close the connection
// (without providing our peer with any details why).
if Peer::is_denied(config, peer_info.addr) {
return Err(Error::ConnectionClose);
}
// send our reply with our info
let shake = Shake {
version: PROTOCOL_VERSION,

View file

@ -118,6 +118,28 @@ impl Peer {
}))
}
pub fn is_denied(config: P2PConfig, peer_addr: SocketAddr) -> bool {
let peer = format!("{}:{}", peer_addr.ip(), peer_addr.port());
if let Some(ref denied) = config.peers_deny {
if denied.contains(&peer) {
debug!(LOGGER, "checking peer allowed/denied: {:?} explicitly denied", peer_addr);
return true;
}
}
if let Some(ref allowed) = config.peers_allow {
if allowed.contains(&peer) {
debug!(LOGGER, "checking peer allowed/denied: {:?} explicitly allowed", peer_addr);
return false;
} else {
debug!(LOGGER, "checking peer allowed/denied: {:?} not explicitly allowed, denying", peer_addr);
return true;
}
}
// default to allowing peer connection if we do not explicitly allow or deny the peer
false
}
/// Whether this peer is still connected.
pub fn is_connected(&self) -> bool {
let state = self.state.read().unwrap();

View file

@ -33,17 +33,19 @@ pub struct Peers {
pub adapter: Arc<ChainAdapter>,
store: Arc<PeerStore>,
peers: Arc<RwLock<HashMap<SocketAddr, Arc<RwLock<Peer>>>>>,
config: P2PConfig,
}
unsafe impl Send for Peers {}
unsafe impl Sync for Peers {}
impl Peers {
pub fn new(store: PeerStore, adapter: Arc<ChainAdapter>) -> Peers {
pub fn new(store: PeerStore, adapter: Arc<ChainAdapter>, config: P2PConfig) -> Peers {
Peers {
adapter: adapter,
adapter,
store: Arc::new(store),
peers: Arc::new(RwLock::new(HashMap::new())),
config,
}
}

View file

@ -94,10 +94,10 @@ impl Server {
pool: CpuPool,
) -> Result<Server, Error> {
Ok(Server {
config: config,
config: config.clone(),
capabilities: capab,
handshake: Arc::new(Handshake::new(genesis)),
peers: Peers::new(PeerStore::new(db_root)?, adapter),
handshake: Arc::new(Handshake::new(genesis, config.clone())),
peers: Peers::new(PeerStore::new(db_root)?, adapter, config.clone()),
pool: pool,
stop: RefCell::new(None),
})
@ -127,7 +127,7 @@ impl Server {
let pool = pool.clone();
future::ok(conn).and_then(move |conn| {
// Refuse banned peers connection
// Refuse connection from banned peers
if let Ok(peer_addr) = conn.peer_addr() {
if peers.is_banned(peer_addr) {
debug!(LOGGER, "Peer {} banned, refusing connection.", peer_addr);
@ -139,7 +139,6 @@ impl Server {
}
Ok(conn)
}).and_then(move |conn| {
let total_diff = peers2.total_difficulty();
// accept the peer and add it to the server map
@ -217,6 +216,11 @@ impl Server {
h: reactor::Handle,
) -> Box<Future<Item = Option<Arc<RwLock<Peer>>>, Error = Error>> {
if Peer::is_denied(self.config.clone(), addr) {
debug!(LOGGER, "Peer {} denied, not connecting.", addr);
return Box::new(future::err(Error::ConnectionClose));
}
if let Some(p) = self.peers.get_connected_peer(&addr) {
// if we're already connected to the addr, just return the peer
debug!(LOGGER, "connect_peer: already connected {}", addr);

View file

@ -79,10 +79,14 @@ impl From<TimerError> for Error {
}
/// Configuration for the peer-to-peer server.
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct P2PConfig {
pub host: IpAddr,
pub port: u16,
pub peers_allow: Option<Vec<String>>,
pub peers_deny: Option<Vec<String>>,
}
/// Default address for peer-to-peer connections.
@ -92,6 +96,8 @@ impl Default for P2PConfig {
P2PConfig {
host: ipaddr,
port: 13414,
peers_allow: None,
peers_deny: None,
}
}
}

View file

@ -48,14 +48,16 @@ fn peer_handshake() {
let handle = evtlp.handle();
let p2p_conf = p2p::P2PConfig {
host: "0.0.0.0".parse().unwrap(),
port: open_port()
port: open_port(),
peers_allow: None,
peers_deny: None,
};
let net_adapter = Arc::new(p2p::DummyAdapter {});
let pool = CpuPool::new(1);
let server = p2p::Server::new(
".grin".to_owned(),
p2p::UNKNOWN,
p2p_conf,
p2p_conf.clone(),
net_adapter.clone(),
Hash::from_vec(vec![]),
pool.clone(),
@ -71,7 +73,8 @@ fn peer_handshake() {
timeout
.from_err()
.and_then(move |_| {
let addr = SocketAddr::new(p2p_conf.host, p2p_conf.port);
let conf = p2p_conf.clone();
let addr = SocketAddr::new(conf.host, conf.port);
let socket =
TcpStream::connect(&addr, &phandle).map_err(|e| p2p::Error::Connection(e));
socket
@ -81,7 +84,12 @@ fn peer_handshake() {
p2p::UNKNOWN,
Difficulty::one(),
my_addr,
Arc::new(p2p::handshake::Handshake::new(Hash::from_vec(vec![]))),
Arc::new(
p2p::handshake::Handshake::new(
Hash::from_vec(vec![]),
p2p_conf.clone(),
),
),
net_adapter.clone(),
)
})

View file

@ -320,7 +320,7 @@ fn server_command(server_args: &ArgMatches, global_config: GlobalConfig) {
let mut server_config = global_config.members.unwrap().server;
if let Some(port) = server_args.value_of("port") {
server_config.p2p_config.as_mut().unwrap().port = port.parse().unwrap();
server_config.p2p_config.port = port.parse().unwrap();
}
if let Some(api_port) = server_args.value_of("api_port") {