Skip to content

Commit b98b3a0

Browse files
committed
feat(zone_manager.rs): implement pagination for Cloudflare zone retrieval to handle large zone lists
refactor(zone_manager.rs): use async RwLock for ZONE_CACHE to improve concurrency fix(zone_manager.rs): correct find_best_match_from_cache to be async for consistency chore(.sh): add new repomix command for Dockerfile and fail2ban to streamline build process fix(jail.local): standardize bantime to 1h for all fail2ban rules for consistency and clarity perf(jail.local): increase maxretry for nginx-request-flood to reduce false positives
1 parent 5771eb2 commit b98b3a0

File tree

3 files changed

+76
-36
lines changed

3 files changed

+76
-36
lines changed

.sh

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,11 @@ repomix --no-file-summary --no-security-check \
3232
--include "src/Dockerfile,src/docker_scripts/**,src/webserver/nginx/nginx.conf,src/fail2ban/**" \
3333
--output "repopack.yml"
3434

35+
# dockerfile + fail2ban
36+
repomix --no-file-summary --no-security-check \
37+
--include "src/Dockerfile,src/docker_scripts/**,src/fail2ban/**" \
38+
--output "repopack.yml"
39+
3540
docker build -t dublok/proxma:latest -f src/Dockerfile .
3641
docker buildx build --platform linux/amd64,linux/arm64 -t dublok/proxma:latest -f src/Dockerfile .
3742
docker run -it --rm --name proxma -v /var/run/docker.sock:/var/run/docker.sock dublok/proxma:latest

src/dns/zone_manager.rs

Lines changed: 63 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,14 @@ use cloudflare::framework::client::async_api::Client as HttpApiClientAsync;
22
use cloudflare::framework::{Environment, response::ApiFailure};
33
use cloudflare::endpoints::zones::zone::{ListZones, ListZonesParams};
44
use lazy_static::lazy_static;
5+
use tokio::sync::RwLockWriteGuard;
56
use std::collections::HashMap;
6-
use std::sync::RwLock;
7+
use tokio::sync::RwLock;
78
use super::auth_manager::AuthManager;
9+
use std::sync::Arc;
810

911
lazy_static! {
10-
static ref ZONE_CACHE: RwLock<HashMap<String, String>> = RwLock::new(HashMap::new());
12+
static ref ZONE_CACHE: Arc<RwLock<HashMap<String, String>>> = Arc::new(RwLock::new(HashMap::new()));
1113
}
1214

