Compare commits
1 Commits
d12d62621c
...
28e59a829c
| Author | SHA1 | Date |
|---|---|---|
|
|
28e59a829c | 7 years ago |
@ -0,0 +1,4 @@ |
||||
linux/build/ |
||||
files/initrd.img |
||||
files/vmlinuz |
||||
files/root.squashfs |
||||
@ -0,0 +1,45 @@ |
||||
FROM debian:buster |
||||
|
||||
RUN apt update |
||||
RUN apt install -y make debootstrap supervisor isc-dhcp-server tftpd-hpa busybox squashfs-tools |
||||
|
||||
# Downloads base Debian 10 to /linux/build/cache. The Makefile in linux/ would |
||||
# download base debian system to linux/build/cache itself, but to speed up |
||||
# rebuilding this Docker container the download is run separately to avoid |
||||
# repeating it in future builds. |
||||
ADD linux /linux |
||||
RUN mkdir -p linux/build && debootstrap buster linux/build/cache |
||||
|
||||
# Copies src/tftpboot directory to /tftpboot |
||||
ADD src/tftpboot /tftpboot |
||||
|
||||
# Downloads Syslinux and moves ldlinux.c32 and pxelinux.0 files to /tftpboot |
||||
RUN wget https://mirrors.edge.kernel.org/pub/linux/utils/boot/syslinux/syslinux-6.03.tar.gz && \ |
||||
tar -xzvf syslinux-6.03.tar.gz && \ |
||||
cp syslinux-6.03/bios/core/pxelinux.0 /tftpboot/pxelinux.0 && \ |
||||
cp syslinux-6.03/bios/com32/elflink/ldlinux/ldlinux.c32 /tftpboot/ldlinux.c32 |
||||
|
||||
# Builds Linux with its Makefile |
||||
RUN cd /linux && make |
||||
|
||||
# Creates and populates /tftpboot directory. Files stored there will be served |
||||
# by TFTP and HTTP (on port 8014). |
||||
RUN cp /linux/build/rootfs/initrd.img /tftpboot/initrd.img && \ |
||||
cp /linux/build/rootfs/vmlinuz /tftpboot/vmlinuz && \ |
||||
mksquashfs /linux/build/rootfs /tftpboot/root.squashfs |
||||
|
||||
# Supervisor's config |
||||
COPY src/supervisor.conf /supervisor.conf |
||||
|
||||
# DHCP server config |
||||
RUN mkdir -p /var/lib/dhcp && touch /var/lib/dhcp/dhcpd.leases |
||||
COPY src/dhcpd.conf /etc/dhcp/dhcpd.conf |
||||
|
||||
# The run.sh script setups networking and starts supervisor. |
||||
COPY src/run.sh /run.sh |
||||
RUN chmod +x /run.sh |
||||
|
||||
# Interface on which the server will be listening. Set with `-e INTERFACE=eth1` |
||||
ENV INTERFACE=eth0 |
||||
|
||||
CMD ["/run.sh"] |
||||
@ -0,0 +1,79 @@ |
||||
## Overview |
||||
|
||||
`hugo/images` is a portable, self contained system for distributing |
||||
disk images. |
||||
It allows to boot connected computers over the network and run a image distribution software on them. |
||||
|
||||
## Usage |
||||
|
||||
### Network setup |
||||
|
||||
This system is meant to be used in physically separated network, with all the computers (including the server) connected to a switch. |
||||
|
||||
### Building |
||||
|
||||
To build, just build the docker container from the `Dockerfile`, for example: |
||||
|
||||
``` |
||||
docker build . -t hugo/images |
||||
``` |
||||
|
||||
When run for the first time, the build might take some time because basic Debian image has to be downloaded. On subsequent builds the cache will be used making the build take much less time. |
||||
|
||||
### Running |
||||
|
||||
This container has some requirements to work properly: |
||||
|
||||
- `INTERFACE` environment variable should be set to an interface on which you want the server to operate. You can do this by appending `-e INETRFACE=whatever` to your `docker run` command. |
||||
|
||||
- The server has to be run in privileged mode, so `--privileged` flag has to be added to the command. |
||||
|
||||
- No network isolation of any kind should be enabled. You can connect the container directly to host's interface with `--net host` flag. |
||||
|
||||
An example run command might look like this: |
||||
|
||||
``` |
||||
docker run -e INETRFACE=eth42 --privileged --net host hugo/images |
||||
``` |
||||
|
||||
After starting the server, all PCs connected to the network, when in network boot mode, should boot specialised Linux for cloning and present the user with a menu. |
||||
|
||||
## Technical details |
||||
|
||||
The container runs three services: DHCP server, TFTP server and HTTP server (on port 8014, to avoid collisions with HTTP server that can be already running on host system). DHCP server is used to assign IP addresses and point to PXE binary which will continue the boot process, TFTP server is used, as specified in PXE standard, to send basic files needed to boot, and HTTP, as it is much faster than TFTP, is used to transfer whole root filesystem. |
||||
|
||||
### Boot process |
||||
|
||||
The boot process of a computer connected to the server consists of the following steps: |
||||
|
||||
1. The computer searches for DHCP server and asks for an IP address. The server responds (as configured in `src/dhcpd.conf`) with an IP address and the filename of PXE binary. |
||||
|
||||
2. The PXE binary is downloaded over TFTP. When run, it searches (by trying many names, including `pxelinux.cfg/default`) for PXE config file and downloads it over TFTP. |
||||
|
||||
3. This config file (`src/pxelinux.cfg/default`) contains filenames of the linux kernel and initial ramdisk, which are downloaded over TFTP. |
||||
|
||||
4. A network is configured in initrd and full filesystem (as squashfs) is downloaded over HTTP (port 8014) and mounted read-only as `/dev/loop0`. See `linux/src/initramfs-tools/scripts/local-top/download.sh` for more details. |
||||
|
||||
5. As specified in `linux/src/etc/fstab`, a `tmpfs` is mounted in `/tmp`. |
||||
|
||||
6. `root` is automatically logged in and it's default shell, the image distribution software (see `linux/scripts`), is started. |
||||
|
||||
### Linux |
||||
|
||||
The Linux system being server is based on minimal Debian 10 Buster and boots to a menu allowing to send/receive: |
||||
1. MBR of `/dev/sda` (raw copy of first 512 bytes, made by `dd`) |
||||
2. Partition table (in `sfdisk` format) |
||||
3. Image of `/dev/sda1` partition (in `partimage` format) |
||||
|
||||
Both sending and receiving is done with UDP broadcast with `udpsender`/`udpreceiver`. Any custom action can be performed with root shell. See `linux/scripts` for more details. |
||||
|
||||
### Transferring images to/from the server |
||||
|
||||
You can use `udpreceiver`/`udpsender` to receive or send data to/from your server. For example: |
||||
``` |
||||
udp-receiver --interface eth0 > save_it_here |
||||
``` |
||||
|
||||
``` |
||||
udp-sender --interface eth0 < file_to_send |
||||
``` |
||||
@ -1,8 +0,0 @@ |
||||
FROM debian:buster |
||||
RUN apt update && apt install -y isc-dhcp-server |
||||
RUN mkdir -p /var/lib/dhcp && touch /var/lib/dhcp/dhcpd.leases |
||||
COPY dhcpd.conf /etc/dhcp/dhcpd.conf |
||||
COPY run.sh /run.sh |
||||
RUN chmod +x /run.sh |
||||
ENV INTERFACE=eth0 |
||||
CMD ["/run.sh"] |
||||
@ -0,0 +1,41 @@ |
||||
CHROOT_CMD := chroot build/rootfs /bin/bash -c
|
||||
|
||||
FILES = $(shell find src/ -type f -print | sed -e 's|^src/|build/rootfs/|')
|
||||
FILES_SRC = $(shell find src/ -type f -print)
|
||||
|
||||
all: $(FILES) menu images build/rootfs/initrd.img |
||||
|
||||
clean: |
||||
rm -rf build/rootfs
|
||||
|
||||
menu: build/rootfs | build |
||||
$(CHROOT_CMD) "apt install dialog"
|
||||
$(CHROOT_CMD) "chmod +x /scripts/menu.sh"
|
||||
$(CHROOT_CMD) "usermod -s /scripts/menu.sh root"
|
||||
|
||||
images: build/rootfs | build |
||||
$(CHROOT_CMD) "apt install -y udpcast"
|
||||
$(CHROOT_CMD) "apt install -y partclone"
|
||||
$(CHROOT_CMD) "apt install -y lzop strace"
|
||||
$(CHROOT_CMD) "touch /etc/mtab"
|
||||
|
||||
build/rootfs/initrd.img: build/rootfs $(FILES) | build |
||||
chmod +x build/rootfs/etc/initramfs-tools/scripts/local-top/download.sh
|
||||
$(CHROOT_CMD) "apt update && apt install -y linux-image-4.19.0-*-amd64"
|
||||
$(CHROOT_CMD) "update-initramfs -c -kall"
|
||||
|
||||
$(FILES): $(FILES_SRC) build/rootfs | build |
||||
mkdir -p $(@D)
|
||||
cp $(shell echo $@ | sed -e 's|^build/rootfs/|src/|') $@
|
||||
|
||||
build/rootfs: build/cache | build |
||||
rm -rf build/rootfs
|
||||
cp -r build/cache build/rootfs
|
||||
rm -rf $(FILES)
|
||||
|
||||
build/cache: | build |
||||
rm -rf build/cache || true
|
||||
debootstrap buster build/cache
|
||||
|
||||
build: |
||||
mkdir -p build
|
||||
@ -0,0 +1,18 @@ |
||||
# Linux for cloning |
||||
|
||||
## About |
||||
`images/linux` is Debian 10 system adapted for network booting and sending/receiving disk images via UDP. It is designed to work together with `images/server`. |
||||
|
||||
On boot, full system image will be downloaded, mounted read only and will become new root (see `src/etc/initramfs-tools`). User will be presented with menu allowing sending/receiving images and root shell access (see `src/scripts`). Whole environment will be read-only, except `/tmp` where writeable `tmpfs` will be mounted. |
||||
|
||||
## Installation |
||||
To build just run: |
||||
``` |
||||
sudo make |
||||
``` |
||||
This will download base system to `build/cache`, copy it to `build/rootfs` and continue build there. After successful completion of this command, a fully functional linux root filesystem will be present in `build/rootfs`. |
||||
|
||||
`debootstrap` is required to download base Linux system. When more advanced (encrypted home etc.) partition setup is used, one might need to remount the partition on which the build is taking place with `exec` and `dev` before running `make`: |
||||
``` |
||||
sudo mount -i -o remount,exec,dev /home/whatever |
||||
``` |
||||
@ -0,0 +1 @@ |
||||
tmpfs /tmp tmpfs defaults,noatime,nosuid,nodev,noexec,mode=1777,size=256M 0 0 |
||||
@ -0,0 +1 @@ |
||||
squashfs |
||||
@ -0,0 +1,8 @@ |
||||
#!/bin/sh |
||||
. /scripts/functions |
||||
configure_networking |
||||
wget http://192.168.14.2:8014/root.squashfs |
||||
mkdir /mnt |
||||
modprobe loop |
||||
losetup /dev/loop0 |
||||
losetup -r /dev/loop0 /root.squashfs |
||||
@ -0,0 +1,2 @@ |
||||
[Login] |
||||
NAutoVTs=1 |
||||
@ -0,0 +1,58 @@ |
||||
# SPDX-License-Identifier: LGPL-2.1+ |
||||
# |
||||
# This file is part of systemd. |
||||
# |
||||
# systemd is free software; you can redistribute it and/or modify it |
||||
# under the terms of the GNU Lesser General Public License as published by |
||||
# the Free Software Foundation; either version 2.1 of the License, or |
||||
# (at your option) any later version. |
||||
|
||||
[Unit] |
||||
Description=Getty on %I |
||||
Documentation=man:agetty(8) man:systemd-getty-generator(8) |
||||
Documentation=http://0pointer.de/blog/projects/serial-console.html |
||||
After=systemd-user-sessions.service plymouth-quit-wait.service getty-pre.target |
||||
After=rc-local.service |
||||
|
||||
# If additional gettys are spawned during boot then we should make |
||||
# sure that this is synchronized before getty.target, even though |
||||
# getty.target didn't actually pull it in. |
||||
Before=getty.target |
||||
IgnoreOnIsolate=yes |
||||
|
||||
# IgnoreOnIsolate causes issues with sulogin, if someone isolates |
||||
# rescue.target or starts rescue.service from multi-user.target or |
||||
# graphical.target. |
||||
Conflicts=rescue.service |
||||
Before=rescue.service |
||||
|
||||
# On systems without virtual consoles, don't start any getty. Note |
||||
# that serial gettys are covered by serial-getty@.service, not this |
||||
# unit. |
||||
ConditionPathExists=/dev/tty0 |
||||
|
||||
[Service] |
||||
# the VT is cleared by TTYVTDisallocate |
||||
# The '-o' option value tells agetty to replace 'login' arguments with an |
||||
# option to preserve environment (-p), followed by '--' for safety, and then |
||||
# the entered username. |
||||
ExecStart=-/sbin/agetty --autologin root --noclear %I 38400 linux |
||||
Type=idle |
||||
Restart=always |
||||
RestartSec=0 |
||||
UtmpIdentifier=%I |
||||
TTYPath=/dev/%I |
||||
TTYReset=yes |
||||
TTYVHangup=yes |
||||
TTYVTDisallocate=yes |
||||
KillMode=process |
||||
IgnoreSIGPIPE=no |
||||
SendSIGHUP=yes |
||||
|
||||
# Unset locale for the console getty since the console has problems |
||||
# displaying some internationalized messages. |
||||
UnsetEnvironment=LANG LANGUAGE LC_CTYPE LC_NUMERIC LC_TIME LC_COLLATE LC_MONETARY LC_MESSAGES LC_PAPER LC_NAME LC_ADDRESS LC_TELEPHONE LC_MEASUREMENT LC_IDENTIFICATION |
||||
|
||||
[Install] |
||||
WantedBy=getty.target |
||||
DefaultInstance=tty1 |
||||
@ -0,0 +1,41 @@ |
||||
#!/bin/bash |
||||
|
||||
INTERFACE=eth0 |
||||
|
||||
RECEIVER="udp-receiver --interface $INTERFACE" |
||||
SENDER="udp-sender --interface $INTERFACE" |
||||
|
||||
pause () { |
||||
echo |
||||
read -p "Press [Enter] to continue..." |
||||
clear |
||||
} |
||||
|
||||
shell () { |
||||
clear |
||||
bash |
||||
} |
||||
|
||||
recv_dd () { |
||||
$RECEIVER | dd of=$1 bs=512 count=1 |
||||
} |
||||
|
||||
recv_sfd () { |
||||
$RECEIVER | sfdisk $1 |
||||
} |
||||
|
||||
recv_pc () { |
||||
$RECEIVER | lzop -d | partclone.extfs -r -s - -o $1 --logfile /tmp/partclone.log |
||||
} |
||||
|
||||
send_dd () { |
||||
dd if=$1 bs=512 count=1 | $SENDER |
||||
} |
||||
|
||||
send_pc () { |
||||
partclone.extfs -s $1 -c --logfile /tmp/partclone.log 2>/dev/null | lzop | $SENDER |
||||
} |
||||
|
||||
send_sfd () { |
||||
sfdisk -d $1 | $SENDER |
||||
} |
||||
@ -0,0 +1,31 @@ |
||||
#!/bin/bash |
||||
|
||||
dmesg -n 1 |
||||
|
||||
source $( dirname "${BASH_SOURCE[0]}" )/functions.sh |
||||
|
||||
dialog --menu "Welcome to hugo/images!" 20 80 10 \ |
||||
1 "Receive image for /dev/sda1 (partclone+lzop)" \ |
||||
2 "Receive partition table for /dev/sda (sfdisk)" \ |
||||
3 "Receive MBR for /dev/sda (dd)" \ |
||||
4 "Send /dev/sda1 image (partclone+lzop)" \ |
||||
5 "Send /dev/sda partition table (sfdisk)" \ |
||||
6 "Send /dev/sda MBR (dd)" \ |
||||
7 "Run root shell" \ |
||||
8 "Reboot" \ |
||||
9 "Poweroff" \ |
||||
2>/tmp/menu |
||||
|
||||
if [ "$?" != "0" ]; then CHOICE=7; else CHOICE=$(cat /tmp/menu); fi |
||||
|
||||
case $CHOICE in |
||||
1) recv_pc /dev/sda1; pause ;; |
||||
2) recv_sfd /dev/sda; pause ;; |
||||
3) recv_dd /dev/sda; pause ;; |
||||
4) send_pc /dev/sda1; pause ;; |
||||
5) send_sfd /dev/sda; pause ;; |
||||
6) send_dd /dev/sda; pause ;; |
||||
7) shell ;; |
||||
8) reboot ;; |
||||
9) poweroff ;; |
||||
esac |
||||
@ -1,6 +1,10 @@ |
||||
#!/bin/bash |
||||
set -e |
||||
|
||||
# Inteface setup |
||||
ip addr flush dev $INTERFACE |
||||
ip addr add 192.168.14.2/24 dev $INTERFACE |
||||
ip link set dev $INTERFACE up |
||||
dhcpd -d --no-pid $INTERFACE |
||||
|
||||
# Supervisor |
||||
supervisord -n -c /supervisor.conf |
||||
@ -0,0 +1,17 @@ |
||||
[supervisord] |
||||
loglevel = DEBUG |
||||
|
||||
[program:dhcp] |
||||
command = dhcpd -d --no-pid %(ENV_INTERFACE)s |
||||
autostart = true |
||||
autorestart = true |
||||
|
||||
[program:tftp] |
||||
command = in.tftpd -L -s /tftpboot |
||||
autostart = true |
||||
autorestart = true |
||||
|
||||
[program:http] |
||||
command = busybox httpd -f -p 8014 -h /tftpboot |
||||
autostart = true |
||||
autorestart = true |
||||
@ -0,0 +1,7 @@ |
||||
DEFAULT linux |
||||
|
||||
LABEL linux |
||||
kernel vmlinuz |
||||
append initrd=initrd.img root=/dev/loop0 net.ifnames=0 biosdevname=0 |
||||
PROMPT 0 |
||||
TIIMEOUT 0 |
||||
Loading…
Reference in new issue