Skip to content

SusheelThapa/secure-offline-update-system

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

3 Commits
 
 
 
 

Repository files navigation

Secure Offline Software Update Distribution System

This repository provides a tutorial‑style guide to simulate how software updates are securely distributed in an air‑gapped environment. The walkthrough demonstrates how to set up virtual machines, configure networking, verify and transfer packages, sign repositories with GPG, and consume updates on client machines — all without internet access on the internal network.

🎥 Demo Video

Secure.Offline.Software.Update.Distribution.System.mp4

How it Work?

The project simulates how software updates are securely distributed in an internal network without internet access.

  • Gateway Server

    • Receives .deb packages from external sources.
    • Performs integrity checks(e.g. ClamAV scan, SHA256).
    • Transfers verified packages into the internal network via SSH
  • Bastion Host

    • Acts as a jump-only SSH gateway
    • Enforces controlled access to the repository server.
  • Repository Server

    • Uses aptly to index incoming packages.
    • Generates signed metadata(Release, Packages.gz)
    • Publishes the repository via Lighttpd
  • Client Machine

    • Run apt update to fetch metadata from the internal repo.
    • Verify GPG signatures before accepting updates.
    • Install packages using apt install, pulling only from the internal source
image

🖥️ Setting up the Virtual Machines

  1. Create four Ubuntu VMs:

    • Update VM – NAT + host‑only, IP 192.168.182.10
    • Bastion Host – host‑only, IP 192.168.182.50
    • Repository Server – host‑only, IP 192.168.182.20
    • Client – host‑only, DHCP
  2. Configure networking:

    • NAT → Update VM only (for external package downloads).
    • Host‑only (vmnet10) → all VMs (isolated subnet, no internet).
VM Name IP Address Network Adapter Purpose
Gateway Server 192.168.226.130 NAT External access for package ingestion
192.168.182.10 VMNet10 Internal communication
Bastion Host 192.168.182.50 VMNet10 SSH jump enforcement
Repository Server 192.168.182.20 VMNet10 Aptly repository hosting via Lighttpd
Client DHCP VMNet10 Consumes updates via apt

Architecture

🔐 Bastion Host Setup

Install and enable SSH:

sudo apt update
sudo apt install -y openssh-server
sudo systemctl enable --now ssh
  • apt update → refreshes package metadata.
  • apt install → installs the OpenSSH daemon.
  • systemctl enable --now → ensures SSH starts immediately and on boot.

🛡️ Gateway VM Security Pipeline

Install required tools:

sudo apt update
sudo apt install -y clamav gnupg rsync openssh-client
  • ClamAV → malware scanning.
  • GnuPG → key generation and signing.
  • rsync → secure file transfer.
  • OpenSSH client → provides scp and jump‑host support.

You’ll use the following pipeline script to automate package verification and transfer.

📜 package_pipeline.sh

#!/bin/bash
set -euo pipefail

PKG_NAME=$1
WORKDIR=/tmp/package-check
VERIFIED_DIR=/opt/verified
REJECTED_DIR=/opt/rejected
REPO_USER=reposrvadmin
REPO_HOST=192.168.182.20
REPO_PATH=/opt/repo/incoming
LOGFILE=/var/log/package-pipeline.log

mkdir -p $WORKDIR $VERIFIED_DIR $REJECTED_DIR
touch $LOGFILE
cd $WORKDIR

log() {
    echo "$(date '+%Y-%m-%d %H:%M:%S') | $1" | tee -a $LOGFILE
}

log "[0] Updating ClamAV signatures..."
sudo freshclam >> $LOGFILE 2>&1 || log "⚠️ ClamAV update failed, continuing with existing database."

log "[1] Downloading package: $PKG_NAME"
apt download $PKG_NAME >> $LOGFILE 2>&1

PKG_FILE=$(ls ${PKG_NAME}_*.deb)

log "[2] Calculating SHA256"
sha256sum $PKG_FILE > ${PKG_FILE}.sha256
sha256sum -c ${PKG_FILE}.sha256 >> $LOGFILE 2>&1 || {
    log "❌ SHA mismatch! Moving to rejected."
    mv $PKG_FILE $REJECTED_DIR/
    exit 1
}

log "[3] Running ClamAV scan"
clamscan $PKG_FILE >> $LOGFILE 2>&1 || {
    log "❌ ClamAV flagged! Moving to rejected."
    mv $PKG_FILE $REJECTED_DIR/
    exit 1
}

log "[4] Package clean. Moving to verified."
mv $PKG_FILE $VERIFIED_DIR/

log "[5] Sending package to Repository via Bastion jump"
rsync -avz -e "ssh -i /home/gatewaysrvadmin/.ssh/id_ed25519 -J bastionadmin@192.168.182.50" \
    $VERIFIED_DIR/$PKG_FILE \
    reposrvadmin@192.168.182.20:/opt/repo/incoming/

log "✅ Workflow complete: $PKG_FILE delivered to Repository"

Explanation:

  • set -euo pipefail → strict error handling.
  • freshclam → updates ClamAV signatures.
  • apt download → fetches .deb without installing.
  • sha256sum → verifies file integrity.
  • clamscan → malware scan.
  • rsync -avz -e "ssh -J" → transfers via Bastion jump host.

📦 Repository Server Setup

Install Aptly:

sudo apt install -y aptly

Create and populate repository:

aptly repo create offline-repo
aptly repo add offline-repo /opt/repo/incoming/

Generate and export GPG key:

gpg --full-generate-key
gpg --export -a "Offline Repo" > /opt/repo/repo-public.key

Send public key to Bastion Host:

scp /opt/repo/repo-public.key bastionadmin@192.168.182.50:/incoming/repo-public.key

Publish repository:

aptly publish repo -gpg-key="YOUR_KEY_ID" offline-repo

Addtionally, you can use the update script that I have used in the demo video.

#!/bin/bash
set -euo pipefail

REPO_NAME=internal-repository
DIST=stable
GPG_KEY="Internal Repository"
INCOMING=/opt/repo/incoming

echo "[`date`] Adding new packages..."
aptly repo add $REPO_NAME $INCOMING

echo "[`date`] Updating published repo..."
aptly publish update -gpg-key="$GPG_KEY" $DIST

Serve via Lighttpd:

sudo apt install -y lighttpd
sudo systemctl enable --now lighttpd
sudo ln -s /var/lib/aptly/public /var/www/html/repo

Repository URL:
http://192.168.182.20/repo/

💻 Client Setup

Copy public key from Bastion:

scp bastionadmin@192.168.182.50:/incoming/repo-public.key ~/repo-public.key

Trust the key:

sudo mkdir -p /etc/apt/trusted.gpg.d/
sudo cp ~/repo-public.key /etc/apt/trusted.gpg.d/

Add repository to sources:

sudo nano /etc/apt/sources.list
deb http://192.168.182.20/repo/ stable main

Update and install packages:

sudo apt update
sudo apt install <package>

⚠️ Note: The pipeline script must be executed on the Gateway VM to prepare and deliver packages before they can be installed on the Client.

About

Simulating secure software updates in air‑gapped networks i.e reproducible, trusted, and offline.

Resources

License

Stars

Watchers

Forks

Contributors