Skip to content

Commit fe82392

Browse files
committed
tests: cleanup test_seccomp
- convert inline JSON to dicts - use pytest temporary files instead of tempfile - create a seccompiler fixture to make running it easy Signed-off-by: Pablo Barbáchano <[email protected]>
1 parent 545b47d commit fe82392

File tree

3 files changed

+136
-287
lines changed

3 files changed

+136
-287
lines changed
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# Copyright 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
"""Fixtures for security tests"""
5+
6+
import json
7+
from pathlib import Path
8+
9+
import pytest
10+
11+
from host_tools.cargo_build import run_seccompiler_bin
12+
13+
14+
@pytest.fixture()
15+
def seccompiler(tmp_path):
16+
"A seccompiler helper fixture"
17+
18+
class Seccompiler:
19+
"A seccompiler helper class"
20+
21+
def compile(self, data: dict, basic=False) -> Path:
22+
"Use seccompiler-bin to compile a filter from a dict"
23+
inp = tmp_path / "input.json"
24+
inp.write_text(json.dumps(data))
25+
bpf = tmp_path / "output.bpfmap"
26+
run_seccompiler_bin(bpf_path=bpf, json_path=inp, basic=basic)
27+
return bpf
28+
29+
return Seccompiler()

tests/integration_tests/security/test_custom_seccomp.py

Lines changed: 47 additions & 151 deletions
Original file line numberDiff line numberDiff line change
@@ -2,179 +2,76 @@
22
# SPDX-License-Identifier: Apache-2.0
33
"""Tests that the --seccomp-filter parameter works as expected."""
44

5-
import os
65
import platform
7-
import tempfile
86
import time
7+
from pathlib import Path
98

10-
import pytest
119
import requests
1210

1311
from framework import utils
14-
from host_tools.cargo_build import run_seccompiler_bin
1512

1613

17-
def _custom_filter_setup(test_microvm, json_filter):
18-
json_temp = tempfile.NamedTemporaryFile(delete=False)
19-
json_temp.write(json_filter)
20-
json_temp.flush()
14+
def install_filter(microvm, bpf_path):
15+
"""Install seccomp filter in microvm."""
16+
microvm.create_jailed_resource(bpf_path)
17+
microvm.jailer.extra_args.update({"seccomp-filter": bpf_path.name})
2118

22-
bpf_path = os.path.join(test_microvm.path, "bpf.out")
2319

24-
run_seccompiler_bin(bpf_path=bpf_path, json_path=json_temp.name)
20+
def test_allow_all(uvm_plain, seccompiler):
21+
"""Test --seccomp-filter, allowing all syscalls."""
22+
seccomp_filter = {
23+
thread: {"default_action": "allow", "filter_action": "trap", "filter": []}
24+
for thread in ["vmm", "api", "vcpu"]
25+
}
2526

26-
os.unlink(json_temp.name)
27-
test_microvm.create_jailed_resource(bpf_path)
28-
test_microvm.jailer.extra_args.update({"seccomp-filter": "bpf.out"})
29-
30-
31-
def _config_file_setup(test_microvm, vm_config_file):
32-
test_microvm.create_jailed_resource(test_microvm.kernel_file)
33-
test_microvm.create_jailed_resource(test_microvm.rootfs_file)
34-
35-
vm_config_path = os.path.join(test_microvm.path, os.path.basename(vm_config_file))
36-
with open(vm_config_file, encoding="utf-8") as f1:
37-
with open(vm_config_path, "w", encoding="utf-8") as f2:
38-
for line in f1:
39-
f2.write(line)
40-
test_microvm.create_jailed_resource(vm_config_path)
41-
test_microvm.jailer.extra_args = {"config-file": os.path.basename(vm_config_file)}
42-
43-
test_microvm.jailer.extra_args.update({"no-api": None})
44-
45-
46-
def test_allow_all(uvm_plain):
47-
"""
48-
Test --seccomp-filter, allowing all syscalls.
49-
"""
27+
bpf_path = seccompiler.compile(seccomp_filter)
5028
test_microvm = uvm_plain
51-
52-
_custom_filter_setup(
53-
test_microvm,
54-
"""{
55-
"Vmm": {
56-
"default_action": "allow",
57-
"filter_action": "trap",
58-
"filter": []
59-
},
60-
"Api": {
61-
"default_action": "allow",
62-
"filter_action": "trap",
63-
"filter": []
64-
},
65-
"Vcpu": {
66-
"default_action": "allow",
67-
"filter_action": "trap",
68-
"filter": []
69-
}
70-
}""".encode(
71-
"utf-8"
72-
),
73-
)
74-
29+
install_filter(test_microvm, bpf_path)
7530
test_microvm.spawn()
76-
7731
test_microvm.basic_config()
78-
7932
test_microvm.start()
80-
8133
utils.assert_seccomp_level(test_microvm.firecracker_pid, "2")
8234

8335

84-
def test_working_filter(uvm_plain):
85-
"""
86-
Test --seccomp-filter, rejecting some dangerous syscalls.
87-
"""
88-
test_microvm = uvm_plain
36+
def test_working_filter(uvm_plain, seccompiler):
37+
"""Test --seccomp-filter, rejecting some dangerous syscalls."""
8938

