Skip to content

Commit

Permalink
Initial release
Browse files Browse the repository at this point in the history
  • Loading branch information
jwmullally committed Jul 2, 2022
0 parents commit 4a9b25a
Show file tree
Hide file tree
Showing 52 changed files with 1,660 additions and 0 deletions.
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
build/
src/rootfs/etc/dropbear/
src/rootfs/etc/uci-defaults/90-custom-password
.vscode
*.swp
339 changes: 339 additions & 0 deletions LICENSE

Large diffs are not rendered by default.

79 changes: 79 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
ALL_CURL_OPTS := $(CURL_OPTS) -L --fail --create-dirs

VERSION := 22.03.0-rc4
BOARD := x86
SUBTARGET := 64
BUILDER := openwrt-imagebuilder-$(VERSION)-$(BOARD)-$(SUBTARGET).Linux-x86_64
PROFILE := generic
EXTRA_IMAGE_NAME := iscsi-target
# Example WiFi support: "wpad-wolfssl kmod-iwlwifi iwlwifi-firmware-iwl8265"
PACKAGES := luci tgt blkid lsblk iperf3 luci-app-commands atop tcpdump ethtool

BUILD_DIR := build
OUTPUT_DIR := $(BUILD_DIR)/$(BUILDER)/bin/targets/$(BOARD)/$(SUBTARGET)
OUTPUT_PREFIX := openwrt-$(VERSION)-$(EXTRA_IMAGE_NAME)-$(BOARD)-$(SUBTARGET)-$(PROFILE)


all: images


$(BUILD_DIR)/downloads:
mkdir -p $(BUILD_DIR)/downloads.tmp
cd $(BUILD_DIR)/downloads.tmp && curl $(ALL_CURL_OPTS) -O https://downloads.openwrt.org/releases/$(VERSION)/targets/$(BOARD)/$(SUBTARGET)/$(BUILDER).tar.xz
cd $(BUILD_DIR)/downloads.tmp && curl $(ALL_CURL_OPTS) -O https://www.kernel.org/pub/linux/utils/boot/syslinux/syslinux-6.03.tar.gz && tar -xf syslinux-6.03.tar.gz
mv $(BUILD_DIR)/downloads.tmp $(BUILD_DIR)/downloads


rootfs-contents: $(BUILD_DIR)/downloads
rm -rf $(BUILD_DIR)/rootfs
cp -rv src/rootfs $(BUILD_DIR)/rootfs
cp -f $(BUILD_DIR)/$(BUILDER)/target/linux/generic/other-files/init $(BUILD_DIR)/rootfs/
mkdir -p $(BUILD_DIR)/rootfs//srv/tftp/
cd $(BUILD_DIR)/downloads/syslinux-6.03/bios/ && cp -f \
core/pxelinux.0 \
com32/elflink/ldlinux/ldlinux.c32 \
com32/menu/menu.c32 \
com32/menu/vesamenu.c32 \
com32/lib/libcom32.c32 \
com32/libutil/libutil.c32 \
../../../rootfs/srv/tftp/


$(BUILD_DIR)/$(BUILDER): $(BUILD_DIR)/downloads
cd $(BUILD_DIR) && tar -xf downloads/$(BUILDER).tar.xz


images: $(BUILD_DIR)/$(BUILDER) rootfs-contents
cd $(BUILD_DIR)/$(BUILDER) && make image PROFILE="$(PROFILE)" EXTRA_IMAGE_NAME="$(EXTRA_IMAGE_NAME)" PACKAGES="$(PACKAGES)" FILES="../rootfs"
cat $(OUTPUT_DIR)/sha256sums
mkdir -p $(BUILD_DIR)/images
cp $(OUTPUT_DIR)/$(OUTPUT_PREFIX)-kernel.bin $(BUILD_DIR)/images/openwrt-$(EXTRA_IMAGE_NAME)-kernel.bin
# TODO: Build initramfs image with OpenWrt ImageBuilder built-in Makefile targets
src/tar2cpio.sh $(OUTPUT_DIR)/$(OUTPUT_PREFIX)-rootfs.tar.gz $(BUILD_DIR)/images/openwrt-$(EXTRA_IMAGE_NAME)-initrd.img
ls -hs $(BUILD_DIR)/images


iso:
src/create-boot-iso.sh \
$(BUILD_DIR)/images/openwrt-$(EXTRA_IMAGE_NAME).iso \
"OpenWrt-$(EXTRA_IMAGE_NAME)" \
$(BUILD_DIR)/downloads/syslinux-6.03 \
$(BUILD_DIR)/images/openwrt-$(EXTRA_IMAGE_NAME)-kernel.bin \
$(BUILD_DIR)/images/openwrt-$(EXTRA_IMAGE_NAME)-initrd.img \
"consoleblank=600"


