Path: blob/main/tests/integration_tests/security/test_seccomp.py
1958 views
# Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.1# SPDX-License-Identifier: Apache-2.02"""Tests that the seccomp filters don't let denied syscalls through."""34import os5import tempfile6import platform7import time8import pytest910from host_tools.cargo_build import run_seccompiler_bin11import framework.utils as utils121314def _get_basic_syscall_list():15"""Return the JSON list of syscalls that the demo jailer needs."""16if platform.machine() == "x86_64":17sys_list = [18"rt_sigprocmask",19"rt_sigaction",20"execve",21"mmap",22"mprotect",23"arch_prctl",24"set_tid_address",25"readlink",26"open",27"read",28"close",29"brk",30"sched_getaffinity",31"sigaltstack",32"munmap",33"exit_group",34"poll"35]36else:37# platform.machine() == "aarch64"38sys_list = [39"rt_sigprocmask",40"rt_sigaction",41"execve",42"mmap",43"mprotect",44"set_tid_address",45"read",46"close",47"brk",48"sched_getaffinity",49"sigaltstack",50"munmap",51"exit_group",52"ppoll"53]5455json = ""56for syscall in sys_list[0:-1]:57json += """58{{59"syscall": \"{}\"60}},61""".format(syscall)6263json += """64{{65"syscall": \"{}\"66}}67""".format(sys_list[-1])6869return json707172def _run_seccompiler_bin(json_data, basic=False):73json_temp = tempfile.NamedTemporaryFile(delete=False)74json_temp.write(json_data.encode('utf-8'))75json_temp.flush()7677bpf_temp = tempfile.NamedTemporaryFile(delete=False)7879run_seccompiler_bin(bpf_path=bpf_temp.name,80json_path=json_temp.name, basic=basic)8182os.unlink(json_temp.name)83return bpf_temp.name848586def test_seccomp_ls(bin_seccomp_paths):87"""Assert that the seccomp filter denies an unallowed syscall."""88# pylint: disable=redefined-outer-name89# pylint: disable=subprocess-run-check90# The fixture pattern causes a pylint false positive for that rule.9192# Path to the `ls` binary, which attempts to execute the forbidden93# `SYS_access`.94ls_command_path = '/bin/ls'95demo_jailer = bin_seccomp_paths['demo_jailer']96assert os.path.exists(demo_jailer)9798json_filter = """{{99"main": {{100"default_action": "trap",101"filter_action": "allow",102"filter": [103{}104]105}}106}}""".format(_get_basic_syscall_list())107108# Run seccompiler-bin.109bpf_path = _run_seccompiler_bin(json_filter)110111# Run the mini jailer.112outcome = utils.run_cmd([demo_jailer, ls_command_path, bpf_path],113no_shell=True,114ignore_return_code=True)115116os.unlink(bpf_path)117118# The seccomp filters should send SIGSYS (31) to the binary. `ls` doesn't119# handle it, so it will exit with error.120assert outcome.returncode != 0121122123def test_advanced_seccomp(bin_seccomp_paths):124"""125Test seccompiler-bin with `demo_jailer`.126127Test that the demo jailer (with advanced seccomp) allows the harmless demo128binary, denies the malicious demo binary and that an empty allowlist129denies everything.130"""131# pylint: disable=redefined-outer-name132# pylint: disable=subprocess-run-check133# The fixture pattern causes a pylint false positive for that rule.134135demo_jailer = bin_seccomp_paths['demo_jailer']136demo_harmless = bin_seccomp_paths['demo_harmless']137demo_malicious = bin_seccomp_paths['demo_malicious']138139assert os.path.exists(demo_jailer)140assert os.path.exists(demo_harmless)141assert os.path.exists(demo_malicious)142143json_filter = """{{144"main": {{145"default_action": "trap",146"filter_action": "allow",147"filter": [148{},149{{150"syscall": "write",151"args": [152{{153"index": 0,154"type": "dword",155"op": "eq",156"val": 1,157"comment": "stdout fd"158}},159{{160"index": 2,161"type": "qword",162"op": "eq",163"val": 14,164"comment": "nr of bytes"165}}166]167}}168]169}}170}}""".format(_get_basic_syscall_list())171172# Run seccompiler-bin.173bpf_path = _run_seccompiler_bin(json_filter)174175# Run the mini jailer for harmless binary.176outcome = utils.run_cmd([demo_jailer, demo_harmless, bpf_path],177no_shell=True,178ignore_return_code=True)179180# The demo harmless binary should have terminated gracefully.181assert outcome.returncode == 0182183# Run the mini jailer for malicious binary.184outcome = utils.run_cmd([demo_jailer, demo_malicious, bpf_path],185no_shell=True,186ignore_return_code=True)187188# The demo malicious binary should have received `SIGSYS`.189assert outcome.returncode == -31190191os.unlink(bpf_path)192193# Run seccompiler-bin with `--basic` flag.194bpf_path = _run_seccompiler_bin(json_filter, basic=True)195196# Run the mini jailer for malicious binary.197outcome = utils.run_cmd([demo_jailer, demo_malicious, bpf_path],198no_shell=True,199ignore_return_code=True)200201# The malicious binary also terminates gracefully, since the --basic option202# disables all argument checks.203assert outcome.returncode == 0204205os.unlink(bpf_path)206207# Run the mini jailer with an empty allowlist. It should trap on any208# syscall.209json_filter = """{210"main": {211"default_action": "trap",212"filter_action": "allow",213"filter": []214}215}"""216217# Run seccompiler-bin.218bpf_path = _run_seccompiler_bin(json_filter)219220outcome = utils.run_cmd([demo_jailer, demo_harmless, bpf_path],221no_shell=True,222ignore_return_code=True)223224# The demo binary should have received `SIGSYS`.225assert outcome.returncode == -31226227os.unlink(bpf_path)228229230def test_no_seccomp(test_microvm_with_api):231"""Test Firecracker --no-seccomp."""232test_microvm = test_microvm_with_api233test_microvm.jailer.extra_args.update({"no-seccomp": None})234test_microvm.spawn()235236test_microvm.basic_config()237238test_microvm.start()239240utils.assert_seccomp_level(test_microvm.jailer_clone_pid, "0")241242243# The possible Firecracker --seccomp-level values.244# "default" stands for no custom parameter.245SECCOMP_LEVELS = ["default", "0", "1", "2"]246247# Map FC seccomp-level to kernel seccomp-level.248# Note that level 1 also maps to kernel level 2, which stands for249# any custom BPF filter.250# The default is 2.251KERNEL_LEVEL = {"default": "2", "0": "0", "1": "2", "2": "2"}252253254@pytest.mark.parametrize(255"level",256SECCOMP_LEVELS257)258def test_seccomp_level(test_microvm_with_api, level):259"""Test Firecracker --seccomp-level value."""260test_microvm = test_microvm_with_api261test_microvm.jailer.daemonize = False262263if level != "default":264test_microvm.jailer.extra_args.update({"seccomp-level": level})265266test_microvm.spawn(create_logger=False)267268test_microvm.basic_config()269270test_microvm.start()271272utils.assert_seccomp_level(273test_microvm.jailer_clone_pid, KERNEL_LEVEL[level])274275test_microvm.kill()276277# For seccomp-level, check that we output the deprecation warnings.278if level != "default":279time.sleep(0.5)280with open(test_microvm.screen_log, 'r') as file:281log_data = file.read()282assert "You are using a deprecated parameter: --seccomp-level " \283f"{level}, that will be removed in a future version." \284in log_data285286287