Skip to content

Commit 2702ee4

Browse files
committed
Allow query_auth configuration reload
This adds support for reloading query_auth configuration. Current implementation: - Checks whether `auth_query` configuration is active, if so, it fetches (or refetches) hashes from servers and sets up pools. When it detects a password change, the pool is dropped and a new one is created, current established connections will be left connected. - When we go from 'auth_query' configured to unconfigured the reload logic detects it and drops Md5 hashes stored in connection pools, so cleartext passwords are used instead.
1 parent 880e649 commit 2702ee4

File tree

3 files changed

+66
-14
lines changed

3 files changed

+66
-14
lines changed

src/auth_passthrough.rs

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,7 @@ impl AuthPassthrough {
3838
pub fn from_config() -> Option<Self> {
3939
let config = get_config();
4040

41-
if config.general.auth_query_password.is_some()
42-
&& config.general.auth_query_user.is_some()
43-
&& config.general.auth_query_password.is_some()
44-
&& config.general.auth_query_database.is_some()
45-
{
41+
if config.is_auth_query_configured() {
4642
return Some(AuthPassthrough::new(
4743
config.general.auth_query.as_ref().unwrap(),
4844
config.general.auth_query_user.as_ref().unwrap(),
@@ -238,3 +234,14 @@ async fn send_auth_query(server: &mut Server, query: &str) -> Result<(), Error>
238234
}
239235
};
240236
}
237+
238+
#[cfg(test)]
239+
mod test {
240+
use super::*;
241+
242+
#[tokio::test]
243+
async fn test_from_config() {
244+
let auth_passthrough = AuthPassthrough::from_config();
245+
assert!(auth_passthrough.is_none());
246+
}
247+
}

src/config.rs

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use tokio::fs::File;
1212
use tokio::io::AsyncReadExt;
1313

1414
use crate::errors::Error;
15-
use crate::pool::{ClientServerMap, ConnectionPool};
15+
use crate::pool::{drop_auth_hashes, ClientServerMap, ConnectionPool};
1616
use crate::sharding::ShardingFunction;
1717
use crate::tls::{load_certs, load_keys};
1818

@@ -698,6 +698,13 @@ impl Config {
698698
}
699699
}
700700

701+
pub fn is_auth_query_configured(&self) -> bool {
702+
self.general.auth_query_password.is_some()
703+
&& self.general.auth_query_user.is_some()
704+
&& self.general.auth_query_password.is_some()
705+
&& self.general.auth_query_database.is_some()
706+
}
707+
701708
pub fn validate(&mut self) -> Result<(), Error> {
702709
// Validation for auth_query feature
703710
if self.general.auth_query.is_some()
@@ -815,10 +822,21 @@ pub async fn reload_config(client_server_map: ClientServerMap) -> Result<bool, E
815822
};
816823
let new_config = get_config();
817824

825+
if old_config.is_auth_query_configured() && !new_config.is_auth_query_configured() {
826+
// We should clean up auth_hashes so new client connections
827+
// dont use old hashes.
828+
info!("Auth query support deactivated. Deleting old auth hashes.");
829+
drop_auth_hashes()
830+
}
831+
818832
if old_config.pools != new_config.pools {
819833
info!("Pool configuration changed");
820834
ConnectionPool::from_config(client_server_map).await?;
821835
Ok(true)
836+
} else if new_config.is_auth_query_configured() {
837+
info!("Auth query configured. Reloading config also checks for password changes in pools.");
838+
ConnectionPool::from_config(client_server_map).await?;
839+
Ok(true)
822840
} else if old_config != new_config {
823841
Ok(true)
824842
} else {

src/pool.rs

Lines changed: 35 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -147,8 +147,25 @@ pub struct ConnectionPool {
147147
}
148148

149149
impl ConnectionPool {
150-
async fn refresh_auth_hashes(&mut self) {
151-
debug!("Auth hash refresh not implemented yet for unchanged pools.")
150+
async fn auth_hash_has_changed(&mut self) -> bool {
151+
if let Some(apt) = AuthPassthrough::from_config() {
152+
if let Some(shard) = self.addresses.first() {
153+
if let Some(address) = shard.first() {
154+
match apt.fetch_hash(address).await {
155+
Ok(md5) => {
156+
if let Some(auth_hash) = self.auth_hash.as_ref() {
157+
if auth_hash != &md5 {
158+
info!("Password hash changed for user: {:?}. Pool will be reloaded.", address.username);
159+
return true
160+
}
161+
}
162+
},
163+
Err(err) => warn!("Could not obtain password hash using auth_query while trying to reload config, ignoring. Error: {:?}", err),
164+
}
165+
}
166+
}
167+
}
168+
false
152169
}
153170

154171
/// Construct the connection pool from the configuration.
@@ -177,12 +194,13 @@ impl ConnectionPool {
177194
"[pool: {}][user: {}] has not changed",
178195
pool_name, user.username
179196
);
180-
pool.refresh_auth_hashes().await;
181-
new_pools.insert(
182-
PoolIdentifier::new(pool_name, &user.username),
183-
pool.clone(),
184-
);
185-
continue;
197+
if !(pool.auth_hash_has_changed().await) {
198+
new_pools.insert(
199+
PoolIdentifier::new(pool_name, &user.username),
200+
pool.clone(),
201+
);
202+
continue;
203+
}
186204
}
187205
None => (),
188206
}
@@ -766,6 +784,15 @@ pub fn get_pool(db: &str, user: &str) -> Option<ConnectionPool> {
766784
.cloned()
767785
}
768786

787+
pub fn drop_auth_hashes() {
788+
let mut new_pools = get_all_pools();
789+
790+
for (_id, pool) in new_pools.iter_mut() {
791+
pool.auth_hash = None;
792+
}
793+
POOLS.store(Arc::new(new_pools));
794+
}
795+
769796
/// Get a pointer to all configured pools.
770797
pub fn get_all_pools() -> HashMap<PoolIdentifier, ConnectionPool> {
771798
(*(*POOLS.load())).clone()

0 commit comments

Comments
 (0)