@push.rocks/smartnftables 1.4.0 → 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/changelog.md +26 -0
- package/dist_rust/smartnftables_linux_amd64_musl +0 -0
- package/dist_rust/smartnftables_linux_amd64_musl.tsrust-build.json +4 -4
- package/dist_rust/smartnftables_linux_arm64_musl +0 -0
- package/dist_rust/smartnftables_linux_arm64_musl.tsrust-build.json +4 -4
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/classes.manageddockerforwarding.d.ts +17 -0
- package/dist_ts/classes.manageddockerforwarding.js +128 -0
- package/dist_ts/classes.managednftables.d.ts +9 -7
- package/dist_ts/classes.managednftables.js +2 -2
- package/dist_ts/index.d.ts +3 -0
- package/dist_ts/index.js +3 -1
- package/dist_ts/managed.docker.types.d.ts +78 -0
- package/dist_ts/managed.docker.types.js +2 -0
- package/dist_ts/managed.egress.types.d.ts +91 -0
- package/dist_ts/managed.egress.types.js +2 -0
- package/dist_ts/managed.types.d.ts +20 -18
- package/package.json +2 -2
- package/readme.md +143 -6
- package/rust/src/docker.frontend.rs +203 -0
- package/rust/src/docker.graph.rs +129 -0
- package/rust/src/docker.policy.rs +140 -0
- package/rust/src/docker.rs +191 -0
- package/rust/src/docker_process_tests.rs +29 -0
- package/rust/src/docker_tests.rs +148 -0
- package/rust/src/egress.compile.rs +288 -0
- package/rust/src/egress.host.rs +103 -0
- package/rust/src/egress.router.rs +231 -0
- package/rust/src/egress.rs +682 -0
- package/rust/src/egress_tests.rs +332 -0
- package/rust/src/main.rs +13 -6
- package/rust/src/managed.rs +105 -0
- package/rust/src/owner.rs +115 -7
- package/rust/src/owner_coexistence_tests.rs +77 -0
- package/rust/src/owner_egress_identity_tests.rs +155 -0
- package/rust/src/owner_egress_tests.rs +153 -0
- package/rust/src/owner_egress_traffic_tests.rs +470 -0
- package/rust/src/owner_host_traffic_tests.rs +305 -0
- package/rust/src/owner_identity_tests.rs +169 -0
- package/rust/src/owner_link_tests.rs +119 -0
- package/rust/src/owner_packet_fixture.rs +200 -0
- package/rust/src/owner_tests.rs +33 -7
- package/rust/src/policy.rs +120 -81
- package/rust/src/tests.rs +22 -0
- package/rust/src/wire.links.rs +279 -0
- package/rust/src/wire.rs +16 -66
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/classes.manageddockerforwarding.ts +113 -0
- package/ts/classes.managednftables.ts +13 -13
- package/ts/index.ts +3 -0
- package/ts/managed.docker.types.ts +49 -0
- package/ts/managed.egress.types.ts +85 -0
- package/ts/managed.types.ts +20 -16
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
//! Guest-only Ethernet observation/injection for packet-path qualifications.
|
|
2
|
+
use super::*;
|
|
3
|
+
use std::os::fd::{AsRawFd, FromRawFd, OwnedFd};
|
|
4
|
+
use std::time::{Duration, Instant};
|
|
5
|
+
|
|
6
|
+
pub(super) struct PacketSocket {
|
|
7
|
+
fd: OwnedFd,
|
|
8
|
+
index: i32,
|
|
9
|
+
}
|
|
10
|
+
impl PacketSocket {
|
|
11
|
+
pub(super) fn open(name: &str) -> Self {
|
|
12
|
+
guest_guard();
|
|
13
|
+
let name = std::ffi::CString::new(name).unwrap();
|
|
14
|
+
let index = unsafe { libc::if_nametoindex(name.as_ptr()) } as i32;
|
|
15
|
+
assert!(index > 0);
|
|
16
|
+
let raw = unsafe {
|
|
17
|
+
libc::socket(
|
|
18
|
+
libc::AF_PACKET,
|
|
19
|
+
libc::SOCK_DGRAM | libc::SOCK_CLOEXEC,
|
|
20
|
+
i32::from((libc::ETH_P_IP as u16).to_be()),
|
|
21
|
+
)
|
|
22
|
+
};
|
|
23
|
+
assert!(
|
|
24
|
+
raw >= 0,
|
|
25
|
+
"packet socket: {}",
|
|
26
|
+
std::io::Error::last_os_error()
|
|
27
|
+
);
|
|
28
|
+
let fd = unsafe { OwnedFd::from_raw_fd(raw) };
|
|
29
|
+
let mut address: libc::sockaddr_ll = unsafe { std::mem::zeroed() };
|
|
30
|
+
address.sll_family = libc::AF_PACKET as u16;
|
|
31
|
+
address.sll_protocol = (libc::ETH_P_IP as u16).to_be();
|
|
32
|
+
address.sll_ifindex = index;
|
|
33
|
+
assert_eq!(
|
|
34
|
+
unsafe {
|
|
35
|
+
libc::bind(
|
|
36
|
+
raw,
|
|
37
|
+
(&address as *const libc::sockaddr_ll).cast(),
|
|
38
|
+
std::mem::size_of_val(&address) as _,
|
|
39
|
+
)
|
|
40
|
+
},
|
|
41
|
+
0
|
|
42
|
+
);
|
|
43
|
+
Self { fd, index }
|
|
44
|
+
}
|
|
45
|
+
pub(super) fn send(&self, mac: [u8; 6], packet: &[u8]) {
|
|
46
|
+
let mut address: libc::sockaddr_ll = unsafe { std::mem::zeroed() };
|
|
47
|
+
address.sll_family = libc::AF_PACKET as u16;
|
|
48
|
+
address.sll_protocol = (libc::ETH_P_IP as u16).to_be();
|
|
49
|
+
address.sll_ifindex = self.index;
|
|
50
|
+
address.sll_halen = 6;
|
|
51
|
+
address.sll_addr[..6].copy_from_slice(&mac);
|
|
52
|
+
assert_eq!(
|
|
53
|
+
unsafe {
|
|
54
|
+
libc::sendto(
|
|
55
|
+
self.fd.as_raw_fd(),
|
|
56
|
+
packet.as_ptr().cast(),
|
|
57
|
+
packet.len(),
|
|
58
|
+
0,
|
|
59
|
+
(&address as *const libc::sockaddr_ll).cast(),
|
|
60
|
+
std::mem::size_of_val(&address) as _,
|
|
61
|
+
)
|
|
62
|
+
},
|
|
63
|
+
packet.len() as isize
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
pub(super) fn matching(&self, mut matches: impl FnMut(&[u8]) -> bool) -> Option<Vec<u8>> {
|
|
67
|
+
let deadline = Instant::now() + Duration::from_millis(500);
|
|
68
|
+
loop {
|
|
69
|
+
let remaining = deadline
|
|
70
|
+
.saturating_duration_since(Instant::now())
|
|
71
|
+
.as_millis();
|
|
72
|
+
if remaining == 0 {
|
|
73
|
+
return None;
|
|
74
|
+
}
|
|
75
|
+
let mut poll = libc::pollfd {
|
|
76
|
+
fd: self.fd.as_raw_fd(),
|
|
77
|
+
events: libc::POLLIN,
|
|
78
|
+
revents: 0,
|
|
79
|
+
};
|
|
80
|
+
let ready = unsafe { libc::poll(&mut poll, 1, remaining as i32) };
|
|
81
|
+
assert!(ready >= 0);
|
|
82
|
+
if ready == 0 {
|
|
83
|
+
return None;
|
|
84
|
+
}
|
|
85
|
+
let mut packet = vec![0; 65536];
|
|
86
|
+
let size = unsafe {
|
|
87
|
+
libc::recv(
|
|
88
|
+
self.fd.as_raw_fd(),
|
|
89
|
+
packet.as_mut_ptr().cast(),
|
|
90
|
+
packet.len(),
|
|
91
|
+
libc::MSG_TRUNC,
|
|
92
|
+
)
|
|
93
|
+
};
|
|
94
|
+
assert!(size >= 0 && (size as usize) <= packet.len());
|
|
95
|
+
packet.truncate(size as usize);
|
|
96
|
+
if matches(&packet) {
|
|
97
|
+
return Some(packet);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
pub(super) fn mac(name: &str) -> [u8; 6] {
|
|
103
|
+
let output = std::process::Command::new("/bin/ip")
|
|
104
|
+
.args(["-j", "link", "show", "dev", name])
|
|
105
|
+
.output()
|
|
106
|
+
.unwrap();
|
|
107
|
+
assert!(output.status.success());
|
|
108
|
+
let value = serde_json::from_slice::<serde_json::Value>(&output.stdout).unwrap();
|
|
109
|
+
value[0]["address"]
|
|
110
|
+
.as_str()
|
|
111
|
+
.unwrap()
|
|
112
|
+
.split(':')
|
|
113
|
+
.map(|byte| u8::from_str_radix(byte, 16).unwrap())
|
|
114
|
+
.collect::<Vec<_>>()
|
|
115
|
+
.try_into()
|
|
116
|
+
.unwrap()
|
|
117
|
+
}
|
|
118
|
+
fn checksum(bytes: &[u8]) -> u16 {
|
|
119
|
+
let mut sum: u32 = bytes
|
|
120
|
+
.chunks(2)
|
|
121
|
+
.map(|bytes| u32::from(bytes[0]) * 256 + u32::from(*bytes.get(1).unwrap_or(&0)))
|
|
122
|
+
.sum();
|
|
123
|
+
while sum >> 16 != 0 {
|
|
124
|
+
sum = (sum & 0xffff) + (sum >> 16);
|
|
125
|
+
}
|
|
126
|
+
!(sum as u16)
|
|
127
|
+
}
|
|
128
|
+
pub(super) fn tcp(
|
|
129
|
+
source: [u8; 4],
|
|
130
|
+
destination: [u8; 4],
|
|
131
|
+
source_port: u16,
|
|
132
|
+
destination_port: u16,
|
|
133
|
+
flags: u8,
|
|
134
|
+
) -> Vec<u8> {
|
|
135
|
+
let mut packet = vec![0; 40];
|
|
136
|
+
packet[0] = 0x45;
|
|
137
|
+
packet[2..4].copy_from_slice(&40_u16.to_be_bytes());
|
|
138
|
+
packet[8] = 64;
|
|
139
|
+
packet[9] = 6;
|
|
140
|
+
packet[12..16].copy_from_slice(&source);
|
|
141
|
+
packet[16..20].copy_from_slice(&destination);
|
|
142
|
+
let sum = checksum(&packet[..20]);
|
|
143
|
+
packet[10..12].copy_from_slice(&sum.to_be_bytes());
|
|
144
|
+
packet[20..22].copy_from_slice(&source_port.to_be_bytes());
|
|
145
|
+
packet[22..24].copy_from_slice(&destination_port.to_be_bytes());
|
|
146
|
+
packet[24..28].copy_from_slice(&100_u32.to_be_bytes());
|
|
147
|
+
packet[32] = 0x50;
|
|
148
|
+
packet[33] = flags;
|
|
149
|
+
packet[34..36].copy_from_slice(&65535_u16.to_be_bytes());
|
|
150
|
+
let mut pseudo = Vec::new();
|
|
151
|
+
pseudo.extend(source);
|
|
152
|
+
pseudo.extend(destination);
|
|
153
|
+
pseudo.extend([0, 6, 0, 20]);
|
|
154
|
+
pseudo.extend(&packet[20..]);
|
|
155
|
+
let sum = checksum(&pseudo);
|
|
156
|
+
packet[36..38].copy_from_slice(&sum.to_be_bytes());
|
|
157
|
+
packet
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
pub(super) fn udp(
|
|
161
|
+
source: [u8; 4],
|
|
162
|
+
destination: [u8; 4],
|
|
163
|
+
source_port: u16,
|
|
164
|
+
destination_port: u16,
|
|
165
|
+
) -> Vec<u8> {
|
|
166
|
+
let mut packet = vec![0; 32];
|
|
167
|
+
packet[0] = 0x45;
|
|
168
|
+
packet[2..4].copy_from_slice(&32_u16.to_be_bytes());
|
|
169
|
+
packet[8] = 64;
|
|
170
|
+
packet[9] = 17;
|
|
171
|
+
packet[12..16].copy_from_slice(&source);
|
|
172
|
+
packet[16..20].copy_from_slice(&destination);
|
|
173
|
+
let sum = checksum(&packet[..20]);
|
|
174
|
+
packet[10..12].copy_from_slice(&sum.to_be_bytes());
|
|
175
|
+
packet[20..22].copy_from_slice(&source_port.to_be_bytes());
|
|
176
|
+
packet[22..24].copy_from_slice(&destination_port.to_be_bytes());
|
|
177
|
+
packet[24..26].copy_from_slice(&12_u16.to_be_bytes());
|
|
178
|
+
packet[28..].copy_from_slice(b"frag");
|
|
179
|
+
packet
|
|
180
|
+
}
|
|
181
|
+
pub(super) fn fragments(packet: &[u8], id: u16) -> [Vec<u8>; 2] {
|
|
182
|
+
assert_eq!(packet.len(), 32);
|
|
183
|
+
assert_eq!(packet[0], 0x45);
|
|
184
|
+
let fragment = |body: &[u8], offset: u16| {
|
|
185
|
+
let mut result = packet[..20].to_vec();
|
|
186
|
+
result.extend(body);
|
|
187
|
+
let length = result.len() as u16;
|
|
188
|
+
result[2..4].copy_from_slice(&length.to_be_bytes());
|
|
189
|
+
result[4..6].copy_from_slice(&id.to_be_bytes());
|
|
190
|
+
result[6..8].copy_from_slice(&offset.to_be_bytes());
|
|
191
|
+
result[10..12].fill(0);
|
|
192
|
+
let sum = checksum(&result[..20]);
|
|
193
|
+
result[10..12].copy_from_slice(&sum.to_be_bytes());
|
|
194
|
+
result
|
|
195
|
+
};
|
|
196
|
+
[
|
|
197
|
+
fragment(&packet[20..28], 0x2000),
|
|
198
|
+
fragment(&packet[28..], 1),
|
|
199
|
+
]
|
|
200
|
+
}
|
package/rust/src/owner_tests.rs
CHANGED
|
@@ -3,7 +3,28 @@ use super::*;
|
|
|
3
3
|
#[path = "owner_namespace_tests.rs"]
|
|
4
4
|
mod namespace_tests;
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
#[path = "owner_egress_identity_tests.rs"]
|
|
7
|
+
mod egress_identity_tests;
|
|
8
|
+
|
|
9
|
+
#[path = "owner_link_tests.rs"]
|
|
10
|
+
mod link_tests;
|
|
11
|
+
|
|
12
|
+
#[path = "owner_egress_tests.rs"]
|
|
13
|
+
mod egress_tests;
|
|
14
|
+
|
|
15
|
+
#[path = "owner_egress_traffic_tests.rs"]
|
|
16
|
+
mod egress_traffic_tests;
|
|
17
|
+
|
|
18
|
+
#[path = "owner_packet_fixture.rs"]
|
|
19
|
+
mod packet_fixture;
|
|
20
|
+
|
|
21
|
+
#[path = "owner_host_traffic_tests.rs"]
|
|
22
|
+
mod host_traffic_tests;
|
|
23
|
+
|
|
24
|
+
#[path = "owner_coexistence_tests.rs"]
|
|
25
|
+
mod coexistence_tests;
|
|
26
|
+
|
|
27
|
+
fn guest_guard() {
|
|
7
28
|
assert_eq!(
|
|
8
29
|
std::env::var("SMARTNFTABLES_ISOLATED_NATIVE").as_deref(),
|
|
9
30
|
Ok("qemu-6.18.35")
|
|
@@ -12,6 +33,10 @@ fn isolated() {
|
|
|
12
33
|
.unwrap()
|
|
13
34
|
.contains("smartnftables_qualification=1"));
|
|
14
35
|
assert_eq!(unsafe { libc::geteuid() }, 0);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
fn isolated() {
|
|
39
|
+
guest_guard();
|
|
15
40
|
let initial = context().unwrap();
|
|
16
41
|
assert_eq!(
|
|
17
42
|
unsafe { libc::unshare(libc::CLONE_NEWNET | libc::CLONE_NEWNS) },
|
|
@@ -279,7 +304,7 @@ fn native_interface_ipv4_directed_grants_and_spoof_denial() {
|
|
|
279
304
|
assert_eq!(
|
|
280
305
|
owner.reconcile(Transition {
|
|
281
306
|
previous: None,
|
|
282
|
-
target: invalid_pair.prepare().unwrap()
|
|
307
|
+
target: invalid_pair.prepare().unwrap().into()
|
|
283
308
|
}),
|
|
284
309
|
Err(Error::Conflict)
|
|
285
310
|
);
|
|
@@ -289,7 +314,7 @@ fn native_interface_ipv4_directed_grants_and_spoof_denial() {
|
|
|
289
314
|
assert_eq!(
|
|
290
315
|
owner.reconcile(Transition {
|
|
291
316
|
previous: None,
|
|
292
|
-
target: policy.clone().prepare().unwrap()
|
|
317
|
+
target: policy.clone().prepare().unwrap().into()
|
|
293
318
|
}),
|
|
294
319
|
Err(Error::Conflict)
|
|
295
320
|
);
|
|
@@ -298,7 +323,7 @@ fn native_interface_ipv4_directed_grants_and_spoof_denial() {
|
|
|
298
323
|
let current = owner
|
|
299
324
|
.reconcile(Transition {
|
|
300
325
|
previous: None,
|
|
301
|
-
target: policy.clone().prepare().unwrap(),
|
|
326
|
+
target: policy.clone().prepare().unwrap().into(),
|
|
302
327
|
})
|
|
303
328
|
.expect("apply interface policy");
|
|
304
329
|
assert!(owner.inspect().enforced);
|
|
@@ -336,7 +361,7 @@ fn native_interface_ipv4_directed_grants_and_spoof_denial() {
|
|
|
336
361
|
let revoked = owner
|
|
337
362
|
.reconcile(Transition {
|
|
338
363
|
previous: Some(current),
|
|
339
|
-
target: denied.prepare().unwrap(),
|
|
364
|
+
target: denied.prepare().unwrap().into(),
|
|
340
365
|
})
|
|
341
366
|
.expect("atomic complete deny");
|
|
342
367
|
udp(&client, &server, false);
|
|
@@ -364,6 +389,7 @@ fn empty(revision: u64) -> Prepared {
|
|
|
364
389
|
}
|
|
365
390
|
.prepare()
|
|
366
391
|
.unwrap()
|
|
392
|
+
.into()
|
|
367
393
|
}
|
|
368
394
|
|
|
369
395
|
#[test]
|
|
@@ -699,14 +725,14 @@ fn native_tun_forwarding_binds_remote_ingress_and_rejects_tap() {
|
|
|
699
725
|
assert_eq!(
|
|
700
726
|
owner.reconcile(Transition {
|
|
701
727
|
previous: None,
|
|
702
|
-
target: invalid.prepare().unwrap()
|
|
728
|
+
target: invalid.prepare().unwrap().into()
|
|
703
729
|
}),
|
|
704
730
|
Err(Error::Conflict)
|
|
705
731
|
);
|
|
706
732
|
let applied = owner
|
|
707
733
|
.reconcile(Transition {
|
|
708
734
|
previous: None,
|
|
709
|
-
target: policy.prepare().unwrap(),
|
|
735
|
+
target: policy.prepare().unwrap().into(),
|
|
710
736
|
})
|
|
711
737
|
.unwrap();
|
|
712
738
|
send_tunnel(&mut device, true);
|
package/rust/src/policy.rs
CHANGED
|
@@ -210,10 +210,10 @@ impl Policy {
|
|
|
210
210
|
}
|
|
211
211
|
}
|
|
212
212
|
|
|
213
|
-
fn expr(name: &str, data: Vec<Attr>) -> Attr {
|
|
213
|
+
pub(crate) fn expr(name: &str, data: Vec<Attr>) -> Attr {
|
|
214
214
|
Attr::nested(1, vec![Attr::string(1, name), Attr::nested(2, data)])
|
|
215
215
|
}
|
|
216
|
-
fn compare(value: Vec<u8>) -> Attr {
|
|
216
|
+
pub(crate) fn compare(value: Vec<u8>) -> Attr {
|
|
217
217
|
expr(
|
|
218
218
|
"cmp",
|
|
219
219
|
vec![
|
|
@@ -223,7 +223,7 @@ fn compare(value: Vec<u8>) -> Attr {
|
|
|
223
223
|
],
|
|
224
224
|
)
|
|
225
225
|
}
|
|
226
|
-
fn meta(key: u32, value: Vec<u8>) -> Vec<Attr> {
|
|
226
|
+
pub(crate) fn meta(key: u32, value: Vec<u8>) -> Vec<Attr> {
|
|
227
227
|
vec![
|
|
228
228
|
expr("meta", vec![Attr::u32(1, 1), Attr::u32(2, key)]),
|
|
229
229
|
compare(value),
|
|
@@ -239,7 +239,7 @@ fn interface(endpoint: &Endpoint, incoming: bool) -> Vec<Attr> {
|
|
|
239
239
|
result.extend(meta(if incoming { 6 } else { 7 }, name));
|
|
240
240
|
result
|
|
241
241
|
}
|
|
242
|
-
fn ipv4() -> Vec<Attr> {
|
|
242
|
+
pub(crate) fn ipv4() -> Vec<Attr> {
|
|
243
243
|
meta(15, vec![libc::NFPROTO_IPV4 as u8])
|
|
244
244
|
}
|
|
245
245
|
fn address(cidr: &str, source: bool) -> Result<Vec<Attr>> {
|
|
@@ -268,7 +268,7 @@ fn address(cidr: &str, source: bool) -> Result<Vec<Attr>> {
|
|
|
268
268
|
compare(address.to_be_bytes().to_vec()),
|
|
269
269
|
])
|
|
270
270
|
}
|
|
271
|
-
fn verdict(code: i32, chain: Option<&str>) -> Attr {
|
|
271
|
+
pub(crate) fn verdict(code: i32, chain: Option<&str>) -> Attr {
|
|
272
272
|
let mut data = vec![Attr::u32(1, code as u32)];
|
|
273
273
|
if let Some(chain) = chain {
|
|
274
274
|
data.push(Attr::string(2, chain));
|
|
@@ -329,99 +329,138 @@ impl Prepared {
|
|
|
329
329
|
],
|
|
330
330
|
));
|
|
331
331
|
}
|
|
332
|
+
for (name, expressions) in self.guard_chains()? {
|
|
333
|
+
program.push((3, vec![Attr::string(1, table), Attr::string(3, &name)]));
|
|
334
|
+
for expressions in expressions {
|
|
335
|
+
rule(&name, expressions, &mut program);
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
for chain in ["input", "forward", "output"] {
|
|
339
|
+
let parts = self.filter_rules(chain)?;
|
|
340
|
+
for expressions in parts
|
|
341
|
+
.guards
|
|
342
|
+
.into_iter()
|
|
343
|
+
.chain(parts.grants)
|
|
344
|
+
.chain(parts.denials)
|
|
345
|
+
{
|
|
346
|
+
rule(chain, expressions, &mut program);
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
Ok(program)
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
/// Reused inside a combined router graph; order remains part of schema-v1's bytes.
|
|
354
|
+
pub(crate) struct PrivateRules {
|
|
355
|
+
pub guards: Vec<Vec<Attr>>,
|
|
356
|
+
pub grants: Vec<Vec<Attr>>,
|
|
357
|
+
pub denials: Vec<Vec<Attr>>,
|
|
358
|
+
}
|
|
359
|
+
impl Prepared {
|
|
360
|
+
pub(crate) fn guard_chains(&self) -> Result<Vec<(String, Vec<Vec<Attr>>)>> {
|
|
361
|
+
let mut result = Vec::new();
|
|
332
362
|
for (index, endpoint) in self.policy.endpoints.iter().enumerate() {
|
|
333
363
|
for incoming in [true, false] {
|
|
334
364
|
let name = format!("guard_{index}_{}", if incoming { "in" } else { "out" });
|
|
335
|
-
|
|
365
|
+
let mut rules = Vec::new();
|
|
336
366
|
for cidr in &endpoint.source_prefixes {
|
|
337
367
|
let mut expressions = ipv4();
|
|
338
368
|
expressions.extend(address(cidr, incoming)?);
|
|
339
369
|
expressions.push(verdict(-5, None));
|
|
340
|
-
|
|
370
|
+
rules.push(expressions);
|
|
341
371
|
}
|
|
342
|
-
|
|
372
|
+
rules.push(vec![verdict(0, None)]);
|
|
373
|
+
result.push((name, rules));
|
|
343
374
|
}
|
|
344
375
|
}
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
for
|
|
358
|
-
if chain == "input" &&
|
|
359
|
-
|| chain == "output" && grant.source_endpoint.is_some()
|
|
360
|
-
|| chain == "forward"
|
|
361
|
-
&& (grant.source_endpoint.is_none() || grant.destination_endpoint.is_none())
|
|
362
|
-
{
|
|
376
|
+
Ok(result)
|
|
377
|
+
}
|
|
378
|
+
pub(crate) fn filter_rules(&self, chain: &str) -> Result<PrivateRules> {
|
|
379
|
+
if !["input", "forward", "output"].contains(&chain) {
|
|
380
|
+
return Err(Error::Invalid);
|
|
381
|
+
}
|
|
382
|
+
let mut result = PrivateRules {
|
|
383
|
+
guards: Vec::new(),
|
|
384
|
+
grants: Vec::new(),
|
|
385
|
+
denials: Vec::new(),
|
|
386
|
+
};
|
|
387
|
+
for (index, endpoint) in self.policy.endpoints.iter().enumerate() {
|
|
388
|
+
for incoming in [true, false] {
|
|
389
|
+
if (chain == "input" && !incoming) || (chain == "output" && incoming) {
|
|
363
390
|
continue;
|
|
364
391
|
}
|
|
365
|
-
let
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
392
|
+
let name = format!("guard_{index}_{}", if incoming { "in" } else { "out" });
|
|
393
|
+
let mut expressions = interface(endpoint, incoming);
|
|
394
|
+
expressions.push(verdict(-3, Some(&name)));
|
|
395
|
+
result.guards.push(expressions);
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
for grant in &self.policy.rules {
|
|
399
|
+
if chain == "input" && grant.destination_endpoint.is_some()
|
|
400
|
+
|| chain == "output" && grant.source_endpoint.is_some()
|
|
401
|
+
|| chain == "forward"
|
|
402
|
+
&& (grant.source_endpoint.is_none() || grant.destination_endpoint.is_none())
|
|
403
|
+
{
|
|
404
|
+
continue;
|
|
405
|
+
}
|
|
406
|
+
let mut expressions = ipv4();
|
|
407
|
+
for (id, incoming) in [
|
|
408
|
+
(&grant.source_endpoint, true),
|
|
409
|
+
(&grant.destination_endpoint, false),
|
|
410
|
+
] {
|
|
411
|
+
if let Some(id) = id {
|
|
412
|
+
let endpoint = self
|
|
413
|
+
.policy
|
|
414
|
+
.endpoints
|
|
415
|
+
.iter()
|
|
416
|
+
.find(|ep| &ep.id == id)
|
|
417
|
+
.ok_or(Error::Invalid)?;
|
|
418
|
+
expressions.extend(interface(endpoint, incoming));
|
|
385
419
|
}
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
420
|
+
}
|
|
421
|
+
expressions.extend(address(&grant.source_prefix, true)?);
|
|
422
|
+
expressions.extend(address(&grant.destination_prefix, false)?);
|
|
423
|
+
if grant.protocol != "any" {
|
|
424
|
+
expressions.extend(meta(16, vec![if grant.protocol == "tcp" { 6 } else { 17 }]));
|
|
425
|
+
}
|
|
426
|
+
for (port, offset) in [(grant.source_port, 0), (grant.destination_port, 2)] {
|
|
427
|
+
if let Some(port) = port {
|
|
428
|
+
expressions.push(expr(
|
|
429
|
+
"payload",
|
|
430
|
+
vec![
|
|
431
|
+
Attr::u32(1, 1),
|
|
432
|
+
Attr::u32(2, 2),
|
|
433
|
+
Attr::u32(3, offset),
|
|
434
|
+
Attr::u32(4, 2),
|
|
435
|
+
],
|
|
436
|
+
));
|
|
437
|
+
expressions.push(compare(port.to_be_bytes().to_vec()));
|
|
399
438
|
}
|
|
400
|
-
expressions.push(verdict(1, None));
|
|
401
|
-
rule(chain, expressions, &mut program);
|
|
402
439
|
}
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
440
|
+
expressions.push(verdict(1, None));
|
|
441
|
+
result.grants.push(expressions);
|
|
442
|
+
}
|
|
443
|
+
for endpoint in &self.policy.endpoints {
|
|
444
|
+
for incoming in [true, false] {
|
|
445
|
+
if (chain == "input" && !incoming) || (chain == "output" && incoming) {
|
|
446
|
+
continue;
|
|
447
|
+
}
|
|
448
|
+
// Either selector matching is enough to retain denial after rename/reuse.
|
|
449
|
+
for mut expressions in [
|
|
450
|
+
meta(
|
|
451
|
+
if incoming { 4 } else { 5 },
|
|
452
|
+
endpoint.interface_index.to_ne_bytes().to_vec(),
|
|
453
|
+
),
|
|
454
|
+
meta(
|
|
455
|
+
if incoming { 6 } else { 7 },
|
|
456
|
+
[endpoint.interface_name.as_bytes(), &[0]].concat(),
|
|
457
|
+
),
|
|
458
|
+
] {
|
|
459
|
+
expressions.push(verdict(0, None));
|
|
460
|
+
result.denials.push(expressions);
|
|
422
461
|
}
|
|
423
462
|
}
|
|
424
463
|
}
|
|
425
|
-
Ok(
|
|
464
|
+
Ok(result)
|
|
426
465
|
}
|
|
427
466
|
}
|
package/rust/src/tests.rs
CHANGED
|
@@ -1,9 +1,31 @@
|
|
|
1
1
|
use crate::{decode, policy::Policy, wire, Error};
|
|
2
|
+
use sha2::{Digest, Sha256};
|
|
2
3
|
|
|
3
4
|
fn policy() -> Policy {
|
|
4
5
|
serde_json::from_str(r#"{"schemaVersion":1,"revision":1,"endpoints":[{"id":"a","interfaceIndex":2,"interfaceName":"worka","interfaceKind":"veth","sourcePrefixes":["10.81.0.2/32"]},{"id":"b","interfaceIndex":3,"interfaceName":"workb","interfaceKind":"veth","sourcePrefixes":["10.81.0.3/32"]}],"rules":[{"sourceEndpoint":"a","destinationEndpoint":"b","sourcePrefix":"10.81.0.2/32","destinationPrefix":"10.81.0.3/32","protocol":"udp","sourcePort":null,"destinationPort":53}]}"#).unwrap()
|
|
5
6
|
}
|
|
6
7
|
|
|
8
|
+
#[test]
|
|
9
|
+
fn v1_policy_and_program_bytes_remain_frozen() {
|
|
10
|
+
let prepared = policy().prepare().unwrap();
|
|
11
|
+
let mut bytes = Vec::new();
|
|
12
|
+
for (kind, attributes) in prepared.program("snft_v1_golden").unwrap() {
|
|
13
|
+
let encoded = wire::encode_attrs(&attributes);
|
|
14
|
+
bytes.extend(kind.to_be_bytes());
|
|
15
|
+
bytes.extend((encoded.len() as u32).to_be_bytes());
|
|
16
|
+
bytes.extend(encoded);
|
|
17
|
+
}
|
|
18
|
+
assert_eq!(
|
|
19
|
+
prepared.digest,
|
|
20
|
+
"sha256:13897f4e711db2111618c61eb3f0be00714ed79f2075bf7f75e16f93f26ce2db"
|
|
21
|
+
);
|
|
22
|
+
assert_eq!(bytes.len(), 11020);
|
|
23
|
+
assert_eq!(
|
|
24
|
+
format!("{:x}", Sha256::digest(bytes)),
|
|
25
|
+
"e57215884bca4bc8e0d7bef4c975cc3a1cc9e9233e44c248502a9efd99fa5c34"
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
|
|
7
29
|
#[test]
|
|
8
30
|
fn canonical_policy_rejects_ambiguous_or_unbound_grants() {
|
|
9
31
|
let prepared = policy().prepare().unwrap();
|