Skip to content

Commit 4ea5450

Browse files
authored
529 internode auth in swarm fix (#622)
* dnsrr in compose file * base resolving * configurable resolving * update changelog * better node request checker * async sleep & no resolved|unresolved distinction * fixes * changes * changes * changes * changes * changes * set inProgress
1 parent 49a30a5 commit 4ea5450

File tree

11 files changed

+368
-122
lines changed

11 files changed

+368
-122
lines changed

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ Bob versions changelog
77

88

99
#### Changed
10-
10+
- Hostname resolving in background tasks (#529)
1111

1212
#### Fixed
1313

bob-access/Cargo.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,8 @@ serde_yaml = "0.8"
2020
serde_json = "1.0"
2121
futures = "0.3"
2222
unicase = "2.6"
23+
24+
[dependencies.tokio]
25+
version = "1.21"
26+
default-features = false
27+
features = []

bob-access/src/authenticator/basic.rs

Lines changed: 103 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,65 +1,135 @@
1-
use std::{collections::HashMap, net::IpAddr};
1+
use std::{
2+
collections::HashMap,
3+
net::SocketAddr,
4+
sync::{RwLock, Arc},
5+
time::Duration,
6+
};
27

3-
use crate::{credentials::{Credentials, CredentialsKind}, AuthenticationType, error::Error, permissions::Permissions};
8+
use crate::{credentials::{DeclaredCredentials, DCredentialsResolveGuard, RequestCredentials, CredentialsKind}, AuthenticationType, error::Error, permissions::Permissions};
49

510
use super::{users_storage::UsersStorage, Authenticator};
611

712
use sha2::{Digest, Sha512};
813

14+
use tokio::net::lookup_host;
15+
16+
#[inline]
17+
async fn lookup(hostname: &str) -> Option<Vec<SocketAddr>> {
18+
lookup_host(hostname).await
19+
.ok()
20+
.map(|addr| addr.collect())
21+
}
22+
23+
type NodesCredentials = HashMap<String, DCredentialsResolveGuard>;
24+
925
#[derive(Debug, Default, Clone)]
1026
pub struct Basic<Storage: UsersStorage> {
1127
users_storage: Storage,
12-
nodes: HashMap<IpAddr, Vec<Credentials>>,
28+
nodes: Arc<RwLock<NodesCredentials>>,
29+
resolve_sleep_period_ms: u64,
1330
}
1431

1532
impl<Storage: UsersStorage> Basic<Storage> {
16-
pub fn new(users_storage: Storage) -> Self {
33+
pub fn new(users_storage: Storage, resolve_sleep_period_ms: u64) -> Self {
1734
Self {
1835
users_storage,
19-
nodes: HashMap::new(),
36+
nodes: Arc::new(RwLock::new(HashMap::new())),
37+
resolve_sleep_period_ms,
2038
}
2139
}
2240

41+
fn node_creds_ok(creds: &HashMap<String, DeclaredCredentials>) -> bool {
42+
creds.values()
43+
.all(|cred| cred.validate_internode())
44+
}
45+
2346
pub fn set_nodes_credentials(
2447
&mut self,
25-
nodes: HashMap<IpAddr, Vec<Credentials>>,
48+
mut nodes: HashMap<String, DeclaredCredentials>,
2649
) -> Result<(), Error> {
27-
if nodes
28-
.values()
29-
.all(|creds|
30-
creds
31-
.iter()
32-
.all(|cred|
33-
cred.ip().is_some() &&
34-
cred.kind().map(|k| k.is_internode()) == Some(true)))
35-
{
36-
self.nodes = nodes;
50+
if Self::node_creds_ok(&nodes) {
51+
let mut nodes_creds = self.nodes.write().expect("nodes credentials lock");
52+
for (nodename, cred) in nodes.drain() {
53+
let mut guard = DCredentialsResolveGuard::new(cred.clone(), self.resolve_sleep_period_ms);
54+
if cred.ip().is_empty() && cred.hostname().is_some() {
55+
guard.set_in_progress();
56+
nodes_creds.insert(nodename, guard);
57+
self.spawn_resolver(cred);
58+
} else {
59+
nodes_creds.insert(nodename, guard);
60+
}
61+
}
3762
Ok(())
3863
} else {
3964
let message = "nodes credentials missing ip or node name";
4065
Err(Error::CredentialsNotProvided(message.to_string()))
4166
}
4267
}
4368

44-
fn check_node_request(&self, node_name: &String, ip: Option<IpAddr>) -> Option<bool> {
45-
if self.nodes.is_empty() {
46-
warn!("nodes credentials not set");
69+
fn spawn_resolver(&self, cred: DeclaredCredentials) {
70+
tokio::spawn(Self::resolve_worker(self.nodes.clone(), cred, self.resolve_sleep_period_ms));
71+
}
72+
73+
async fn resolve_worker(nodes: Arc<RwLock<NodesCredentials>>, cred: DeclaredCredentials, sleep_period_ms: u64) {
74+
let hostname = cred.hostname().as_ref().expect("resolve worker without hostname");
75+
let mut addr: Option<Vec<SocketAddr>> = lookup(hostname).await;
76+
let mut cur_sleep_period_ms = 100;
77+
while addr.is_none() || (addr.is_some() && addr.as_ref().unwrap().len() == 0) {
78+
tokio::time::sleep(Duration::from_millis(cur_sleep_period_ms)).await;
79+
80+
addr = lookup(hostname).await;
81+
82+
cur_sleep_period_ms = sleep_period_ms.min(cur_sleep_period_ms * 2);
83+
}
84+
85+
let addr = addr.expect("somehow addr is none");
86+
if let CredentialsKind::InterNode(nodename) = cred.kind() {
87+
let mut nodes = nodes.write().expect("nodes credentials lock");
88+
if let Some(creds) = nodes.get_mut(nodename) {
89+
creds.set_resolved(addr);
90+
}
91+
} else {
92+
error!("resolved credentials are not internode");
93+
}
94+
}
95+
96+
fn process_auth_result(&self, nodename: &str, authenticated: bool) {
97+
let mut nodes = self.nodes.write().expect("nodes credentials lock");
98+
if let Some(node) = nodes.get_mut(nodename) {
99+
if node.update_resolve_state(authenticated) {
100+
node.set_in_progress();
101+
self.spawn_resolver(node.creds().clone());
102+
}
47103
}
48-
self.nodes
49-
.get(ip.as_ref()?)
50-
.map(|creds| {
51-
for cred in creds {
52-
if let Some(CredentialsKind::InterNode(other_name)) = cred.kind() {
53-
if node_name == other_name {
54-
return true;
55-
}
104+
}
105+
106+
fn check_node_request(&self, node_name: &String, ip: Option<SocketAddr>) -> bool {
107+
let mut authenticated = false;
108+
let mut needs_update = false;
109+
{
110+
if ip.is_none() {
111+
return false;
112+
}
113+
let ip = ip.unwrap().ip();
114+
let nodes = self.nodes.read().expect("nodes credentials lock");
115+
if let Some(guard) = nodes.get(node_name) {
116+
let cred = guard.creds();
117+
if let CredentialsKind::InterNode(other_name) = cred.kind() {
118+
debug_assert!(node_name == other_name);
119+
if cred.ip().iter().find(|cred_ip| cred_ip.ip() == ip).is_some() {
120+
authenticated = true;
56121
}
122+
needs_update = guard.needs_update(authenticated);
57123
}
58-
false
59-
})
124+
}
125+
}
126+
if needs_update {
127+
self.process_auth_result(node_name, authenticated);
128+
}
129+
authenticated
60130
}
61131

62-
fn check_credentials_common(&self, credentials: Credentials) -> Result<Permissions, Error> {
132+
fn check_credentials_common(&self, credentials: RequestCredentials) -> Result<Permissions, Error> {
63133
match credentials.kind() {
64134
Some(CredentialsKind::Basic { username, password }) => {
65135
debug!(
@@ -101,11 +171,11 @@ impl<Storage> Authenticator for Basic<Storage>
101171
where
102172
Storage: UsersStorage,
103173
{
104-
fn check_credentials_grpc(&self, credentials: Credentials) -> Result<Permissions, Error> {
174+
fn check_credentials_grpc(&self, credentials: RequestCredentials) -> Result<Permissions, Error> {
105175
debug!("check {:?}", credentials);
106176
match credentials.kind() {
107177
Some(CredentialsKind::InterNode(node_name)) => {
108-
if let Some(true) = self.check_node_request(node_name, credentials.ip()) {
178+
if self.check_node_request(node_name, credentials.ip()) {
109179
debug!("request from node: {:?}", credentials.ip());
110180
Ok(Permissions::all())
111181
} else {
@@ -116,7 +186,7 @@ where
116186
}
117187
}
118188

119-
fn check_credentials_rest(&self, credentials: Credentials) -> Result<Permissions, Error> {
189+
fn check_credentials_rest(&self, credentials: RequestCredentials) -> Result<Permissions, Error> {
120190
debug!("check {:?}", credentials);
121191
self.check_credentials_common(credentials)
122192
}

bob-access/src/authenticator/mod.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,13 @@ pub mod basic;
22
pub mod stub;
33
mod users_storage;
44

5-
use crate::{credentials::Credentials, error::Error, permissions::Permissions};
5+
use crate::{credentials::RequestCredentials, error::Error, permissions::Permissions};
66

77
pub use users_storage::{User, UsersMap};
88

99
pub trait Authenticator: Clone + Send + Sync + 'static {
10-
fn check_credentials_rest(&self, credentials: Credentials) -> Result<Permissions, Error>;
11-
fn check_credentials_grpc(&self, credentials: Credentials) -> Result<Permissions, Error>;
10+
fn check_credentials_rest(&self, credentials: RequestCredentials) -> Result<Permissions, Error>;
11+
fn check_credentials_grpc(&self, credentials: RequestCredentials) -> Result<Permissions, Error>;
1212
fn credentials_type() -> AuthenticationType;
1313
}
1414

bob-access/src/authenticator/stub.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::{credentials::Credentials, error::Error, permissions::Permissions};
1+
use crate::{credentials::RequestCredentials, error::Error, permissions::Permissions};
22

33
use super::{Authenticator, AuthenticationType};
44

@@ -12,11 +12,11 @@ impl Stub {
1212
}
1313

1414
impl Authenticator for Stub {
15-
fn check_credentials_grpc(&self, _: Credentials) -> Result<Permissions, Error> {
15+
fn check_credentials_grpc(&self, _: RequestCredentials) -> Result<Permissions, Error> {
1616
Ok(Permissions::all())
1717
}
1818

19-
fn check_credentials_rest(&self, _: Credentials) -> Result<Permissions, Error> {
19+
fn check_credentials_rest(&self, _: RequestCredentials) -> Result<Permissions, Error> {
2020
Ok(Permissions::all())
2121
}
2222

0 commit comments

Comments
 (0)