Add basic pledge/unveil sandbox on OpenBSD
[tinc] / test / integration / sandbox.py
1 #!/usr/bin/env python3
2
3 """Test that tincd works through proxies."""
4
5 import os
6 import time
7
8 from testlib import check, cmd, path, util
9 from testlib.proc import Tinc, Script
10 from testlib.test import Test
11 from testlib.log import log
12 from testlib.feature import HAVE_SANDBOX
13
14
15 def init(ctx: Test, level: str) -> Tinc:
16     """Create a new tinc node."""
17
18     node = ctx.node()
19
20     stdin = f"""
21         init {node}
22         set Address 127.0.0.1
23         set Port 0
24         set DeviceType dummy
25         set Sandbox {level}
26     """
27     node.cmd(stdin=stdin)
28
29     return node
30
31
32 def test_scripts_work(ctx: Test, level: str) -> None:
33     """Test that scripts work under the sandbox level."""
34     foo = init(ctx, level)
35     foo.cmd("set", "Subnet", "1.2.3.4")
36
37     for script in Script:
38         foo.add_script(script)
39
40     foo.cmd("start")
41     foo[Script.TINC_UP].wait()
42     foo[Script.SUBNET_UP].wait()
43
44     if os.name != "nt":
45         foo.cmd("set", "ScriptsInterpreter", path.PYTHON_PATH)
46
47     foo.cmd("stop")
48     foo[Script.SUBNET_DOWN].wait()
49     foo[Script.TINC_DOWN].wait()
50
51
52 def test_high_scripts(ctx: Test) -> None:
53     """Test that only tinc-up/subnet-up work on highest isolation level."""
54     foo = init(ctx, "high")
55     foo.cmd("set", "Subnet", "1.2.3.4")
56
57     for script in Script:
58         foo.add_script(script)
59
60     foo.cmd("start")
61     for script in Script.TINC_UP, Script.SUBNET_UP:
62         foo[script].wait()
63
64     time.sleep(1)
65     foo.cmd("stop")
66
67     while True:
68         try:
69             foo.cmd("pid", code=1)
70             break
71         except ValueError:
72             time.sleep(0.5)
73
74     log.info("check that no other scripts were called")
75     for script in Script.SUBNET_DOWN, Script.TINC_DOWN:
76         check.false(foo[script].wait(0.01))
77
78
79 def create_exec_proxy() -> str:
80     """Create a fake exec proxy that stops the test with an error."""
81     code = f"""
82 import os
83 import signal
84
85 os.kill({os.getpid()}, signal.SIGTERM)
86 """
87     return util.temp_file(code)
88
89
90 def test_exec_proxy_does_not_start_on_high(ctx: Test) -> None:
91     """Check that tincd does not start if both exec proxy and high level are set."""
92     foo = init(ctx, "high")
93     foo.cmd("set", "Proxy", "exec", path.PYTHON_INTERPRETER)
94     foo.cmd("start", code=1)
95
96
97 def test_bad_sandbox_level(ctx: Test, level: str) -> None:
98     """Check that tincd does not start if a bad sandbox level is used."""
99     foo = init(ctx, level)
100     foo.cmd("start", code=1)
101
102
103 def test_exec_proxy_high(ctx: Test) -> None:
104     """Test that exec proxy does not work at maximum isolation."""
105     foo, bar = init(ctx, "high"), init(ctx, "high")
106
107     foo.add_script(Script.TINC_UP)
108     foo.start()
109
110     proxy = create_exec_proxy()
111     foo.cmd("set", "Proxy", "exec", f"{path.PYTHON_INTERPRETER} {proxy}")
112
113     cmd.exchange(foo, bar)
114     bar.cmd("set", f"{foo}.Port", str(foo.port))
115
116     bar.add_script(Script.TINC_UP)
117     bar.cmd("start")
118     bar[Script.TINC_UP].wait()
119
120     time.sleep(1)
121
122     bar.cmd("stop")
123     foo.cmd("stop")
124
125
126 with Test("all scripts work at level 'off'") as context:
127     test_scripts_work(context, "off")
128
129 if HAVE_SANDBOX:
130     with Test("all scripts work at level 'normal'") as context:
131         test_scripts_work(context, "normal")
132
133     with Test("only tinc-up and first subnet-up work at level 'high'") as context:
134         test_high_scripts(context)
135
136     with Test("tincd does not start with exec proxy and level 'high'") as context:
137         test_exec_proxy_does_not_start_on_high(context)
138
139     with Test("tincd does not start with bad sandbox level") as context:
140         test_bad_sandbox_level(context, "foobar")
141
142     with Test("exec proxy does not work at level 'high'") as context:
143         test_exec_proxy_high(context)
144 else:
145     with Test("tincd does not start with bad sandbox level") as context:
146         for lvl in "normal", "high", "foobar":
147             test_bad_sandbox_level(context, lvl)