Skip to main content

Someone wanted to learn this too, so Grasp built them a personal learning path.

You can keep exploring this one or create your own

Validating and Documenting an Isolated Labshock Deployment

Hello! We’ll start by establishing a safe, reproducible lab before using it to study industrial processes or security controls. This is an installation and capability-validation lesson: the goal is not to assume that every feature advertised for Labshock exists in the edition you install, but to determine what your deployment can actually do.

By the end, you should have an isolated deployment and a short record of its version, trial limits, running services, network exposure, and safe restart procedure. Allow about 45–55 minutes of active work, plus any image-download time. The published trial has a five-minute initialization delay; plan your checks before its working session begins.


Establish the documented baseline

Use a dedicated Linux VM for this walkthrough. Your existing virtualisation and Linux experience should make that simpler than introducing Docker Desktop into the same session. The project lists Docker as its prerequisite and publishes a minimum footprint of 2 CPU cores, 2 GB RAM, and 10 GB storage, alongside a larger 4-core, 8 GB, 20 GB footprint. Allocate the larger footprint if convenient; those figures are project-published guidance, not a performance guarantee.

The project also describes an unlicensed trial with a five-minute initialization delay, a 40-minute session limit, and unlimited restarts. Treat those as documented terms to verify against your installed release, not proof that a particular service or saved project will work throughout repeated sessions.

GitHub - zakharb/labshock: OT Security Lab for ICS networks · GitHub

Read the project's README to establish its stated requirements, trial terms, and proposed components before comparing them with your installation.

Find “Requirements” and “Trial License Info” below “Who Is It For.” Read the resource requirements, then the trial terms. Under “Services,” scan the smallest relevant subsections: “Portal,” “SCADA,” “PLC,” “Network Swiftness,” “EWS,” “Firewall,” and “SIEM.” For example, the Firewall description states an advertised capability. Do not yet mark any of these components as running or accessible.

Quickstart Guide · zakharb/labshock Wiki · GitHub

Read the project's Quickstart Guide for its Docker-based entry point, portal address, and stop instruction.

Find “Install Docker on Debian & Ubuntu” or “Install Docker Desktop on MacOS & Windows” for the appropriate prerequisite, then “Install Labshock,” “Run/Stop Labshock,” and “Login Labshock.” In “Run/Stop Labshock,” the stop instruction identifies a project-scoped action. In “Login Labshock,” the portal instructions give the documented local address and initial credentials. Also locate “Cleanup Labshock,” but do not execute its broad cleanup commands: they could remove unrelated Docker data. Some installation and run command blocks in the supplied extraction appear incomplete; check the live guide rather than copying an incomplete && block.

A recorded walkthrough is useful for recognizing the build, startup, trial-delay, and HTTPS stages. It demonstrates one installation, not the current state of every release.

OT/ICS Hands-On Labs with LABSHOCK!

Watch “OT/ICS Hands-On Labs with LABSHOCK!” by Daniel Lowrie for a visual reference while preparing to launch your own deployment.

Watch repository layout for the example of finding the Compose file. Continue through build and startup to see docker compose build, docker compose up, and the trial initialization message. At HTTPS access, note why https://localhost matters. Verify your own directory layout and certificate details rather than copying the demonstrator’s assumptions or dismissing browser warnings automatically.


LAB-M1-07: Install inside a controlled boundary

Objective: Bring up Labshock without exposing a training system to networks you did not intend to reach. The diagram is your target setup, not a claim about Labshock’s as-installed Docker topology.

Use a VM network arrangement that does not bridge the VM directly onto a production or other sensitive network. Temporary outbound connectivity may be needed to obtain Docker packages, source, and images; NAT is not the same as no egress. Record whether that path remains enabled after installation. Do not create macvlan/ipvlan links, add routes into OT networks, or run discovery scans for this lesson. Docker network names alone are not evidence of isolation: published host ports and container egress must also be checked.

1. Check prerequisites

Install Docker Engine and the Compose plugin using the Docker installation guide linked from the Quickstart, if they are not already installed. In the Linux VM, confirm the tools and available resources:

docker --version
docker compose version
docker info --format '{{.ServerVersion}}'
git --version
nproc
free -h
df -h .

