Skip to content

Commit 810cfb7

Browse files
committed
add support for parsing IP aliases
1 parent 929aced commit 810cfb7

File tree

11 files changed

+113
-74
lines changed

11 files changed

+113
-74
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ actix-cors = "0.7.1"
4848
rand = "0.9.1"
4949
env_logger = "0.11.8"
5050
async-trait = "0.1.88"
51+
ipnetwork = "0.21.1"
5152

5253
[build-dependencies]
5354
tonic-build = "0.13.1"

src/db/datastore_wrapper.rs

Lines changed: 31 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use crate::db::installation_code::InstallationCode;
55
use crate::db::tables::DbTable;
66
use crate::firewall::firewall::Firewall;
77
use crate::proto::appguard::{AppGuardIpInfo, Log};
8+
use ipnetwork::IpNetwork;
89
use nullnet_libdatastore::{
910
AdvanceFilter, BatchCreateBody, BatchCreateRequest, BatchDeleteBody, BatchDeleteRequest,
1011
BatchUpdateBody, BatchUpdateRequest, CreateBody, CreateParams, CreateRequest, DeleteQuery,
@@ -16,6 +17,8 @@ use nullnet_libdatastore::{DatastoreClient, DatastoreConfig};
1617
use nullnet_liberror::{location, Error, ErrorHandler, Location};
1718
use serde_json::{json, Value};
1819
use std::collections::HashMap;
20+
use std::net::IpAddr;
21+
use std::str::FromStr;
1922

2023
#[derive(Debug, Clone)]
2124
pub struct DatastoreWrapper {
@@ -1059,12 +1062,12 @@ impl DatastoreWrapper {
10591062
Ok(count)
10601063
}
10611064

1062-
pub(crate) async fn get_ip_alias(
1065+
pub(crate) async fn get_ip_aliases(
10631066
&mut self,
10641067
token: String,
10651068
name: &str,
1066-
) -> Result<Vec<String>, Error> {
1067-
let table = "device_aliases";
1069+
) -> Result<Vec<IpNetwork>, Error> {
1070+
let table = DbTable::Alias.to_str();
10681071

10691072
let filter = AdvanceFilter {
10701073
r#type: String::from("criteria"),
@@ -1081,13 +1084,15 @@ impl DatastoreWrapper {
10811084
r#type: String::from("root"),
10821085
}),
10831086
body: Some(GetByFilterBody {
1084-
pluck: vec!["value".to_string()],
1087+
pluck: vec!["ip".to_string(), "prefix".to_string()],
10851088
advance_filters: vec![filter],
10861089
order_by: String::new(),
10871090
limit: 1,
10881091
offset: 0,
10891092
order_direction: String::new(),
1090-
joins: vec![],
1093+
joins: vec![
1094+
// TODO: join with ip_aliases table
1095+
],
10911096
multiple_sort: vec![],
10921097
pluck_object: HashMap::default(),
10931098
date_format: String::new(),
@@ -1102,29 +1107,34 @@ impl DatastoreWrapper {
11021107
Self::internal_ip_alias_parse_response_data(result)
11031108
}
11041109

1105-
fn internal_ip_alias_parse_response_data(data: String) -> Result<Vec<String>, Error> {
1110+
fn internal_ip_alias_parse_response_data(data: String) -> Result<Vec<IpNetwork>, Error> {
11061111
let array_val = serde_json::from_str::<serde_json::Value>(&data).handle_err(location!())?;
11071112
let array = array_val
11081113
.as_array()
11091114
.ok_or("Failed to parse response")
11101115
.handle_err(location!())?;
11111116

1112-
let i = array
1113-
.first()
1114-
.ok_or("No alias found")
1115-
.handle_err(location!())?;
1117+
let mut ret_val = Vec::new();
11161118

1117-
let Some(map) = i.as_object() else {
1118-
return Err("Failed to parse response").handle_err(location!());
1119-
};
1120-
let Some(alias_val) = map.get("value") else {
1121-
return Err("Failed to parse response").handle_err(location!());
1122-
};
1123-
let Some(alias) = alias_val.as_str() else {
1124-
return Err("Failed to parse response").handle_err(location!());
1125-
};
1126-
1127-
let ret_val = alias.split_whitespace().map(|s| s.to_string()).collect();
1119+
for i in array {
1120+
let Some(map) = i.as_object() else { continue };
1121+
let Some(ip_val) = map.get("ip") else {
1122+
continue;
1123+
};
1124+
let Some(ip_addr) = ip_val.as_str().and_then(|ip| IpAddr::from_str(ip).ok()) else {
1125+
continue;
1126+
};
1127+
let Some(prefix_val) = map.get("prefix") else {
1128+
continue;
1129+
};
1130+
let Some(prefix) = prefix_val.as_u64().and_then(|int| u8::try_from(int).ok()) else {
1131+
continue;
1132+
};
1133+
let Ok(ip_network) = IpNetwork::new(ip_addr, prefix) else {
1134+
continue;
1135+
};
1136+
ret_val.push(ip_network);
1137+
}
11281138

11291139
Ok(ret_val)
11301140
}

src/db/entries.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ impl DbEntry {
7070
log::info!("Firewall inserted in datastore");
7171
}
7272
DbEntry::DeniedIp((_, denied_ip, _)) => {
73+
// todo: change this to a custom query to aliases
7374
let _ = ds.insert(self, token.as_str()).await?;
7475
log::info!(
7576
"Denied IP inserted in datastore: {} {:?}",
@@ -118,7 +119,7 @@ impl DbEntry {
118119
DbEntry::TcpConnection(_) => DbTable::TcpConnection,
119120
// DbEntry::Blacklist(_) => DbTable::Blacklist,
120121
DbEntry::Firewall(_) => DbTable::Firewall,
121-
DbEntry::DeniedIp(_) => DbTable::DeniedIp,
122+
DbEntry::DeniedIp(_) => DbTable::Alias,
122123
DbEntry::Config(_) => DbTable::Config,
123124
}
124125
}
@@ -190,7 +191,7 @@ impl EntryIds {
190191
DbTable::IpInfo
191192
// | DbTable::Blacklist
192193
| DbTable::Firewall
193-
| DbTable::DeniedIp
194+
| DbTable::Alias
194195
| DbTable::Config => return Err("Not applicable").handle_err(location!()),
195196
}
196197
.lock()

src/db/tables.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ pub enum DbTable {
88
SmtpResponse,
99
// Blacklist,
1010
Firewall,
11-
DeniedIp,
11+
Alias,
1212
Config,
1313
}
1414

@@ -23,7 +23,7 @@ impl DbTable {
2323
DbTable::SmtpResponse => "smtp_responses",
2424
// DbTable::Blacklist => "ip_blacklists",
2525
DbTable::Firewall => "app_firewalls",
26-
DbTable::DeniedIp => "app_denied_ips",
26+
DbTable::Alias => "aliases",
2727
DbTable::Config => "appguard_configs",
2828
}
2929
}

src/firewall/items/http_request.rs

Lines changed: 15 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ use crate::helpers::get_header;
88
use crate::proto::appguard::{AppGuardHttpRequest, AppGuardTcpInfo};
99
use rpn_predicate_interpreter::PredicateEvaluator;
1010
use serde::{Deserialize, Serialize};
11-
use std::borrow::Cow;
1211

1312
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
1413
#[allow(clippy::enum_variant_names)]
@@ -43,30 +42,30 @@ impl HttpRequestField {
4342
item: &'a AppGuardHttpRequest,
4443
) -> Option<FirewallCompareType<'a>> {
4544
match self {
46-
HttpRequestField::HttpRequestUrl(v) => Some(FirewallCompareType::String((
47-
&item.original_url,
48-
Cow::Borrowed(v),
49-
))),
50-
HttpRequestField::HttpRequestMethod(v) => Some(FirewallCompareType::String((
51-
&item.method,
52-
Cow::Borrowed(v),
53-
))),
54-
HttpRequestField::HttpRequestQuery(HeaderVal(k, v)) => get_header(&item.query, k)
55-
.map(|query| FirewallCompareType::String((query, Cow::Borrowed(v)))),
45+
HttpRequestField::HttpRequestUrl(v) => {
46+
Some(FirewallCompareType::String((&item.original_url, v)))
47+
}
48+
HttpRequestField::HttpRequestMethod(v) => {
49+
Some(FirewallCompareType::String((&item.method, v)))
50+
}
51+
HttpRequestField::HttpRequestQuery(HeaderVal(k, v)) => {
52+
get_header(&item.query, k).map(|query| FirewallCompareType::String((query, v)))
53+
}
5654
HttpRequestField::HttpRequestCookie(v) => get_header(&item.headers, "Cookie")
57-
.map(|cookie| FirewallCompareType::String((cookie, Cow::Borrowed(v)))),
58-
HttpRequestField::HttpRequestHeader(HeaderVal(k, v)) => get_header(&item.headers, k)
59-
.map(|header| FirewallCompareType::String((header, Cow::Borrowed(v)))),
55+
.map(|cookie| FirewallCompareType::String((cookie, v))),
56+
HttpRequestField::HttpRequestHeader(HeaderVal(k, v)) => {
57+
get_header(&item.headers, k).map(|header| FirewallCompareType::String((header, v)))
58+
}
6059
HttpRequestField::HttpRequestBody(v) => item
6160
.body
6261
.as_ref()
63-
.map(|body| FirewallCompareType::String((body, Cow::Borrowed(v)))),
62+
.map(|body| FirewallCompareType::String((body, v))),
6463
HttpRequestField::HttpRequestBodyLen(v) => item
6564
.body
6665
.as_ref()
6766
.map(|body| FirewallCompareType::Usize((body.len(), v))),
6867
HttpRequestField::HttpRequestUserAgent(v) => get_header(&item.headers, "User-Agent")
69-
.map(|user_agent| FirewallCompareType::String((user_agent, Cow::Borrowed(v)))),
68+
.map(|user_agent| FirewallCompareType::String((user_agent, v))),
7069
}
7170
}
7271
}

src/firewall/items/http_response.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ use crate::helpers::get_header;
88
use crate::proto::appguard::{AppGuardHttpResponse, AppGuardTcpInfo};
99
use rpn_predicate_interpreter::PredicateEvaluator;
1010
use serde::{Deserialize, Serialize};
11-
use std::borrow::Cow;
1211

1312
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
1413
#[allow(clippy::enum_variant_names)]
@@ -46,8 +45,9 @@ impl HttpResponseField {
4645
None
4746
}
4847
}
49-
HttpResponseField::HttpResponseHeader(HeaderVal(k, v)) => get_header(&item.headers, k)
50-
.map(|header| FirewallCompareType::String((header, Cow::Borrowed(v)))),
48+
HttpResponseField::HttpResponseHeader(HeaderVal(k, v)) => {
49+
get_header(&item.headers, k).map(|header| FirewallCompareType::String((header, v)))
50+
}
5151
}
5252
}
5353
}

src/firewall/items/ip_info.rs

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ use crate::firewall::rules::{FirewallCompareType, FirewallRuleField, FirewallRul
33
use crate::proto::appguard::AppGuardIpInfo;
44
use rpn_predicate_interpreter::PredicateEvaluator;
55
use serde::{Deserialize, Serialize};
6-
use std::borrow::Cow;
76

87
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
98
#[serde(rename_all = "snake_case")]
@@ -40,35 +39,35 @@ impl IpInfoField {
4039
IpInfoField::Country(v) => item
4140
.country
4241
.as_ref()
43-
.map(|country| FirewallCompareType::String((country, Cow::Borrowed(v)))),
42+
.map(|country| FirewallCompareType::String((country, v))),
4443
IpInfoField::Asn(v) => item
4544
.asn
4645
.as_ref()
47-
.map(|asn| FirewallCompareType::String((asn, Cow::Borrowed(v)))),
46+
.map(|asn| FirewallCompareType::String((asn, v))),
4847
IpInfoField::Org(v) => item
4948
.org
5049
.as_ref()
51-
.map(|org| FirewallCompareType::String((org, Cow::Borrowed(v)))),
50+
.map(|org| FirewallCompareType::String((org, v))),
5251
IpInfoField::Continent(v) => item
5352
.continent_code
5453
.as_ref()
55-
.map(|continent| FirewallCompareType::String((continent, Cow::Borrowed(v)))),
54+
.map(|continent| FirewallCompareType::String((continent, v))),
5655
IpInfoField::City(v) => item
5756
.city
5857
.as_ref()
59-
.map(|city| FirewallCompareType::String((city, Cow::Borrowed(v)))),
58+
.map(|city| FirewallCompareType::String((city, v))),
6059
IpInfoField::Region(v) => item
6160
.region
6261
.as_ref()
63-
.map(|region| FirewallCompareType::String((region, Cow::Borrowed(v)))),
62+
.map(|region| FirewallCompareType::String((region, v))),
6463
IpInfoField::Postal(v) => item
6564
.postal
6665
.as_ref()
67-
.map(|postal| FirewallCompareType::String((postal, Cow::Borrowed(v)))),
66+
.map(|postal| FirewallCompareType::String((postal, v))),
6867
IpInfoField::Timezone(v) => item
6968
.timezone
7069
.as_ref()
71-
.map(|timezone| FirewallCompareType::String((timezone, Cow::Borrowed(v)))),
70+
.map(|timezone| FirewallCompareType::String((timezone, v))),
7271
}
7372
}
7473
}

