greentic_types/
policy.rs

1//! Network policy primitives.
2
3use alloc::string::String;
4use alloc::vec::Vec;
5
6#[cfg(feature = "schemars")]
7use schemars::JsonSchema;
8#[cfg(feature = "serde")]
9use serde::{Deserialize, Serialize};
10
11/// Network protocols supported by allow lists.
12#[derive(Clone, Debug, PartialEq, Eq, Hash)]
13#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
14#[cfg_attr(feature = "schemars", derive(JsonSchema))]
15#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
16pub enum Protocol {
17    /// Hypertext Transfer Protocol.
18    Http,
19    /// Hypertext Transfer Protocol Secure.
20    Https,
21    /// Generic TCP connectivity.
22    Tcp,
23    /// Generic UDP connectivity.
24    Udp,
25    /// gRPC.
26    Grpc,
27    /// Any protocol not covered above.
28    Custom(String),
29}
30
31/// Allow list describing permitted domains, ports, and protocols.
32#[derive(Clone, Debug, PartialEq, Eq)]
33#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
34#[cfg_attr(feature = "schemars", derive(JsonSchema))]
35pub struct AllowList {
36    /// Allowed domain suffixes or exact hosts.
37    #[cfg_attr(
38        feature = "serde",
39        serde(default, skip_serializing_if = "Vec::is_empty")
40    )]
41    pub domains: Vec<String>,
42    /// Allowed port numbers.
43    #[cfg_attr(
44        feature = "serde",
45        serde(default, skip_serializing_if = "Vec::is_empty")
46    )]
47    pub ports: Vec<u16>,
48    /// Allowed network protocols.
49    #[cfg_attr(
50        feature = "serde",
51        serde(default, skip_serializing_if = "Vec::is_empty")
52    )]
53    pub protocols: Vec<Protocol>,
54}
55
56impl AllowList {
57    /// Creates an empty allow list.
58    pub fn empty() -> Self {
59        Self {
60            domains: Vec::new(),
61            ports: Vec::new(),
62            protocols: Vec::new(),
63        }
64    }
65
66    /// Returns `true` when the allow list contains no rules.
67    pub fn is_empty(&self) -> bool {
68        self.domains.is_empty() && self.ports.is_empty() && self.protocols.is_empty()
69    }
70}
71
72impl Default for AllowList {
73    fn default() -> Self {
74        Self::empty()
75    }
76}
77
78/// High-level network policy composed of allow lists.
79#[derive(Clone, Debug, PartialEq, Eq, Default)]
80#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
81#[cfg_attr(feature = "schemars", derive(JsonSchema))]
82pub struct NetworkPolicy {
83    /// Allow list enforced for egress connections.
84    pub egress: AllowList,
85    /// Whether destinations not present in the allow list should be denied.
86    pub deny_on_miss: bool,
87}
88
89impl NetworkPolicy {
90    /// Creates a policy denying unknown destinations by default.
91    pub fn strict(egress: AllowList) -> Self {
92        Self {
93            egress,
94            deny_on_miss: true,
95        }
96    }
97}
98
99/// Result of evaluating a network policy.
100#[derive(Clone, Debug, PartialEq, Eq)]
101#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
102#[cfg_attr(feature = "schemars", derive(JsonSchema))]
103pub struct PolicyDecision {
104    /// Whether the request is allowed.
105    pub allow: bool,
106    /// Optional human-readable reason.
107    #[cfg_attr(
108        feature = "serde",
109        serde(default, skip_serializing_if = "Option::is_none")
110    )]
111    pub reason: Option<String>,
112}