You need a functioning Docker server, not merely a Docker client binary. If docker info reports a permission error, resolve access deliberately; membership in the Docker group conveys powerful host privileges. If a prerequisite is missing, stop here rather than substituting an unreviewed installation script.

2. Obtain the project and locate its Compose file

The following Git command uses the project repository address. It is a transparent way to obtain the repository, not a claim that the partially extracted Quickstart contains this exact command.

mkdir -p "$HOME/ot-labs"
cd "$HOME/ot-labs"
git clone https://github.com/zakharb/labshock.git
cd labshock
git rev-parse HEAD
find . -maxdepth 3 \( -name compose.yaml -o -name compose.yml -o -name docker-compose.yaml -o -name docker-compose.yml \) -print

Record the commit hash. Move into the directory containing the Compose file reported by find; do not assume that the repository root—or the nested directory shown in the video—matches your checkout. From that directory, validate the configuration without printing a potentially sensitive expanded configuration:

docker compose config --quiet
docker compose config --services

If configuration validation fails, investigate the reported missing file, variable, or syntax error before starting anything. Record the service names; these are configured services, not yet evidence of running components.

3. Build and start

The walkthrough demonstrates a build followed by startup. Run these from the validated Compose directory, reviewing errors at each step:

docker compose build
docker compose up -d
docker compose ps

build may have little to do if your release uses prebuilt images. If the live project instructions specify a materially different procedure, pause and document the difference rather than forcing this one. Watch locally for startup or trial-status messages if needed:

docker compose logs --tail=60

Logs can contain credentials or tokens; redact before sharing them. Allow for the documented five-minute trial initialization delay, then reassess docker compose ps. A running container does not by itself prove its application is ready.

4. Reach the portal without broadening exposure

From a browser inside the VM, try the documented https://localhost. The Quickstart publishes labshock as both the initial portal username and password; treat those as lab-only credentials. If the browser presents a certificate warning, confirm that you are connecting to your intended local lab and examine the certificate details before making a trust decision. Do not generalize an exception to another host.

Record whether login succeeds and whether the portal displays a version, licence state, timer, or component status. If no application version is displayed, write “application version not confirmed”; the Git commit and Docker image identifiers are still useful deployment identifiers, but they are not necessarily an application release number.


Validate what is present—and where it can communicate

A useful distinction for OT architecture work is advertised, configured, running, and usable. For example, a README may advertise a firewall simulator; a Compose file may define it; a container may run; yet its interface may not be available in your trial. Record each level separately.

From the Compose directory, collect local evidence:

docker compose ps
docker compose images
docker network ls
docker ps --format 'table {{.Names}}\t{{.Ports}}'
ip -br addr
ip route

Review docker compose ps and the Docker port listing for host bindings. A binding shown as 0.0.0.0 or [::] warrants particular attention because it is not restricted to IPv4 or IPv6 loopback, respectively. The VM’s hypervisor networking and host firewall also affect who can reach it. Record the observed bindings and VM network mode rather than declaring the lab isolated solely because its portal opens at localhost.

For each Docker network belonging to this deployment, inspect it using the name you observed in docker network ls:

docker network inspect NETWORK_NAME

Replace NETWORK_NAME with the actual name. Record its driver, subnet, attached containers, and Internal setting; review the output locally before sharing it. An internal Docker network may restrict one path, but containers connected to other networks, host publishing, and the VM’s outbound route still need consideration. Do not change the project’s network design blindly to make an exposure finding disappear.

Now inspect the portal read-only. Look for SCADA, PLC, engineering workstation (EWS), monitoring/IDS, collector, firewall, and SIEM-related entries described in the README. Avoid pump controls, PLC writes, scans, or configuration changes: later lessons will establish what those actions mean. Use this evidence scale in your notes:

Status Evidence needed
Advertised Listed in the project README
Configured Named in the validated Compose project or portal configuration
Running Reported by docker compose ps, with any health status noted
Usable in this trial Interface opens or a permitted read-only function works
Unverified You could not check it before the session ended