1315
pub struct ZoneManager {}
@@ -24,41 +26,69 @@ impl ZoneManager {
2426
api_token: Option<String>,
2527
) -> Result<(), String> {
2628
let credentials = AuthManager::get_credentials(email, api_key, api_token)?;
27-
28-
// Corrected client instantiation
29+
2930
let client = HttpApiClientAsync::new(
3031
credentials,
3132
Default::default(),
3233
Environment::Production,
33-
).map_err(|e| format!("Cloudflare API client creation failed: {:?}", e))?;
34-
35-
let endpoint = ListZones {
36-
params: ListZonesParams::default(),
37-
};
38-
34+
)
35+
.map_err(|e| format!("Cloudflare API client creation failed: {:?}", e))?;
36+
37+
let mut page = 1;
38+
let per_page = 50;
39+
let mut fetched_all = false;
40+
41+
{
42+
let mut cache: RwLockWriteGuard<'_, HashMap<String, String>> = ZONE_CACHE.write().await;
43+
cache.clear();
44+
}
45+
3946
println!("# Retrieving zones from Cloudflare API...");
40-
41-
// Make the async request using the correct client & endpoints
42-
match client.request(&endpoint).await {
43-
Ok(api_success) => {
44-
let zones = api_success.result;
45-
let mut cache = ZONE_CACHE.write().map_err(|e| format!("Cache write lock error: {}", e))?;
46-
cache.clear();
47-
for zone in &zones {
48-
println!("# Zone found: {} ({})", zone.name, zone.id);
49-
cache.insert(zone.name.clone(), zone.id.clone());
47+
while !fetched_all {
48+
let params = ListZonesParams {
49+
page: Some(page),
50+
per_page: Some(per_page),
51+
..Default::default()
52+
};
53+
let endpoint = ListZones { params };
54+
55+
match client.request(&endpoint).await {
56+
Ok(api_success) => {
57+
let zones = api_success.result;
58+
59+
{
60+
let mut cache = ZONE_CACHE.write().await;
61+
for zone in &zones {
62+
println!("# Zone found: {} ({})", zone.name, zone.id);
63+
cache.insert(zone.name.clone(), zone.id.clone());
64+
}
65+
}
66+
67+
if let Some(result_info) = api_success.result_info {
68+
if let Some(total_pages) = result_info.get("total_pages").and_then(|v| v.as_u64()) {
69+
if page >= total_pages as u32 {
70+
fetched_all = true;
71+
} else {
72+
page += 1;
73+
}
74+
} else {
75+
fetched_all = true;
76+
}
77+
} else {
78+
fetched_all = true;
79+
}
80+
}
81+
Err(ApiFailure::Error(status, errors)) => {
82+
println!("# Cloudflare API returned error (HTTP {}): {:?}", status, errors);
83+
return Err(format!("Cloudflare API error ({}): {:?}", status, errors));
84+
}
85+
Err(ApiFailure::Invalid(e)) => {
86+
println!("# HTTP request failure: {:?}", e);
87+
return Err(format!("Cloudflare HTTP request failed: {}", e));
5088
}
51-
Ok(())
52-
}
53-
Err(ApiFailure::Error(status, errors)) => {
54-
println!("# Cloudflare API returned error (HTTP {}): {:?}", status, errors);
55-
Err(format!("Cloudflare API error ({}): {:?}", status, errors))
56-
}
57-
Err(ApiFailure::Invalid(e)) => {
58-
println!("# HTTP request failure: {:?}", e);
59-
Err(format!("Cloudflare HTTP request failed: {}", e))
6089
}
6190
}
91+
Ok(())
6292
}
6393

6494
pub async fn find_best_matching_zone(
@@ -69,20 +99,20 @@ impl ZoneManager {
6999
api_token: Option<String>,
70100
) -> Result<(Option<String>, Option<String>), String> {
71101
// Try to find from cache first
72-
if let Some((zone_name, zone_id)) = Self::find_best_match_from_cache(hostname) {
102+
if let Some((zone_name, zone_id)) = Self::find_best_match_from_cache(hostname).await {
73103
return Ok((Some(zone_name), Some(zone_id)));
74104
}
75105

76106
// If not found in cache, refresh zones and try again
77107
self.refresh_zones(email, api_key, api_token).await?;
78108

79109
// Return the best match after refresh
80-
Ok(Self::find_best_match_from_cache(hostname).map(|(name, id)| (Some(name), Some(id))).unwrap_or((None, None)))
110+
Ok(Self::find_best_match_from_cache(hostname).await.map(|(name, id)| (Some(name), Some(id))).unwrap_or((None, None)))
81111
}
82112

83113
/// Helper method to find the best matching zone from the cache
84-
fn find_best_match_from_cache(hostname: &str) -> Option<(String, String)> {
85-
let cache = ZONE_CACHE.read().ok()?;
114+
async fn find_best_match_from_cache(hostname: &str) -> Option<(String, String)> {
115+
let cache = ZONE_CACHE.read().await;
86116

87117
// Find all zone names that are part of the hostname (domain matching)
88118
// A proper match is when the hostname ends with the zone name,

src/fail2ban/jail.local

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ enabled = false
2222
enabled = true
2323
filter = nginx-http-auth
2424
logpath = /var/proxma/logs/nginx_access.log
25+
bantime = 1h
2526
port = http,https
2627

2728
[nginx-http-auth-proxma]
@@ -30,19 +31,22 @@ filter = nginx-http-auth-proxma
3031
logpath = /var/proxma/logs/nginx_access.log
3132
findtime = 1m
3233
maxretry = 3
34+
bantime = 1h
3335
port = http,https
3436
banaction = nginx-deny
3537

3638
[nginx-bad-request]
3739
enabled = true
3840
filter = nginx-bad-request
3941
logpath = /var/proxma/logs/nginx_access.log
42+
bantime = 1h
4043
port = http,https
4144

4245
[nginx-botsearch]
4346
enabled = true
4447
filter = nginx-botsearch
4548
logpath = /var/proxma/logs/nginx_access.log
49+
bantime = 1h
4650
port = http,https
4751

4852
[nginx-probing]
@@ -51,13 +55,14 @@ filter = nginx-probing
5155
logpath = /var/proxma/logs/nginx_access.log
5256
findtime = 2m
5357
maxretry = 5
58+
bantime = 1h
5459
port = http,https
5560

5661
[nginx-request-flood]
5762
enabled = true
5863
filter = nginx-request-flood
5964
logpath = /var/proxma/logs/nginx_access.log
60-
findtime = 60
61-
maxretry = 100
62-
bantime = 3600
65+
findtime = 60s
66+
maxretry = 500
67+
bantime = 1h
6368
port = http,https

0 commit comments

Comments
 (0)