Skip to content

Commit 5316793

Browse files
JoshuaWattEmantor
authored andcommitted
labgrid-raw-interface: Add permissions
Adds support for the helper.yaml file to specify which subcommands users are allowed to execute using labgrid-raw-interface. Denied commands will fail with an error. The RawNetworkInterfaceDriver is updated to query if the `ip` command is allowed before attemping to bring the interface up and down on activate/deactivate. This prevents it from erroring out if this behavior is not allowed Signed-off-by: Joshua Watt <Joshua.Watt@garmin.com>
1 parent bfb6ff2 commit 5316793

2 files changed

Lines changed: 46 additions & 15 deletions

File tree

helpers/labgrid-raw-interface

Lines changed: 40 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -11,19 +11,27 @@ import argparse
1111
import os
1212
import string
1313
import sys
14+
import json
1415

1516
import yaml
1617

18+
DENYLIST_FILE = "/etc/labgrid/helpers.yaml"
19+
PROGRAMS = ("tcpreplay", "tcpdump", "ip", "ethtool", "allowed-commands")
1720

18-
def get_denylist():
19-
denylist_file = "/etc/labgrid/helpers.yaml"
21+
22+
def get_config():
2023
try:
21-
with open(denylist_file) as stream:
24+
with open(DENYLIST_FILE) as stream:
2225
data = yaml.load(stream, Loader=yaml.SafeLoader)
2326
except (PermissionError, FileNotFoundError, AttributeError) as e:
24-
raise Exception(f"No configuration file ({denylist_file}), inaccessable or invalid yaml") from e
27+
raise Exception(f"No configuration file ({DENYLIST_FILE}), inaccessable or invalid yaml") from e
28+
29+
return data.get("raw-interface", {})
30+
2531

26-
denylist = data.get("raw-interface", {}).get("denied-interfaces", [])
32+
def get_denylist(config):
33+
# FIXME: don't default to empty list for the check below
34+
denylist = config.get("denied-interfaces", [])
2735

2836
if not isinstance(denylist, list):
2937
raise Exception("No explicit denied-interfaces or not a list, please check your configuration")
@@ -33,26 +41,42 @@ def get_denylist():
3341
return denylist
3442

3543

44+
def get_allowed_commands(config):
45+
allowed = set(config.get("allowed-commands", PROGRAMS))
46+
allowed.add("allowed-commands")
47+
return allowed
48+
49+
3650
def main(program, options):
51+
config = get_config()
52+
53+
if program not in PROGRAMS:
54+
raise ValueError(f"Invalid program {program} called with wrapper, valid programs are: {PROGRAMS}")
55+
56+
allowed_commands = get_allowed_commands(config)
57+
if program not in allowed_commands:
58+
raise ValueError(f"Command '{program}' is not allowed.")
59+
60+
args = [
61+
program,
62+
]
63+
64+
if program == "allowed-commands":
65+
print(json.dumps(sorted(list(allowed_commands))))
66+
return 0
67+
3768
if not options.ifname:
3869
raise ValueError("Empty interface name.")
3970
if any((c == "/" or c.isspace()) for c in options.ifname):
4071
raise ValueError(f"Interface name '{options.ifname}' contains invalid characters.")
4172
if len(options.ifname) > 15:
4273
raise ValueError(f"Interface name '{options.ifname}' is too long.")
4374

44-
denylist = get_denylist()
75+
denylist = get_denylist(config)
4576

4677
if options.ifname in denylist:
4778
raise ValueError(f"Interface name '{options.ifname}' is denied in denylist.")
4879

49-
programs = ["tcpreplay", "tcpdump", "ip", "ethtool"]
50-
if program not in programs:
51-
raise ValueError(f"Invalid program {program} called with wrapper, valid programs are: {programs}")
52-
53-
args = [
54-
program,
55-
]
5680

5781
if program == "tcpreplay":
5882
args.append(f"--intf1={options.ifname}")
@@ -123,6 +147,9 @@ if __name__ == "__main__":
123147
parser.add_argument("-d", "--debug", action="store_true", default=False, help="enable debug mode")
124148
subparsers = parser.add_subparsers(dest="program", help="program to run")
125149

150+
# allowed-commands
151+
allowed_commands_parser = subparsers.add_parser("allowed-commands")
152+
126153
# tcpdump
127154
tcpdump_parser = subparsers.add_parser("tcpdump")
128155
tcpdump_parser.add_argument("ifname", type=str, help="interface name")

labgrid/driver/rawnetworkinterfacedriver.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,14 +35,18 @@ def __attrs_post_init__(self):
3535
super().__attrs_post_init__()
3636
self._record_handle = None
3737
self._replay_handle = None
38+
self._allowed_commands = []
3839

3940
def on_activate(self):
40-
if self.manage_interface:
41+
self._allowed_commands = set(
42+
json.loads(subprocess.run(self._wrap_command(["allowed-commands"]), stdout=subprocess.PIPE).stdout)
43+
)
44+
if self.manage_interface and "ip" in self._allowed_commands:
4145
self._set_interface("up")
4246
self._wait_state("up")
4347

4448
def on_deactivate(self):
45-
if self.manage_interface:
49+
if self.manage_interface and "ip" in self._allowed_commands:
4650
self._set_interface("down")
4751
self._wait_state("down")
4852

0 commit comments

Comments
 (0)