diff --git a/.github/workflows/testing.yml b/.github/workflows/testing.yml index bf1d6ecc0..6fef99953 100644 --- a/.github/workflows/testing.yml +++ b/.github/workflows/testing.yml @@ -13,7 +13,16 @@ jobs: steps: - name: Checkout source uses: actions/checkout@v2.3.4 - - name: Run tests + - name: Install dependencies + shell: bash {0} + run: cmd/prepare + - name: Package Testrun + shell: bash {0} + run: cmd/package + - name: Install Testrun + shell: bash {0} + run: sudo dpkg -i testrun*.deb + - name: Run baseline tests shell: bash {0} run: testing/baseline/test_baseline @@ -24,12 +33,21 @@ jobs: steps: - name: Checkout source uses: actions/checkout@v2.3.4 + - name: Install dependencies + shell: bash {0} + run: cmd/prepare + - name: Package Testrun + shell: bash {0} + run: cmd/package + - name: Install Testrun + shell: bash {0} + run: sudo dpkg -i testrun*.deb - name: Run tests shell: bash {0} run: testing/tests/test_tests - name: Archive runtime results if: ${{ always() }} - run: sudo tar --exclude-vcs -czf runtime.tgz runtime/ + run: sudo tar --exclude-vcs -czf runtime.tgz /usr/local/testrun/runtime/ - name: Upload runtime results uses: actions/upload-artifact@v3 if: ${{ always() }} @@ -45,6 +63,15 @@ jobs: steps: - name: Checkout source uses: actions/checkout@v2.3.4 + - name: Install dependencies + shell: bash {0} + run: cmd/prepare + - name: Package Testrun + shell: bash {0} + run: cmd/package + - name: Install Testrun + shell: bash {0} + run: sudo dpkg -i testrun*.deb - name: Run tests shell: bash {0} run: testing/api/test_api @@ -56,6 +83,6 @@ jobs: steps: - name: Checkout source uses: actions/checkout@v2.3.4 - - name: Run tests + - name: Run pylint shell: bash {0} run: testing/pylint/test_pylint diff --git a/.gitignore b/.gitignore index 7ef392c5e..336202f24 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ pylint.out __pycache__/ build/ testing/unit_test/temp/ +*.deb diff --git a/README.md b/README.md index 41c559499..5ed2d03de 100644 --- a/README.md +++ b/README.md @@ -1,20 +1,20 @@ - Testrun logo + Testrun logo ## Introduction :wave: -Test Run is a tool to automate the validation of network-based functionality of IoT devices. Any device which is capable of receiving an IP address via DHCP is considered an IoT device by Test Run and can be tested. +Testrun is a tool to automate the validation of network-based functionality of IoT devices. Any device which is capable of receiving an IP address via DHCP is considered an IoT device by Testrun and can be tested. ## Motivation :bulb: -Without tools like Test Run, testing labs may be maintaining a large and complex network using equipment such as: A managed layer 3 switch, an enterprise-grade network router, virtualized or physical servers to provide DNS, NTP, 802.1x etc. With this amount of moving parts, all with dynamic configuration files and constant software updates, more time is likely to be spent on preparation and clean up of functinality or penetration testing - not forgetting the number of software tools required to perform the testing. The major issues which can and should be solved: +Without tools like Testrun, testing labs may be maintaining a large and complex network using equipment such as: A managed layer 3 switch, an enterprise-grade network router, virtualized or physical servers to provide DNS, NTP, 802.1x etc. With this amount of moving parts, all with dynamic configuration files and constant software updates, more time is likely to be spent on preparation and clean up of functinality or penetration testing - not forgetting the number of software tools required to perform the testing. The major issues which can and should be solved: 1) The complexity of managing a testing network 2) The time required to perform testing of network functionality 3) The accuracy and consistency of testing network functionality ## How it works :triangular_ruler: -Test Run creates an isolated and controlled network environment to fully simulate enterprise network deployments in your device testing lab. +Testrun creates an isolated and controlled network environment to fully simulate enterprise network deployments in your device testing lab. This removes the necessity for complex hardware, advanced knowledge and networking experience whilst enabling semi-technical engineers to validate device behaviour against industry cyber standards. -Two runtime modes will be supported by Test Run: +Two runtime modes will be supported by Testrun: 1) Automated Testing @@ -22,7 +22,7 @@ Once the device has become operational (steady state), automated testing of the 2) Lab network -Test Run cannot automate everything, and so additional manual testing may be required (or configuration changes may be required on the device). Rather than having to maintain a separate but idential lab network, Test Run will provide the network and some tools to assist an engineer performing the additional testing. At the same time, packet captures of the device behaviour will be recorded, alongside logs for each network service, for further debugging. +Testrun cannot automate everything, and so additional manual testing may be required (or configuration changes may be required on the device). Rather than having to maintain a separate but idential lab network, Testrun will provide the network and some tools to assist an engineer performing the additional testing. At the same time, packet captures of the device behaviour will be recorded, alongside logs for each network service, for further debugging. ## Minimum Requirements :computer: ### Hardware @@ -34,8 +34,11 @@ Test Run cannot automate everything, and so additional manual testing may be req - Docker - [Install guide](https://docs.docker.com/engine/install/ubuntu/) - Open vSwitch ``sudo apt-get install openvswitch-common openvswitch-switch`` +## Get started ▶️ +Once you have met the hardware and software requirements, you can get started with Testrun by following the [Get started guide](docs/get_started.md). + ## Roadmap :chart_with_upwards_trend: -Test Run will constantly evolve to further support end-users by automating device network behaviour against industry standards. +Testrun will constantly evolve to further support end-users by automating device network behaviour against industry standards. ## Issue reporting :triangular_flag_on_post: If the application has come across a problem at any point during setup or use, please raise an issue under the [issues tab](https://github.com/auto-iot/test-run/issues). Issue templates exist for both bug reports and feature requests. If neither of these are appropriate for your issue, raise a blank issue instead. @@ -44,11 +47,11 @@ If the application has come across a problem at any point during setup or use, p The contributing requirements can be found in [CONTRIBUTING.md](CONTRIBUTING.md). In short, checkout the [Google CLA](https://cla.developers.google.com/) site to get started. ## FAQ :raising_hand: -1) What device networking functionality is validated by Test Run? +1) What device networking functionality is validated by Testrun? Best practices and requirements for IoT devices are constantly changing due to technological advances and discovery of vulnerabilities. The current expectations for IoT devices on Google deployments can be found in the [Application Security Requirements for IoT Devices](https://partner-security.withgoogle.com/docs/iot_requirements). - Test Run aims to automate as much of the Application Security Requirements as possible. + Testrun aims to automate as much of the Application Security Requirements as possible. 2) What services are provided on the virtual network? @@ -58,11 +61,11 @@ The contributing requirements can be found in [CONTRIBUTING.md](CONTRIBUTING.md) - NTPv4 - 802.1x Port Based Authentication -3) Can I run Test Run on a virtual machine? +3) Can I run Testrun on a virtual machine? - Probably. Provided that the required 2x USB ethernet adapters are passed to the virtual machine as USB devices rather than network adapters, Test Run should - still work. We will look to test and approve the use of virtualisation to run Test Run in the future. + Probably. Provided that the required 2x USB ethernet adapters are passed to the virtual machine as USB devices rather than network adapters, Testrun should + still work. We will look to test and approve the use of virtualisation to run Testrun in the future. - 4) Can I connect multiple devices to Test Run? + 4) Can I connect multiple devices to Testrun? In short, Yes you can. The way in which multiple devices could be tested simultaneously is yet to be decided. However, if you simply want to add field/peer devices during runtime (even another laptop performing manual testing) then you may connect the USB ethernet adapter to an unmanaged switch. diff --git a/bin/testrun b/bin/testrun index ea65d3565..82c8ab237 100755 --- a/bin/testrun +++ b/bin/testrun @@ -26,12 +26,12 @@ fi # Ensure that /var/run/netns folder exists sudo mkdir -p /var/run/netns +export TESTRUNPATH=/usr/local/testrun +cd $TESTRUNPATH + # Create device folder if it doesn't exist mkdir -p local/devices -# Check if Python modules exist. Install if not -[ ! -d "venv" ] && sudo cmd/install - # Remove existing runtime data rm -rf runtime/* @@ -39,7 +39,7 @@ rm -rf runtime/* source venv/bin/activate # Set the PYTHONPATH to include the "src" directory -export PYTHONPATH="$PWD/framework/python/src" +export PYTHONPATH="$TESTRUNPATH/framework/python/src" python -u framework/python/src/core/test_runner.py $@ deactivate \ No newline at end of file diff --git a/cmd/build b/cmd/build new file mode 100755 index 000000000..7e69393c8 --- /dev/null +++ b/cmd/build @@ -0,0 +1,52 @@ +#!/bin/bash -e + +# Copyright 2023 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Builds all docker images +echo Building docker images + +# Build user interface +echo Building user interface +mkdir -p build/ui +docker build -t test-run/ui -f modules/ui/ui.Dockerfile . > build/ui/ui.log 2>&1 + +# Build network modules +echo Building network modules +mkdir -p build/network +for dir in modules/network/* ; do + module=$(basename $dir) + echo Building network module $module... + docker build -f modules/network/$module/$module.Dockerfile -t test-run/$module . > build/network/$module.log 2>&1 +done + +# Build validators +echo Building network validators +mkdir -p build/devices +for dir in modules/devices/* ; do + module=$(basename $dir) + echo Building validator module $module... + docker build -f modules/devices/$module/$module.Dockerfile -t test-run/$module . > build/devices/$module.log 2>&1 +done + +# Build test modules +echo Building test modules +mkdir -p build/test +for dir in modules/test/* ; do + module=$(basename $dir) + echo Building test module $module... + docker build -f modules/test/$module/$module.Dockerfile -t test-run/$module-test . > build/test/$module.log 2>&1 +done + +echo Finished building modules \ No newline at end of file diff --git a/cmd/install b/cmd/install index 7997f37fa..929f9136c 100755 --- a/cmd/install +++ b/cmd/install @@ -14,17 +14,23 @@ # See the License for the specific language governing permissions and # limitations under the License. +echo Installing application dependencies + +TESTRUN_DIR=/usr/local/testrun +cd $TESTRUN_DIR + python3 -m venv venv source venv/bin/activate pip3 install -r framework/requirements.txt -# Dependency for printing reports to pdf -# required by python package weasyprint -sudo apt-get install libpangocairo-1.0-0 +# Copy the default configuration +cp -u local/system.json.example local/system.json + +deactivate -#TODO move into docker build process -(cd modules/ui && npm install && npm run build) +# Build docker images +sudo cmd/build -deactivate \ No newline at end of file +echo Finished installing Testrun diff --git a/cmd/package b/cmd/package new file mode 100755 index 000000000..d134896d3 --- /dev/null +++ b/cmd/package @@ -0,0 +1,53 @@ +#!/bin/bash -e + +# Copyright 2023 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Creates a package for Testrun + +MAKE_SRC_DIR=make + +# Copy testrun script to /bin +mkdir -p $MAKE_SRC_DIR/bin +cp bin/testrun $MAKE_SRC_DIR/bin/testrun + +# Create testrun folder +mkdir -p $MAKE_SRC_DIR/usr/local/testrun + +# Create postinst script +cp cmd/install $MAKE_SRC_DIR/DEBIAN/postinst + +# Copy other commands +mkdir -p $MAKE_SRC_DIR/usr/local/testrun/cmd +cp cmd/{prepare,build} $MAKE_SRC_DIR/usr/local/testrun/cmd + +# Create local folder +mkdir -p $MAKE_SRC_DIR/usr/local/testrun/local +cp local/system.json.example $MAKE_SRC_DIR/usr/local/testrun/local/system.json.example + +# Create device repository +mkdir -p $MAKE_SRC_DIR/usr/local/testrun/local/devices + +# Copy root_certs folder +mkdir -p local/root_certs +cp -r local/root_certs $MAKE_SRC_DIR/usr/local/testrun/local/root_certs + +# Copy framework and modules into testrun folder +cp -r {framework,modules} $MAKE_SRC_DIR/usr/local/testrun + +# Build .deb file +dpkg-deb --build --root-owner-group make + +# Rename the .deb file +mv make.deb testrun_1-0_amd64.deb \ No newline at end of file diff --git a/cmd/prepare b/cmd/prepare new file mode 100755 index 000000000..950051bd3 --- /dev/null +++ b/cmd/prepare @@ -0,0 +1,24 @@ +#!/bin/bash -e + +# Copyright 2023 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Optional script to prepare your system for use with Testrun. +# Installs system dependencies + +echo Installing system dependencies + +sudo apt-get install openvswitch-common openvswitch-switch python3 libpangocairo-1.0-0 + +echo Finished installing system dependencies diff --git a/docs/configure_device.md b/docs/configure_device.md index 320d3c325..9eefcd866 100644 --- a/docs/configure_device.md +++ b/docs/configure_device.md @@ -8,24 +8,12 @@ The device information section includes the manufacturer, model, and MAC address ## Test Modules -Test modules are groups of tests that can be enabled or disabled as needed. You can choose which test modules to include for your device. The device configuration file contains the following test module: - -- DNS Test Module +Test modules are groups of tests that can be enabled or disabled as needed. You can choose which test modules to run on your device. ### Enabling and Disabling Test Modules To enable or disable a test module, modify the `enabled` field within the respective module. Setting it to `true` enables the module, while setting it to `false` disables the module. -## Individual Tests - -Within the DNS test module, there are individual tests that can be enabled or disabled. These tests focus on specific aspects of network behavior. You can customize the tests based on your device and testing requirements. - -### Enabling and Disabling Tests - -To enable or disable an individual test, modify the `enabled` field within the respective test. Setting it to `true` enables the test, while setting it to `false` disables the test. - -> Note: The example device configuration file (`resources/devices/template/device_config.json`) provides a complete usage example, including the structure and configuration options for the DNS test module and its tests. You can refer to this file to understand how to configure your device tests effectively. - ## Customizing the Device Configuration To customize the device configuration for your specific device, follow these steps: diff --git a/docs/dev/architecture.png b/docs/dev/architecture.png new file mode 100644 index 000000000..e349141b6 Binary files /dev/null and b/docs/dev/architecture.png differ diff --git a/docs/get_started.md b/docs/get_started.md index 7b8cf9e13..fcdd21fe7 100644 --- a/docs/get_started.md +++ b/docs/get_started.md @@ -4,7 +4,7 @@ ### Hardware -Before starting with Test Run, ensure you have the following hardware: +Before starting with Testrun, ensure you have the following hardware: - PC running Ubuntu LTS (laptop or desktop) - 2x USB Ethernet adapter (one may be a built-in Ethernet port) @@ -20,15 +20,9 @@ Ensure the following software is installed on your Ubuntu LTS PC: ## Installation -1. Download Test Run from the releases page or the appropriate source. +1. Download the latest version of Testrun from the [releases page](https://github.com/google/test-run/releases) -2. Run the install script. - -## Configuration - -1. Copy the default configuration file. - -2. Open the `local/system.json` file and modify the configuration as needed. Specify the interface names for the internet and device interfaces. +2. Install the package using ``sudo dpkg -i testrun_*.deb`` ## Test Your Device @@ -37,9 +31,11 @@ Ensure the following software is installed on your Ubuntu LTS PC: - Connect one USB Ethernet adapter to the internet source (e.g., router or switch) using an Ethernet cable. - Connect the other USB Ethernet adapter directly to the IoT device you want to test using an Ethernet cable. -2. Start Test Run. +2. Start Testrun. + +Start Testrun with the command `sudo testrun` - - To run Test Run in network-only mode (without running any tests), use the `--net-only` option. + - To run Testrun in network-only mode (without running any tests), use the `--net-only` option. - To skip network validation before use and not launch the faux device on startup, use the `--no-validate` option. @@ -49,5 +45,5 @@ If you encounter any issues or need assistance, consider the following: - Ensure that all hardware and software prerequisites are met. - Verify that the network interfaces are connected correctly. -- Check the configuration in the `local/system.json` file. +- Check the configuration settings. - Refer to the Test Run documentation or ask for further assistance from the support team. diff --git a/docs/network/add_new_service.md b/docs/network/add_new_service.md index 1ad07b60d..5f7b470cd 100644 --- a/docs/network/add_new_service.md +++ b/docs/network/add_new_service.md @@ -1,8 +1,8 @@ # Adding a New Network Service -The Test Run framework allows users to add their own network services with ease. A template network service can be used to get started quickly, this can be found at [modules/network/template](../../modules/network/template). Otherwise, see below for details of the requirements for new network services. +The Testrun framework allows users to add their own network services with ease. A template network service can be used to get started quickly, this can be found at [modules/network/template](../../modules/network/template). Otherwise, see below for details of the requirements for new network services. -To add a new network service to Test Run, follow the procedure below: +To add a new network service to Testrun, follow the procedure below: 1. Create a folder under `modules/network/` with the name of the network service in lowercase, using only alphanumeric characters and hyphens (`-`). 2. Inside the created folder, include the following files and folders: diff --git a/framework/python/src/common/session.py b/framework/python/src/common/session.py index edf3ce5da..638d213a8 100644 --- a/framework/python/src/common/session.py +++ b/framework/python/src/common/session.py @@ -17,7 +17,7 @@ import datetime import json import os -from common import util +from common import util, logger NETWORK_KEY = 'network' DEVICE_INTF_KEY = 'device_intf' @@ -29,6 +29,8 @@ API_PORT_KEY = 'api_port' MAX_DEVICE_REPORTS_KEY = 'max_device_reports' +LOGGER = logger.get_logger('session') + class TestRunSession(): """Represents the current session of Test Run.""" @@ -77,7 +79,10 @@ def get_config(self): def _load_config(self): + LOGGER.debug(f'Loading configuration file at {self._config_file}') if not os.path.isfile(self._config_file): + LOGGER.error(f'No configuration file present at {self._config_file}. ' + + 'Default configuration will be used.') return with open(self._config_file, 'r', encoding='utf-8') as f: @@ -113,6 +118,8 @@ def _load_config(self): self._config[MAX_DEVICE_REPORTS_KEY] = config_file_json.get( MAX_DEVICE_REPORTS_KEY) + LOGGER.debug(self._config) + def _save_config(self): with open(self._config_file, 'w', encoding='utf-8') as f: f.write(json.dumps(self._config, indent=2)) @@ -149,7 +156,7 @@ def get_max_device_reports(self): return self._config.get(MAX_DEVICE_REPORTS_KEY) def set_config(self, config_json): - self._config = config_json + self._config.update(config_json) self._save_config() def set_target_device(self, device): @@ -199,7 +206,6 @@ def get_all_reports(self): device_reports = device.get_reports() for device_report in device_reports: reports.append(device_report.to_json()) - return sorted(reports, key=lambda report: report['started'], reverse=True) def add_total_tests(self, no_tests): diff --git a/framework/python/src/core/testrun.py b/framework/python/src/core/testrun.py index 8eadcf441..e10c888ae 100644 --- a/framework/python/src/core/testrun.py +++ b/framework/python/src/core/testrun.py @@ -117,7 +117,6 @@ def __init__(self, # Start UI container self.start_ui() - # Build UI image self._api = Api(self) self._api.start() @@ -291,7 +290,6 @@ def start(self): self.get_net_orc().start_listener() self._set_status('Waiting for Device') LOGGER.info('Waiting for devices on the network...') - time.sleep(self.get_session().get_runtime()) if not (self._test_orc.test_in_progress() or @@ -384,19 +382,17 @@ def _device_stable(self, mac_addr): self._set_status('In Progress') result = self._test_orc.run_test_modules() self._set_status(result) + + def get_session(self): + return self._session def _set_status(self, status): self.get_session().set_status(status) - def get_session(self): - return self._session - def start_ui(self): LOGGER.info('Starting UI') - self._build_ui() - client = docker.from_env() client.containers.run( @@ -413,22 +409,6 @@ def start_ui(self): # TODO: Make port configurable LOGGER.info('User interface is ready on http://localhost:8080') - def _build_ui(self): - - # TODO: Improve this process - build_file = os.path.join(root_dir, - 'modules', - 'ui', - 'ui.Dockerfile') - client = docker.from_env() - - LOGGER.debug('Building user interface') - - client.images.build(dockerfile=build_file, - path=root_dir, - forcerm=True, - tag='test-run/ui') - def _stop_ui(self): client = docker.from_env() try: diff --git a/framework/python/src/net_orc/network_orchestrator.py b/framework/python/src/net_orc/network_orchestrator.py index d1fd9cdb0..975cde112 100644 --- a/framework/python/src/net_orc/network_orchestrator.py +++ b/framework/python/src/net_orc/network_orchestrator.py @@ -114,7 +114,7 @@ def start_network(self): """Start the virtual testing network.""" LOGGER.info('Starting network') - self.build_network_modules() + #self.build_network_modules() self.create_net() self.start_network_services() @@ -130,6 +130,7 @@ def get_listener(self): return self._listener def start_listener(self): + LOGGER.debug("Starting network listener") self.get_listener().start_listener() def stop(self, kill=False): @@ -329,7 +330,7 @@ def create_net(self): self.stop() sys.exit(1) - if os.getenv("GITHUB_ACTIONS"): + if os.getenv('GITHUB_ACTIONS'): self._ci_post_network_create() self._create_private_net() diff --git a/framework/python/src/net_orc/network_validator.py b/framework/python/src/net_orc/network_validator.py index 2a4112764..3866bd3ae 100644 --- a/framework/python/src/net_orc/network_validator.py +++ b/framework/python/src/net_orc/network_validator.py @@ -56,7 +56,7 @@ def start(self): util.run_command(f'chown -R {host_user} {OUTPUT_DIR}') self._load_devices() - self._build_network_devices() + #self._build_network_devices() self._start_network_devices() def stop(self, kill=False): diff --git a/framework/python/src/test_orc/test_orchestrator.py b/framework/python/src/test_orc/test_orchestrator.py index 94b0e4446..8fb0b1c85 100644 --- a/framework/python/src/test_orc/test_orchestrator.py +++ b/framework/python/src/test_orc/test_orchestrator.py @@ -65,7 +65,7 @@ def start(self): os.makedirs(DEVICE_ROOT_CERTS, exist_ok=True) self._load_test_modules() - self.build_test_modules() + #self.build_test_modules() def stop(self): """Stop any running tests""" @@ -85,9 +85,7 @@ def run_test_modules(self): self._session.stop() report = TestReport().from_json(self._generate_report()) device.add_report(report) - self._write_reports(report) - self._test_in_progress = False self._timestamp_results(device) @@ -128,6 +126,14 @@ def _generate_report(self): "%Y-%m-%d %H:%M:%S") report["status"] = self._calculate_result() report["tests"] = self._session.get_report_tests() + out_file = os.path.join( + self._root_path, RUNTIME_DIR, + self._session.get_target_device().mac_addr.replace(":", ""), + "report.json") + + with open(out_file, "w", encoding="utf-8") as f: + json.dump(report, f, indent=2) + util.run_command(f"chown -R {self._host_user} {out_file}") return report def _calculate_result(self): @@ -466,7 +472,7 @@ def _stop_module(self, module, kill=False): def get_test_modules(self): return self._test_modules - + def get_test_module(self, name): for test_module in self.get_test_modules(): if test_module.name == name: diff --git a/make/.gitignore b/make/.gitignore new file mode 100644 index 000000000..1be953b79 --- /dev/null +++ b/make/.gitignore @@ -0,0 +1,2 @@ +usr/ +bin/ \ No newline at end of file diff --git a/make/DEBIAN/control b/make/DEBIAN/control new file mode 100644 index 000000000..20463e996 --- /dev/null +++ b/make/DEBIAN/control @@ -0,0 +1,6 @@ +Package: Testrun +Version: 1.0 +Architecture: amd64 +Maintainer: Google +Description: Automatically verify IoT device network behavior +Depends: libpangocairo-1.0-0, openvswitch-common, openvswitch-switch, python3 diff --git a/make/DEBIAN/postinst b/make/DEBIAN/postinst new file mode 100755 index 000000000..929f9136c --- /dev/null +++ b/make/DEBIAN/postinst @@ -0,0 +1,36 @@ +#!/bin/bash -e + +# Copyright 2023 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +echo Installing application dependencies + +TESTRUN_DIR=/usr/local/testrun +cd $TESTRUN_DIR + +python3 -m venv venv + +source venv/bin/activate + +pip3 install -r framework/requirements.txt + +# Copy the default configuration +cp -u local/system.json.example local/system.json + +deactivate + +# Build docker images +sudo cmd/build + +echo Finished installing Testrun diff --git a/modules/network/dhcp-1/dhcp-1.Dockerfile b/modules/network/dhcp-1/dhcp-1.Dockerfile index 49845cc3b..272405ccd 100644 --- a/modules/network/dhcp-1/dhcp-1.Dockerfile +++ b/modules/network/dhcp-1/dhcp-1.Dockerfile @@ -19,13 +19,13 @@ ARG MODULE_NAME=dhcp-1 ARG MODULE_DIR=modules/network/$MODULE_NAME # Install all necessary packages -RUN apt-get install -y wget +RUN apt-get update && apt-get install -y wget apt-transport-https -#Update the oui.txt file from ieee +# Update the oui.txt file from ieee RUN wget http://standards-oui.ieee.org/oui.txt -P /usr/local/etc/ # Install dhcp server -RUN apt-get update && apt-get install -y isc-dhcp-server radvd systemd +RUN apt-get install -y --fix-missing isc-dhcp-server radvd systemd # Copy over all configuration files COPY $MODULE_DIR/conf /testrun/conf diff --git a/modules/network/dhcp-2/dhcp-2.Dockerfile b/modules/network/dhcp-2/dhcp-2.Dockerfile index e91465f36..dc6f6da2c 100644 --- a/modules/network/dhcp-2/dhcp-2.Dockerfile +++ b/modules/network/dhcp-2/dhcp-2.Dockerfile @@ -19,7 +19,7 @@ ARG MODULE_NAME=dhcp-2 ARG MODULE_DIR=modules/network/$MODULE_NAME # Install all necessary packages -RUN apt-get install -y wget +RUN apt-get update && apt-get install -y wget apt-transport-https #Update the oui.txt file from ieee RUN wget http://standards-oui.ieee.org/oui.txt -P /usr/local/etc/ diff --git a/modules/test/base/python/src/test_module.py b/modules/test/base/python/src/test_module.py index fe654decd..26a6decdf 100644 --- a/modules/test/base/python/src/test_module.py +++ b/modules/test/base/python/src/test_module.py @@ -119,7 +119,7 @@ def run_tests(self): 'name'] + ' failed - see result details for more info' else: test['result_description'] = test[ - 'name'] + ' Skipped - see result details for more info' + 'name'] + ' skipped - see result details for more info' test['end'] = datetime.now().isoformat() duration = datetime.fromisoformat(test['end']) - datetime.fromisoformat( diff --git a/modules/test/conn/python/src/connection_module.py b/modules/test/conn/python/src/connection_module.py index 779dd7d4c..558748bbd 100644 --- a/modules/test/conn/python/src/connection_module.py +++ b/modules/test/conn/python/src/connection_module.py @@ -194,7 +194,7 @@ def _connection_ipaddr_ip_change(self): result = None, 'Device has no current DHCP lease' # Restore the network self._dhcp_util.restore_failover_dhcp_server() - LOGGER.info("Waiting 30 seconds for reserved lease to expire") + LOGGER.info('Waiting 30 seconds for reserved lease to expire') time.sleep(30) self._dhcp_util.get_new_lease(self._device_mac) else: @@ -281,7 +281,6 @@ def _connection_ipv6_slaac(self): def _connection_ipv6_ping(self): LOGGER.info('Running connection.ipv6_ping') result = None - if self._device_ipv6_addr is None: LOGGER.info('No IPv6 SLAAC address found. Cannot ping') result = None, 'No IPv6 SLAAC address found. Cannot ping' diff --git a/modules/test/nmap/python/src/nmap_module.py b/modules/test/nmap/python/src/nmap_module.py index 6bcbd141a..94597f03e 100644 --- a/modules/test/nmap/python/src/nmap_module.py +++ b/modules/test/nmap/python/src/nmap_module.py @@ -109,10 +109,10 @@ def _check_unknown_ports(self,tests,scan_results): for test in tests: if "tcp_ports" in tests[test]: for port in tests[test]['tcp_ports']: - known_ports.append(port) + known_ports.append(port) if "udp_ports" in tests[test]: for port in tests[test]['udp_ports']: - known_ports.append(port) + known_ports.append(port) for port_result in scan_results: if not port_result in known_ports: @@ -134,7 +134,7 @@ def _add_unknown_ports(self,tests,unallowed_port): LOGGER.info("Unknown Port Service: " + unallowed_port['service']) for test in tests: LOGGER.debug("Checking for known service: " + test) - # Create a regular expression pattern to match the variable at the + # Create a regular expression pattern to match the variable at the # end of the string port_service = r"\b" + re.escape(unallowed_port['service']) + r"\b$" service_match = re.search(port_service, test) @@ -166,7 +166,6 @@ def _check_scan_results(self,test_config,scan_results): if "udp_ports" in test_config: port_config = test_config["udp_ports"] self._check_scan_result(port_config=port_config,scan_results=scan_results) - def _check_scan_result(self,port_config,scan_results): if port_config is not None: @@ -213,16 +212,16 @@ def _check_unallowed_port(self,unallowed_ports,tests): version = None service = None for port in unallowed_ports: - LOGGER.info('Checking unallowed port: ' + port['port']) - LOGGER.info('Looking for service: ' + port['service']) - LOGGER.debug('Unallowed Port Config: ' + str(port)) - if port['tcp_udp'] == 'tcp': - port_style = 'tcp_ports' - elif port['tcp_udp'] == 'udp': - port_style = 'udp_ports' + LOGGER.info("Checking unallowed port: " + port["port"]) + LOGGER.info("Looking for service: " + port["service"]) + LOGGER.debug("Unallowed Port Config: " + str(port)) + if port["tcp_udp"] == "tcp": + port_style = "tcp_ports" + elif port["tcp_udp"] == "udp": + port_style = "udp_ports" for test in tests: - LOGGER.debug('Checking test: ' + str(test)) - # Create a regular expression pattern to match the variable at the + LOGGER.debug("Checking test: " + str(test)) + # Create a regular expression pattern to match the variable at the # end of the string port_service = r"\b" + re.escape(port['service']) + r"\b$" service_match = re.search(port_service, test) @@ -247,7 +246,7 @@ def _check_unallowed_port(self,unallowed_ports,tests): for u_port in self._unallowed_ports: if port['port'] in u_port['port']: self._unallowed_ports.remove(u_port) - break + break break def _check_version(self,service,version_detected,version_expected): @@ -259,8 +258,8 @@ def _check_version(self,service,version_detected,version_expected): result. """ LOGGER.info("Checking version for service: " + service) - LOGGER.info("NMAP Version Detected: " + version_detected) - LOGGER.info("Version Expected: " + version_expected) + LOGGER.info("NMAP Version Detected: " + version_detected) + LOGGER.info("Version Expected: " + version_expected) version_check = None match service: case "ssh": @@ -355,12 +354,12 @@ def _scan_udp_ports(self, tests): def _nmap_results_to_json(self,nmap_results): try: - xml_data = xmltodict.parse(nmap_results) - json_data = json.dumps(xml_data, indent=4) - return json.loads(json_data) + xml_data = xmltodict.parse(nmap_results) + json_data = json.dumps(xml_data, indent=4) + return json.loads(json_data) except Exception as e: - LOGGER.error(f"Error parsing Nmap output: {e}") + LOGGER.error(f"Error parsing Nmap output: {e}") def _process_nmap_json_results(self,nmap_results_json): LOGGER.debug("nmap results\n" + json.dumps(nmap_results_json,indent=2)) @@ -369,10 +368,10 @@ def _process_nmap_json_results(self,nmap_results_json): ports = nmap_results_json["nmaprun"]["host"]["ports"] # Checking if an object is a JSON object if isinstance(ports["port"], dict): - results.update(self._json_port_to_dict(ports["port"])) + results.update(self._json_port_to_dict(ports["port"])) elif isinstance(ports["port"], list): - for port in ports["port"]: - results.update(self._json_port_to_dict(port)) + for port in ports["port"]: + results.update(self._json_port_to_dict(port)) return results def _json_port_to_dict(self,port_json): @@ -387,4 +386,4 @@ def _json_port_to_dict(self,port_json): if "@extrainfo" in port_json["service"]: port["version"] += " " + port_json["service"]["@extrainfo"] port_result = {port_json["@portid"]:port} - return port_result \ No newline at end of file + return port_result diff --git a/modules/ui/.gitignore b/modules/ui/.gitignore index 0711527ef..57fa6bf61 100644 --- a/modules/ui/.gitignore +++ b/modules/ui/.gitignore @@ -1,3 +1,7 @@ +node_modules/ +.angular/ +dist/ + # See http://help.github.com/ignore-files/ for more about ignoring files. # Compiled output @@ -39,4 +43,4 @@ testem.log # System files .DS_Store -Thumbs.db +Thumbs.db \ No newline at end of file diff --git a/modules/ui/conf/nginx.conf b/modules/ui/conf/nginx.conf new file mode 100644 index 000000000..ade6ad17a --- /dev/null +++ b/modules/ui/conf/nginx.conf @@ -0,0 +1,13 @@ +events{} +http { + include /etc/nginx/mime.types; + server { + listen 80; + server_name localhost; + root /usr/share/nginx/html; + index index.html; + location / { + try_files $uri $uri/ /index.html; + } + } +} \ No newline at end of file diff --git a/modules/ui/src/app/app.component.html b/modules/ui/src/app/app.component.html index de0baf85f..2edb798b0 100644 --- a/modules/ui/src/app/app.component.html +++ b/modules/ui/src/app/app.component.html @@ -24,6 +24,7 @@