keys:
# Create persistent ssh host keys
mkdir -p src/rootfs/etc/dropbear
ssh-keygen -N "" -t rsa -b 2048 -f src/rootfs/etc/dropbear/dropbear_rsa_host_key
ssh-keygen -N "" -t ed25519 -b 256 -f src/rootfs/etc/dropbear/dropbear_ed25519_host_key


passwords:
# TODO: Password rotation


clean:
rm -rf build

231 changes: 231 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,231 @@
# openwrt-iscsi-target-ramdisk

## Overview

This simple project builds a preconfigured x86_64 OpenWrt ramdisk image that serves
your Linux kernels via PXE and disk drives via iSCSI, allowing you to boot
your OS over the network on another computer using Dracut `netroot=iscsi:`.

For example, you can run your laptop OS on your more powerful desktop while
still having access to all your laptop's files and programs.

Furthermore, you can customize the OpenWrt build with any additional features
you want.

![Usage Diagram](doc/overview.png)


## Usage

* Connect your two computers via Ethernet

* On the computer where this is installed (target), power on and select
`OpenWrt iSCSI Target` from the boot menu.

* Power on the other computer (initiator), use BIOS's built-in PXE boot

* The target OS should now be running on the initiator

If you want to share network/WiFi connections from the target to the initiator:

* Connect to <http://192.168.200.1> to access the OpenWrt Admin UI and
configure network routing / WiFi, etc.


## Requirements

* Linux

* Disable SecureBoot (see TODO below)

* PXE BIOS Boot


## Precautions

Try this out first with the VM images in `test`.

*!! Beta software - may prevent your computer from booting. Be comfortable with editing files in /boot,
and have a backup bootdisk/CD/USB in case anything goes wrong*

*!! Currently there is NO ENCRYPTION for the iSCSI endpoint. See TODO
below. For now, only run this on a trusted network with trusted hosts.*

* While running, treat disconnecting the network cable like unplugging your harddrive while your computer is
running. Some distributions seem better are recovering the connection than others. Changing the
settings of the network interface carrying the iSCSI traffic can have the same effect.

* On OpenWrt, use `logread -f` to keep an eye on the PXE boot progress.


## Installation

Review and adjust the configuration files in this project to match your system.

You'll mainly just want to update:

* [`src/rootfs/etc/uci-defaults/90-custom-bootentries`](src/rootfs/etc/uci-defaults/90-custom-bootentries)

* `boot_partition`

* Run `blkid` and replace the UUID with the one from your partition containing `/boot`

* `boot_path`

* Usually `/` if on its own partition, or `/boot/` if its on the main root filesystem.

* `cmdline_default`

* Find your default Kernel command line from your grub config or `/proc/cmdline`. Note, this is ignored when it can be read fully from `/boot/loader/entries` files.

* [`src/rootfs/etc/uci-defaults/90-custom-tgt`](src/rootfs/etc/uci-defaults/90-custom-tgt)

* Update the list of of block devices to share as iSCSI LUNs.


### Debian/Ubuntu

NOTE: This replaces `initramfs-tools` with `dracut`