src/firewall/items/smtp_request.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ use crate::helpers::get_header;
88
use crate::proto::appguard::{AppGuardSmtpRequest, AppGuardTcpInfo};
99
use rpn_predicate_interpreter::PredicateEvaluator;
1010
use serde::{Deserialize, Serialize};
11-
use std::borrow::Cow;
1211

1312
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
1413
#[allow(clippy::enum_variant_names)]
@@ -35,14 +34,15 @@ impl SmtpRequestField {
3534
item: &'a AppGuardSmtpRequest,
3635
) -> Option<FirewallCompareType<'a>> {
3736
match self {
38-
SmtpRequestField::SmtpRequestHeader(HeaderVal(k, v)) => get_header(&item.headers, k)
39-
.map(|header| FirewallCompareType::String((header, Cow::Borrowed(v)))),
37+
SmtpRequestField::SmtpRequestHeader(HeaderVal(k, v)) => {
38+
get_header(&item.headers, k).map(|header| FirewallCompareType::String((header, v)))
39+
}
4040
SmtpRequestField::SmtpRequestUserAgent(v) => get_header(&item.headers, "User-Agent")
41-
.map(|user_agent| FirewallCompareType::String((user_agent, Cow::Borrowed(v)))),
41+
.map(|user_agent| FirewallCompareType::String((user_agent, v))),
4242
SmtpRequestField::SmtpRequestBody(v) => item
4343
.body
4444
.as_ref()
45-
.map(|body| FirewallCompareType::String((body, Cow::Borrowed(v)))),
45+
.map(|body| FirewallCompareType::String((body, v))),
4646
SmtpRequestField::SmtpRequestBodyLen(l) => item
4747
.body
4848
.as_ref()

src/firewall/items/tcp_connection.rs

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,11 @@ use crate::firewall::rules::{
33
FirewallCompareType, FirewallRuleDirection, FirewallRuleField, FirewallRuleWithDirection,
44
};
55
use crate::proto::appguard::AppGuardTcpConnection;
6+
use ipnetwork::IpNetwork;
67
use rpn_predicate_interpreter::PredicateEvaluator;
78
use serde::{Deserialize, Serialize};
8-
use std::borrow::Cow;
9+
use std::net::IpAddr;
10+
use std::str::FromStr;
911

1012
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
1113
#[serde(rename_all = "snake_case")]
@@ -40,8 +42,8 @@ impl TcpConnectionField {
4042
FirewallRuleDirection::In => item.source_ip.as_ref(),
4143
FirewallRuleDirection::Out => item.destination_ip.as_ref(),
4244
};
43-
if let Some(ip) = ip_opt {
44-
Some(FirewallCompareType::String((ip, a.to_ips(context).await?)))
45+
if let Some(ip) = ip_opt.and_then(|ip| IpAddr::from_str(ip).ok()) {
46+
Some(FirewallCompareType::Ip((ip, a.to_ips(context).await?)))
4547
} else {
4648
None
4749
}
@@ -51,8 +53,8 @@ impl TcpConnectionField {
5153
FirewallRuleDirection::In => item.destination_ip.as_ref(),
5254
FirewallRuleDirection::Out => item.source_ip.as_ref(),
5355
};
54-
if let Some(ip) = ip_opt {
55-
Some(FirewallCompareType::String((ip, a.to_ips(context).await?)))
56+
if let Some(ip) = ip_opt.and_then(|ip| IpAddr::from_str(ip).ok()) {
57+
Some(FirewallCompareType::Ip((ip, a.to_ips(context).await?)))
5658
} else {
5759
None
5860
}
@@ -67,10 +69,9 @@ impl TcpConnectionField {
6769
FirewallRuleDirection::Out => item.source_port,
6870
}
6971
.map(|p| FirewallCompareType::U32((p, v))),
70-
TcpConnectionField::Protocol(v) => Some(FirewallCompareType::String((
71-
&item.protocol,
72-
Cow::Borrowed(v),
73-
))),
72+
TcpConnectionField::Protocol(v) => {
73+
Some(FirewallCompareType::String((&item.protocol, v)))
74+
}
7475
}
7576
}
7677
}
@@ -112,19 +113,26 @@ pub enum IpAlias {
112113
}
113114

114115
impl IpAlias {
115-
async fn to_ips(&self, context: &AppContext) -> Option<Cow<'_, [String]>> {
116+
async fn to_ips(&self, context: &AppContext) -> Option<Vec<IpNetwork>> {
116117
match self {
117118
IpAlias::Name(name) => {
118119
let token = context.root_token_provider.get().await.ok()?.jwt.clone();
119120
context
120121
.datastore
121122
.clone()
122-
.get_ip_alias(token, name)
123+
.get_ip_aliases(token, name)
123124
.await
124125
.ok()
125-
.map(Cow::Owned)
126126
}
127-
IpAlias::Addresses(addresses) => Some(Cow::Borrowed(addresses)),
127+
IpAlias::Addresses(addresses) => {
128+
let mut ipnetworks = Vec::new();
129+
for address in addresses {
130+
if let Ok(cidr) = IpNetwork::from_str(address) {
131+
ipnetworks.push(cidr);
132+
}
133+
}
134+
Some(ipnetworks)
135+
}
128136
}
129137
}
130138
}

0 commit comments

Comments
 (0)