90-
_custom_filter_setup(
91-
test_microvm,
92-
"""{
93-
"Vmm": {
94-
"default_action": "allow",
95-
"filter_action": "kill_process",
96-
"filter": [
97-
{
98-
"syscall": "clone"
99-
},
100-
{
101-
"syscall": "execve"
102-
}
103-
]
104-
},
105-
"Api": {
106-
"default_action": "allow",
107-
"filter_action": "kill_process",
108-
"filter": [
109-
{
110-
"syscall": "clone"
111-
},
112-
{
113-
"syscall": "execve"
114-
}
115-
]
116-
},
117-
"Vcpu": {
39+
seccomp_filter = {
40+
thread: {
11841
"default_action": "allow",
11942
"filter_action": "kill_process",
120-
"filter": [
121-
{
122-
"syscall": "clone"
123-
},
124-
{
125-
"syscall": "execve",
126-
"comment": "sample comment"
127-
}
128-
]
43+
"filter": [{"syscall": "clone"}, {"syscall": "execve"}],
12944
}
130-
}""".encode(
131-
"utf-8"
132-
),
133-
)
45+
for thread in ["vmm", "api", "vcpu"]
46+
}
13447

48+
bpf_path = seccompiler.compile(seccomp_filter)
49+
test_microvm = uvm_plain
50+
install_filter(test_microvm, bpf_path)
13551
test_microvm.spawn()
136-
13752
test_microvm.basic_config()
138-
13953
test_microvm.start()
14054

14155
# level should be 2, with no additional errors
14256
utils.assert_seccomp_level(test_microvm.firecracker_pid, "2")
14357

14458

145-
def test_failing_filter(uvm_plain):
146-
"""
147-
Test --seccomp-filter, denying some needed syscalls.
148-
"""
149-
test_microvm = uvm_plain
59+
def test_failing_filter(uvm_plain, seccompiler):
60+
"""Test --seccomp-filter, denying some needed syscalls."""
15061

151-
_custom_filter_setup(
152-
test_microvm,
153-
"""{
154-
"Vmm": {
62+
seccomp_filter = {
63+
"vmm": {"default_action": "allow", "filter_action": "trap", "filter": []},
64+
"api": {"default_action": "allow", "filter_action": "trap", "filter": []},
65+
"vcpu": {
15566
"default_action": "allow",
15667
"filter_action": "trap",
157-
"filter": []
68+
"filter": [{"syscall": "ioctl"}],
15869
},
159-
"Api": {
160-
"default_action": "allow",
161-
"filter_action": "trap",
162-
"filter": []
163-
},
164-
"Vcpu": {
165-
"default_action": "allow",
166-
"filter_action": "trap",
167-
"filter": [
168-
{
169-
"syscall": "ioctl"
170-
}
171-
]
172-
}
173-
}""".encode(
174-
"utf-8"
175-
),
176-
)
70+
}
17771

72+
bpf_path = seccompiler.compile(seccomp_filter)
73+
test_microvm = uvm_plain
74+
install_filter(test_microvm, bpf_path)
17875
test_microvm.spawn()
17976
test_microvm.basic_config(vcpu_count=1)
18077

@@ -190,8 +87,7 @@ def test_failing_filter(uvm_plain):
19087
# Check the logger output
19188
ioctl_num = 16 if platform.machine() == "x86_64" else 29
19289
test_microvm.check_log_message(
193-
"Shutting down VM after intercepting a bad"
194-
" syscall ({})".format(str(ioctl_num))
90+
f"Shutting down VM after intercepting a bad syscall ({ioctl_num})"
19591
)
19692

19793
# Check the metrics
@@ -208,28 +104,28 @@ def test_failing_filter(uvm_plain):
208104
test_microvm.mark_killed()
209105

210106

211-
@pytest.mark.parametrize("vm_config_file", ["framework/vm_config.json"])
212-
def test_invalid_bpf(uvm_plain, vm_config_file):
213-
"""
214-
Test that FC does not start, given an invalid binary filter.
215-
"""
107+
def test_invalid_bpf(uvm_plain):
108+
"""Test that FC does not start, given an invalid binary filter."""
216109
test_microvm = uvm_plain
217110

218111
# Configure VM from JSON. Otherwise, the test will error because
219112
# the process will be killed before configuring the API socket.
220-
_config_file_setup(uvm_plain, vm_config_file)
113+
test_microvm.create_jailed_resource(test_microvm.kernel_file)
114+
test_microvm.create_jailed_resource(test_microvm.rootfs_file)
221115

222-
bpf_path = os.path.join(test_microvm.path, "bpf.out")
223-
file = open(bpf_path, "w", encoding="utf-8")
224-
file.write("Invalid BPF!")
225-
file.close()
116+
vm_config_file = Path("framework/vm_config.json")
117+
test_microvm.create_jailed_resource(vm_config_file)
118+
test_microvm.jailer.extra_args = {"config-file": vm_config_file.name}
119+
test_microvm.jailer.extra_args.update({"no-api": None})
226120

121+
bpf_path = Path(test_microvm.path) / "bpf.out"
122+
bpf_path.write_bytes(b"Invalid BPF!")
227123
test_microvm.create_jailed_resource(bpf_path)
228-
test_microvm.jailer.extra_args.update({"seccomp-filter": "bpf.out"})
124+
test_microvm.jailer.extra_args.update({"seccomp-filter": bpf_path.name})
229125

230126
test_microvm.spawn()
231-
232127
# give time for the process to get killed
233128
time.sleep(1)
129+
assert "Seccomp error: Filter deserialization failed" in test_microvm.log_data
234130

235131
test_microvm.mark_killed()

0 commit comments

Comments
 (0)