```
sudo dependencies/debian/build.sh`
make images
sudo dependencies/debian/install.sh
sudo ./install.sh
```

### Fedora

```
sudo dependencies/fedora/build.sh
make images
sudo dependencies/fedora/install.sh
sudo ./install.sh
```

### Fedora Silverblue

```
dependencies/silverblue/build.sh
toolbox run --container openwrt-iscsi-target-build make images
sudo dependencies/silverblue/install.sh
sudo ./install.sh
```

### Arch

```
sudo dependencies/archlinux/build.sh
make images
sudo dependencies/archlinux/install.sh
sudo ./install.sh
```


## Updating

After the initial install, this solution should work indefinitely even as you upgrade your kernels.

If you want to make changes to the OpenWrt configuration, you will only need to update `/boot/openwrt-iscsi-target-kernel.bin` and `/boot/openwrt-iscsi-target-initrd.img` by doing the following:

```
make images
sudo ./update.sh
```


## How it works

Typical Linux distributions use a simple boot loader (e.g. GRUB) to load the Linux
Kernel and an [Initial ramdisk](https://en.wikipedia.org/wiki/Initial_ramdisk)
root file system. The purpose of this root filesystem is to do everything
necessary to prepare the storage block devices and mount the real root filesystem.
This provides the OS with great flexibility about how the root filesystem
is stored, for example on different types of network storage, logical volumes,
RAID arrays, encrypted filesystems etc. All the configuration and complexity is
handled by software in the Initial Ramdisk; all the kernel needs is a final
logical block device that it can pass to the filesystem layer for mounting the
root partition, and continue the init boot sequence.

Modern Linux distributions also use UUID-based partition identification in
/etc/fstab, which makes them work deterministically even when the names of
the underlying block devices change (e.g. /dev/sda, /dev/sdb ordering when
other drives or USB keys are inserted).

Here, we add standard iSCSI Initiator support to the Initial Ramdisk
using the Dracut iSCSI module, and supply the necessary kernel cmdline
paramaters to start it. During the Initial Ramdisk init sequence, it will
connect over the network to the iSCSI target, and those block devices
will appear as local block devices.

But from the target computer, we also need to share the kernel images and
disk drives over the network somehow. We can't use the original OS itself
to share them, as the filesystems can be only mounted and used by one computer
at a time, otherwise data corruption would occur.

This is where the OpenWrt ramdisk image comes in. This is a standalone mini-OS
with just enough functionality to share the drives via iSCSI and serve the
kernels via PXE. It is stateless and runs completely from RAM, so the iSCSI
initiator has exclusive read/write access to the drive.

The seperate OpenWrt system also means you don't have to reconfigure your OS to
do all this sharing, which can be complicated and interfere with regular
operation.

An advantage of this block-device based approach compared to NFS, etc. is that
the OS is totally agnostic to what is going on underneath the block devices,
and considers the iSCSI devices to be the same as the regular disk block
devices. In practice, this means you can do upgrades, kernel updates,
bootloader changes, etc. as if you were doing them on the original computer.
On modern systems, you should get full 1GB/s transfer speed and relatively low IOP
latency.

As modern Linux distributions are mostly plug and play, there should be little
issue with your OS seeing a completely different set of hardware.

When the PXE files are being prepared, discovering the kernels and initramfs images
is a bit tricky as the naming conventions and paths varies between distributions.
We parse every BootLoaderSpec spec files found and copy the referenced images.
Parsing grub config is more complicated in comparison, so in that case we simply
select the most recent kernel+initrd files and supply the default cmdline.


## Developing

Patches are welcome.

* Test with the sample VMs in [`test`](test) before opening a pull request.
* Match OpenWrt structure and conventions as much as possible


## TODO

* Uninstall script

* Debian: Disable default open-iscsi service by default during normal use to prevent error

* [MACSEC L2 encryption](https://developers.redhat.com/blog/2016/10/14/macsec-a-different-solution-to-encrypt-network-traffic/)

* SecureBoot. ([Unlikely?](https://forum.openwrt.org/t/x86-uefi-secure-boot-installation/115666)). Provide instructions for self-signed images with `mokutil`?

* UEFI PXE binaries

* Assign static IP to initiator's network interface instead of NetworkManager managed DHCP

* Sort "OpenWrt iSCSI Target" entry under OS entries in bootloader menu


## Reference

- [dracut.conf(5)](http://man7.org/linux/man-pages/man5/dracut.conf.5.html)
- [dracut.cmdline(7)](http://man7.org/linux/man-pages/man7/dracut.cmdline.7.html)
- [BootLoaderSpec](https://www.freedesktop.org/wiki/Specifications/BootLoaderSpec/)


## Author

Copyright (C) 2022 Joseph Mullally

License: [GPLv2](./LICENCE.txt)

Project: <https://github.com/jwmullally/openwrt-iscsi-target-ramdisk>
23 changes: 23 additions & 0 deletions dependencies/archlinux/build.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#!/bin/sh
set -ex

# https://openwrt.org/docs/guide-user/additional-software/imagebuilder#archmanjaro
pacman --sync --needed --noconfirm \
base-devel \
gawk \
gettext \
git \
libxslt \
ncurses \
openssl \
python \
unzip \
wget \
zlib



# ISO/USB building
pacman --sync --needed --noconfirm \
cdrkit \
syslinux
11 changes: 11 additions & 0 deletions dependencies/archlinux/install.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#!/bin/sh
set -ex

# Dracut iSCSI target support
pacman --sync --needed --noconfirm \
dracut \
open-iscsi

pacman --remove --recursive --cascade --nosave --noconfirm \
mkinitcpio \
|| echo "No package to remove"
27 changes: 27 additions & 0 deletions dependencies/debian/build.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#!/bin/sh
set -ex

# https://openwrt.org/docs/guide-user/additional-software/imagebuilder#debianubuntu
apt-get install --yes --no-install-recommends \
build-essential \
file \
gawk \
gettext \
git \
libncurses5-dev \
libncursesw5-dev \
libssl-dev \
python3 \
rsync \
unzip \
wget \
xsltproc \
zlib1g-dev


# ISO/USB building
apt-get install --yes --no-install-recommends \
ca-certificates \
curl \
genisoimage \
syslinux-utils
9 changes: 9 additions & 0 deletions dependencies/debian/install.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#!/bin/sh
set -ex

# Dracut iSCSI target support
# On Debian, this replaces the default initramfs-tools
apt-get install --yes --no-install-recommends \
dracut \
dracut-network \
open-iscsi
Loading

0 comments on commit 4a9b25a

Please sign in to comment.