forked from vrnetlab/vrnetlab
-
Notifications
You must be signed in to change notification settings - Fork 158
Added GenuScreen (#1) #362
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
Muddyblack
wants to merge
1
commit into
srl-labs:master
Choose a base branch
from
Muddyblack:master
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -8,6 +8,7 @@ cisco*.bin | |
| *.vmdk | ||
| *.iso | ||
| *cidfile | ||
| built-image-sha* | ||
|
|
||
| .DS_Store | ||
| */.DS_Store | ||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| VENDOR=Genua | ||
| NAME=genuscreen | ||
| IMAGE_FORMAT=iso | ||
| IMAGE_GLOB=*.iso | ||
|
|
||
| # match versions like: | ||
| # genuscreen-8.0.iso | ||
| # genuscreen-1.0.0.iso | ||
| # genuscreen-2.1.3.iso | ||
| VERSION=$(shell echo $(IMAGE) | sed -E 's/.*genuscreen-([0-9]+\.[0-9]+(\.[0-9]+)?)\.iso/\1/') | ||
|
|
||
| # Check if ISO files exist | ||
| ISO_FILES=$(wildcard $(IMAGE_GLOB)) | ||
| ifeq ($(ISO_FILES),) | ||
| $(error No ISO files found! Please place genuscreen ISO files (e.g., genuscreen-8.0.iso) in this directory) | ||
| endif | ||
|
|
||
| -include ../makefile-sanity.include | ||
| -include ../makefile.include | ||
| -include ../makefile-install.include |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,98 @@ | ||
| # vrnetlab / Genua genuscreen | ||
|
|
||
| This is the vrnetlab docker image for Genua genuscreen firewall appliances. | ||
|
|
||
| ## Building the docker image | ||
|
|
||
| Download the genuscreen ISO image and place it in this directory. The expected naming format is: | ||
| - `genuscreen-8.0.iso` | ||
| - `genuscreen-1.0.0.iso` | ||
| - `genuscreen-2.1.3.iso` | ||
|
|
||
| After placing the ISO file, run `make` to build the docker image. The resulting image will be called `vrnetlab/genua_genuscreen:X.Y.Z` where X.Y.Z matches the version from the ISO filename. | ||
|
|
||
| ## Installation Process | ||
|
|
||
| Genuscreen requires an initial installation from ISO to create the qcow2 disk image. The build process automatically handles this: | ||
|
|
||
| 1. The ISO is mounted as a CD-ROM device | ||
| 2. An empty 20GB qcow2 disk is created for installation | ||
| 3. The automated installation configures: | ||
| - Hostname (from `--hostname` parameter) | ||
| - Network interface (eth0 with IP 10.0.0.15/24) | ||
| - Default gateway (10.0.0.2) | ||
| - Administrative password (from `--password` parameter) | ||
| - SSH daemon (enabled) | ||
| - Web-GUI access restrictions | ||
| - Admin ACL network settings | ||
|
|
||
| ## Usage | ||
|
|
||
| ### With containerlab | ||
|
|
||
| ```yaml | ||
| name: genuscreen-lab | ||
| topology: | ||
| nodes: | ||
| gw1: | ||
| kind: vr-genuscreen | ||
| image: vrnetlab/genua_genuscreen:8.0 | ||
| ``` | ||
|
|
||
| ### Manual docker run | ||
|
|
||
| ```bash | ||
| docker run -d --privileged --name my-genuscreen vrnetlab/genua_genuscreen:8.0 | ||
| ``` | ||
|
|
||
| ## Configuration | ||
|
|
||
| The genuscreen image supports the following parameters: | ||
|
|
||
| - `--hostname`: Router hostname (default: `vr-genuscreen`) | ||
| - `--username`: Login username (default: `root`) | ||
| - `--password`: Login password (default: `VR-netlab9`) | ||
| - `--connection-mode`: Datapath connection mode (default: `tc`) | ||
| - `--install`: Run installation mode (used during build process) | ||
| - `--trace`: Enable trace level logging | ||
|
|
||
| ## Interface mapping | ||
|
|
||
| The genuscreen VM exposes 8 network interfaces using virtio-net-pci: | ||
| - eth0: Management interface (configured during installation) | ||
| - eth1-eth7: Additional data interfaces | ||
|
|
||
| ## Network Configuration | ||
|
|
||
| During installation, the system is configured with: | ||
| - **Management IP**: 10.0.0.15/24 | ||
| - **Default Gateway**: 10.0.0.2 | ||
| - **DNS**: Default system DNS | ||
| - **SSH**: Enabled on port 22 | ||
| - **Admin ACL**: 192.168.1.0/24 (Only when web-gui restrictions is set true) | ||
|
|
||
| ## Tested versions | ||
|
|
||
| The image has been developed and tested with: | ||
| - genuscreen-8.0.iso | ||
|
|
||
| ## Troubleshooting | ||
|
|
||
| ### Installation Issues | ||
|
|
||
| If installation fails: | ||
| 1. Check that the ISO file is properly named and in the correct directory | ||
| 2. Ensure sufficient disk space (>25GB) | ||
| 3. Verify the ISO image is not corrupted | ||
| 4. Enable trace logging with `--trace` for detailed output | ||
|
|
||
| ### SSH Access | ||
|
|
||
| Default SSH credentials: | ||
| - Username: `root` | ||
| - Password: `VR-netlab9` | ||
| - Port: 22 | ||
|
|
||
| ## License | ||
|
|
||
| This vrnetlab image is provided under the same license terms as the main vrnetlab project. The actual genuscreen software requires appropriate licensing from [Genua GmbH](https://www.genua.de/). | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| FROM ghcr.io/srl-labs/vrnetlab-base:0.2.1 | ||
|
|
||
| ARG IMAGE | ||
| COPY $IMAGE* / | ||
| COPY *.py / | ||
|
|
||
| EXPOSE 22 161/udp 830 5000 10000-10099 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,257 @@ | ||
| #!/usr/bin/env -S uv run | ||
|
|
||
| import datetime | ||
| import logging | ||
| import os | ||
| import re | ||
| import signal | ||
| import sys | ||
| import time | ||
|
|
||
| import vrnetlab | ||
|
|
||
|
|
||
| def handle_SIGCHLD(signal, frame): | ||
| os.waitpid(-1, os.WNOHANG) | ||
|
|
||
|
|
||
| def handle_SIGTERM(signal, frame): | ||
| sys.exit(0) | ||
|
|
||
|
|
||
| signal.signal(signal.SIGINT, handle_SIGTERM) | ||
| signal.signal(signal.SIGTERM, handle_SIGTERM) | ||
| signal.signal(signal.SIGCHLD, handle_SIGCHLD) | ||
|
|
||
| TRACE_LEVEL_NUM = 9 | ||
| logging.addLevelName(TRACE_LEVEL_NUM, "TRACE") | ||
|
|
||
|
|
||
| def trace(self, message, *args, **kws): | ||
| # Yes, logger takes its '*args' as 'args'. | ||
| if self.isEnabledFor(TRACE_LEVEL_NUM): | ||
| self._log(TRACE_LEVEL_NUM, message, args, **kws) | ||
|
|
||
|
|
||
| logging.Logger.trace = trace | ||
|
|
||
|
|
||
| class GENUSCREEN_vm(vrnetlab.VM): | ||
| def __init__(self, hostname, username, password, conn_mode, install_mode=False): | ||
| disk_image = None | ||
| iso_image = None | ||
|
|
||
| for e in os.listdir("/"): | ||
| if re.search(r"\.qcow2$", e): | ||
| disk_image = "/" + e | ||
| elif re.search(r"\.iso$", e): | ||
| iso_image = "/" + e | ||
|
|
||
| self.install_mode = install_mode | ||
|
|
||
| if self.install_mode and iso_image: | ||
| # Create empty disk for installation | ||
| disk_image = "/genuscreen-disk.qcow2" | ||
| if not os.path.exists(disk_image): | ||
| vrnetlab.run_command([ | ||
| "qemu-img", "create", "-f", "qcow2", disk_image, "20G" | ||
| ]) | ||
| self.iso_image = iso_image | ||
| elif not disk_image: | ||
| raise ValueError("No disk image found") | ||
|
|
||
| super(GENUSCREEN_vm, self).__init__( | ||
| username, password, disk_image=disk_image, ram=4096, smp="2" | ||
| ) | ||
|
|
||
| self.hostname = hostname | ||
| self.conn_mode = conn_mode | ||
| self.num_nics = 8 | ||
| self.nic_type = "virtio-net-pci" | ||
|
|
||
| # Add entropy sources for better performance | ||
| self.qemu_args.extend([ | ||
| "-object", "rng-random,filename=/dev/urandom,id=rng0", | ||
| "-device", "virtio-rng-pci,rng=rng0,max-bytes=1024,period=1000" | ||
| ]) | ||
|
|
||
| if self.install_mode and hasattr(self, 'iso_image'): | ||
| self.qemu_args.extend([ | ||
| "-boot", "order=cd", | ||
| "-cdrom", self.iso_image | ||
| ]) | ||
|
|
||
| def bootstrap_spin(self): | ||
| """This function should be called periodically to do work.""" | ||
|
|
||
| if self.spins > 1200: | ||
| # too many spins with no result -> give up | ||
| self.stop() | ||
| self.start() | ||
| return | ||
|
|
||
| if self.install_mode: | ||
| return self._handle_installation() | ||
| else: | ||
| return self._handle_normal_boot() | ||
|
|
||
| def _handle_installation(self): | ||
| """Handle the installation process""" | ||
| installation_prompts = [ | ||
| b"proceed", | ||
| b"32-bit appliance", | ||
| b"Keyboard mapping", | ||
| b"Fully Qualified Domain Name", | ||
| b"Which interface", | ||
| b"Address?", | ||
| b"Netmask length", | ||
| b"Media", | ||
| b"Default gateway", | ||
| b"New password:", | ||
| b"Retype new password:", | ||
| b"Enable SSH daemon", | ||
| b"Restrict access to Web-GUI", | ||
| b"Admin-ACL network", | ||
| b"Admin-ACL netmask length", | ||
| b"Save configuration to disk", | ||
| b"wait for more?", | ||
| b"login:" | ||
| ] | ||
|
|
||
| installation_responses = [ | ||
| "yes", # proceed | ||
| "no", # 32-bit appliance | ||
| "de", # Keyboard mapping | ||
| self.hostname, # FQDN | ||
| "", # Which interface (default) | ||
| "10.0.0.15", # Address | ||
| "24", # Netmask length | ||
| "", # Media (default) | ||
| "10.0.0.2", # Default gateway | ||
| self.password, # New password | ||
| self.password, # Retype password | ||
| "yes", # Enable SSH | ||
| "no", # Restrict Web-GUI | ||
| "192.168.1.0", # Admin-ACL network | ||
| "24", # Admin-ACL netmask length | ||
| "yes", # Save configuration | ||
| "no", # wait for more | ||
| None # login prompt - installation complete | ||
| ] | ||
|
|
||
| (ridx, match, res) = self.tn.expect(installation_prompts, 1) | ||
|
|
||
| if match: | ||
| if ridx == len(installation_prompts) - 1: # login prompt - installation complete | ||
| self.logger.info("Installation completed successfully") | ||
| install_time = datetime.datetime.now() - self.start_time | ||
| self.logger.info("Install complete in: %s", install_time) | ||
| self.running = True | ||
| return | ||
| elif ridx < len(installation_responses) and installation_responses[ridx] is not None: | ||
| self.wait_write(installation_responses[ridx], wait=None) | ||
|
|
||
| # no match, if we saw some output it's probably still installing | ||
| if res != b"": | ||
| self.logger.trace("INSTALL OUTPUT: %s", res.decode()) | ||
| self.spins = 0 | ||
|
|
||
| self.spins += 1 | ||
|
|
||
| def _handle_normal_boot(self): | ||
| """Handle normal boot process""" | ||
| (ridx, match, res) = self.tn.expect([b"login:"], 1) | ||
|
|
||
| if match and ridx == 0: | ||
| self.logger.info("Genuscreen boot completed") | ||
| self.wait_write(self.username, wait=None) | ||
| self.wait_write(self.password, wait="Password:") | ||
|
|
||
| # close telnet connection | ||
| self.tn.close() | ||
| startup_time = datetime.datetime.now() - self.start_time | ||
| self.logger.info("Startup complete in: %s", startup_time) | ||
| self.running = True | ||
| return | ||
|
|
||
| # no match, if we saw some output from the router it's probably | ||
| # booting, so let's give it some more time | ||
| if res != b"": | ||
| self.logger.trace("BOOT OUTPUT: %s", res.decode()) | ||
| # reset spins if we saw some output | ||
| self.spins = 0 | ||
|
|
||
| self.spins += 1 | ||
|
|
||
|
|
||
| class GENUSCREEN(vrnetlab.VR): | ||
| def __init__(self, hostname, username, password, conn_mode): | ||
| super(GENUSCREEN, self).__init__(username, password) | ||
| self.vms = [GENUSCREEN_vm(hostname, username, password, conn_mode)] | ||
|
|
||
|
|
||
| class GENUSCREEN_installer(GENUSCREEN): | ||
| """GENUSCREEN installer | ||
|
|
||
| Will start Genuscreen with ISO mounted and perform installation | ||
| to create the final QCOW2 image for subsequent boots. | ||
| """ | ||
|
|
||
| def __init__(self, hostname, username, password, conn_mode): | ||
| super(GENUSCREEN, self).__init__(username, password) | ||
| self.vms = [ | ||
| GENUSCREEN_vm(hostname, username, password, conn_mode, install_mode=True) | ||
| ] | ||
|
|
||
| def install(self): | ||
| """Run the installation process""" | ||
| self.logger.info("Installing Genuscreen") | ||
| genuscreen = self.vms[0] | ||
| while not genuscreen.running: | ||
| genuscreen.work() | ||
|
|
||
| time.sleep(10) | ||
| genuscreen.stop() | ||
| self.logger.info("Installation complete") | ||
|
|
||
|
|
||
| if __name__ == "__main__": | ||
| import argparse | ||
|
|
||
| parser = argparse.ArgumentParser(description="") | ||
| parser.add_argument( | ||
| "--trace", action="store_true", help="enable trace level logging" | ||
| ) | ||
| parser.add_argument("--hostname", default="vr-genuscreen", help="Router hostname") | ||
| parser.add_argument("--username", default="root", help="Username") | ||
| parser.add_argument("--password", default="VR-netlab9", help="Password") | ||
| parser.add_argument( | ||
| "--connection-mode", | ||
| default="tc", | ||
| help="Connection mode to use in the datapath", | ||
| ) | ||
| parser.add_argument("--install", action="store_true", help="Install Genuscreen") | ||
|
|
||
| args = parser.parse_args() | ||
|
|
||
| LOG_FORMAT = "%(asctime)s: %(module)-10s %(levelname)-8s %(message)s" | ||
| logging.basicConfig(format=LOG_FORMAT) | ||
| logger = logging.getLogger() | ||
|
|
||
| logger.setLevel(logging.DEBUG) | ||
| if args.trace: | ||
| logger.setLevel(1) | ||
|
|
||
| logger.debug(f"Environment variables: {os.environ}") | ||
| vrnetlab.boot_delay() | ||
|
|
||
| if args.install: | ||
| vr = GENUSCREEN_installer( | ||
| args.hostname, args.username, args.password, args.connection_mode | ||
| ) | ||
| vr.install() | ||
| else: | ||
| vr = GENUSCREEN( | ||
| args.hostname, args.username, args.password, args.connection_mode | ||
| ) | ||
| vr.start() |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi @Muddyblack
do you plan to add the relevant PR for containerlab? It is fine if you don't, someone can take this up as well
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How do you mean has something changed in the logic?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
without adding the kind support in clab, it won't work
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Aye I think i used
linux-kindback then. But I think it would be a good exercise to start with golang for me. I will try it but I can't tell when I will take my time for it to also test the new kind then 😅Is it just creating new file in
nodesfolder and and add toregister.go?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
M, you are raising a good point about the usage of linux_vm kind. I would prefer it use it given the low popularity of the platform