Installation wizard
For each step you'll see:
- What you do — what to enter / click
- Behind the scenes — what the wizard does on your machine and on Proxmox
Step 0 — Clone the main repo
You only need the range42 repo locally — it contains the wizard. The wizard
itself will clone the rest (playbooks, catalog, controller, devkit) on the
deployer-cli during deploy.
sudo apt-get update ; apt-get upgrade -y
sudo apt-get install python3-venv git
mkdir -p $HOME/range42 && cd $HOME/range42
git clone https://github.com/range42/range42.git
:::tip Recommended
Keep the default paths ($HOME/range42 for git repos, $HOME/range42.config
for workspaces). The wizard offers to change them if you really need to, but
the defaults are well-tested and many scripts / configs reference them.
This is the only structural constraint — the rest of the wizard is fully
configurable.
:::
Step 1 — Launch the wizard (preflight)
cd ~/range42/range42
./range42-init.py
What you do: wait for the preflight checks. If anything is missing, the wizard offers to install it (textual, ansible, sshpass, keychain).
Behind the scenes:
- Checks
which ansible ssh-keygen ssh-agent sshpass git keychain zsh - Checks Ansible collections:
community.crypto,community.general - Checks if
inventories/example/exists - Checks ssh-agent is running
If anything is missing, the wizard either auto-installs (apt) or shows the fix command for you to run manually.
Step 1b — Confirm install paths
What you do: review the install paths and confirm.
The wizard asks where to put two things:
- range42 git repos → default:
$HOME/range42/ - range42 workspaces (per-codename configs, secrets, SSH keys) → default:
$HOME/range42.config/
Recommended: keep the defaults. Changing them is possible but it is one of the rare elements we recommend not to modify — many internal scripts, templates and helper functions reference these paths, and a non-default layout can make troubleshooting harder.
Behind the scenes:
- The chosen paths are stored in the wizard config and propagated to:
inventories/<codename>/group_vars/all/vars.yml~/range42.config/<codename>-<scenario>/sourced_range42.sh- SSH config templates, vault paths, devkit scripts
Step 2 — Choose new or existing
What you see at this step depends on whether you've already deployed range42 on this machine.
First-time setup — no previous configuration is detected, the wizard goes straight to "new".
Subsequent runs — at least one previous configuration exists, the wizard offers a choice.
What you do: first-time setup → nothing to pick, just continue. If you already deployed a config, you'll see it listed and can either start a new one or overwrite an existing one.
Behind the scenes:
- Scans
inventories/for existing setups (folders withhosts.yml) - For each, parses
group_vars/to detect deployed scenarios - Shows one button per
codename + scenariocombination, plus◆ new
If you pick "new", an empty inventory will be created. If you pick an existing one, the wizard pre-fills all fields from group_vars/all/vars.yml.
Step 3 — Enter your infrastructure codename
What you do: pick a label for your Proxmox infrastructure (e.g., mylab).
This becomes the namespace for everything related to this Proxmox.
Behind the scenes:
- Will create
inventories/<codename>/frominventories/example/template - All subsequent files (vault, SSH keys, workspace) are scoped under this codename
Step 4 — Proxmox connection details
What you do:
- Address: IP or hostname of your Proxmox (e.g.,
192.168.1.10) - Node: Proxmox node name (see below)
The PVE node name must be exact
The wizard asks for the Proxmox node name. This is not a label you choose — it must match exactly the node name as it appears in the Proxmox web UI / API. Often it's pve on a single-node setup, but it can also be pve01, the hostname, or anything the cluster admin set.
You can find the exact name in the Proxmox web console (left sidebar tree, the entry under "Datacenter") or via SSH on the Proxmox host:
pvesh get /nodes --output-format json | jq -r '.[].node'
If the name doesn't match exactly, the wizard's API calls will fail at Step 4 validation (or later at deploy time on vm_create).
Behind the scenes:
- Tests HTTPS reachability to
https://<address>:8006 - Validates the node name exists in Proxmox API (
GET /nodes)
No changes are made yet — this is read-only verification.
Proxmox root password
The wizard then prompts for the Proxmox root password.
What it's used for:
- Install the range42 root SSH key in
/root/.ssh/authorized_keys(one-shot, viasshpass) - Create the
jump_userLinux account on Proxmox - Create the
range42_apiPAM user + API token viapveum - Configure Proxmox locale, NTP, IP forwarding, network bridges (
vmbr140-148), NAT rules
After this bootstrap, root SSH is no longer used — daily operations go through the jump_user and the API token (see Why a jump_user and not just root? below).
Privacy note: the password is held in memory by the wizard for the duration of the run and never written to disk.
Deployer-cli sudo password
The wizard also prompts for the sudo password on the deployer-cli (your local machine in the default setup).
What it's used for:
apt installpackages required by the deployer-cli role (ansible, git, keychain, zsh, sshpass, etc.)- Install dotfiles and configure system services (locale, NTP)
- Write SSH config under
~/.ssh/(no sudo strictly needed for~/.ssh, but other steps in the role need it)
If the deployer-cli is your local machine (the default), this is your own sudo password. If you target a remote deployer-cli VM, this is the sudo password of the user on that VM.
Step 5 — Network (NAT auto-detect + bridge toggles)
What you do:
- The wizard auto-detects your outbound NAT interface (typically
vmbr0) - Bridges
vmbr140tovmbr148are listed with a NAT toggle each - Defaults are fine — accept
Disable outgoing NAT on a specific bridge
If you want to isolate one or several subnets from internet access, just click on the corresponding bridge in the list to toggle off its outgoing NAT.
This is useful for fully air-gapped subnets (e.g., a sensitive forensic VM, an offline analysis lab) — VMs on a NAT-disabled bridge can still talk to other VMs on the same subnet, but cannot reach the internet through the Proxmox host.
Why bridges are pre-created
By default, range42 pre-creates all the bridges listed in the wizard (vmbr140 to vmbr148) on the Proxmox host as part of this step, even if your scenario only uses a few of them.
Why: it saves time on later deployments. Once the bridges exist, deploying any scenario (or adding a new one with more subnets) requires no Proxmox network reconfiguration — the wizard just clones VMs onto the already-existing bridges. The cost is minimal: an unused bridge is just a Linux interface with no traffic.
Behind the scenes:
- SSH to Proxmox as root, runs
ip route get 1.1.1.1 | awk '{print $5}' - Identifies outbound interface (typically
vmbr0) - Creates the
vmbr140-148bridges viapvesh create /nodes/<node>/network(idempotent — skipped if already present) - Injects per-bridge NAT rules (post-up/post-down iptables MASQUERADE) for bridges with NAT enabled
- Reloads Proxmox network config (
ifreload -a) - Stores in inventory:
infrastructure_proxmox_default_network_card_interface: vmbr0- Per-bridge
nat: true/falsetoggle
Step 6 — Pick scenario
What you do: type blank_scenario_2_subnets.
Behind the scenes:
- Stored as
INFRASTRUCTURE_SCENARIOingroup_vars/all/vars.yml - Determines which J2 templates the deploy will use
Step 7 — Deployer + auto-deploy
This step asks where the deployer-cli will run (location + user) and then launches the full deployment. The two passwords (Proxmox root + deployer-cli sudo) were already collected at Step 4.
Deployer-cli location (IP)
What you do: enter the IP / hostname where the deployer-cli will be configured.
By default, range42 deploys the deployer-cli on the same machine where you run the wizard — so the default value is 127.0.0.1. Most users keep this.
If you want a dedicated deployer-cli VM (e.g., to manage multiple Proxmox infrastructures from one place, or to keep credentials off your laptop), enter that VM's IP instead. The VM must already exist and be reachable over SSH.
Deployer-cli user
What you do: enter the Linux user that will own the workspace on the deployer-cli.
If you kept 127.0.0.1 above, this is your current local user (typically what whoami returns). On a dedicated deployer-cli VM, this is the user that will hold ~/range42/, ~/range42.config/, ~/.ssh/range42/, etc.
The user must:
- Already exist on the deployer-cli machine
- Have sudo rights (for the apt installs in Playbook 03)
- Be reachable over SSH from the wizard machine (only if you target a remote deployer-cli)
Confirm and trigger the deployer-cli install
This is the last interactive prompt — the wizard shows a recap of everything it's about to do (Proxmox address, codename, scenario, deployer-cli location/user, network bridges) and asks you to confirm before any change is made on Proxmox or on the deployer-cli.
What you do: review the recap, then confirm to launch the install.
Up to here the wizard has only been collecting input and doing read-only checks (HTTPS reachability, node name validation). The moment you confirm, the wizard starts making real changes — installing SSH keys on Proxmox, creating the jump_user, generating the API token, writing the vault, configuring the deployer-cli. This is your last opportunity to abort cleanly.
If you abort here (Ctrl-C or "Cancel"), nothing has been touched on Proxmox or on the deployer-cli yet — your input is just discarded.
Auto-deploy starts
After confirming, the wizard runs the full deployment automatically (~10–15 min).
Behind the scenes: the wizard runs ansible-playbook site.yml which executes 3 playbooks in sequence.
Playbook 01 — credentials.generate
Local actions:
- Generate 4 SSH keypairs (ed25519) in
config/<codename>-<scenario>/ssh_keys/:px.<codename>-<scenario>-ssh_cli.root— Proxmox root SSHpx.<codename>-<scenario>-ssh_cli.jump_user— Proxmox jump user SSHr42.<codename>-<scenario>-deployer-key_alice— admin user on VMsr42.<codename>-<scenario>-student-key_bob— student user on VMs
- Generate vault with random VM passwords + Wazuh password
- Encrypt vault with
vault_pass.txt - Generate operator's SSH config snippet
Playbook 02 — configure proxmox
Proxmox actions (via root SSH using password from wizard):
- Install the root SSH key in
/root/.ssh/authorized_keys - Create
jump_userLinux user - Install Linux locale (en_US.UTF-8)
- Configure NTP
Proxmox actions (via API token, then via root SSH):
- Create
range42_apiPAM user - Generate
range42_api_tokentoken (auto-recovers if exists with wrong secret) - Inject token secret into vault
- Create bridges
vmbr140tovmbr148viapvesh - Inject NAT rules per bridge (post-up/post-down iptables MASQUERADE)
- Reload Proxmox network (
ifreload -a) - Enable IP forwarding
Why a jump_user and not just root?
You'll notice range42 creates a separate jump_user Linux account on Proxmox,
even though it already installed the root SSH key. Two reasons:
-
Separation of concerns. Root is used only once during bootstrap (install the root key, create the jump user, set the API token). After that, day-to-day operations (
range42-context use,ssh r42.<vm>) use the API token andjump_user. Root SSH is no longer needed. -
Reduced attack surface for ProxyJump. A SSH connection through a
jump_useronly needs to forward TCP to the internal subnets — it doesn't need a shell. Even if the jump key leaks, the attacker has no shell on Proxmox (you can lock the user down further withForceCommandor restricted shell if desired).Honestly, this doesn't add a huge amount of security on its own — the
jump_useron Proxmox is still a Linux account. But it's a good hygiene practice and lets you rotate the jump key without touching root.
Playbook 03 — deploy deployer-cli
Deployer-cli actions (via SSH from your local machine):
- Install packages:
ansible,git,keychain,oh-my-zsh,zsh,vim, etc. - Configure NTP and locale
- Install dotfiles (vim, zsh)
- Clone all 5 range42 repos to
~/range42/(see table below) - Create workspace at
~/range42.config/<codename>-<scenario>/ - Upload SSH keys + vault from local machine
- Create symlinks:
scenario →(in workspace),secrets →(in playbook scenario dir) - Generate two SSH config files from J2 templates:
~/.ssh/config— addsIncludefor the next file~/.ssh/config_range42-<codename>-<scenario>— actual host entries
- Inject
source ~/range42.config/range42-context.shinto.zshrc - Set the active context to this codename + scenario
After this, range42-context use <codename> <scenario> works.
The 5 repos cloned on the deployer-cli
| Repo | Purpose |
|---|---|
range42 | Main repo. Wizard, 11 Ansible roles, 3 playbooks, the range42-context shell tool. |
range42-playbooks | Lab scenarios (demo_lab, blank_scenario_*). What gets deployed on the Proxmox VMs. |
range42-catalog | Reusable Ansible roles (firewalls, packages, dotfiles, wazuh, etc.) used by scenarios. |
range42-ansible_roles-proxmox_controller | Wraps the Proxmox API (create/clone/delete VMs, manage templates, networks). |
range42-ansible_roles-debug-devkit | Helper scripts for snapshots, reverts, debugging individual VMs. |
Step 8 — Deploy the scenario itself
This isn't a wizard step — you run it manually after the wizard finishes.
8a. Load your context
Open a new terminal (or source ~/.zshrc in the current one), then load the workspace you just created:
range42-context use YOUR_CODENAME_INFRASTRUCTURE blank_scenario_2_subnets
You should see range42-context switch into the workspace, with output like this:
----[ switching to px-testing-blank_scenario_2_subnets ]----
➜ commented all active Include lines
➜ uncommented Include for px-testing-blank_scenario_2_subnets
➜ commented all sourced_range42.sh in .zshrc
➜ uncommented sourced_range42.sh for px-testing-blank_scenario_2_subnets in .zshrc
➜ sourced /home/grml/range42.config/px-testing-blank_scenario_2_subnets/sourced_range42.sh
➜ updated secrets symlink in devkit → px-testing-blank_scenario_2_subnets
➜ updated secrets symlink in playbooks → px-testing-blank_scenario_2_subnets
➜ exported RANGE42_VAULT_PASSWORD_FILE=/home/grml/range42.config/px-testing-blank_scenario_2_subnets/secrets/vault_pass.txt
➜ exported ANSIBLE_CONFIG=/home/grml/range42/range42/ansible.cfg
✓ ssh keys reloaded (3 key(s) loaded)
--- status : px-testing-blank_scenario_2_subnets ---
workspace px-testing-blank_scenario_2_subnets ok
vault pass vault_pass.txt ok
vault encrypted ok
vault decrypt password valid ok
ssh-agent 3 key(s) loaded ok
inventory inventory_default.yml ok
scenario blank_scenario_2_subnets ok
If every line of the status block ends with ok, the workspace is loaded correctly and you're ready to deploy. If anything is ko, see Troubleshooting.
8b. Deploy the scenario VMs
range42-context deploy # ~15-20 min for first deploy
Behind the scenes:
- Downloads cloud-init images (Ubuntu Noble, Server, Debian 12) to Proxmox storage
- Creates 9 VM templates (nano, micro, small, medium, large) on
vmbr140 - For each of 4 team VMs:
- Clones template (small, vm_id 9221) to a new VM
- Sets cloud-init variables (user, password, SSH key, IP, gateway, bridge)
- Starts the VM
- Waits for SSH and cloud-init completion
- On all 4 VMs:
- Installs basic packages (vim, htop, net-utils)
- Installs dotfiles for
aliceuser - Configures UFW firewall (port 22 only)
When deploy completes, SSH into a VM:
ssh r42.bs2-team-143-01
You're now alice@bs2-team-143-01. From here you can ping the other 3 VMs
(192.168.143.201, 192.168.144.200, 192.168.144.201) and reach the internet
(NAT routes through vmbr0).
range42 generated both the Ansible inventory and your ~/.ssh/config for
you. SSH keys are loaded automatically when you run range42-context use. No
manual SSH key import or -i keyfile flag needed — just ssh r42.<vm-name>.
Continue with First Deployment for daily operations:
range42-context, credentials, and backup.