agentshield/egress/policy/
network.rs1use serde::{Deserialize, Serialize};
2
3#[derive(Debug, Clone, Serialize, Deserialize)]
5pub struct NetworkPolicy {
6 #[serde(default = "default_true")]
8 pub block_private: bool,
9 #[serde(default = "default_true")]
11 pub block_link_local: bool,
12 #[serde(default = "default_true")]
14 pub block_localhost: bool,
15 #[serde(default = "default_true")]
17 pub block_metadata: bool,
18}
19
20fn default_true() -> bool {
21 true
22}
23
24impl Default for NetworkPolicy {
25 fn default() -> Self {
26 Self {
27 block_private: true,
28 block_link_local: true,
29 block_localhost: true,
30 block_metadata: true,
31 }
32 }
33}
34
35impl NetworkPolicy {
36 pub(super) fn is_ip_blocked(&self, ip: &str) -> bool {
38 if self.block_localhost && is_localhost(ip) {
39 return true;
40 }
41 if self.block_private && is_private_ip(ip) {
42 return true;
43 }
44 if self.block_link_local && is_link_local(ip) {
45 return true;
46 }
47 if self.block_metadata && is_metadata_ip(ip) {
48 return true;
49 }
50 false
51 }
52}
53
54pub(super) fn is_localhost(ip: &str) -> bool {
55 ip.starts_with("127.") || ip == "::1" || ip == "localhost"
56}
57
58pub(super) fn is_private_ip(ip: &str) -> bool {
59 ip.starts_with("10.")
60 || (ip.starts_with("172.") && is_172_private(ip))
61 || ip.starts_with("192.168.")
62 || ip.starts_with("fd") }
64
65pub(super) fn is_172_private(ip: &str) -> bool {
66 if let Some(second_octet) = ip
67 .strip_prefix("172.")
68 .and_then(|rest| rest.split('.').next())
69 {
70 if let Ok(n) = second_octet.parse::<u8>() {
71 return (16..=31).contains(&n);
72 }
73 }
74 false
75}
76
77pub(super) fn is_link_local(ip: &str) -> bool {
78 ip.starts_with("169.254.") || ip.starts_with("fe80:")
79}
80
81pub(super) fn is_metadata_ip(ip: &str) -> bool {
82 ip == "169.254.169.254"
83 || ip.contains("metadata.google.internal")
84 || ip == "100.100.100.200" || ip == "169.254.170.2" }