gns3 deploy initial repo

This commit is contained in:
2026-05-28 06:56:46 -07:00
commit d1ca7c6692
17 changed files with 4652 additions and 0 deletions
Vendored
BIN
View File
Binary file not shown.
+49
View File
@@ -0,0 +1,49 @@
# gns3-remote-install-redux
Revised version of the OG [upstream GNS3 remote installer](https://github.com/GNS3/gns3-server/blob/master/scripts/remote-install.sh).
## What's different
- **Single primary apt pass**: packages, repos, and groups rolled up from flags before install runs (as much as possible)
- **Preflight**: checks root, OS, needed commands, ports, modules, CPU virt extensions. Reports every error condition at once
- **Auto-loads KVM**: `modprobe` + persist to `/etc/modules-load.d/`, unless `--without-kvm`
- **Retry + verify**: apt retries 3x with backoff; services verified with `systemctl is-active`; GNS3 API curled post-install
- **WireGuard**: adds modern VPN option
- **EC crypto**: P-384 by default for OpenVPN (faster keygen, no DH params). `--legacy-rsa` if needed
- **Encrypted configs**: VPN configs are encrypted with one-time passphrase. A copy+paste one-liner is printed to console to curl and decrypt the file at once. `--unsafe-configs` to skip
- **Scoped firewall**: UFW rules allow VPN from 0.0.0.0/0, others scoped to SSH source + local subnets
- **Sysctl hardening**: rp_filter, syncookies, ICMP redirect rejection, source route disable
- **Landing page**: ephemeral web page gives server info, version-matched client downloads, VPN configs, warnings, and resources. Auto-expires via systemd timer. Uses Python httpd.server module, instead of installing nginx package.
- **Reconfigure**: enhanced idempotence. detects prior install, stops services, re-runs cleanly
- **Testable**: `main()` guarded so functions can be sourced for BATS or plain bash tests
## Usage
```
sudo ./gns3-remote-install-redux.sh [OPTIONS]
```
## Flags
| Flag | Default | Description |
|---|---|---|
| `--with-openvpn` | off | Install and configure OpenVPN |
| `--with-wireguard` | off | Install and configure WireGuard |
| `--with-welcome` | off | Install GNS3-VM welcome console UI |
| `--without-kvm` | KVM on | Disable KVM hardware acceleration |
| `--without-docker` | Docker on | Skip Docker CE installation |
| `--without-firewall` | UFW on | Skip automatic UFW rule configuration |
| `--without-system-upgrade` | upgrade on | Skip `apt upgrade` |
| `--unsafe-configs` | encrypted | Serve VPN configs unencrypted |
| `--legacy-rsa` | EC P-384 | Use RSA-2048 + DH for OpenVPN |
| `--unstable` | stable PPA | Use the GNS3 unstable PPA |
| `--custom-repository REPO` | `ppa` | Use a custom GNS3 PPA name |
| `-h`, `--help` | | Show help |
## Requirements
Ubuntu Server 24.04 LTS (amd64). Run as root or with `sudo`.
## License
Based on the original GNS3 remote installer (GPLv3).
+142
View File
@@ -0,0 +1,142 @@
# TODO.md Convention Adoption
`[x]` done, `[-]` partial, `[ ]` not started
---
## Phase 1: Low-Hanging Fruit (done or nearly done)
- [x] `set -euo pipefail` + `IFS=$'\n\t'`
- [x] `readonly PATH` (Linux-only, homebrew paths removed)
- [x] Structured header block with metadata
- [x] `_READONLY_GLOBAL_` naming for constants
- [x] `_local_var` naming for function locals and iterators
- [x] `"${VAR}"` expansion everywhere (quoted + braced)
- [x] `log_*` hierarchy (`info`, `ok`, `warn`, `error`, `fatal`)
- [x] `log_warn_sticky`->`WARN_MESSAGES` array
- [x] `ensure_directory` / `ensure_config` / `require_file` helpers
- [x] `fetch_file` (stdout-based, composable)
- [x] `render_template` (stdin-based, `{{KEY}}` substitution)
- [x] `enable_and_start` with retry loop
- [x] Predicates: `file_exists`, `dir_exists`, `var_exists`, `port_open`
- [x] `if/then/fi` instead of `[[ ]] &&` shorthand (most converted)
- [x] `encrypted_copy` uses `openssl enc` with `-pbkdf2`
- [x] Decrypt commands match encryption params
- [x] `gns3_repo_present` handles deb822 format
- [x] Sudoers lines have no leading whitespace
- [x] Stray box-drawing chars removed from section headers
- [x] `fetch_resource` removed `fetch_file` used everywhere
- [x] IP lookups cached as `SERVER_PUBLIC_IP` / `SERVER_PRIVATE_IP` / `SERVER_HOSTNAME`
- [x] No redundant IP re-checks mid-script
- [x] `.shellcheckrc` with justified exclusions
- [-] Remaining `[[ ]] && / ||` patterns most converted, grep to find stragglers
---
## Phase 2: Structural Alignment
- [ ] Replace `on_error()` + separate `INT/TERM` trap with single `cleanup()` on `EXIT`
- Convention: `trap cleanup EXIT` as first line of `main()`
- `cleanup()` preserves exit code, disarms traps, `set +e`
- Restart GNS3 on failure (current `on_error` behavior) moves into cleanup
- INT/TERM: set context flag and `exit` - cleanup runs via EXIT trap
- [ ] Add `setup_tempdir()` - centralized `TEMP_DIR` used by `apt_retry` and `ensure_config`
- Currently both use ad-hoc `mktemp`
- `ensure_config` should use `mktemp "${TEMP_DIR}/..."` instead of `mktemp "${_path}.XXXXXX"`
- [ ] Add tracking arrays wired to helpers:
- `DIRECTORIES_CREATED``ensure_directory` appends
- `SERVICES_ENABLED``enable_and_start` appends
- `PACKAGES_INSTALLED``install_packages` populates (optional)
- Summary reads from these arrays
- [ ] Add `ROLLBACK_SERVICES` / `ROLLBACK_DIRS` typed registries
- `cleanup()` iterates in reverse on failure
- [ ] Replace `flock` lockfile with `mkdir`-based lock + stale PID detection (skeleton pattern)
- [ ] Remove `WARNINGS_OCCURRED` or use it - currently set but never read
- [ ] Add `require_args` to helper functions (from skeleton)
---
## Phase 3: Naming & Organization
- [ ] Standardize template placeholder names
- Current: mixed `_DEPLOY_MARKER_` / `_listen_host` / `GNS3_PORT` / `_hw_accel`
- Target: consistent `SCREAMING_SNAKE` for all placeholders (they're config values, not locals)
- [ ] Rename phase functions to convention verbs:
- `preflight_checks`->`preflight`
- `setup_config_server`->`config_server`
- `configure_firewall`->`firewall`
- `apply_sysctl_hardening`->`hardening`
- `start_services`->(merge into per-service phases or remove)
- `print_summary`->`summary`
- [ ] Rename sub-functions with phase prefix:
- `setup_groups`->`acct_create_groups`
- `setup_gns3_user`->`acct_create_gns3_user`
- `propagate_groups_to_invoker`->`acct_add_invoker`
- `add_gns3_repository`->`repo_add_gns3`
- `add_docker_repository`->`repo_add_docker`
- VPN functions: `ovpn_*` and `wg_*` prefixes
- [ ] Reorder function definitions to match execution order
- [ ] Add INDEX section to header mapping functions to sections
- [ ] Add `VERSION` constant, print in `--help` and summary
- [ ] Cache `_SCRIPT_DIR_` as readonly global (currently computed multiple times in `fetch_file`)
---
## Phase 4: Macro Refactor
- [ ] Two-tier `main()` structure:
```
main->bootstrap->preflight->repositories->packages →
accounts->gns3->docker->openvpn->wireguard →
welcome->firewall->hardening->config_server →
validate->summary
```
- [ ] Each phase is a noun function containing only sub-function calls
- [ ] Sub-functions use `phase_verb_noun` naming
- [ ] Phase banner comments with purpose description
- [ ] Flag gating in `main()` with `if/then`:
```bash
if [[ "${WITH_DOCKER}" -eq 1 ]]; then docker; fi
if [[ "${WITH_OPENVPN}" -eq 1 ]]; then openvpn; fi
```
- [ ] Add predicate functions for recurring checks:
- `service_active` / `user_exists` / `interface_up` / `repo_present` / `packages_installed`
- [ ] Move `apply_option_flags` (rollup) into its own named phase or call from `main()` before bootstrap
---
## Phase 5: Multi-File Split (future, when script exceeds ~1000 LOC of logic)
- [ ] `conf/` - readonly vars/arrays per feature
- [ ] `lib/` source-only helpers, no `main()`
- [ ] `mod/` executable modules with numeric prefix ordering
- [ ] `bootstrap.sh` top-level orchestrator, executes `mod/*.sh`
- [ ] Source guards: `[[ -n "${_LIB_X_LOADED:-}" ]] && return 0`
- [ ] Direct-execution guards on sourced files
---
## Ongoing / Per-Commit
- [ ] `shfmt -i 2 -ci` before commit
- [ ] `shellcheck` zero warnings (with `.shellcheckrc` exclusions)
- [ ] Update `Changed:` date in header on substantive edits
- [ ] Update INDEX when function structure changes
- [ ] Convention: Check / Do / Verify for every action
- [ ] Convention: secrets never as function arguments; use env/files/stdin
- [ ] Convention: `backup_file` before modifying existing configs (not yet implemented)
---
## Known Deferred Items
- [ ] `check_integrity()` SHA256 validation for downloaded resources
- [ ] `--with-optimization` phase TCP/KVM tuning (BBR, swappiness, etc.)
- [ ] 26.04 codename fallback check GNS3 PPA Packages.xz for actual content
- [ ] OpenVPN client.ovpn still uses heredoc (PEM blocks don't work with `render_template`)
- [ ] `docker.list` still uses heredoc (inline `$(dpkg --print-architecture)` call)
- [ ] Validate function could check `ufw status numbered` rule count matches expectations
- [ ] Landing page: version-matched GNS3 client download URLs via JS
- [ ] GPG-signed bootstrap at `lrk.cx/gns3`
---
File diff suppressed because it is too large Load Diff
+1724
View File
File diff suppressed because it is too large Load Diff
+62
View File
@@ -0,0 +1,62 @@
-----BEGIN PGP PUBLIC KEY BLOCK-----
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
YT90qFF93M3v01BbxP+EIY2/9tiIPbrd
=0YYh
-----END PGP PUBLIC KEY BLOCK-----
+577
View File
@@ -0,0 +1,577 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
# Copyright (C) 2015 GNS3 Technologies Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import configparser
import locale
import os
import re
import subprocess
import sys
import time
import urllib.request
from json import loads as convert
from dialog import Dialog, PythonDialogBug
class Welcome_dialog:
def __init__(self):
try:
locale.setlocale(locale.LC_ALL, "")
except locale.Error:
# Not supported via SSH
pass
self.display = Dialog(dialog="dialog", autowidgetsize=True)
if self.gns3_version() is None:
self.display.set_background_title("GNS3")
else:
self.display.set_background_title("GNS3 {}".format(self.gns3_version()))
def get_ip(self):
"""
Return the active IP
"""
# request 'ip addr' data in JSON format from shell
ip_addr_response = subprocess.run(["ip", "--json", "addr"], capture_output=True)
# process response, decode and use json.loads to convert the string to a dict
ip_addr_data = convert(ip_addr_response.stdout.decode("utf-8"))
# search ip_addr_data for the first ip adress that is not under a virtual bridge or loopback interface
for i in ip_addr_data:
if ("virbr" in i["ifname"]) or ("lo" in i["ifname"]):
continue
try:
if "UP" in i["flags"]:
ip_addr = i["addr_info"][0]["local"]
break
except:
continue
ip_addr = None
return ip_addr
def repair_remote_install(self):
"""
This method is only called by remote-install.sh during setup to ensure it is setting the same IP as shown by Dialog
"""
ip_addr = self.get_ip()
subprocess.run(
[
"sed",
"-i",
f"s/host = 0.0.0.0/host = {ip_addr}/",
"/etc/gns3/gns3_server.conf",
],
capture_output=False,
)
subprocess.run(["service", "gns3", "stop"], capture_output=False)
subprocess.run(["service", "gns3", "start"], capture_output=False)
def get_config(self):
"""
Read the config
"""
config = configparser.RawConfigParser()
path = os.path.expanduser("~/.config/GNS3/gns3_server.conf")
config.read([path], encoding="utf-8")
return config
def write_config(self, config):
"""
Write the config file
"""
with open(os.path.expanduser("~/.config/GNS3/gns3_server.conf"), "w") as f:
config.write(f)
def gns3_major_version(self):
"""
Returns the GNS3 major server version
"""
version = self.gns3_version()
if version:
match = re.search(r"\d+.\d+", version)
return match.group(0)
return ""
def gns3_version(self):
"""
Return the GNS3 server version
"""
try:
return subprocess.check_output(["gns3server", "--version"]).strip().decode()
except (subprocess.CalledProcessError, FileNotFoundError):
return None
def gns3vm_version(self):
"""
Return the GNS3 VM version
"""
try:
with open("/home/gns3/.config/GNS3/gns3vm_version") as f:
return f.read().strip()
except FileNotFoundError:
return "Remote Install"
def mode(self):
if (
self.display.yesno(
"This feature is for testers only. You may break your GNS3 installation. Are you REALLY sure you want to continue?",
yes_label="Exit (Safe option)",
no_label="Continue",
)
== self.display.OK
):
return
code, tag = self.display.menu(
"Select the GNS3 version",
choices=[
("2.1", "Stable release for this GNS3 VM (RECOMMENDED)"),
("2.1dev", "Development version for stable release"),
("2.2", "Latest stable release"),
],
)
self.display.clear()
if code == Dialog.OK:
os.makedirs(os.path.expanduser("~/.config/GNS3"), exist_ok=True)
with open(os.path.expanduser("~/.config/GNS3/gns3_release"), "w+") as f:
f.write(tag)
self.update(force=True)
def get_release(self):
try:
with open(os.path.expanduser("~/.config/GNS3/gns3_release")) as f:
content = f.read()
# Support old VM versions
if content == "stable":
content = "1.5"
elif content == "testing":
content = "1.5"
elif content == "unstable":
content = "1.5dev"
return content
except OSError:
return "1.5"
def update(self, force=False):
if not force:
if (
self.display.yesno(
"It is recommended to ensure all Nodes are shutdown before upgrading. Continue?"
)
!= self.display.OK
):
return
code, option = self.display.menu(
"Select an option",
choices=[
("Upgrade GNS3", "Upgrades only the GNS3 pakage and dependences."),
("Upgrade All", "Upgrades all avaiable packages"),
(
"Dist Upgrade",
"Upgrades all avaiable packages and the Linux Kernel. Requires a reboot.",
),
],
)
if code == Dialog.OK:
if option == "Upgrade GNS3":
ret = os.system(
"sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A2E3EF7B \
&& sudo apt-get update \
&& sudo apt-get install -y --only-upgrade gns3-server"
)
elif option == "Upgrade All":
ret = os.system(
'sudo apt-key adv --refresh-keys --keyserver keyserver.ubuntu.com \
&& sudo apt-get update \
&& sudo apt-get upgrade --yes --force-yes -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold"'
)
elif option == "Dist Upgrade":
ret = os.system(
'sudo apt-key adv --refresh-keys --keyserver keyserver.ubuntu.com \
&& sudo apt-get update \
&& sudo apt-get dist-upgrade --yes --force-yes -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold"'
)
if ret != 0:
print(
"ERROR DURING UPGRADE PROCESS PLEASE TAKE A SCREENSHOT IF YOU NEED SUPPORT"
)
time.sleep(15)
return
if option == "Dist Upgrade":
if self.display.yesno("Reboot now?") == self.display.OK:
os.system("sudo reboot now")
def migrate(self):
"""
Migrate GNS3 VM data.
"""
code, option = self.display.menu(
"Select an option",
choices=[
("Setup", "Configure this VM to send data to another GNS3 VM"),
("Send", "Send images and projects to another GNS3 VM"),
],
)
self.display.clear()
if code == Dialog.OK:
(answer, destination) = self.display.inputbox(
"What is IP address or hostname of the other GNS3 VM?",
init="172.16.1.128",
)
if answer != self.display.OK:
return
if destination == self.get_ip():
self.display.msgbox(
"The destination cannot be the same as this VM IP address ({})".format(
destination
)
)
return
if option == "Send":
# first make sure they are no files belonging to root
os.system("sudo chown -R gns3:gns3 /opt/gns3")
# then rsync the data
command = r"rsync -az --progress -e 'ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /home/gns3/.ssh/gns3-vm-key' /opt/gns3 gns3@{}:/opt".format(
destination
)
ret = os.system('bash -c "{}"'.format(command))
time.sleep(10)
if ret != 0:
self.display.msgbox(
"Could not send data to the other GNS3 VM located at {}".format(
destination
)
)
else:
self.display.msgbox(
"Images and projects have been successfully sent to the other GNS3 VM located at {}".format(
destination
)
)
elif option == "Setup":
script = """
if [ ! -f ~/.ssh/gns3-vm-key ]
then
ssh-keygen -f ~/.ssh/gns3-vm-key -N '' -C gns3@{}
fi
ssh-copy-id -i ~/.ssh/gns3-vm-key gns3@{}
""".format(self.get_ip(), destination)
ret = os.system('bash -c "{}"'.format(script))
time.sleep(10)
if ret != 0:
self.display.msgbox("Error while setting up the migrate feature")
else:
self.display.msgbox(
"Configuration successful, you can now send data to the GNS3 VM located at {} without password".format(
destination
)
)
def shrink_disk(self):
ret = os.system("lspci | grep -i vmware")
if ret != 0:
self.display.msgbox(
"Shrinking the disk is only supported when running inside VMware"
)
return
if (
self.display.yesno(
"Would you like to shrink the VM disk? The VM will reboot at the end of the process. Continue?"
)
!= self.display.OK
):
return
os.system("sudo service gns3 stop")
os.system("sudo service docker stop")
os.system("sudo vmware-toolbox-cmd disk shrink /opt")
os.system("sudo vmware-toolbox-cmd disk shrink /")
self.display.msgbox("The GNS3 VM will reboot")
os.execvp("sudo", ["/usr/bin/sudo", "reboot"])
def vm_information(self):
"""
Show IP, SSH settings....
"""
content = "Welcome to GNS3 appliance\n\n"
version = self.gns3_version()
if version is None:
content += "GNS3 is not installed please install it with sudo pip3 install gns3-server. Or download a preinstalled VM.\n\n"
else:
content = "GNS3 version: {gns3_version}\nVM version: {gns3vm_version}\nKVM support available: {kvm}\n\n".format(
gns3vm_version=self.gns3vm_version(),
gns3_version=version,
kvm=self.kvm_support(),
)
ip = self.get_ip()
if ip:
content += f"""
IP: {ip}
Web UI: http://{ip}:3080
To log in using SSH:
ssh gns3@{ip}
Password: gns3
Images and projects are located in /opt/gns3
""".strip()
else:
content += "eth0 is not configured. Please manually configure it via the Networking menu."
content += "\n\nRelease channel: " + self.get_release()
try:
self.display.msgbox(content)
# If it's an scp command or any bugs
except:
os.execvp("bash", ["/bin/bash"])
def check_internet_connectivity(self):
self.display.pause("Please wait...\n\n")
try:
response = urllib.request.urlopen("http://pypi.python.org/", timeout=5)
except urllib.request.URLError as err:
self.display.infobox(
"Can't connect to Internet (pypi.python.org): {}".format(str(err))
)
time.sleep(15)
return
self.display.infobox("Connection to Internet: OK")
time.sleep(2)
def keyboard_configuration():
"""
Allow user to change the keyboard layout
"""
os.system("/usr/bin/sudo dpkg-reconfigure keyboard-configuration")
def set_security(self):
config = self.get_config()
if self.display.yesno("Enable server authentication?") == self.display.OK:
if not config.has_section("Server"):
config.add_section("Server")
config.set("Server", "auth", True)
(answer, text) = self.display.inputbox("Login?")
if answer != self.display.OK:
return
config.set("Server", "user", text)
(answer, text) = self.display.passwordbox("Password?")
if answer != self.display.OK:
return
config.set("Server", "password", text)
else:
config.set("Server", "auth", False)
self.write_config(config)
def log(self):
os.system("/usr/bin/sudo chmod 755 /var/log/upstart/gns3.log")
with open("/var/log/upstart/gns3.log") as f:
try:
while True:
line = f.readline()
sys.stdout.write(line)
except (KeyboardInterrupt, MemoryError):
return
def edit_config(self):
"""
Edit GNS3 configuration file
"""
major_version = self.gns3_major_version()
if major_version == "2.2":
os.system("nano ~/.config/GNS3/{}/gns3_server.conf".format(major_version))
else:
os.system("nano ~/.config/GNS3/gns3_server.conf")
def edit_network(self):
"""
Edit network configuration file
"""
if (
self.display.yesno(
"The server will reboot at the end of the process. Continue?"
)
!= self.display.OK
):
return
os.system("sudo nano /etc/network/interfaces")
os.execvp("sudo", ["/usr/bin/sudo", "reboot"])
def edit_proxy(self):
"""
Configure proxy settings
"""
res, http_proxy = self.display.inputbox(
text="HTTP proxy string, for example http://<user>:<password>@<proxy>:<port>. Leave empty for no proxy."
)
if res != self.display.OK:
return
res, https_proxy = self.display.inputbox(
text="HTTPS proxy string, for example http://<user>:<password>@<proxy>:<port>. Leave empty for no proxy."
)
if res != self.display.OK:
return
with open("/tmp/00proxy", "w+") as f:
f.write('Acquire::http::Proxy "' + http_proxy + '";')
os.system("sudo mv /tmp/00proxy /etc/apt/apt.conf.d/00proxy")
os.system("sudo chown root /etc/apt/apt.conf.d/00proxy")
os.system("sudo chmod 744 /etc/apt/apt.conf.d/00proxy")
with open("/tmp/proxy.sh", "w+") as f:
f.write('export http_proxy="' + http_proxy + '"\n')
f.write('export https_proxy="' + https_proxy + '"\n')
f.write('export HTTP_PROXY="' + http_proxy + '"\n')
f.write('export HTTPS_PROXY="' + https_proxy + '"\n')
os.system("sudo mv /tmp/proxy.sh /etc/profile.d/proxy.sh")
os.system("sudo chown root /etc/profile.d/proxy.sh")
os.system("sudo chmod 744 /etc/profile.d/proxy.sh")
os.system("sudo cp /etc/profile.d/proxy.sh /etc/default/docker")
self.display.msgbox("The GNS3 VM will reboot")
os.execvp("sudo", ["/usr/bin/sudo", "reboot"])
def kvm_support(self):
"""
Returns true if KVM is available
"""
return subprocess.call("kvm-ok") == 0
def kvm_control(self):
"""
Check if KVM is correctly configured
"""
kvm_ok = self.kvm_support()
config = self.get_config()
try:
if config.getboolean("Qemu", "enable_kvm") is True:
if kvm_ok is False:
if (
self.display.yesno(
"KVM is not available!\n\nQemu VM will crash!!\n\nThe reason could be unsupported hardware or another virtualization solution is already running.\n\nDisable KVM and get lower performances?"
)
== self.display.OK
):
config.set("Qemu", "enable_kvm", False)
self.write_config(config)
os.execvp("sudo", ["/usr/bin/sudo", "reboot"])
else:
if kvm_ok is True:
if (
self.display.yesno(
"KVM is available on your computer.\n\nEnable KVM and get better performances?"
)
== self.display.OK
):
config.set("Qemu", "enable_kvm", True)
self.write_config(config)
os.execvp("sudo", ["/usr/bin/sudo", "reboot"])
except configparser.NoSectionError:
return
def display_loop(self):
try:
while True:
code, tag = self.display.menu(
"GNS3 {}".format(self.gns3_version()),
choices=[
("Information", "Display VM information"),
("Upgrade", "Upgrade GNS3"),
("Migrate", "Migrate data to another GNS3 VM"),
("Shell", "Open a console"),
("Security", "Configure authentication"),
("Keyboard", "Change keyboard layout"),
(
"Configure",
"Edit server configuration (advanced users ONLY)",
),
("Proxy", "Configure proxy settings"),
("Networking", "Configure networking settings"),
("Log", "Show server log"),
("Test", "Check internet connection"),
("Shrink", "Shrink the VM disk"),
("Version", "Select the GNS3 version"),
("Restore", "Restore the VM (if you have trouble for upgrade)"),
("Reboot", "Reboot the VM"),
("Shutdown", "Shutdown the VM"),
],
)
self.display.clear()
if code == Dialog.OK:
if tag == "Shell":
print("Type: 'welcome.py' to get back to the dialog menu.")
sys.exit(0)
elif tag == "Version":
self.mode()
elif tag == "Restore":
os.execvp(
"sudo", ["/usr/bin/sudo", "/usr/local/bin/gns3restore"]
)
elif tag == "Reboot":
os.execvp("sudo", ["/usr/bin/sudo", "reboot"])
elif tag == "Shutdown":
os.execvp("sudo", ["/usr/bin/sudo", "poweroff"])
elif tag == "Upgrade":
self.update()
elif tag == "Information":
self.vm_information()
elif tag == "Log":
self.log()
elif tag == "Migrate":
self.migrate()
elif tag == "Configure":
self.edit_config()
elif tag == "Networking":
self.edit_network()
elif tag == "Security":
self.set_security()
elif tag == "Keyboard":
self.keyboard_configuration()
elif tag == "Test":
self.check_internet_connectivity()
elif tag == "Proxy":
self.edit_proxy()
elif tag == "Shrink":
self.shrink_disk()
except KeyboardInterrupt:
sys.exit(0)
if __name__ == "__main__":
ws = Welcome_dialog()
ws.vm_information()
ws.kvm_control()
ws.display_loop()
+213
View File
@@ -0,0 +1,213 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>GNS3 Server</title>
<style>
@import url("https://fonts.googleapis.com/css2?family=Bebas+Neue&family=Fira+Sans+Extra+Condensed:wght@200&family=Fira+Mono&display=swap");
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
background: #070600;
color: #e0e0e0;
font-family: "Fira Sans Extra Condensed", sans-serif;
font-size: 24px;
line-height: 1.8;
padding: 2rem;
max-width: 950px;
margin: 0 auto;
}
h1 {
font-family: "Bebas Neue", sans-serif;
color: #ff1053;
font-size: 4rem;
letter-spacing: 2px;
margin-bottom: 0.5rem;
}
h2 {
font-family: "Bebas Neue", sans-serif;
color: #ff1053;
font-size: 2rem;
letter-spacing: 1px;
margin: 2rem 0 0.5rem;
border-bottom: 1px solid #333;
padding-bottom: 0.3rem;
}
a {
color: #ff1053;
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
code,
.mono {
font-family: "Fira Mono", monospace;
font-size: 1.4rem;
padding: 2px 6px;
border-radius: 3px;
}
pre {
background: #1a1a1a;
padding: 1.2rem;
border-radius: 4px;
overflow-x: auto;
font-family: "Fira Mono", monospace;
font-size: 0.85em;
margin: 0.5rem 0;
white-space: pre-wrap;
word-break: break-all;
}
.card {
background: #111;
border: 1px solid #222;
padding: 1rem;
margin: 0.5rem 0;
border-radius: 4px;
}
.warn {
border-left: 3px solid #ff1053;
}
.dl {
display: inline-block;
background: #ff1053;
color: #070600;
padding: 0.4rem 1rem;
font-weight: 700;
margin: 0.3rem 0.3rem 0.3rem 0;
border-radius: 3px;
font-size: 0.9rem;
}
.dl:hover {
background: #cc0d42;
text-decoration: none;
}
.muted {
color: #666;
font-size: 1.1rem;
}
.grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 0.5rem;
}
@media (max-width: 720px) {
.grid {
grid-template-columns: 1fr;
}
}
</style>
</head>
<body>
<h1>GNS3 SERVER</h1>
<div id="app"></div>
<script>
// Data injected by bash script via sed
const D = {
lan_ip: "{{LAN_IP}}",
public_ip: "{{PUBLIC_IP}}",
hostname: "{{HOSTNAME}}",
gns3_version: "{{GNS3_VERSION}}",
with_openvpn: {{WITH_OPENVPN}},
with_wireguard: {{WITH_WIREGUARD}},
with_docker: {{WITH_DOCKER}},
disable_kvm: {{DISABLE_KVM}},
unsafe_configs: {{UNSAFE_CONFIGS}},
serve_slug: "{{SERVE_SLUG}}",
serve_hours: {{SERVE_HOURS}},
serve_port: {{SERVE_PORT}},
warnings: {{WARNINGS_JSON}}
};
const $ = document.getElementById('app');
let h = '';
h += '<h2>YOUR SERVER</h2>';
h += '<div class="card">';
h += `<p><strong>GNS3:</strong> <a href="http://${D.lan_ip}:3080">http://${D.lan_ip}:3080</a></p>`;
h += `<p><strong>Version:</strong> <code>${D.gns3_version}</code></p>`;
h += `<p><strong>Host:</strong> ${D.hostname}</p>`;
h += `<p><strong>Docker:</strong> ${D.with_docker ? 'Yes' : 'No'}</p>`;
h += `<p><strong>KVM:</strong> ${D.disable_kvm ? '<span style="color:#FF1053">Disabled</span>' : 'Enabled'}</p>`;
h += '</div>';
if (D.with_wireguard || D.with_openvpn) {
h += '<h2>VPN</h2>';
h += '<div class="card">';
const ext = D.unsafe_configs ? '' : '.enc';
if (D.with_wireguard) {
h += `<p><strong>WireGuard:</strong> <code>${D.public_ip}:51820</code></p>`;
h += `<p><strong>Tunnel:</strong> <code>172.16.254.1:3080</code></p>`;
h += `<a class="dl" href="${D.serve_slug}/w">Download WireGuard Config${ext ? ' (encrypted)' : ''}</a> `;
}
if (D.with_openvpn) {
h += `<p><strong>OpenVPN:</strong> <code>${D.public_ip}:1194/udp</code></p>`;
h += `<p><strong>Tunnel:</strong> <code>172.16.253.1:3080</code></p>`;
h += `<a class="dl" href="${D.serve_slug}/o">Download OpenVPN Config${ext ? ' (encrypted)' : ''}</a> `;
}
if (!D.unsafe_configs) {
h += '<p class="muted" style="margin-top:0.5rem">Configs are encrypted. Passphrase is on your server terminal.</p>';
}
h += '<p class="muted">Port forwarding required on your router.</p>';
h += '</div>';
}
// Dynamic Client Download Links based on Semantic Version
const dl_win = D.gns3_version !== "unknown"
? `https://github.com/GNS3/gns3-gui/releases/download/v${D.gns3_version}/GNS3-${D.gns3_version}-all-in-one.exe`
: `https://github.com/GNS3/gns3-gui/releases/latest`;
const dl_mac = D.gns3_version !== "unknown"
? `https://github.com/GNS3/gns3-gui/releases/download/v${D.gns3_version}/GNS3-${D.gns3_version}.dmg`
: `https://github.com/GNS3/gns3-gui/releases/latest`;
h += '<h2>GNS3 CLIENT</h2>';
h += '<div class="grid">';
h += '<div class="card"><strong>Windows</strong><br>';
h += `<a class="dl" href="${dl_win}">GNS3 Client ${D.gns3_version !== "unknown" ? '(v' + D.gns3_version + ')' : ''}</a>`;
h += '<br><a class="dl" href="https://www.chiark.greenend.org.uk/~sgtatham/putty/latest.html">PuTTY</a>';
h += '</div>';
h += '<div class="card"><strong>macOS</strong><br>';
h += `<a class="dl" href="${dl_mac}">GNS3 Client ${D.gns3_version !== "unknown" ? '(v' + D.gns3_version + ')' : ''}</a>`;
h += '<br><a class="dl" href="https://iterm2.com">iTerm2</a>';
h += '</div>';
h += '<div class="card"><strong>Linux</strong><br>';
h += '<a class="dl" href="https://github.com/GNS3/gns3-gui/releases/latest">GNS3 Client</a>';
h += '<br><code>sudo apt install gns3-gui</code>';
h += '</div>';
h += '</div>';
h += '<h2>RESOURCES</h2>';
h += '<div class="card">';
h += '<p><a href="https://gns3.com/marketplace/appliances">GNS3 Appliance Marketplace</a></p>';
h += '<p><a href="https://fedorapeople.org/groups/virt/virtio-win/direct-downloads/stable-virtio/virtio-win.iso">VirtIO Windows Drivers</a></p>';
h += '<p><a href="https://docs.gns3.com">GNS3 Documentation</a></p>';
h += '<p><a href="https://lark.cx">lark.cx tutorials</a></p>';
h += '</div>';
if (D.warnings && D.warnings.length > 0) {
h += '<h2>WARNINGS</h2>';
D.warnings.forEach(w => {
h += `<div class="card warn"><code>${w}</code></div>`;
});
}
h += '<h2>THIS PAGE</h2>';
h += '<div class="card">';
h += `<p>Auto-expires in <span style="color:#FF1053;font-family:'Fira Mono',monospace">${D.serve_hours}h</span></p>`;
h += '<p class="muted">Download configs before this page disappears.</p>';
h += '</div>';
$.innerHTML = h;
</script>
</body>
</html>
+6
View File
@@ -0,0 +1,6 @@
#!/bin/sh
if systemctl is-active --quiet gns3-config-serve.service 2>/dev/null; then
echo ""
echo " GNS3 config: http://${_lan_ip}:${_CONFIG_SERVE_PORT_}/"
echo " (auto-expires — download configs now)"
fi
+10
View File
@@ -0,0 +1,10 @@
[Interface]
Address = 172.16.254.2/24
PrivateKey = ${_client_privkey}
DNS = 1.1.1.1
[Peer]
PublicKey = ${_server_pubkey}
Endpoint = ${_public_ip}:${_WG_PORT_}
AllowedIPs = 172.16.253.0/24, 172.16.254.0/24
PersistentKeepalive = 25
+10
View File
@@ -0,0 +1,10 @@
[Unit]
Description=Stop and clean up GNS3 config server
[Service]
Type=oneshot
ExecStart=/bin/systemctl stop gns3-config-serve.service
ExecStart=/bin/systemctl disable gns3-config-serve.service
ExecStart=/bin/rm -rf ${_CONFIG_SERVE_DIR_}
ExecStart=/bin/systemctl disable gns3-config-serve-stop.timer
ExecStart=-/usr/sbin/ufw delete allow ${_CONFIG_SERVE_PORT_}/tcp
+12
View File
@@ -0,0 +1,12 @@
[Unit]
Description=GNS3 ephemeral config server
After=network-online.target
[Service]
Type=simple
WorkingDirectory=${_CONFIG_SERVE_DIR_}
ExecStart=/usr/bin/python3 -m http.server ${_CONFIG_SERVE_PORT_} --bind 0.0.0.0
Restart=no
[Install]
WantedBy=multi-user.target
+10
View File
@@ -0,0 +1,10 @@
[Unit]
Description=Stop GNS3 config server after ${_CONFIG_SERVE_HOURS_} hours
[Timer]
OnActiveSec=${_CONFIG_SERVE_HOURS_}h
AccuracySec=1min
Unit=gns3-config-serve-stop.service
[Install]
WantedBy=timers.target
+21
View File
@@ -0,0 +1,21 @@
[Unit]
Description=GNS3 server
After=network-online.target
Wants=network-online.target
Conflicts=shutdown.target
[Service]
User=${_GNS3_USER_}
Group=${_GNS3_USER_}
PermissionsStartOnly=true
EnvironmentFile=/etc/environment
ExecStartPre=/bin/mkdir -p /var/log/gns3 /var/run/gns3
ExecStartPre=/bin/chown -R ${_GNS3_USER_}:${_GNS3_USER_} /var/log/gns3 /var/run/gns3
ExecStart=${_gns3_bin} --log /var/log/gns3/gns3.log
ExecReload=/bin/kill -s HUP $MAINPID
Restart=on-failure
RestartSec=5
LimitNOFILE=16384
[Install]
WantedBy=multi-user.target
+13
View File
@@ -0,0 +1,13 @@
${_DEPLOY_MARKER_}
[Server]
host = ${_listen_host}
port = ${_GNS3_PORT_}
images_path = ${_GNS3_HOME_}/images
projects_path = ${_GNS3_HOME_}/projects
appliances_path = ${_GNS3_HOME_}/appliances
configs_path = ${_GNS3_HOME_}/configs
report_errors = True
[Qemu]
enable_hardware_acceleration = ${_hw_accel}
require_hardware_acceleration = ${_hw_accel}
+114
View File
@@ -0,0 +1,114 @@
<!doctype html>
<!-- Note: cozy by design. Keeping "small but decipherable". -->
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>GNS3 Server</title>
<style>
@import url("https://fonts.googleapis.com/css2?family=Bebas+Neue&family=Fira+Sans+Extra+Condensed:wght@200&family=Fira+Mono&display=swap");
* { margin: 0; padding: 0; box-sizing: border-box; }
body { background: #070600; color: #e0e0e0; font-family: "Fira Sans Extra Condensed", sans-serif; font-size: 24px;
line-height: 1.8; padding: 2rem; max-width: 950px; margin: 0 auto; }
h1 { font-family: "Bebas Neue", sans-serif; color: #ff1053; font-size: 4rem; letter-spacing: 2px; margin-bottom: 0.5rem; }
h2 { font-family: "Bebas Neue", sans-serif; color: #ff1053; font-size: 2rem; letter-spacing: 1px; margin: 2rem 0 0.5rem; border-bottom: 1px solid #333; padding-bottom: 0.3rem; }
a { color: #ff1053; text-decoration: none; }
a:hover { text-decoration: underline; }
code, .mono { font-family: "Fira Mono", monospace; font-size: 1.4rem; padding: 2px 6px; border-radius: 3px; }
pre { background: #1a1a1a; padding: 1.2rem; border-radius: 4px; overflow-x: auto; font-family: "Fira Mono", monospace; font-size: 0.85em; margin: 0.5rem 0; white-space: pre-wrap; word-break: break-all; }
.card { background: #111; border: 1px solid #222; padding: 1rem; margin: 0.5rem 0; border-radius: 4px; }
.warn { border-left: 3px solid #ff1053; }
.dl { display: inline-block; background: #ff1053; color: #070600; padding: 0.4rem 1rem; font-weight: 700; margin: 0.3rem 0.3rem 0.3rem 0; border-radius: 3px; font-size: 0.9rem; }
.dl:hover { background: #cc0d42; text-decoration: none; }
.muted { color: #666; font-size: 1.1rem; }
.grid { display: grid; grid-template-columns: 1fr 1fr; gap: 0.5rem; }
@media (max-width: 720px) { .grid { grid-template-columns: 1fr; } }
</style>
</head>
<body>
<h1>GNS3 SERVER</h1>
<div id="app"></div>
<script>
// Data injected by gns3-remote.sh
const D = {
lan_ip: "{{LAN_IP}}", public_ip: "{{PUBLIC_IP}}", hostname: "{{HOSTNAME}}", gns3_version: "{{GNS3_VERSION}}",
with_openvpn: {{WITH_OPENVPN}}, with_wireguard: {{WITH_WIREGUARD}}, with_docker: {{WITH_DOCKER}},
disable_kvm: {{DISABLE_KVM}}, unsafe_configs: {{UNSAFE_CONFIGS}},
serve_slug: "{{SERVE_SLUG}}", serve_hours: {{SERVE_HOURS}}, serve_port: {{SERVE_PORT}},
gns3_port: {{_GNS3_PORT_}}, ovpn_port: {{_OVPN_PORT_}}, wg_port: {{_WG_PORT_}},
warnings: {{WARNINGS_JSON}}
};
const $ = document.getElementById('app');
let h = '';
h += '<h2>YOUR SERVER</h2>';h += '<div class="card">';
h += `<p><strong>GNS3:</strong> <a href="http://${D.lan_ip}:${D.gns3_port}">http://${D.lan_ip}:${D.gns3_port}</a></p>`;
h += `<p><strong>Version:</strong> <code>${D.gns3_version}</code></p>`;
h += `<p><strong>Host:</strong> ${D.hostname}</p>`;
h += `<p><strong>Docker:</strong> ${D.with_docker ? 'Yes' : 'No'}</p>`;
h += `<p><strong>KVM:</strong> ${D.disable_kvm ? '<span style="color:#FF1053">Disabled</span>' : 'Enabled'}</p>`;
h += '</div>';
if (D.with_wireguard || D.with_openvpn) {
h += '<h2>VPN</h2><div class="card">';
const ext = D.unsafe_configs ? '' : '.enc';
if (D.with_wireguard) {
h += `<p><strong>WireGuard:</strong> <code>${D.public_ip}:${D.wg_port}</code></p>`;
h += `<p><strong>Tunnel:</strong> <code>172.16.254.1:${D.gns3_port}</code></p>`;
h += `<a class="dl" href="${D.serve_slug}/w">Download WireGuard Config${ext ? ' (encrypted)' : ''}</a> `;
}
if (D.with_openvpn) {
h += `<p><strong>OpenVPN:</strong> <code>${D.public_ip}:${D.ovpn_port}/udp</code></p>`;
h += `<p><strong>Tunnel:</strong> <code>172.16.253.1:${D.gns3_port}</code></p>`;
h += `<a class="dl" href="${D.serve_slug}/o">Download OpenVPN Config${ext ? ' (encrypted)' : ''}</a> `;
}
if (!D.unsafe_configs) {
h += '<p class="muted" style="margin-top:0.5rem">Configs encrypted. Passphrase on server terminal.</p>';
}
h += '<p class="muted">Port forwarding required on your router.</p></div>';
}
// Dynamic Download Links based on Semantic Version
const dl_win = D.gns3_version !== "unknown"
? `https://github.com/GNS3/gns3-gui/releases/download/v${D.gns3_version}/GNS3-${D.gns3_version}-all-in-one.exe`
: `https://github.com/GNS3/gns3-gui/releases/latest`;
const dl_mac = D.gns3_version !== "unknown"
? `https://github.com/GNS3/gns3-gui/releases/download/v${D.gns3_version}/GNS3-${D.gns3_version}.dmg`
: `https://github.com/GNS3/gns3-gui/releases/latest`;
h += '<h2>GNS3 CLIENT</h2>';
h += '<div class="grid">';
h += '<div class="card"><strong>Windows</strong><br>';
h += `<a class="dl" href="${dl_win}">GNS3 Client ${D.gns3_version !== "unknown" ? '(v' + D.gns3_version + ')' : ''}</a>`;
h += '<br><a class="dl" href="https://www.chiark.greenend.org.uk/~sgtatham/putty/latest.html">PuTTY</a></div>';
h += '<div class="card"><strong>macOS</strong><br>';
h += `<a class="dl" href="${dl_mac}">GNS3 Client ${D.gns3_version !== "unknown" ? '(v' + D.gns3_version + ')' : ''}</a>`;
h += '<br><a class="dl" href="https://iterm2.com">iTerm2</a></div>';
h += '<div class="card"><strong>Linux</strong><br>';
h += '<a class="dl" href="https://github.com/GNS3/gns3-gui/releases/latest">GNS3 Client</a>';
h += '<br><code>sudo apt install gns3-gui</code></div></div>';
h += '<h2>RESOURCES</h2>'; h += '<div class="card">';
h += '<p><a href="https://gns3.com/marketplace/appliances">GNS3 Appliance Marketplace</a></p>';
h += '<p><a href="https://fedorapeople.org/groups/virt/virtio-win/direct-downloads/stable-virtio/virtio-win.iso">VirtIO Windows Drivers</a></p>';
h += '<p><a href="https://docs.gns3.com">GNS3 Documentation</a></p>';
h += '<p><a href="https://lark.cx">lark.cx tutorials</a></p></div>';
if (D.warnings && D.warnings.length > 0) {
h += '<h2>WARNINGS</h2>';
D.warnings.forEach(w => { h += `<div class="card warn"><code>${w}</code></div>`; });
}
h += '<h2>THIS PAGE</h2>';
h += '<div class="card">';
h += `<p>Auto-expires in <span style="color:#FF1053;font-family:'Fira Mono',monospace">${D.serve_hours}h</span></p>`;
h += '<p class="muted">Download configs before this page disappears.</p></div>';
$.innerHTML = h;
</script>
</body>
</html>
+11
View File
@@ -0,0 +1,11 @@
[Interface]
Address = 172.16.254.1/24
ListenPort = ${_WG_PORT_}
PrivateKey = ${_server_privkey}
PostUp = iptables -A FORWARD -i wg0 -j ACCEPT; iptables -t nat -A POSTROUTING -o ${_default_iface} -j MASQUERADE
PostDown = iptables -D FORWARD -i wg0 -j ACCEPT; iptables -t nat -D POSTROUTING -o ${_default_iface} -j MASQUERADE
[Peer]
# client1
PublicKey = ${_client_pubkey}
AllowedIPs = 172.16.254.2/32