Expected versus observed: the README suggests that several industrial and security components exist, and documents trial timing. Your actual service list, health, accessible interfaces, image versions, and network bindings must come from your deployment. A failed or locked interface is a finding, not an invitation to assume a paid licence is required.

Keep a compact deployment record

Copy this template into your lab notes; replace examples with observations and mark unknowns explicitly.

LAB-M1-07 deployment record
Date/time and time zone:
VM OS / CPU / RAM / free disk:
Docker Engine / Compose versions:
Project Git commit:
Compose-file location:
Application version (source, or "not confirmed"):
Image names/tags or IDs retained locally:
Licence state shown by installed release:
Documented trial terms: 5-minute initialization; 40-minute session; unlimited restarts
Observed initialization/session behaviour (or "not timed"):
Configured services:
Running/healthy services:
Read-only interfaces reached:
Unavailable, locked, or unverified capabilities:
VM network mode and remaining outbound path:
Host-published ports and bind addresses:
Docker networks, drivers, subnets, and relevant attachments:
Evidence locations (screenshots/log excerpts, with secrets redacted):
Stop/start results:
Data persistence after restart: verified / not verified
Open questions for later labs:

This record becomes your capability gate for subsequent practical work. Labshock remains the primary environment where its installed capabilities support the task. PCAPs, synthetic controller artefacts, and mock telemetry are valid isolated fallbacks where they do not.


Prove a safe restart, not a destructive “reset”

The Quickstart explicitly documents docker compose stop. A conservative availability check is to stop this Compose project, confirm its status, then start it again:

docker compose stop
docker compose ps
docker compose up -d
docker compose ps

Wait through trial initialization if required, and confirm that the local portal is reachable again. This validates an operational stop/start procedure. It does not establish that controller projects, logs, or portal state persist—or that they have been cleared. Record persistence as not verified unless you have compared a known, non-sensitive saved item before and after a restart.

For a clean future baseline, a stopped VM snapshot can be useful if your hypervisor supports one. Do not use global container, image, volume, or system pruning as a “Labshock reset”; those actions may delete other labs. A project-specific destructive reset should only be designed after identifying its volumes, backups, and consequences.

If a check fails

Work from the narrowest observation rather than repeatedly reinstalling:

  • Compose file not found: use the find result and confirm your current directory before rerunning Compose.
  • Docker client works but server does not: check daemon availability and your account’s access; do not assume this is a Labshock defect.
  • Containers start but the portal does not: check the trial delay, docker compose ps, relevant local logs, the exact HTTPS address, and published-port conflicts. An unrelated page on HTTP does not verify Labshock.
  • Portal works but a component does not: distinguish “not configured,” “not healthy,” “licence-limited,” and “not yet investigated.” Record the evidence rather than guessing.
  • Unexpected exposure: stop the project with docker compose stop, review VM networking and host bindings, and restore a controlled boundary before continuing.

Your acceptance checklist is deliberately short:

  • The Compose configuration validates, and its directory and Git commit are recorded.
  • Docker and Compose versions, running services, and portal access are recorded—or failures are documented precisely.
  • Trial terms are separated from behaviour actually observed on this release.
  • Host bindings, Docker networks, VM connectivity, and outbound access have been reviewed.
  • docker compose stop and a subsequent startup work without global cleanup.
  • Every planned capability is marked usable, unavailable, or unverified; none is inferred solely from the README.

Is the free trial sufficient? It is sufficient for now if you can complete read-only architecture checks and later targeted tasks within repeatable sessions, with a way to retain your notes and evidence outside the timed environment. Mark longer tasks, PLC-to-HMI interaction, packet capture, firewall controls, or telemetry integration as pending validation until you test them in their respective lessons. The published restart allowance does not establish state persistence or remove the 40-minute interruption. No purchase is assumed.


Closing perspective

You have established the difference between a project’s feature list and a defensible statement about your deployed lab. The most important outputs are its reproducible identity, observed trial limits, component-status record, network-boundary review, and non-destructive restart procedure. Keep the deployment record: later labs will use it to select supported activities and safe fallbacks.

Next, we’ll turn from the training environment to the process it represents—why OT decisions must account for physical safety and availability, not just conventional IT security priorities.

Can't find a good explanation? Sign up and we'll make it for you

Sign up